|
|
@@ -3,11 +3,14 @@ package com.etotem.cfc.controller;
|
|
|
import com.etotem.cfc.common.Result;
|
|
|
import com.etotem.cfc.entity.*;
|
|
|
import com.etotem.cfc.service.*;
|
|
|
+import com.etotem.cfc.service.DanReportParseService.DanParsedReport;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
@@ -30,6 +33,12 @@ public class DanAssessmentController {
|
|
|
@Resource
|
|
|
private com.etotem.cfc.mapper.DanAssessmentResultMapper danAssessmentResultMapper;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private DanReportParseService danReportParseService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private DimensionScoreService dimensionScoreService;
|
|
|
+
|
|
|
// ===== 测评材料 =====
|
|
|
|
|
|
@Operation(summary = "获取当前测评材料")
|
|
|
@@ -270,4 +279,150 @@ public class DanAssessmentController {
|
|
|
data.put("structuredAnalysis", result.getStructuredAnalysis());
|
|
|
return Result.success(data);
|
|
|
}
|
|
|
+
|
|
|
+ // ===== DAN 报告上传与解析 =====
|
|
|
+
|
|
|
+ @Operation(summary = "上传 DAN 报告并预览解析结果(Phase 1)")
|
|
|
+ @PostMapping("/report/parse-preview")
|
|
|
+ public Result<Map<String, Object>> parsePreview(
|
|
|
+ @RequestParam("file") MultipartFile file,
|
|
|
+ @RequestParam("dimension") String dimension,
|
|
|
+ @RequestParam("memberId") Long memberId,
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
+ if (file.isEmpty()) {
|
|
|
+ return Result.error("请选择文件");
|
|
|
+ }
|
|
|
+ if (!"mind".equals(dimension) && !"wisdom".equals(dimension)) {
|
|
|
+ return Result.error("dimension 必须为 mind 或 wisdom");
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ // 1. 解析 PDF
|
|
|
+ DanParsedReport parsed = danReportParseService.parse(file.getInputStream(), dimension);
|
|
|
+
|
|
|
+ // 2. 创建草稿记录(draft_status = pending)
|
|
|
+ DanAssessmentResult draft = new DanAssessmentResult();
|
|
|
+ draft.setChildName(""); // 前端预览后再填
|
|
|
+ draft.setAssessmentDate(new java.util.Date());
|
|
|
+ draft.setStructuredAnalysis(parsedToJson(parsed));
|
|
|
+ draft.setDimension(dimension);
|
|
|
+ draft.setReportFileId(file.getOriginalFilename());
|
|
|
+ draft.setDraftStatus("pending");
|
|
|
+ draft.setCreatedAt(new java.util.Date());
|
|
|
+ draft.setUpdatedAt(new java.util.Date());
|
|
|
+ danAssessmentResultMapper.insert(draft);
|
|
|
+
|
|
|
+ // 3. 返回预览数据
|
|
|
+ Map<String, Object> data = new java.util.LinkedHashMap<>();
|
|
|
+ data.put("draftId", draft.getId());
|
|
|
+ data.put("dimension", dimension);
|
|
|
+ data.put("summary", parsed.getSummary() != null ? parsed.getSummary()
|
|
|
+ : (parsed.getItems() != null ? parsed.getItems().size() + " 项数据" : ""));
|
|
|
+ data.put("items", parsed.getItems() != null
|
|
|
+ ? parsed.getItems().stream().map(item -> {
|
|
|
+ Map<String, Object> m = new java.util.LinkedHashMap<>();
|
|
|
+ m.put("code", item.getCode());
|
|
|
+ m.put("name", item.getName());
|
|
|
+ m.put("value", item.getValue());
|
|
|
+ m.put("category", item.getCategory());
|
|
|
+ return m;
|
|
|
+ }).collect(java.util.stream.Collectors.toList())
|
|
|
+ : java.util.Collections.emptyList());
|
|
|
+ data.put("suggestions", parsed.getSuggestions());
|
|
|
+ data.put("message", "解析完成,请确认数据准确性");
|
|
|
+ return Result.success(data);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return Result.error("解析失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Operation(summary = "确认并保存 DAN 报告(Phase 2)")
|
|
|
+ @PostMapping("/report/confirm")
|
|
|
+ public Result<String> confirmReport(@RequestBody Map<String, Object> body) {
|
|
|
+ Long draftId = body.get("draftId") != null ? Long.valueOf(body.get("draftId").toString()) : null;
|
|
|
+ String childName = (String) body.get("childName");
|
|
|
+ String assessmentDateStr = (String) body.get("assessmentDate");
|
|
|
+ String dimension = (String) body.get("dimension");
|
|
|
+ String structuredAnalysis = (String) body.get("structuredAnalysis");
|
|
|
+ Long memberId = body.get("memberId") != null ? Long.valueOf(body.get("memberId").toString()) : null;
|
|
|
+
|
|
|
+ if (draftId == null || memberId == null) {
|
|
|
+ return Result.error("draftId 和 memberId 不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ DanAssessmentResult draft = danAssessmentResultMapper.selectById(draftId);
|
|
|
+ if (draft == null) {
|
|
|
+ return Result.error("草稿不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新草稿信息
|
|
|
+ if (childName != null && !childName.isEmpty()) {
|
|
|
+ draft.setChildName(childName);
|
|
|
+ }
|
|
|
+ if (assessmentDateStr != null && !assessmentDateStr.isEmpty()) {
|
|
|
+ try {
|
|
|
+ draft.setAssessmentDate(new java.text.SimpleDateFormat("yyyy-MM-dd").parse(assessmentDateStr));
|
|
|
+ } catch (Exception ignored) {}
|
|
|
+ }
|
|
|
+ if (structuredAnalysis != null && !structuredAnalysis.isEmpty()) {
|
|
|
+ draft.setStructuredAnalysis(structuredAnalysis);
|
|
|
+ }
|
|
|
+ if (dimension != null) {
|
|
|
+ draft.setDimension(dimension);
|
|
|
+ }
|
|
|
+ draft.setDraftStatus("confirmed");
|
|
|
+ draft.setUpdatedAt(new java.util.Date());
|
|
|
+ danAssessmentResultMapper.updateById(draft);
|
|
|
+
|
|
|
+ // 更新维度分数
|
|
|
+ dimensionScoreService.refreshFromDanAssessment(memberId, draftId,
|
|
|
+ dimension != null ? dimension : draft.getDimension());
|
|
|
+
|
|
|
+ return Result.success("报告已确认并保存");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Operation(summary = "获取某孩子某维度的 DAN 报告列表")
|
|
|
+ @PostMapping("/report/child")
|
|
|
+ public Result<List<DanAssessmentResult>> getChildDanReports(@RequestBody Map<String, Object> body) {
|
|
|
+ Long memberId = body.get("memberId") != null ? Long.valueOf(body.get("memberId").toString()) : null;
|
|
|
+ String dimension = (String) body.get("dimension");
|
|
|
+ if (memberId == null || dimension == null) {
|
|
|
+ return Result.error("memberId 和 dimension 不能为空");
|
|
|
+ }
|
|
|
+ List<DanAssessmentResult> list = danAssessmentResultMapper.selectList(
|
|
|
+ new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<DanAssessmentResult>()
|
|
|
+ .eq(DanAssessmentResult::getDimension, dimension)
|
|
|
+ .eq(DanAssessmentResult::getDraftStatus, "confirmed")
|
|
|
+ .orderByDesc(DanAssessmentResult::getCreatedAt));
|
|
|
+ return Result.success(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将 DanParsedReport 序列化为 JSON 字符串
|
|
|
+ */
|
|
|
+ private String parsedToJson(DanParsedReport parsed) {
|
|
|
+ try {
|
|
|
+ return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(parsed);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // fallback: 手动构建简单 JSON
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("{\"reportType\":\"").append(parsed.getReportType()).append("\",");
|
|
|
+ sb.append("\"dimension\":\"").append(parsed.getDimension()).append("\",");
|
|
|
+ sb.append("\"items\":[");
|
|
|
+ if (parsed.getItems() != null) {
|
|
|
+ boolean first = true;
|
|
|
+ for (DanReportParseService.DataItem item : parsed.getItems()) {
|
|
|
+ if (!first) sb.append(",");
|
|
|
+ sb.append("{\"code\":\"").append(item.getCode()).append("\",");
|
|
|
+ sb.append("\"name\":\"").append(item.getName()).append("\",");
|
|
|
+ sb.append("\"value\":\"").append(item.getValue()).append("\",");
|
|
|
+ sb.append("\"category\":\"").append(item.getCategory()).append("\"}");
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append("],");
|
|
|
+ sb.append("\"summary\":\"").append(parsed.getSummary() != null ? parsed.getSummary().replace("\"", "\\\"") : "").append("\",");
|
|
|
+ sb.append("\"suggestions\":\"").append(parsed.getSuggestions() != null ? parsed.getSuggestions().replace("\"", "\\\"") : "").append("\"}");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|