|
@@ -50,6 +50,7 @@ import java.nio.file.Path;
|
|
|
import java.nio.file.Paths;
|
|
import java.nio.file.Paths;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.HashSet;
|
|
import java.util.HashSet;
|
|
|
import java.util.LinkedHashMap;
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
@@ -57,6 +58,9 @@ import java.util.Map;
|
|
|
import java.util.Set;
|
|
import java.util.Set;
|
|
|
import java.util.UUID;
|
|
import java.util.UUID;
|
|
|
|
|
|
|
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 健康检测报告接口 — 体检报告/菌群检测报告的录入与查询
|
|
* 健康检测报告接口 — 体检报告/菌群检测报告的录入与查询
|
|
|
*/
|
|
*/
|
|
@@ -66,6 +70,9 @@ import java.util.UUID;
|
|
|
@RequestMapping("/api/health")
|
|
@RequestMapping("/api/health")
|
|
|
public class HealthReportController {
|
|
public class HealthReportController {
|
|
|
|
|
|
|
|
|
|
+ private static final String LANGGRAPH_BASE_URL = "http://localhost:9000";
|
|
|
|
|
+ private static final String REPORT_PARSE_URL = LANGGRAPH_BASE_URL + "/api/v1/report/parse";
|
|
|
|
|
+
|
|
|
@Resource
|
|
@Resource
|
|
|
private HealthReportService healthReportService;
|
|
private HealthReportService healthReportService;
|
|
|
|
|
|
|
@@ -90,6 +97,9 @@ public class HealthReportController {
|
|
|
@Resource
|
|
@Resource
|
|
|
private ObjectMapper objectMapper;
|
|
private ObjectMapper objectMapper;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private RestTemplate restTemplate;
|
|
|
|
|
+
|
|
|
@Resource
|
|
@Resource
|
|
|
private DimensionScoreService dimensionScoreService;
|
|
private DimensionScoreService dimensionScoreService;
|
|
|
|
|
|
|
@@ -475,13 +485,71 @@ public class HealthReportController {
|
|
|
return Result.error("仅支持PDF文件");
|
|
return Result.error("仅支持PDF文件");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 保存文件到磁盘(LangGraph 和本地解析共用)
|
|
|
|
|
+ String fileUrl = saveUploadFile(file, userId);
|
|
|
|
|
+ String absolutePath = new File(fileUrl).getAbsolutePath();
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试 LangGraph 远程解析
|
|
|
|
|
+ try {
|
|
|
|
|
+ Map<String, Object> lgRequest = new HashMap<>();
|
|
|
|
|
+ lgRequest.put("file_path", absolutePath);
|
|
|
|
|
+ lgRequest.put("user_id", userId);
|
|
|
|
|
+ if (familyId != null) {
|
|
|
|
|
+ lgRequest.put("family_id", familyId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ResponseEntity<Map> lgResponse = restTemplate.postForEntity(
|
|
|
|
|
+ REPORT_PARSE_URL, lgRequest, Map.class);
|
|
|
|
|
+ Map<String, Object> lgBody = lgResponse.getBody();
|
|
|
|
|
+ if (lgBody != null && Integer.valueOf(200).equals(lgBody.get("code"))) {
|
|
|
|
|
+ Map<String, Object> lgData = (Map<String, Object>) lgBody.get("data");
|
|
|
|
|
+ if (lgData != null) {
|
|
|
|
|
+ ParsedReportPayload.Payload lgPayload = buildPayloadFromLgData(lgData);
|
|
|
|
|
+
|
|
|
|
|
+ String payloadJson = objectMapper.writeValueAsString(lgPayload);
|
|
|
|
|
+
|
|
|
|
|
+ HealthReportDraft draft = healthReportDraftService.createDraft(
|
|
|
|
|
+ userId, familyId, "gut_flora", fileUrl, originalFilename, payloadJson);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> overview = (Map<String, Object>) lgData.get("overview");
|
|
|
|
|
+ String personName = overview != null ? (String) overview.get("person_name") : null;
|
|
|
|
|
+ String gender = overview != null ? (String) overview.get("gender") : null;
|
|
|
|
|
+ Integer age = overview != null ? toInteger(overview.get("age")) : null;
|
|
|
|
|
+
|
|
|
|
|
+ MemberMatchResult matchResult = healthReportService.matchMemberByProfile(
|
|
|
|
|
+ personName, gender, age, familyId);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
|
|
+ result.put("draftId", draft.getId());
|
|
|
|
|
+ result.put("payload", lgPayload);
|
|
|
|
|
+ result.put("matchedMemberId", matchResult.getMatchedMemberId());
|
|
|
|
|
+ result.put("confidence", matchResult.getConfidence());
|
|
|
|
|
+ result.put("matchLabel", matchResult.getMatchLabel());
|
|
|
|
|
+ result.put("candidates", matchResult.getCandidates());
|
|
|
|
|
+ result.put("extractedName", personName);
|
|
|
|
|
+ result.put("extractedGender", gender);
|
|
|
|
|
+ result.put("extractedAge", age);
|
|
|
|
|
+ result.put("needBind", matchResult.getMatchedMemberId() == null && familyId != null);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ saveResultToJsonFile(result);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("保存结果到 JSON 文件失败:{}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return Result.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("LangGraph 解析失败,回退到本地 Java 解析: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Fallback: 本地 Java 解析
|
|
|
ParsedReportResult parsed = pdfParseService.parse(file.getInputStream());
|
|
ParsedReportResult parsed = pdfParseService.parse(file.getInputStream());
|
|
|
if (parsed.getOverallScore() == null && parsed.getGutHealthScore() == null) {
|
|
if (parsed.getOverallScore() == null && parsed.getGutHealthScore() == null) {
|
|
|
return Result.error("无法解析PDF文件,请确认是募极生物肠道菌群报告");
|
|
return Result.error("无法解析PDF文件,请确认是募极生物肠道菌群报告");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- String fileUrl = saveUploadFile(file, userId);
|
|
|
|
|
-
|
|
|
|
|
ParsedReportPayload.Payload payload = convertToPayload(parsed);
|
|
ParsedReportPayload.Payload payload = convertToPayload(parsed);
|
|
|
|
|
|
|
|
String payloadJson = objectMapper.writeValueAsString(payload);
|
|
String payloadJson = objectMapper.writeValueAsString(payload);
|
|
@@ -1178,6 +1246,80 @@ public class HealthReportController {
|
|
|
return f;
|
|
return f;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private ParsedReportPayload.Payload buildPayloadFromLgData(Map<String, Object> lgData) {
|
|
|
|
|
+ ParsedReportPayload.Payload payload = new ParsedReportPayload.Payload();
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> overview = (Map<String, Object>) lgData.get("overview");
|
|
|
|
|
+ if (overview != null) {
|
|
|
|
|
+ ParsedReportPayload.Summary summary = new ParsedReportPayload.Summary();
|
|
|
|
|
+ summary.setPersonName((String) overview.get("person_name"));
|
|
|
|
|
+ summary.setAge(toInteger(overview.get("age")));
|
|
|
|
|
+ summary.setGender((String) overview.get("gender"));
|
|
|
|
|
+ summary.setOverallScore(toInteger(overview.get("overallScore")));
|
|
|
|
|
+ summary.setGutHealthScore(toInteger(overview.get("gutHealthScore")));
|
|
|
|
|
+ summary.setChronicDiseaseScore(toInteger(overview.get("chronicDiseaseScore")));
|
|
|
|
|
+ summary.setNutritionScore(toInteger(overview.get("nutritionScore")));
|
|
|
|
|
+ summary.setBalanceScore(toInteger(overview.get("balanceScore")));
|
|
|
|
|
+ summary.setDiversityScore(toInteger(overview.get("diversityScore")));
|
|
|
|
|
+ summary.setBeneficialScore(toInteger(overview.get("beneficialScore")));
|
|
|
|
|
+ summary.setHarmfulScore(toInteger(overview.get("harmfulScore")));
|
|
|
|
|
+ summary.setCoreGenusScore(toInteger(overview.get("coreGenusScore")));
|
|
|
|
|
+ summary.setGutAge(objectToString(overview.get("gutAge")));
|
|
|
|
|
+ summary.setGutType(objectToString(overview.get("gutType")));
|
|
|
|
|
+ summary.setReportNumber(objectToString(overview.get("report_number")));
|
|
|
|
|
+ summary.setBirthday(objectToString(overview.get("birthday")));
|
|
|
|
|
+ payload.setSummary(summary);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> diseaseRisks = (List<Map<String, Object>>) lgData.get("disease_risks");
|
|
|
|
|
+ if (diseaseRisks != null) {
|
|
|
|
|
+ List<ParsedReportPayload.DiseaseRisk> risks = new ArrayList<>();
|
|
|
|
|
+ for (Map<String, Object> dr : diseaseRisks) {
|
|
|
|
|
+ ParsedReportPayload.DiseaseRisk risk = new ParsedReportPayload.DiseaseRisk();
|
|
|
|
|
+ risk.setDiseaseName((String) dr.get("name"));
|
|
|
|
|
+ risk.setRiskValue(objectToString(dr.get("value")));
|
|
|
|
|
+ risk.setRiskLevel((String) dr.get("status"));
|
|
|
|
|
+ risks.add(risk);
|
|
|
|
|
+ }
|
|
|
|
|
+ payload.setDiseaseRisks(risks);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> indicators = (List<Map<String, Object>>) lgData.get("indicators");
|
|
|
|
|
+ if (indicators != null) {
|
|
|
|
|
+ List<ParsedReportPayload.Indicator> inds = new ArrayList<>();
|
|
|
|
|
+ for (Map<String, Object> ind : indicators) {
|
|
|
|
|
+ ParsedReportPayload.Indicator indicator = new ParsedReportPayload.Indicator();
|
|
|
|
|
+ indicator.setCategory((String) ind.get("category"));
|
|
|
|
|
+ indicator.setIndicatorName((String) ind.get("indicatorName"));
|
|
|
|
|
+ indicator.setIndicatorValue(objectToString(ind.get("indicatorValue")));
|
|
|
|
|
+ indicator.setUnit((String) ind.get("unit"));
|
|
|
|
|
+ indicator.setRefRange((String) ind.get("refRange"));
|
|
|
|
|
+ indicator.setStatus((String) ind.get("status"));
|
|
|
|
|
+ indicator.setSymptoms((String) ind.get("symptoms"));
|
|
|
|
|
+ inds.add(indicator);
|
|
|
|
|
+ }
|
|
|
|
|
+ payload.setIndicators(inds);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return payload;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Integer toInteger(Object value) {
|
|
|
|
|
+ if (value == null) return null;
|
|
|
|
|
+ if (value instanceof Integer) return (Integer) value;
|
|
|
|
|
+ if (value instanceof Number) return ((Number) value).intValue();
|
|
|
|
|
+ try {
|
|
|
|
|
+ return Integer.valueOf(value.toString());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String objectToString(Object value) {
|
|
|
|
|
+ if (value == null) return null;
|
|
|
|
|
+ return value.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private HealthReport buildReportFromPayload(HealthReportDraft draft, ParsedReportPayload.Payload payload, Long subjectId) {
|
|
private HealthReport buildReportFromPayload(HealthReportDraft draft, ParsedReportPayload.Payload payload, Long subjectId) {
|
|
|
HealthReport report = new HealthReport();
|
|
HealthReport report = new HealthReport();
|
|
|
report.setUserId(draft.getUserId());
|
|
report.setUserId(draft.getUserId());
|