Эх сурвалжийг харах

feat: 家庭天盘新增周/月/周报接口并对接前端

后端:
- MindFortuneController 新增 3 个接口:
  - POST /api/mind/fortune/week — 本周7天运势
  - POST /api/mind/fortune/month — 本月运势
  - POST /api/mind/fortune/reports — 历史周报列表
- FortuneService 新增 getFamilyFortuneBatch 批量查询

前端:
- family-dashboard.vue 三个 load 方法从 mock 改为真实接口
- api.js 新增 getWeekFortune/getMonthFortune/getFortuneReports
Sisyphus Agent 4 өдөр өмнө
parent
commit
e148dba06d

+ 93 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/mind/MindFortuneController.java

@@ -1,8 +1,10 @@
 package com.etotem.cfc.controller.mind;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.FamilyFortune;
 import com.etotem.cfc.entity.FamilyFortuneReport;
+import com.etotem.cfc.mapper.FamilyFortuneReportMapper;
 import com.etotem.cfc.service.FortuneService;
 import com.etotem.cfc.service.MembershipService;
 import com.etotem.cfc.service.PdfReportService;
@@ -13,7 +15,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.*;
 import javax.annotation.Resource;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @RestController
@@ -28,6 +32,9 @@ public class MindFortuneController {
 
     @Resource
     private MembershipService membershipService;
+
+    @Resource
+    private FamilyFortuneReportMapper familyFortuneReportMapper;
     
     /**
      * 获取今日家庭运势
@@ -147,4 +154,90 @@ public class MindFortuneController {
         result.put("weeklyTip", fortune.getWeeklyTip());
         return result;
     }
+
+    /**
+     * 获取本周运势数据(最近7天)
+     */
+    @PostMapping("/fortune/week")
+    public Result<List<Map<String, Object>>> getWeekFortune(@RequestAttribute("userId") Long userId) {
+        if (userId == null) return Result.error("请先登录");
+        Long familyId = membershipService.getOrCreateUserFamilyId(userId);
+        LocalDate today = LocalDate.now();
+        List<LocalDate> weekDates = new ArrayList<>();
+        for (int i = 0; i < 7; i++) {
+            weekDates.add(today.minusDays(i));
+        }
+        List<FamilyFortune> fortunes = fortuneService.getFamilyFortuneBatch(familyId, weekDates);
+        List<Map<String, Object>> result = new ArrayList<>();
+        for (FamilyFortune f : fortunes) {
+            Map<String, Object> item = new HashMap<>();
+            item.put("date", f.getFortuneDate().toString());
+            item.put("fortuneLevel", f.getFortuneLevel());
+            item.put("fortuneLevelText", f.getFortuneLevelText());
+            item.put("fortuneScore", f.getMoodScore());
+            item.put("dominantElement", f.getDominantElement());
+            item.put("luckyDirection", f.getLuckyDirection() != null ? f.getLuckyDirection().replace("方", "") : "东");
+            result.add(item);
+        }
+        result.sort((a, b) -> a.get("date").toString().compareTo(b.get("date").toString()));
+        return Result.success(result);
+    }
+
+    /**
+     * 获取本月运势数据
+     */
+    @PostMapping("/fortune/month")
+    public Result<List<Map<String, Object>>> getMonthFortune(@RequestAttribute("userId") Long userId) {
+        if (userId == null) return Result.error("请先登录");
+        Long familyId = membershipService.getOrCreateUserFamilyId(userId);
+        LocalDate today = LocalDate.now();
+        LocalDate firstDayOfMonth = today.withDayOfMonth(1);
+        int daysInMonth = today.lengthOfMonth();
+        List<LocalDate> monthDates = new ArrayList<>();
+        for (int i = 0; i < daysInMonth; i++) {
+            monthDates.add(firstDayOfMonth.plusDays(i));
+        }
+        List<FamilyFortune> fortunes = fortuneService.getFamilyFortuneBatch(familyId, monthDates);
+        List<Map<String, Object>> result = new ArrayList<>();
+        for (FamilyFortune f : fortunes) {
+            Map<String, Object> item = new HashMap<>();
+            item.put("fortuneDate", f.getFortuneDate().toString());
+            item.put("fortuneLevel", f.getFortuneLevel());
+            item.put("fortuneLevelText", f.getFortuneLevelText());
+            item.put("luckyDirection", f.getLuckyDirection() != null ? f.getLuckyDirection().replace("方", "") : "东");
+            item.put("dominantElement", f.getDominantElement());
+            result.add(item);
+        }
+        result.sort((a, b) -> a.get("fortuneDate").toString().compareTo(b.get("fortuneDate").toString()));
+        return Result.success(result);
+    }
+
+    /**
+     * 获取历史周报列表
+     */
+    @PostMapping("/fortune/reports")
+    public Result<List<Map<String, Object>>> getFortuneReports(@RequestAttribute("userId") Long userId) {
+        if (userId == null) return Result.error("请先登录");
+        Long familyId = membershipService.getUserFamilyId(userId);
+        if (familyId == null) return Result.noFamily("请先创建或加入家庭");
+        // 查询该家庭的历史周报
+        List<FamilyFortuneReport> reports = familyFortuneReportMapper.selectList(
+                new LambdaQueryWrapper<FamilyFortuneReport>()
+                        .eq(FamilyFortuneReport::getFamilyId, familyId.intValue())
+                        .orderByAsc(FamilyFortuneReport::getCreatedAt));
+        List<Map<String, Object>> result = new ArrayList<>();
+        java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
+        int weekNumber = 1;
+        for (FamilyFortuneReport r : reports) {
+            Map<String, Object> item = new HashMap<>();
+            item.put("weekNumber", weekNumber++);
+            String dateStr = r.getCreatedAt() != null ? sdf.format(r.getCreatedAt()) : "";
+            item.put("startDate", dateStr);
+            item.put("endDate", dateStr);
+            item.put("content", r.getElement() != null ? "主导五行:" + r.getElement() + ",吉位:" + r.getLuckyDirection() : "");
+            item.put("pdfUrl", r.getPdfPath() != null ? "/storage/reports/" + r.getPdfPath() : "");
+            result.add(item);
+        }
+        return Result.success(result);
+    }
 }

