|
@@ -3,7 +3,9 @@ package com.etotem.cfc.service;
|
|
|
import com.etotem.cfc.dto.ParsedReportPayload;
|
|
import com.etotem.cfc.dto.ParsedReportPayload;
|
|
|
import com.etotem.cfc.entity.FoodRecommendIndex;
|
|
import com.etotem.cfc.entity.FoodRecommendIndex;
|
|
|
import com.etotem.cfc.entity.HealthReport;
|
|
import com.etotem.cfc.entity.HealthReport;
|
|
|
|
|
+import com.etotem.cfc.entity.IndicatorDefinition;
|
|
|
import com.etotem.cfc.entity.IndicatorValue;
|
|
import com.etotem.cfc.entity.IndicatorValue;
|
|
|
|
|
+import com.etotem.cfc.mapper.IndicatorDefinitionMapper;
|
|
|
import com.etotem.cfc.mapper.IndicatorValueMapper;
|
|
import com.etotem.cfc.mapper.IndicatorValueMapper;
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
@@ -31,6 +33,9 @@ public class NormalizeIndicatorPipeline {
|
|
|
@Resource
|
|
@Resource
|
|
|
private IndicatorValueMapper valueMapper;
|
|
private IndicatorValueMapper valueMapper;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private IndicatorDefinitionMapper definitionMapper;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 报告指标 + 菌属归一化写入。
|
|
* 报告指标 + 菌属归一化写入。
|
|
|
* 调用点:insertReport()(subjectId 块后)/ updateReportFromPayload()(步骤 5 后)。
|
|
* 调用点:insertReport()(subjectId 块后)/ updateReportFromPayload()(步骤 5 后)。
|
|
@@ -49,6 +54,7 @@ public class NormalizeIndicatorPipeline {
|
|
|
String sourceType = report.getReportType();
|
|
String sourceType = report.getReportType();
|
|
|
Date reportDate = report.getReportDate() != null
|
|
Date reportDate = report.getReportDate() != null
|
|
|
? new Date(report.getReportDate().getTime()) : null;
|
|
? new Date(report.getReportDate().getTime()) : null;
|
|
|
|
|
+ String dateSource = resolveDateSource(report);
|
|
|
|
|
|
|
|
int written = 0;
|
|
int written = 0;
|
|
|
if (payload != null && payload.getIndicators() != null) {
|
|
if (payload != null && payload.getIndicators() != null) {
|
|
@@ -60,7 +66,8 @@ public class NormalizeIndicatorPipeline {
|
|
|
if (defId == null) {
|
|
if (defId == null) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- IndicatorValue v = baseValue(sourceType, reportId, defId, subjectId, familyId, reportDate);
|
|
|
|
|
|
|
+ IndicatorValue v = baseValue(sourceType, reportId, defId, subjectId, familyId, reportDate,
|
|
|
|
|
+ dateSource, refRangeOf(defId));
|
|
|
v.setValue(ind.getIndicatorValue());
|
|
v.setValue(ind.getIndicatorValue());
|
|
|
v.setNumericValue(mappingService.parseNumericValue(ind.getIndicatorValue()));
|
|
v.setNumericValue(mappingService.parseNumericValue(ind.getIndicatorValue()));
|
|
|
v.setUnit(ind.getUnit());
|
|
v.setUnit(ind.getUnit());
|
|
@@ -78,7 +85,8 @@ public class NormalizeIndicatorPipeline {
|
|
|
if (defId == null) {
|
|
if (defId == null) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- IndicatorValue v = baseValue(sourceType, reportId, defId, subjectId, familyId, reportDate);
|
|
|
|
|
|
|
+ IndicatorValue v = baseValue(sourceType, reportId, defId, subjectId, familyId, reportDate,
|
|
|
|
|
+ dateSource, refRangeOf(defId));
|
|
|
v.setValue(flora.getBacteriaValue());
|
|
v.setValue(flora.getBacteriaValue());
|
|
|
v.setNumericValue(mappingService.parseNumericValue(flora.getBacteriaValue()));
|
|
v.setNumericValue(mappingService.parseNumericValue(flora.getBacteriaValue()));
|
|
|
v.setUnit("%");
|
|
v.setUnit("%");
|
|
@@ -106,7 +114,7 @@ public class NormalizeIndicatorPipeline {
|
|
|
if (defId == null) {
|
|
if (defId == null) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- IndicatorValue v = baseValue("food_recommend", idx.getId(), defId, userId, null, null);
|
|
|
|
|
|
|
+ IndicatorValue v = baseValue("food_recommend", idx.getId(), defId, userId, null, null, "unknown", null);
|
|
|
v.setValue(idx.getIndexScore() != null ? idx.getIndexScore().toString() : null);
|
|
v.setValue(idx.getIndexScore() != null ? idx.getIndexScore().toString() : null);
|
|
|
v.setNumericValue(idx.getIndexScore() != null
|
|
v.setNumericValue(idx.getIndexScore() != null
|
|
|
? BigDecimal.valueOf(idx.getIndexScore()) : null);
|
|
? BigDecimal.valueOf(idx.getIndexScore()) : null);
|
|
@@ -117,7 +125,8 @@ public class NormalizeIndicatorPipeline {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private IndicatorValue baseValue(String sourceType, Long sourceId, Long defId,
|
|
private IndicatorValue baseValue(String sourceType, Long sourceId, Long defId,
|
|
|
- Long subjectId, Long familyId, Date reportDate) {
|
|
|
|
|
|
|
+ Long subjectId, Long familyId, Date reportDate,
|
|
|
|
|
+ String dateSource, String refRangeSnapshot) {
|
|
|
IndicatorValue v = new IndicatorValue();
|
|
IndicatorValue v = new IndicatorValue();
|
|
|
v.setSourceType(sourceType);
|
|
v.setSourceType(sourceType);
|
|
|
v.setSourceId(sourceId);
|
|
v.setSourceId(sourceId);
|
|
@@ -125,6 +134,48 @@ public class NormalizeIndicatorPipeline {
|
|
|
v.setSubjectId(subjectId);
|
|
v.setSubjectId(subjectId);
|
|
|
v.setFamilyId(familyId);
|
|
v.setFamilyId(familyId);
|
|
|
v.setReportDate(reportDate);
|
|
v.setReportDate(reportDate);
|
|
|
|
|
+ v.setDateSource(dateSource);
|
|
|
|
|
+ v.setRefRangeSnapshot(refRangeSnapshot);
|
|
|
return v;
|
|
return v;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /** 参考范围快照:落库时点字典的值,保证历史判定可复现(失败返回 null,回落字典当前值) */
|
|
|
|
|
+ private String refRangeOf(Long defId) {
|
|
|
|
|
+ if (defId == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ IndicatorDefinition def = definitionMapper.selectById(defId);
|
|
|
|
|
+ return def != null ? def.getRefRange() : null;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("读取指标定义参考范围失败 defId={}: {}", defId, e.getMessage());
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 日期来源判定(只打标记,不改变 reportDate 取值逻辑)。
|
|
|
|
|
+ * real —— report_date 早于入库日,可判定为从报告内容/元数据提取的真实采样日
|
|
|
|
|
+ * upload —— 日期与入库日相同或更晚(new Date() fallback 的特征),按上传日对待
|
|
|
|
|
+ * confirm —— 同上但来源为 DAN 报告(其 reportDate 取自确认入库时间)
|
|
|
|
|
+ * unknown —— reportDate 或 createdAt 缺失,无法判断
|
|
|
|
|
+ */
|
|
|
|
|
+ private String resolveDateSource(HealthReport report) {
|
|
|
|
|
+ if (report == null || report.getReportDate() == null || report.getCreatedAt() == null) {
|
|
|
|
|
+ return "unknown";
|
|
|
|
|
+ }
|
|
|
|
|
+ java.sql.Date reportDay = toDay(report.getReportDate());
|
|
|
|
|
+ java.sql.Date createdDay = toDay(report.getCreatedAt());
|
|
|
|
|
+ if (reportDay.before(createdDay)) {
|
|
|
|
|
+ return "real";
|
|
|
|
|
+ }
|
|
|
|
|
+ return "dan_a2".equals(report.getReportType()) ? "confirm" : "upload";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 截断到日(避免同一天因时分秒不同被误判为"采样日早于入库日") */
|
|
|
|
|
+ private java.sql.Date toDay(java.util.Date d) {
|
|
|
|
|
+ return java.sql.Date.valueOf(
|
|
|
|
|
+ java.time.Instant.ofEpochMilli(d.getTime())
|
|
|
|
|
+ .atZone(java.time.ZoneId.systemDefault()).toLocalDate());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|