Просмотр исходного кода

feat(self-check): 新增 /generate-plan + /trend-analysis 接口 + HealthPlanService.generateFromSelfCheck

iwt 2 месяцев назад
Родитель
Сommit
d7a0a9a818

+ 33 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/family/FiveDimensionSelfCheckController.java

@@ -41,6 +41,9 @@ public class FiveDimensionSelfCheckController {
     @Resource
     private UserMapper userMapper;
 
+    @Resource
+    private com.etotem.cfc.service.HealthPlanService healthPlanService;
+
     @Operation(summary = "获取自检问卷题目(首次固定 15 题;再次合并题库随机抽)")
     @PostMapping("/questions")
     public Result<List<SelfCheckQuestionVO>> getQuestions(
@@ -132,4 +135,34 @@ public class FiveDimensionSelfCheckController {
                 new Date(ignoredAt.getTime() + 15L * 24 * 60 * 60 * 1000)));
         return Result.success(resp);
     }
+
+    @Operation(summary = "用户点击生成健康计划(基于自检低分维度)")
+    @PostMapping("/generate-plan")
+    public Result<Map<String, Object>> generatePlan(
+            @RequestBody(required = false) Map<String, Object> body,
+            @RequestAttribute("userId") Long userId) {
+        if (userId == null) return Result.error("请先登录");
+        Long checkId = body != null ? com.etotem.cfc.util.ParamUtils.getLong(body.get("checkId")) : null;
+        try {
+            Long planId = healthPlanService.generateFromSelfCheck(userId, checkId);
+            if (planId == null) return Result.error("计划生成失败,请稍后重试");
+            Map<String, Object> resp = new HashMap<>();
+            resp.put("planId", planId);
+            resp.put("status", "draft");
+            return Result.success(resp);
+        } catch (Exception e) {
+            return Result.error("计划生成失败");
+        }
+    }
+
+    @Operation(summary = "获取自检历史趋势分析")
+    @PostMapping("/trend-analysis")
+    public Result<Map<String, Object>> getTrendAnalysis(@RequestAttribute("userId") Long userId) {
+        if (userId == null) return Result.error("请先登录");
+        try {
+            return Result.success(selfCheckService.getTrendAnalysis(userId));
+        } catch (Exception e) {
+            return Result.error("趋势分析失败");
+        }
+    }
 }

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java

@@ -35,4 +35,7 @@ public interface HealthPlanService {
 
     /** 查询方案分解后的任务列表(source_type=plan, source_id=planId) */
     List<Task> getPlanTasks(Long planId);
+
+    /** 基于自检低分维度自动生成 draft 计划 */
+    Long generateFromSelfCheck(Long userId, Long checkId);
 }

+ 53 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/impl/HealthPlanServiceImpl.java

@@ -3,12 +3,14 @@ package com.etotem.cfc.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.entity.FamilyMember;
+import com.etotem.cfc.entity.FiveDimensionSelfCheck;
 import com.etotem.cfc.entity.HealthPlan;
 import com.etotem.cfc.entity.Task;
 import com.etotem.cfc.entity.Family;
 import com.etotem.cfc.entity.User;
 import com.etotem.cfc.mapper.FamilyMapper;
 import com.etotem.cfc.mapper.FamilyMemberMapper;
+import com.etotem.cfc.mapper.FiveDimensionSelfCheckMapper;
 import com.etotem.cfc.mapper.HealthPlanMapper;
 import com.etotem.cfc.mapper.TaskMapper;
 import com.etotem.cfc.mapper.UserMapper;
@@ -24,6 +26,7 @@ import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -53,6 +56,12 @@ public class HealthPlanServiceImpl implements HealthPlanService {
     @Resource
     private FamilyMemberMapper familyMemberMapper;
 
+    @Resource
+    private FiveDimensionSelfCheckMapper selfCheckMapper;
+
+    @Resource
+    private com.etotem.cfc.service.AiGateway aiGateway;
+
     @Value("${langgraph.base-url:}")
     private String langgraphBaseUrl;
 
@@ -754,6 +763,50 @@ public class HealthPlanServiceImpl implements HealthPlanService {
         return result;
     }
 
+    @Override
+    public Long generateFromSelfCheck(Long userId, Long checkId) {
+        try {
+            FiveDimensionSelfCheck check;
+            if (checkId != null) {
+                check = selfCheckMapper.selectById(checkId);
+            } else {
+                check = selfCheckMapper.selectOne(
+                        new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<FiveDimensionSelfCheck>()
+                                .eq(FiveDimensionSelfCheck::getUserId, userId)
+                                .orderByDesc(FiveDimensionSelfCheck::getCreatedAt)
+                                .last("LIMIT 1"));
+            }
+            if (check == null) return null;
+            Map<String, Integer> scores = objectMapper.readValue(check.getScoresJson(),
+                    new com.fasterxml.jackson.core.type.TypeReference<Map<String, Integer>>() {});
+            List<String> lowDims = new ArrayList<>();
+            for (Map.Entry<String, Integer> e : scores.entrySet()) {
+                if (e.getValue() != null && e.getValue() <= 6) lowDims.add(e.getKey());
+            }
+            if (lowDims.isEmpty()) return null;
+            User user = userMapper.selectById(userId);
+            if (user == null || user.getFamilyId() == null) return null;
+            Map<String, Object> inputs = new LinkedHashMap<>();
+            inputs.put("familyId", user.getFamilyId());
+            inputs.put("dimensions", String.join(",", lowDims));
+            inputs.put("goal", "基于五维自检结果,针对低分维度制定改善计划");
+            String planContent = aiGateway.generateHealthPlan(inputs);
+            if (planContent == null) return null;
+            HealthPlan plan = new HealthPlan();
+            plan.setFamilyId(user.getFamilyId());
+            plan.setDimensions(String.join(",", lowDims));
+            plan.setGoal("五维自检自动生成");
+            plan.setPlanContent(planContent);
+            plan.setStatus("draft");
+            plan.setCreatedAt(new Date());
+            healthPlanMapper.insert(plan);
+            return plan.getId();
+        } catch (Exception e) {
+            log.warn("generateFromSelfCheck 失败: {}", e.getMessage());
+            return null;
+        }
+    }
+
     @Override
     public Long autoGenerateAndPublish(Long familyId, Long memberId, String reportType) {
         if (familyId == null || memberId == null) {