+ 11 - 2
cfc-backend/src/main/java/com/etotem/cfc/service/FortuneService.java

@@ -2,6 +2,7 @@ package com.etotem.cfc.service;
 
 import com.etotem.cfc.entity.FamilyFortune;
 import java.time.LocalDate;
+import java.util.List;
 
 public interface FortuneService {
     /**
@@ -11,14 +12,14 @@ public interface FortuneService {
      * @return 运势信息
      */
     FamilyFortune getFamilyFortune(Long familyId, LocalDate date);
-    
+
     /**
      * 创建或更新家庭运势信息
      * @param familyFortune 运势信息
      * @return 保存后的运势信息
      */
     FamilyFortune saveFamilyFortune(FamilyFortune familyFortune);
-    
+
     /**
      * 计算五行吉位(占位算法,待完善)
      * @param familyId 家庭ID
@@ -26,4 +27,12 @@ public interface FortuneService {
      * @return 吉位方向
      */
     String calculateLuckyDirection(Long familyId, LocalDate date);
+
+    /**
+     * 批量获取指定日期列表的运势信息
+     * @param familyId 家庭ID
+     * @param dates 日期列表
+     * @return 按日期排序的运势列表
+     */
+    List<FamilyFortune> getFamilyFortuneBatch(Long familyId, List<LocalDate> dates);
 }

+ 18 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/impl/FortuneServiceImpl.java

@@ -57,7 +57,24 @@ public class FortuneServiceImpl implements FortuneService {
         // 根据五行生克确定吉位
         return determineLuckyDirection(chineseDate, familyElements);
     }
-    
+
+    @Override
+    public List<FamilyFortune> getFamilyFortuneBatch(Long familyId, List<LocalDate> dates) {
+        if (familyId == null || dates == null || dates.isEmpty()) {
+            return Collections.emptyList();
+        }
+        dates = new ArrayList<>(dates);
+        dates.sort(LocalDate::compareTo);
+        List<FamilyFortune> result = new ArrayList<>(dates.size());
+        for (LocalDate date : dates) {
+            FamilyFortune fortune = getFamilyFortune(familyId, date);
+            if (fortune != null) {
+                result.add(fortune);
+            }
+        }
+        return result;
+    }
+
     /**
      * 获取日期的农历信息(简化实现)
      */

+ 25 - 7
cfc-frontend/pages/mind-detail/family-dashboard.vue

@@ -163,7 +163,7 @@
 <script>
 import MpHtml from '@/components/mp-html/mp-html.vue'
 import { parseDate } from '../../utils/format.js'
-import { getFamilyFortune } from '../../utils/api.js'
+import { getFamilyFortune, getWeekFortune, getMonthFortune, getFortuneReports } from '../../utils/api.js'
 /**
  * FamilyDashboard — 家庭天盘详情页
  * 包含:运势数据、能量趋势、历史周报、月度日历等
@@ -298,21 +298,39 @@ export default {
     },
     
     // 加载本周运势数据
-    // TODO: 后端实现 /api/mind/fortune/week 后对接
     loadWeekFortuneData() {
-      // 暂无后端接口,保持空数组(不展示随机假数据)
+      var self = this
+      getWeekFortune().then(function(res) {
+        if (res.code === 200 && res.data) {
+          self.weekFortunes = res.data
+        }
+      }).catch(function(e) {
+        console.error('加载本周运势失败', e)
+      })
     },
 
     // 加载本月运势数据
-    // TODO: 后端实现 /api/mind/fortune/month 后对接
     loadMonthlyFortuneData() {
-      // 暂无后端接口,保持空数组(不展示随机假数据)
+      var self = this
+      getMonthFortune().then(function(res) {
+        if (res.code === 200 && res.data) {
+          self.monthFortunes = res.data
+        }
+      }).catch(function(e) {
+        console.error('加载本月运势失败', e)
+      })
     },
 
     // 加载历史周报
-    // TODO: 后端实现 /api/mind/fortune/reports 后对接
     loadWeeklyReports() {
-      // 暂无后端接口,保持空数组(不展示随机假数据)
+      var self = this
+      getFortuneReports().then(function(res) {
+        if (res.code === 200 && res.data) {
+          self.weeklyReports = res.data
+        }
+      }).catch(function(e) {
+        console.error('加载历史周报失败', e)
+      })
     },
 
     // 初始化罗盘 Canvas

+ 3 - 0
cfc-frontend/utils/api.js

@@ -2321,6 +2321,9 @@ export const getChildNutritionProfile = (memberId) => request('/api/health/nutri
 
 // ===== 家庭天盘 =====
 export const getFamilyFortune = () => request('/api/mind/fortune', 'POST')
+export const getWeekFortune = () => request('/api/mind/fortune/week', 'POST')
+export const getMonthFortune = () => request('/api/mind/fortune/month', 'POST')
+export const getFortuneReports = () => request('/api/mind/fortune/reports', 'POST')
 export const getDeficiencyAnalysis = (memberId) => request('/api/health/nutrition/deficiency', 'POST', { memberId })
 export const getDeficiencySummary = (memberId) => request('/api/health/nutrition/deficiency-summary', 'POST', { memberId })
 export const getNutritionTrend = (memberId) => request('/api/health/nutrition/trend', 'POST', { memberId })