Browse Source

feat(self-check): 通过 AiGateway 接入智能出题能力,新增 POST /api/family/self-check/ai-generate-questions

- 移除裸 RestTemplate 服务 LangGraphService.java(未跟踪 WIP,3 处缺陷 + 架构冲突)
- AiGateway 新增 generateSelfCheckQuestions(Map) 方法,调 /api/v1/question-generate
- FiveDimensionSelfCheckController 新增 ai-generate-questions 路由,返回 Result<List<SelfCheckQuestionVO>>
- questions 列表的 options 字段暂不映射,待后续补全

解除了本地 mvn clean compile 因 LangGraphService 导致的构建中断
Sisyphus Agent 3 days ago
parent
commit
1baff166d8

+ 40 - 1
cfc-backend/src/main/java/com/etotem/cfc/controller/family/FiveDimensionSelfCheckController.java

@@ -6,6 +6,7 @@ import com.etotem.cfc.dto.SelfCheckResultVO;
 import com.etotem.cfc.dto.SubmitSelfCheckDTO;
 import com.etotem.cfc.entity.User;
 import com.etotem.cfc.mapper.UserMapper;
+import com.etotem.cfc.service.AiGateway;
 import com.etotem.cfc.service.FiveDimensionSelfCheckService;
 import com.etotem.cfc.service.UnlockGateService;
 import com.etotem.cfc.util.ParamUtils;
@@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -44,6 +46,9 @@ public class FiveDimensionSelfCheckController {
     @Resource
     private com.etotem.cfc.service.HealthPlanService healthPlanService;
 
+    @Resource
+    private AiGateway aiGateway;
+
     @Operation(summary = "获取自检问卷题目(首次固定 15 题;再次合并题库随机抽)")
     @PostMapping("/questions")
     public Result<List<SelfCheckQuestionVO>> getQuestions(
@@ -68,7 +73,7 @@ public class FiveDimensionSelfCheckController {
         }
         try {
             SelfCheckResultVO vo = selfCheckService.submitSelfCheck(dto, userId);
-            // 触发通关解锁刷新:自检通过 → 解锁个人沙盘
+
             User user = userMapper.selectById(userId);
             if (user != null) {
                 unlockGateService.refreshPersonalGates(userId, user.getFamilyId());
@@ -165,4 +170,38 @@ public class FiveDimensionSelfCheckController {
             return Result.error("趋势分析失败");
         }
     }
+
+    @Operation(summary = "AI生成五维自检题目(智能出题)")
+    @PostMapping("/ai-generate-questions")
+    public Result<List<SelfCheckQuestionVO>> aiGenerateQuestions(
+            @RequestAttribute("userId") Long userId,
+            @RequestBody(required = false) Map<String, Object> body) {
+        if (userId == null) return Result.error("请先登录");
+        try {
+            Map<String, Object> inputs = new HashMap<>();
+            inputs.put("user_id", userId);
+            if (body != null) {
+                if (body.get("count") != null) inputs.put("count", body.get("count"));
+                if (body.get("difficulty") != null) inputs.put("difficulty", body.get("difficulty"));
+                if (body.get("dimensions") != null) inputs.put("dimensions", body.get("dimensions"));
+                if (body.get("exclude_ids") != null) inputs.put("exclude_ids", body.get("exclude_ids"));
+            }
+            List<Map<String, Object>> rawQuestions = aiGateway.generateSelfCheckQuestions(inputs);
+            List<SelfCheckQuestionVO> questions = new ArrayList<>();
+            if (rawQuestions != null) {
+                for (Map<String, Object> q : rawQuestions) {
+                    SelfCheckQuestionVO vo = new SelfCheckQuestionVO();
+                    if (q.get("id") != null) vo.setId(Integer.parseInt(q.get("id").toString()));
+                    if (q.get("dimension") != null) vo.setDimension(q.get("dimension").toString());
+                    if (q.get("sub_dim") != null) vo.setSubDim(q.get("sub_dim").toString());
+                    if (q.get("text") != null) vo.setText(q.get("text").toString());
+                    // options 暂不映射,请根据实际返回格式补充
+                    questions.add(vo);
+                }
+            }
+            return Result.success(questions);
+        } catch (Exception e) {
+            return Result.error("AI生成题目失败: " + e.getMessage());
+        }
+    }
 }

+ 30 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/AiGateway.java

@@ -364,6 +364,36 @@ public class AiGateway {
         }
     }
 
+    /**
+     * 生成五维自检题目(调用 LangGraph question_generation_graph)
+     * @param inputs 含 user_id(int)、count、difficulty、dimensions、exclude_ids
+     * @return 题目列表(每项含 id/dimension/text/options/sub_dim);失败返回 null(调用方决定 fallback)
+     */
+    public List<Map<String, Object>> generateSelfCheckQuestions(Map<String, Object> inputs) {
+        if (!enabled || isCircuitOpen()) return null;
+        try {
+            ObjectNode body = objectMapper.valueToTree(inputs);
+            HttpEntity<String> entity = new HttpEntity<>(body.toString(), createJsonHeaders());
+            String url = baseUrl + "/api/v1/question-generate";
+            ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
+            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
+                JsonNode root = objectMapper.readTree(response.getBody());
+                JsonNode questions = root.get("questions");
+                if (questions != null && questions.isArray() && questions.size() > 0) {
+                    List<Map<String, Object>> list = objectMapper.convertValue(questions, List.class);
+                    consecutiveFailures.set(0);
+                    log.debug("AiGateway generateSelfCheckQuestions 成功: {} 题", list.size());
+                    return list;
+                }
+            }
+            return null;
+        } catch (Exception e) {
+            log.warn("AiGateway generateSelfCheckQuestions 调用失败: {}", e.getMessage());
+            recordFailure();
+            return null;
+        }
+    }
+
     private List<Map<String, Object>> parseTasks(JsonNode tasksNode) {
         List<Map<String, Object>> tasks = new ArrayList<>();
         if (tasksNode != null && tasksNode.isArray()) {