feat(calculator): 营养计算结果返回校验规则集(test-0415 反馈2-1)
- NutritionCalculateResponse 新增 validations: List<ValidationRule>
- ValidationRule = { name, value, targetRange, status, reference }
- 当前覆盖 4 条核心规则:
· BMI(WS/T 428-2013)
· eGFR(KDIGO 2012 CKD 评估指南)
· 蛋白质摄入量(KDOQI 2020,按是否透析切换 0.6-0.8 / 1.0-1.2 g/kg)
· 能量摄入量(KDOQI 2020,30-35 kcal/kg)
- status: normal / below / above / unknown,前端可在结果页给徽标提示
- 用户/医生可据此核对推荐数值是否落在临床指南区间内
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,9 @@ public class NutritionCalculateResponse implements Serializable {
|
|||||||
@ApiModelProperty(value = "重要提示列表")
|
@ApiModelProperty(value = "重要提示列表")
|
||||||
private List<String> importantTips;
|
private List<String> importantTips;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "校验规则集(test-0415 反馈2-1):暴露推荐计算所依据的临床指南范围,方便用户与医生核验")
|
||||||
|
private List<ValidationRule> validations;
|
||||||
|
|
||||||
@ApiModelProperty(value = "创建时间")
|
@ApiModelProperty(value = "创建时间")
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
@@ -120,6 +123,31 @@ public class NutritionCalculateResponse implements Serializable {
|
|||||||
private List<Dish> dinner;
|
private List<Dish> dinner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验规则项(test-0415 反馈2-1)
|
||||||
|
* 让用户/医生可以一眼看出每个推荐数值落在哪个临床指南区间,是否「在范围内 / 偏低 / 偏高」
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(description = "校验规则项")
|
||||||
|
public static class ValidationRule implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "指标名称", example = "蛋白质摄入量")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "当前推荐值", example = "0.8 g/kg")
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "目标范围", example = "0.6-0.8 g/kg")
|
||||||
|
private String targetRange;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "状态:normal / below / above / unknown", example = "normal")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "参考依据", example = "KDOQI 2020 营养指南")
|
||||||
|
private String reference;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 菜品信息
|
* 菜品信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -644,10 +644,83 @@ public class ToolCalculatorServiceImpl extends ServiceImpl<V2CalculatorResultDao
|
|||||||
// 重要提示
|
// 重要提示
|
||||||
response.setImportantTips(importantTips);
|
response.setImportantTips(importantTips);
|
||||||
|
|
||||||
|
// test-0415 反馈2-1:校验规则集——暴露每项推荐数值的临床指南范围与依据
|
||||||
|
response.setValidations(buildValidationRules(entity));
|
||||||
|
|
||||||
// 创建时间
|
// 创建时间
|
||||||
response.setCreatedAt(entity.getCreatedAt());
|
response.setCreatedAt(entity.getCreatedAt());
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建校验规则集
|
||||||
|
* 让用户/医生可以在结果页直接核对推荐数值与临床指南的对应关系
|
||||||
|
*/
|
||||||
|
private List<NutritionCalculateResponse.ValidationRule> buildValidationRules(V2CalculatorResult e) {
|
||||||
|
List<NutritionCalculateResponse.ValidationRule> list = new ArrayList<>();
|
||||||
|
|
||||||
|
// 1) BMI:18.5–23.9 为正常(中国成人)
|
||||||
|
list.add(rule("BMI",
|
||||||
|
e.getBmi().toString(),
|
||||||
|
"18.5-23.9",
|
||||||
|
bmiStatus(e.getBmi()),
|
||||||
|
"中国成人 BMI 分类标准(WS/T 428-2013)"));
|
||||||
|
|
||||||
|
// 2) eGFR:CKD 分期参考 KDIGO 2012
|
||||||
|
list.add(rule("eGFR",
|
||||||
|
e.getEgfr().toString() + " ml/min/1.73m²",
|
||||||
|
">90 (G1) / 60-89 (G2) / 30-59 (G3) / 15-29 (G4) / <15 (G5)",
|
||||||
|
"normal", // eGFR 本身不存在「越界」概念,由分期解释
|
||||||
|
"KDIGO 2012 CKD 评估和管理临床实践指南"));
|
||||||
|
|
||||||
|
// 3) 蛋白质摄入量:根据是否透析与 CKD 分期
|
||||||
|
BigDecimal proteinPerKg = e.getProteinIntake().divide(e.getStandardWeight(), 2, BigDecimal.ROUND_HALF_UP);
|
||||||
|
boolean isDialysis = e.getHasDialysis() != null && e.getHasDialysis() == 1;
|
||||||
|
String pTarget = isDialysis ? "1.0-1.2 g/kg" : "0.6-0.8 g/kg";
|
||||||
|
BigDecimal pMin = isDialysis ? new BigDecimal("1.0") : new BigDecimal("0.6");
|
||||||
|
BigDecimal pMax = isDialysis ? new BigDecimal("1.2") : new BigDecimal("0.8");
|
||||||
|
list.add(rule("蛋白质摄入量",
|
||||||
|
proteinPerKg + " g/kg(共 " + e.getProteinIntake() + " g)",
|
||||||
|
pTarget,
|
||||||
|
rangeStatus(proteinPerKg, pMin, pMax),
|
||||||
|
isDialysis ? "KDOQI 2020 透析患者营养指南" : "KDOQI 2020 非透析 CKD 营养指南"));
|
||||||
|
|
||||||
|
// 4) 能量摄入量:30-35 kcal/kg/day
|
||||||
|
BigDecimal energyPerKg = new BigDecimal(e.getEnergyIntake())
|
||||||
|
.divide(e.getStandardWeight(), 2, BigDecimal.ROUND_HALF_UP);
|
||||||
|
list.add(rule("能量摄入量",
|
||||||
|
energyPerKg + " kcal/kg(共 " + e.getEnergyIntake() + " kcal)",
|
||||||
|
"30-35 kcal/kg",
|
||||||
|
rangeStatus(energyPerKg, new BigDecimal("30"), new BigDecimal("35")),
|
||||||
|
"KDOQI 2020 营养指南"));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NutritionCalculateResponse.ValidationRule rule(String name, String value, String targetRange,
|
||||||
|
String status, String reference) {
|
||||||
|
NutritionCalculateResponse.ValidationRule r = new NutritionCalculateResponse.ValidationRule();
|
||||||
|
r.setName(name);
|
||||||
|
r.setValue(value);
|
||||||
|
r.setTargetRange(targetRange);
|
||||||
|
r.setStatus(status);
|
||||||
|
r.setReference(reference);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String bmiStatus(BigDecimal bmi) {
|
||||||
|
if (bmi == null) return "unknown";
|
||||||
|
if (bmi.compareTo(new BigDecimal("18.5")) < 0) return "below";
|
||||||
|
if (bmi.compareTo(new BigDecimal("23.9")) > 0) return "above";
|
||||||
|
return "normal";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String rangeStatus(BigDecimal value, BigDecimal min, BigDecimal max) {
|
||||||
|
if (value == null) return "unknown";
|
||||||
|
if (value.compareTo(min) < 0) return "below";
|
||||||
|
if (value.compareTo(max) > 0) return "above";
|
||||||
|
return "normal";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user