|
|
@@ -6,6 +6,7 @@ import com.etotem.cfc.dto.ParsedDiseaseRisk;
|
|
|
import com.etotem.cfc.dto.ParsedGutFlora;
|
|
|
import com.etotem.cfc.dto.ParsedIndicator;
|
|
|
import com.etotem.cfc.dto.ParsedFoodSuitability;
|
|
|
+import com.etotem.cfc.dto.ParsedReportPayload;
|
|
|
import com.etotem.cfc.dto.ParsedReportResult;
|
|
|
import java.math.BigDecimal;
|
|
|
import com.etotem.cfc.entity.Food;
|
|
|
@@ -13,16 +14,18 @@ import com.etotem.cfc.entity.HealthDiseaseRisk;
|
|
|
import com.etotem.cfc.entity.HealthGutFlora;
|
|
|
import com.etotem.cfc.entity.HealthIndicator;
|
|
|
import com.etotem.cfc.entity.HealthReport;
|
|
|
+import com.etotem.cfc.entity.HealthReportDraft;
|
|
|
import com.etotem.cfc.entity.NutritionDeficiencyRecord;
|
|
|
import com.etotem.cfc.entity.NutritionIndicatorMapping;
|
|
|
-import com.etotem.cfc.mapper.FoodMapper;
|
|
|
import com.etotem.cfc.service.ChildNutritionProfileService;
|
|
|
import com.etotem.cfc.service.DimensionScoreService;
|
|
|
import com.etotem.cfc.service.FoodService;
|
|
|
import com.etotem.cfc.service.HealthAnalysisService;
|
|
|
+import com.etotem.cfc.service.HealthReportDraftService;
|
|
|
import com.etotem.cfc.service.HealthReportService;
|
|
|
import com.etotem.cfc.service.NutritionDeficiencyService;
|
|
|
import com.etotem.cfc.service.PdfParseService;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
@@ -37,6 +40,7 @@ import java.nio.file.Path;
|
|
|
import java.nio.file.Paths;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Date;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.UUID;
|
|
|
@@ -66,10 +70,13 @@ public class HealthReportController {
|
|
|
private HealthAnalysisService healthAnalysisService;
|
|
|
|
|
|
@Resource
|
|
|
- private FoodMapper foodMapper;
|
|
|
+ private FoodService foodService;
|
|
|
|
|
|
@Resource
|
|
|
- private FoodService foodService;
|
|
|
+ private HealthReportDraftService healthReportDraftService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ObjectMapper objectMapper;
|
|
|
|
|
|
@Resource
|
|
|
private DimensionScoreService dimensionScoreService;
|
|
|
@@ -233,10 +240,9 @@ public class HealthReportController {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 上传并解析肠道菌群报告PDF
|
|
|
- * 流程: 上传 → 解析 → 保存报告(暂属上传人) → 自动匹配成员 → 前端确认/选择
|
|
|
+ * 上传并解析肠道菌群报告PDF(向后兼容 — 内部自动执行两阶段:解析→确认)
|
|
|
*/
|
|
|
- @Operation(summary = "上传并解析肠道菌群报告PDF")
|
|
|
+ @Operation(summary = "上传并解析肠道菌群报告PDF(兼容接口,自动确认)")
|
|
|
@PostMapping("/report/upload")
|
|
|
public Result<Map<String, Object>> uploadReport(
|
|
|
@RequestParam("file") MultipartFile file,
|
|
|
@@ -247,7 +253,6 @@ public class HealthReportController {
|
|
|
return Result.error("文件不能为空");
|
|
|
}
|
|
|
|
|
|
- // 验证文件类型
|
|
|
String contentType = file.getContentType();
|
|
|
String originalFilename = file.getOriginalFilename();
|
|
|
if (originalFilename == null || (!originalFilename.toLowerCase().endsWith(".pdf")
|
|
|
@@ -256,168 +261,235 @@ public class HealthReportController {
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- // 1. 解析PDF
|
|
|
ParsedReportResult parsed = pdfParseService.parse(file.getInputStream());
|
|
|
if (parsed.getOverallScore() == null && parsed.getGutHealthScore() == null) {
|
|
|
return Result.error("无法解析PDF文件,请确认是募极生物肠道菌群报告");
|
|
|
}
|
|
|
|
|
|
- // 2. 保存文件到本地存储(用上传人ID分目录)
|
|
|
String fileUrl = saveUploadFile(file, userId);
|
|
|
|
|
|
- // 3. 构建健康报告实体
|
|
|
- HealthReport report = new HealthReport();
|
|
|
- report.setUserId(userId); // 上传人/创建人
|
|
|
- report.setFamilyId(familyId);
|
|
|
- report.setReportType("gut_flora");
|
|
|
- // 报告日期:优先使用PDF提取的日期
|
|
|
- if (parsed.getReportDate() != null && !parsed.getReportDate().isEmpty()) {
|
|
|
- try {
|
|
|
- java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
- report.setReportDate(sdf.parse(parsed.getReportDate()));
|
|
|
- } catch (Exception e) {
|
|
|
- report.setReportDate(new Date());
|
|
|
- }
|
|
|
- } else {
|
|
|
- report.setReportDate(new Date());
|
|
|
- }
|
|
|
- report.setReportNumber(parsed.getReportNumber());
|
|
|
- report.setPersonName(parsed.getPersonName());
|
|
|
- report.setOverallScore(parsed.getOverallScore());
|
|
|
- report.setGutHealthScore(parsed.getGutHealthScore());
|
|
|
- report.setChronicDiseaseScore(parsed.getChronicDiseaseScore());
|
|
|
- report.setNutritionScore(parsed.getNutritionScore());
|
|
|
- report.setBalanceScore(parsed.getBalanceScore());
|
|
|
- report.setDiversityScore(parsed.getDiversityScore());
|
|
|
- report.setBeneficialScore(parsed.getBeneficialScore());
|
|
|
- report.setHarmfulScore(parsed.getHarmfulScore());
|
|
|
- report.setCoreGenusScore(parsed.getCoreGenusScore());
|
|
|
- report.setGutAge(parsed.getGutAge());
|
|
|
- report.setGutType(parsed.getGutType());
|
|
|
- report.setFileUrl(fileUrl);
|
|
|
- report.setRemark("募极生物肠道菌群检测报告");
|
|
|
-
|
|
|
- // 4. 转换指标列表
|
|
|
- List<HealthIndicator> indicators = new ArrayList<>();
|
|
|
- if (parsed.getIndicators() != null) {
|
|
|
- for (int i = 0; i < parsed.getIndicators().size(); i++) {
|
|
|
- ParsedIndicator pi = parsed.getIndicators().get(i);
|
|
|
- HealthIndicator ind = new HealthIndicator();
|
|
|
- ind.setCategory(pi.getCategory());
|
|
|
- ind.setIndicatorName(pi.getIndicatorName());
|
|
|
- ind.setIndicatorValue(pi.getIndicatorValue());
|
|
|
- ind.setStatus(pi.getStatus());
|
|
|
- ind.setRefRange(pi.getRefRange());
|
|
|
- ind.setSymptoms(pi.getSymptoms());
|
|
|
- ind.setSortOrder(i);
|
|
|
- indicators.add(ind);
|
|
|
- }
|
|
|
- }
|
|
|
+ ParsedReportPayload.Payload payload = convertToPayload(parsed);
|
|
|
+ String payloadJson = objectMapper.writeValueAsString(payload);
|
|
|
|
|
|
- // 5. 转换菌群数据(核心菌属 + 有害菌属)
|
|
|
- List<HealthGutFlora> gutFloraList = new ArrayList<>();
|
|
|
- if (parsed.getGutFlora() != null) {
|
|
|
- for (ParsedGutFlora pgf : parsed.getGutFlora()) {
|
|
|
- HealthGutFlora gf = new HealthGutFlora();
|
|
|
- gf.setBacteriaName(pgf.getBacteriaName());
|
|
|
- gf.setBacteriaValue(pgf.getBacteriaValue());
|
|
|
- gf.setNormalRange(pgf.getNormalRange());
|
|
|
- gf.setPopulationLevel(pgf.getPopulationLevel());
|
|
|
- gf.setDetectionRate(pgf.getDetectionRate());
|
|
|
- gf.setDescription(pgf.getDescription());
|
|
|
- gf.setCategory(pgf.getCategory());
|
|
|
- gf.setLevel(pgf.getLevel());
|
|
|
- gutFloraList.add(gf);
|
|
|
- }
|
|
|
+ HealthReportDraft draft = healthReportDraftService.createDraft(
|
|
|
+ userId, familyId, "gut_flora", fileUrl, originalFilename, payloadJson);
|
|
|
+
|
|
|
+ Long matchedMemberId = null;
|
|
|
+ String matchedMemberName = null;
|
|
|
+ if (familyId != null && parsed.getPersonName() != null) {
|
|
|
+ matchedMemberId = healthReportService.matchFamilyMemberByName(parsed.getPersonName(), familyId);
|
|
|
}
|
|
|
|
|
|
- // 6. 转换益生菌(level=SPECIES)
|
|
|
- if (parsed.getProbioticSpecies() != null) {
|
|
|
- for (ParsedGutFlora pgf : parsed.getProbioticSpecies()) {
|
|
|
- HealthGutFlora gf = new HealthGutFlora();
|
|
|
- gf.setBacteriaName(pgf.getBacteriaName());
|
|
|
- gf.setBacteriaValue(pgf.getBacteriaValue());
|
|
|
- gf.setPopulationLevel(pgf.getPopulationLevel());
|
|
|
- gf.setDetectionRate(pgf.getDetectionRate());
|
|
|
- gf.setCategory(pgf.getCategory());
|
|
|
- gf.setLevel(pgf.getLevel());
|
|
|
- gutFloraList.add(gf);
|
|
|
- }
|
|
|
+ HealthReport report = buildReportFromPayload(draft, payload, matchedMemberId);
|
|
|
+ List<HealthIndicator> indicators = buildIndicatorsFromPayload(payload);
|
|
|
+ List<HealthGutFlora> gutFlora = buildGutFloraFromPayload(payload);
|
|
|
+ List<HealthDiseaseRisk> diseaseRisks = buildDiseaseRisksFromPayload(payload);
|
|
|
+
|
|
|
+ HealthReport created = healthReportService.createReport(report, indicators, gutFlora, diseaseRisks);
|
|
|
+
|
|
|
+ if (payload.getFoods() != null && !payload.getFoods().isEmpty()) {
|
|
|
+ List<Food> foods = buildFoodsFromPayload(payload);
|
|
|
+ foodService.saveFoodsForReport(created.getId(), foods);
|
|
|
}
|
|
|
|
|
|
- // 7. 转换疾病风险评估
|
|
|
- List<HealthDiseaseRisk> diseaseRiskList = new ArrayList<>();
|
|
|
- if (parsed.getDiseaseRisks() != null) {
|
|
|
- for (ParsedDiseaseRisk pdr : parsed.getDiseaseRisks()) {
|
|
|
- HealthDiseaseRisk dr = new HealthDiseaseRisk();
|
|
|
- dr.setDiseaseName(pdr.getDiseaseName());
|
|
|
- dr.setRiskValue(pdr.getRiskValue());
|
|
|
- dr.setRiskLevel(pdr.getRiskLevel());
|
|
|
- diseaseRiskList.add(dr);
|
|
|
+ if (matchedMemberId != null) {
|
|
|
+ matchedMemberName = parsed.getPersonName();
|
|
|
+ log.info("报告{}自动匹配到成员{}", created.getId(), matchedMemberId);
|
|
|
+ try {
|
|
|
+ dimensionScoreService.refreshFromGutReport(matchedMemberId, created.getId());
|
|
|
+ log.info("已刷新成员{}的7维健康评分", matchedMemberId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("刷新7维健康评分失败: {}", e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 8. 创建完整报告(subjectId = null,尚未绑定成员)
|
|
|
- HealthReport created = healthReportService.createReport(report, indicators, gutFloraList, diseaseRiskList);
|
|
|
-
|
|
|
- // 11. 保存食材推荐数据
|
|
|
- if (parsed.getFoodSuitability() != null && !parsed.getFoodSuitability().isEmpty()) {
|
|
|
- int sortOrder = 0;
|
|
|
- for (ParsedFoodSuitability pfs : parsed.getFoodSuitability()) {
|
|
|
- Food food = new Food();
|
|
|
- food.setReportId(created.getId());
|
|
|
- food.setName(pfs.getFoodName());
|
|
|
- food.setCategory(pfs.getCategory());
|
|
|
- food.setScore(pfs.getScore());
|
|
|
- food.setEnergyKj(pfs.getEnergyKj());
|
|
|
- food.setProtein(pfs.getProtein());
|
|
|
- food.setFat(pfs.getFat());
|
|
|
- food.setCarbs(pfs.getCarbs());
|
|
|
- food.setStarch(pfs.getStarch());
|
|
|
- food.setFiber(pfs.getFiber());
|
|
|
- food.setCholesterol(pfs.getCholesterol());
|
|
|
- food.setStatus("recommended");
|
|
|
- food.setSortOrder(sortOrder++);
|
|
|
- food.setCreatedAt(new Date());
|
|
|
- food.setUpdatedAt(new Date());
|
|
|
- foodMapper.insert(food);
|
|
|
- }
|
|
|
- log.info("已保存{}条食材推荐", parsed.getFoodSuitability().size());
|
|
|
+ healthReportDraftService.markConfirmed(draft.getId(), created.getId());
|
|
|
+
|
|
|
+ Map<String, Object> detail = healthReportService.getReportDetail(created.getId());
|
|
|
+ detail.put("matchedMemberId", matchedMemberId);
|
|
|
+ detail.put("matchedMemberName", matchedMemberName);
|
|
|
+ detail.put("extractedName", parsed.getPersonName());
|
|
|
+ detail.put("extractedReportNumber", parsed.getReportNumber());
|
|
|
+ detail.put("extractedReportDate", parsed.getReportDate());
|
|
|
+ detail.put("needBind", matchedMemberId == null);
|
|
|
+ return Result.success(detail);
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("PDF解析失败", e);
|
|
|
+ return Result.error("PDF解析失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 两阶段入库 Phase 1: 上传PDF → 解析 → 存草稿 → 返回draftId + 解析载荷 + 成员匹配
|
|
|
+ * 不落库到 health_reports 等正式表。
|
|
|
+ */
|
|
|
+ @Operation(summary = "上传解析报告(预览,不入库)")
|
|
|
+ @PostMapping("/report/parse-preview")
|
|
|
+ public Result<Map<String, Object>> parsePreview(
|
|
|
+ @RequestParam("file") MultipartFile file,
|
|
|
+ @RequestParam(value = "familyId", required = false) Long familyId,
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
+
|
|
|
+ if (file.isEmpty()) {
|
|
|
+ return Result.error("文件不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ String contentType = file.getContentType();
|
|
|
+ String originalFilename = file.getOriginalFilename();
|
|
|
+ if (originalFilename == null || (!originalFilename.toLowerCase().endsWith(".pdf")
|
|
|
+ && (contentType == null || !contentType.equalsIgnoreCase("application/pdf")))) {
|
|
|
+ return Result.error("仅支持PDF文件");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ ParsedReportResult parsed = pdfParseService.parse(file.getInputStream());
|
|
|
+ if (parsed.getOverallScore() == null && parsed.getGutHealthScore() == null) {
|
|
|
+ return Result.error("无法解析PDF文件,请确认是募极生物肠道菌群报告");
|
|
|
}
|
|
|
|
|
|
- // 10. 尝试自动匹配家庭成员(通过PDF提取的姓名)
|
|
|
+ String fileUrl = saveUploadFile(file, userId);
|
|
|
+
|
|
|
+ ParsedReportPayload.Payload payload = convertToPayload(parsed);
|
|
|
+
|
|
|
+ String payloadJson = objectMapper.writeValueAsString(payload);
|
|
|
+
|
|
|
+ HealthReportDraft draft = healthReportDraftService.createDraft(
|
|
|
+ userId, familyId, "gut_flora", fileUrl, originalFilename, payloadJson);
|
|
|
+
|
|
|
Long matchedMemberId = null;
|
|
|
- String matchedMemberName = null;
|
|
|
if (familyId != null && parsed.getPersonName() != null) {
|
|
|
matchedMemberId = healthReportService.matchFamilyMemberByName(parsed.getPersonName(), familyId);
|
|
|
- if (matchedMemberId != null) {
|
|
|
- healthReportService.bindReportToMember(created.getId(), matchedMemberId);
|
|
|
- matchedMemberName = parsed.getPersonName();
|
|
|
- log.info("报告{}自动匹配到成员{}", created.getId(), matchedMemberId);
|
|
|
- // 刷新7维评分(基于菌群报告5维映射矩阵)
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ result.put("draftId", draft.getId());
|
|
|
+ result.put("payload", payload);
|
|
|
+ result.put("matchedMemberId", matchedMemberId);
|
|
|
+ result.put("extractedName", parsed.getPersonName());
|
|
|
+ result.put("needBind", matchedMemberId == null && familyId != null);
|
|
|
+ return Result.success(result);
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("PDF解析失败", e);
|
|
|
+ return Result.error("PDF解析失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 两阶段入库 Phase 2: 确认草稿 → 写入正式表 → 刷新7维评分
|
|
|
+ * 前端可回传修改后的 payload(用户在线编辑指标后整体提交)。
|
|
|
+ */
|
|
|
+ @Operation(summary = "确认报告草稿(正式入库)")
|
|
|
+ @PostMapping("/report/confirm")
|
|
|
+ public Result<Map<String, Object>> confirmDraft(
|
|
|
+ @RequestBody Map<String, Object> params,
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
+
|
|
|
+ Long draftId = params.get("draftId") != null
|
|
|
+ ? Long.valueOf(params.get("draftId").toString()) : null;
|
|
|
+ if (draftId == null) {
|
|
|
+ return Result.error("draftId不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ HealthReportDraft draft = healthReportDraftService.getActiveDraft(draftId);
|
|
|
+ if (draft == null) {
|
|
|
+ return Result.error("草稿不存在、已确认或已过期");
|
|
|
+ }
|
|
|
+ if (!draft.getUserId().equals(userId)) {
|
|
|
+ return Result.error("无权操作此草稿");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ ParsedReportPayload.Payload payload;
|
|
|
+ Object payloadObj = params.get("payload");
|
|
|
+ if (payloadObj != null) {
|
|
|
+ String payloadJson = objectMapper.writeValueAsString(payloadObj);
|
|
|
+ payload = objectMapper.readValue(payloadJson, ParsedReportPayload.Payload.class);
|
|
|
+ } else {
|
|
|
+ payload = objectMapper.readValue(draft.getPayloadJson(), ParsedReportPayload.Payload.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ Long subjectId = params.get("subjectId") != null
|
|
|
+ ? Long.valueOf(params.get("subjectId").toString()) : null;
|
|
|
+
|
|
|
+ HealthReport report = buildReportFromPayload(draft, payload, subjectId);
|
|
|
+
|
|
|
+ List<HealthIndicator> indicators = buildIndicatorsFromPayload(payload);
|
|
|
+
|
|
|
+ List<HealthGutFlora> gutFlora = buildGutFloraFromPayload(payload);
|
|
|
+
|
|
|
+ List<HealthDiseaseRisk> diseaseRisks = buildDiseaseRisksFromPayload(payload);
|
|
|
+
|
|
|
+ HealthReport created = healthReportService.createReport(report, indicators, gutFlora, diseaseRisks);
|
|
|
+
|
|
|
+ if (payload.getFoods() != null && !payload.getFoods().isEmpty()) {
|
|
|
+ List<Food> foods = buildFoodsFromPayload(payload);
|
|
|
+ foodService.saveFoodsForReport(created.getId(), foods);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (subjectId != null) {
|
|
|
+ healthReportService.bindReportToMember(created.getId(), subjectId);
|
|
|
+ try {
|
|
|
+ dimensionScoreService.refreshFromGutReport(subjectId, created.getId());
|
|
|
+ log.info("已刷新成员{}的7维健康评分", subjectId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("刷新7维健康评分失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ } else if (draft.getFamilyId() != null && payload.getSummary() != null
|
|
|
+ && payload.getSummary().getPersonName() != null) {
|
|
|
+ Long matchedId = healthReportService.matchFamilyMemberByName(
|
|
|
+ payload.getSummary().getPersonName(), draft.getFamilyId());
|
|
|
+ if (matchedId != null) {
|
|
|
+ healthReportService.bindReportToMember(created.getId(), matchedId);
|
|
|
try {
|
|
|
- dimensionScoreService.refreshFromGutReport(matchedMemberId, created.getId());
|
|
|
- log.info("已刷新成员{}的7维健康评分", matchedMemberId);
|
|
|
+ dimensionScoreService.refreshFromGutReport(matchedId, created.getId());
|
|
|
} catch (Exception e) {
|
|
|
log.warn("刷新7维健康评分失败: {}", e.getMessage());
|
|
|
}
|
|
|
+ subjectId = matchedId;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 11. 返回详情
|
|
|
+ healthReportDraftService.markConfirmed(draftId, created.getId());
|
|
|
+
|
|
|
Map<String, Object> detail = healthReportService.getReportDetail(created.getId());
|
|
|
- detail.put("matchedMemberId", matchedMemberId);
|
|
|
- detail.put("matchedMemberName", matchedMemberName);
|
|
|
- detail.put("extractedName", parsed.getPersonName());
|
|
|
- detail.put("extractedReportNumber", parsed.getReportNumber());
|
|
|
- detail.put("extractedReportDate", parsed.getReportDate());
|
|
|
- detail.put("needBind", matchedMemberId == null);
|
|
|
+ if (subjectId != null) {
|
|
|
+ detail.put("subjectId", subjectId);
|
|
|
+ }
|
|
|
return Result.success(detail);
|
|
|
|
|
|
- } catch (IOException e) {
|
|
|
- log.error("PDF解析失败", e);
|
|
|
- return Result.error("PDF解析失败: " + e.getMessage());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("确认草稿失败", e);
|
|
|
+ return Result.error("确认草稿失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 放弃草稿
|
|
|
+ */
|
|
|
+ @Operation(summary = "放弃报告草稿")
|
|
|
+ @PostMapping("/report/discard")
|
|
|
+ public Result<String> discardDraft(
|
|
|
+ @RequestBody Map<String, Object> params,
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
+
|
|
|
+ Long draftId = params.get("draftId") != null
|
|
|
+ ? Long.valueOf(params.get("draftId").toString()) : null;
|
|
|
+ if (draftId == null) {
|
|
|
+ return Result.error("draftId不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ HealthReportDraft draft = healthReportDraftService.getActiveDraft(draftId);
|
|
|
+ if (draft == null) {
|
|
|
+ return Result.error("草稿不存在或已处理");
|
|
|
}
|
|
|
+ if (!draft.getUserId().equals(userId)) {
|
|
|
+ return Result.error("无权操作此草稿");
|
|
|
+ }
|
|
|
+
|
|
|
+ healthReportDraftService.markDiscarded(draftId);
|
|
|
+ return Result.success("草稿已放弃");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -640,6 +712,236 @@ public class HealthReportController {
|
|
|
return Result.success("报告已归档");
|
|
|
}
|
|
|
|
|
|
+ private ParsedReportPayload.Payload convertToPayload(ParsedReportResult parsed) {
|
|
|
+ ParsedReportPayload.Payload payload = new ParsedReportPayload.Payload();
|
|
|
+
|
|
|
+ ParsedReportPayload.Summary summary = new ParsedReportPayload.Summary();
|
|
|
+ summary.setOverallScore(parsed.getOverallScore());
|
|
|
+ summary.setGutHealthScore(parsed.getGutHealthScore());
|
|
|
+ summary.setChronicDiseaseScore(parsed.getChronicDiseaseScore());
|
|
|
+ summary.setNutritionScore(parsed.getNutritionScore());
|
|
|
+ summary.setBalanceScore(parsed.getBalanceScore());
|
|
|
+ summary.setDiversityScore(parsed.getDiversityScore());
|
|
|
+ summary.setBeneficialScore(parsed.getBeneficialScore());
|
|
|
+ summary.setHarmfulScore(parsed.getHarmfulScore());
|
|
|
+ summary.setCoreGenusScore(parsed.getCoreGenusScore());
|
|
|
+ summary.setGutAge(parsed.getGutAge());
|
|
|
+ summary.setGutType(parsed.getGutType());
|
|
|
+ summary.setReportNumber(parsed.getReportNumber());
|
|
|
+ summary.setReportDate(parsed.getReportDate());
|
|
|
+ summary.setPersonName(parsed.getPersonName());
|
|
|
+ payload.setSummary(summary);
|
|
|
+
|
|
|
+ if (parsed.getIndicators() != null) {
|
|
|
+ List<ParsedReportPayload.Indicator> indicators = new ArrayList<>();
|
|
|
+ for (ParsedIndicator pi : parsed.getIndicators()) {
|
|
|
+ ParsedReportPayload.Indicator ind = new ParsedReportPayload.Indicator();
|
|
|
+ ind.setCategory(pi.getCategory());
|
|
|
+ ind.setIndicatorName(pi.getIndicatorName());
|
|
|
+ ind.setIndicatorValue(pi.getIndicatorValue());
|
|
|
+ ind.setRefRange(pi.getRefRange());
|
|
|
+ ind.setStatus(pi.getStatus());
|
|
|
+ ind.setSymptoms(pi.getSymptoms());
|
|
|
+ indicators.add(ind);
|
|
|
+ }
|
|
|
+ payload.setIndicators(indicators);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parsed.getGutFlora() != null) {
|
|
|
+ List<ParsedReportPayload.Flora> gutFlora = new ArrayList<>();
|
|
|
+ for (ParsedGutFlora pgf : parsed.getGutFlora()) {
|
|
|
+ gutFlora.add(convertFlora(pgf));
|
|
|
+ }
|
|
|
+ payload.setGutFlora(gutFlora);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parsed.getProbioticSpecies() != null) {
|
|
|
+ List<ParsedReportPayload.Flora> probiotics = new ArrayList<>();
|
|
|
+ for (ParsedGutFlora pgf : parsed.getProbioticSpecies()) {
|
|
|
+ probiotics.add(convertFlora(pgf));
|
|
|
+ }
|
|
|
+ payload.setProbioticSpecies(probiotics);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parsed.getFoodSuitability() != null) {
|
|
|
+ List<ParsedReportPayload.FoodItem> foods = new ArrayList<>();
|
|
|
+ for (ParsedFoodSuitability pfs : parsed.getFoodSuitability()) {
|
|
|
+ ParsedReportPayload.FoodItem fi = new ParsedReportPayload.FoodItem();
|
|
|
+ fi.setName(pfs.getFoodName());
|
|
|
+ fi.setCategory(pfs.getCategory());
|
|
|
+ fi.setScore(pfs.getScore());
|
|
|
+ fi.setEnergyKj(pfs.getEnergyKj());
|
|
|
+ fi.setProtein(pfs.getProtein());
|
|
|
+ fi.setFat(pfs.getFat());
|
|
|
+ fi.setCarbs(pfs.getCarbs());
|
|
|
+ fi.setStarch(pfs.getStarch());
|
|
|
+ fi.setFiber(pfs.getFiber());
|
|
|
+ fi.setCholesterol(pfs.getCholesterol());
|
|
|
+ foods.add(fi);
|
|
|
+ }
|
|
|
+ payload.setFoods(foods);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parsed.getDiseaseRisks() != null) {
|
|
|
+ List<ParsedReportPayload.DiseaseRisk> risks = new ArrayList<>();
|
|
|
+ for (ParsedDiseaseRisk pdr : parsed.getDiseaseRisks()) {
|
|
|
+ ParsedReportPayload.DiseaseRisk dr = new ParsedReportPayload.DiseaseRisk();
|
|
|
+ dr.setDiseaseName(pdr.getDiseaseName());
|
|
|
+ dr.setRiskValue(pdr.getRiskValue());
|
|
|
+ dr.setRiskLevel(pdr.getRiskLevel());
|
|
|
+ risks.add(dr);
|
|
|
+ }
|
|
|
+ payload.setDiseaseRisks(risks);
|
|
|
+ }
|
|
|
+
|
|
|
+ return payload;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ParsedReportPayload.Flora convertFlora(ParsedGutFlora pgf) {
|
|
|
+ ParsedReportPayload.Flora f = new ParsedReportPayload.Flora();
|
|
|
+ f.setBacteriaName(pgf.getBacteriaName());
|
|
|
+ f.setBacteriaValue(pgf.getBacteriaValue());
|
|
|
+ f.setNormalRange(pgf.getNormalRange());
|
|
|
+ f.setPopulationLevel(pgf.getPopulationLevel());
|
|
|
+ f.setDetectionRate(pgf.getDetectionRate());
|
|
|
+ f.setDescription(pgf.getDescription());
|
|
|
+ f.setCategory(pgf.getCategory());
|
|
|
+ f.setLevel(pgf.getLevel());
|
|
|
+ return f;
|
|
|
+ }
|
|
|
+
|
|
|
+ private HealthReport buildReportFromPayload(HealthReportDraft draft, ParsedReportPayload.Payload payload, Long subjectId) {
|
|
|
+ HealthReport report = new HealthReport();
|
|
|
+ report.setUserId(draft.getUserId());
|
|
|
+ report.setFamilyId(draft.getFamilyId());
|
|
|
+ report.setSubjectId(subjectId);
|
|
|
+ report.setReportType(draft.getReportType());
|
|
|
+ report.setFileUrl(draft.getFileUrl());
|
|
|
+
|
|
|
+ ParsedReportPayload.Summary s = payload.getSummary();
|
|
|
+ if (s != null) {
|
|
|
+ report.setOverallScore(s.getOverallScore());
|
|
|
+ report.setGutHealthScore(s.getGutHealthScore());
|
|
|
+ report.setChronicDiseaseScore(s.getChronicDiseaseScore());
|
|
|
+ report.setNutritionScore(s.getNutritionScore());
|
|
|
+ report.setBalanceScore(s.getBalanceScore());
|
|
|
+ report.setDiversityScore(s.getDiversityScore());
|
|
|
+ report.setBeneficialScore(s.getBeneficialScore());
|
|
|
+ report.setHarmfulScore(s.getHarmfulScore());
|
|
|
+ report.setCoreGenusScore(s.getCoreGenusScore());
|
|
|
+ report.setGutAge(s.getGutAge());
|
|
|
+ report.setGutType(s.getGutType());
|
|
|
+ report.setReportNumber(s.getReportNumber());
|
|
|
+ report.setPersonName(s.getPersonName());
|
|
|
+
|
|
|
+ if (s.getReportDate() != null && !s.getReportDate().isEmpty()) {
|
|
|
+ try {
|
|
|
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ report.setReportDate(sdf.parse(s.getReportDate()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ report.setReportDate(new Date());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ report.setReportDate(new Date());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ report.setReportDate(new Date());
|
|
|
+ }
|
|
|
+
|
|
|
+ report.setRemark("募极生物肠道菌群检测报告");
|
|
|
+ return report;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<HealthIndicator> buildIndicatorsFromPayload(ParsedReportPayload.Payload payload) {
|
|
|
+ List<ParsedReportPayload.Indicator> list = payload.getIndicators();
|
|
|
+ if (list == null || list.isEmpty()) return new ArrayList<>();
|
|
|
+
|
|
|
+ List<HealthIndicator> indicators = new ArrayList<>();
|
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
|
+ ParsedReportPayload.Indicator pi = list.get(i);
|
|
|
+ HealthIndicator ind = new HealthIndicator();
|
|
|
+ ind.setCategory(pi.getCategory());
|
|
|
+ ind.setIndicatorName(pi.getIndicatorName());
|
|
|
+ ind.setIndicatorValue(pi.getIndicatorValue());
|
|
|
+ ind.setUnit(pi.getUnit());
|
|
|
+ ind.setRefRange(pi.getRefRange());
|
|
|
+ ind.setStatus(pi.getStatus());
|
|
|
+ ind.setSymptoms(pi.getSymptoms());
|
|
|
+ ind.setSortOrder(i);
|
|
|
+ indicators.add(ind);
|
|
|
+ }
|
|
|
+ return indicators;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<HealthGutFlora> buildGutFloraFromPayload(ParsedReportPayload.Payload payload) {
|
|
|
+ List<HealthGutFlora> result = new ArrayList<>();
|
|
|
+
|
|
|
+ if (payload.getGutFlora() != null) {
|
|
|
+ for (ParsedReportPayload.Flora pf : payload.getGutFlora()) {
|
|
|
+ result.add(convertToGutFlora(pf));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (payload.getProbioticSpecies() != null) {
|
|
|
+ for (ParsedReportPayload.Flora pf : payload.getProbioticSpecies()) {
|
|
|
+ result.add(convertToGutFlora(pf));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private HealthGutFlora convertToGutFlora(ParsedReportPayload.Flora pf) {
|
|
|
+ HealthGutFlora gf = new HealthGutFlora();
|
|
|
+ gf.setBacteriaName(pf.getBacteriaName());
|
|
|
+ gf.setBacteriaValue(pf.getBacteriaValue());
|
|
|
+ gf.setNormalRange(pf.getNormalRange());
|
|
|
+ gf.setPopulationLevel(pf.getPopulationLevel());
|
|
|
+ gf.setDetectionRate(pf.getDetectionRate());
|
|
|
+ gf.setDescription(pf.getDescription());
|
|
|
+ gf.setCategory(pf.getCategory());
|
|
|
+ gf.setLevel(pf.getLevel());
|
|
|
+ return gf;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<HealthDiseaseRisk> buildDiseaseRisksFromPayload(ParsedReportPayload.Payload payload) {
|
|
|
+ List<ParsedReportPayload.DiseaseRisk> list = payload.getDiseaseRisks();
|
|
|
+ if (list == null || list.isEmpty()) return new ArrayList<>();
|
|
|
+
|
|
|
+ List<HealthDiseaseRisk> risks = new ArrayList<>();
|
|
|
+ for (ParsedReportPayload.DiseaseRisk pr : list) {
|
|
|
+ HealthDiseaseRisk dr = new HealthDiseaseRisk();
|
|
|
+ dr.setDiseaseName(pr.getDiseaseName());
|
|
|
+ dr.setRiskValue(pr.getRiskValue());
|
|
|
+ dr.setRiskLevel(pr.getRiskLevel());
|
|
|
+ risks.add(dr);
|
|
|
+ }
|
|
|
+ return risks;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Food> buildFoodsFromPayload(ParsedReportPayload.Payload payload) {
|
|
|
+ List<ParsedReportPayload.FoodItem> list = payload.getFoods();
|
|
|
+ if (list == null || list.isEmpty()) return new ArrayList<>();
|
|
|
+
|
|
|
+ List<Food> foods = new ArrayList<>();
|
|
|
+ for (ParsedReportPayload.FoodItem pf : list) {
|
|
|
+ Food food = new Food();
|
|
|
+ food.setName(pf.getName());
|
|
|
+ food.setCategory(pf.getCategory());
|
|
|
+ food.setScore(pf.getScore());
|
|
|
+ food.setEnergyKj(pf.getEnergyKj());
|
|
|
+ food.setProtein(pf.getProtein());
|
|
|
+ food.setFat(pf.getFat());
|
|
|
+ food.setCarbs(pf.getCarbs());
|
|
|
+ food.setStarch(pf.getStarch());
|
|
|
+ food.setFiber(pf.getFiber());
|
|
|
+ food.setCholesterol(pf.getCholesterol());
|
|
|
+ food.setStatus("recommended");
|
|
|
+ foods.add(food);
|
|
|
+ }
|
|
|
+ return foods;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 保存上传文件到本地存储
|
|
|
*/
|