diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/IRationalDrugAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/IRationalDrugAppService.java index 0e8aa9fd4..53e4cbb9b 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/IRationalDrugAppService.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/IRationalDrugAppService.java @@ -4,6 +4,7 @@ import com.healthlink.his.rationaldrug.domain.DrugInteractionRule; import com.healthlink.his.rationaldrug.domain.DrugDosageRange; import com.healthlink.his.rationaldrug.dto.AuditResultDto; import com.healthlink.his.rationaldrug.dto.AuditStatisticsDto; +import com.healthlink.his.rationaldrug.dto.DosageAdjustmentRequestDto; import com.healthlink.his.rationaldrug.dto.InteractionCheckResultDto; import com.healthlink.his.rationaldrug.dto.PrescriptionAuditDto; @@ -74,4 +75,12 @@ public interface IRationalDrugAppService { * @return 审核结果 */ AuditResultDto checkDosage(String drugCode, BigDecimal dosage, String population); + + /** + * 根据肝肾功能自动建议调量 + * + * @param request 调量请求 + * @return 调量建议结果 + */ + AuditResultDto adjustDosageByOrganFunction(DosageAdjustmentRequestDto request); } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/impl/RationalDrugAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/impl/RationalDrugAppServiceImpl.java index b30e1295c..4cab50864 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/impl/RationalDrugAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/appservice/impl/RationalDrugAppServiceImpl.java @@ -6,6 +6,7 @@ import com.healthlink.his.rationaldrug.domain.DrugInteractionRule; import com.healthlink.his.rationaldrug.domain.PrescriptionAuditLog; import com.healthlink.his.rationaldrug.dto.AuditResultDto; import com.healthlink.his.rationaldrug.dto.AuditStatisticsDto; +import com.healthlink.his.rationaldrug.dto.DosageAdjustmentRequestDto; import com.healthlink.his.rationaldrug.dto.InteractionCheckResultDto; import com.healthlink.his.rationaldrug.dto.PrescriptionAuditDto; import com.healthlink.his.rationaldrug.service.IDrugDosageRangeService; @@ -257,6 +258,218 @@ public class RationalDrugAppServiceImpl implements IRationalDrugAppService { return buildResult("PASS", null, 0, "剂量在合理范围内", null); } + /** + * 根据肝肾功能自动建议调量 + *

+ * 流程:评估肝肾功能损害程度 → 匹配药品剂量规则 → 比较当前剂量 → 生成调量建议 + *

+ */ + @Override + public AuditResultDto adjustDosageByOrganFunction(DosageAdjustmentRequestDto request) { + String drugCode = request.getDrugCode(); + String organType = request.getOrganFunctionType(); + String population = request.getPopulation(); + + if (drugCode == null || organType == null) { + return buildResult("MANUAL", "INVALID_INPUT", 1, + "药品编码和肝肾功能类型不能为空", + "请提供完整的药品编码和肝肾功能类型"); + } + + // 1. 评估器官功能损害程度 + String impairmentLevel = assessImpairmentLevel(request); + if ("NORMAL".equals(impairmentLevel)) { + return buildResult("PASS", null, 0, + "肝肾功能正常,当前剂量无需调整", + "无需调量"); + } + + // 2. 查询该药品的剂量规则 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DrugDosageRange::getDrugCode, drugCode) + .eq(DrugDosageRange::getEnabled, "1") + .eq(DrugDosageRange::getDelFlag, "0"); + if (population != null) { + wrapper.eq(DrugDosageRange::getPopulation, population); + } + List ranges = drugDosageRangeService.list(wrapper); + + if (ranges.isEmpty()) { + return buildResult("MANUAL", "DOSAGE_RANGE_NOT_FOUND", 1, + "药品 " + drugCode + " 无剂量范围规则,请先维护剂量规则", + "建议人工确认剂量合理性"); + } + + // 3. 根据损害程度生成调量建议 + BigDecimal currentDose = request.getCurrentDose(); + DrugDosageRange matchedRange = ranges.get(0); + + StringBuilder detail = new StringBuilder(); + detail.append("肝肾功能评估: ").append(formatOrganType(organType)); + detail.append(" — ").append(formatImpairmentLevel(impairmentLevel)); + + String suggestion; + String ruleHit; + + if (currentDose == null) { + // 未提供当前剂量,仅给出通用建议 + suggestion = "基于" + formatImpairmentLevel(impairmentLevel) + "," + + "建议参考剂量范围 " + matchedRange.getMinDose() + "-" + + matchedRange.getMaxDose() + matchedRange.getDoseUnit(); + if (matchedRange.getAdjustmentNote() != null) { + suggestion += "。" + matchedRange.getAdjustmentNote(); + } + ruleHit = "DOSE_NOT_PROVIDED"; + detail.append("。未提供当前剂量,给出通用调量建议"); + } else { + BigDecimal minDose = matchedRange.getMinDose(); + BigDecimal maxDose = matchedRange.getMaxDose(); + + if (currentDose.compareTo(minDose) >= 0 && currentDose.compareTo(maxDose) <= 0) { + // 当前剂量在正常范围内,但器官功能受损,仍建议减量 + if ("SEVERE".equals(impairmentLevel)) { + BigDecimal reducedDose = currentDose.multiply(BigDecimal.valueOf(0.5)) + .setScale(1, RoundingMode.HALF_UP); + suggestion = "当前剂量 " + currentDose + matchedRange.getDoseUnit() + + " 虽在常规范围内,但" + formatImpairmentLevel(impairmentLevel) + + ",建议减量至 " + reducedDose + matchedRange.getDoseUnit(); + if (matchedRange.getAdjustmentNote() != null) { + suggestion += "。" + matchedRange.getAdjustmentNote(); + } + ruleHit = "SEVERE_IMPAIRMENT_REDUCE"; + detail.append("。当前剂量在常规范围内,但重度损害需减量"); + } else if ("MODERATE".equals(impairmentLevel)) { + BigDecimal reducedDose = currentDose.multiply(BigDecimal.valueOf(0.75)) + .setScale(1, RoundingMode.HALF_UP); + suggestion = "当前剂量 " + currentDose + matchedRange.getDoseUnit() + + " 虽在常规范围内,但" + formatImpairmentLevel(impairmentLevel) + + ",建议减量至 " + reducedDose + matchedRange.getDoseUnit(); + if (matchedRange.getAdjustmentNote() != null) { + suggestion += "。" + matchedRange.getAdjustmentNote(); + } + ruleHit = "MODERATE_IMPAIRMENT_REDUCE"; + detail.append("。当前剂量在常规范围内,中度损害建议适当减量"); + } else { + suggestion = "当前剂量 " + currentDose + matchedRange.getDoseUnit() + + " 在常规范围内,轻度" + formatOrganType(organType) + "损害,暂无需调整,建议密切监测"; + ruleHit = "MILD_IMPAIRMENT_MONITOR"; + detail.append("。轻度损害,当前剂量可维持"); + } + } else if (currentDose.compareTo(maxDose) > 0) { + // 当前剂量超出范围 + suggestion = "当前剂量 " + currentDose + matchedRange.getDoseUnit() + + " 超过推荐范围 " + minDose + "-" + maxDose + matchedRange.getDoseUnit() + + ",且存在" + formatImpairmentLevel(impairmentLevel) + + ",强烈建议减量至 " + minDose + "-" + maxDose + matchedRange.getDoseUnit(); + if (matchedRange.getAdjustmentNote() != null) { + suggestion += "。" + matchedRange.getAdjustmentNote(); + } + ruleHit = "DOSAGE_EXCEEDS_RANGE_WITH_IMPAIRMENT"; + detail.append("。当前剂量超出推荐范围,叠加器官损害风险更高"); + } else { + // 当前剂量低于范围 + suggestion = "当前剂量 " + currentDose + matchedRange.getDoseUnit() + + " 低于推荐范围 " + minDose + "-" + maxDose + matchedRange.getDoseUnit() + + ",结合" + formatImpairmentLevel(impairmentLevel) + + ",建议调整至 " + minDose + "-" + maxDose + matchedRange.getDoseUnit(); + ruleHit = "DOSAGE_BELOW_RANGE_WITH_IMPAIRMENT"; + detail.append("。当前剂量偏低,结合器官损害情况建议调整"); + } + } + + return buildResult("MANUAL", ruleHit, 1, detail.toString(), suggestion); + } + + /** + * 评估器官功能损害程度 + * + * @param request 调量请求 + * @return 损害程度: NORMAL / MILD / MODERATE / SEVERE + */ + private String assessImpairmentLevel(DosageAdjustmentRequestDto request) { + String organType = request.getOrganFunctionType(); + String level = "NORMAL"; + + if ("KIDNEY".equals(organType) || "BOTH".equals(organType)) { + String kidneyLevel = assessKidneyFunction(request.getEgfr(), request.getCreatinine()); + level = mergeLevel(level, kidneyLevel); + } + if ("LIVER".equals(organType) || "BOTH".equals(organType)) { + String liverLevel = assessLiverFunction(request.getAlt(), request.getAst()); + level = mergeLevel(level, liverLevel); + } + return level; + } + + private String assessKidneyFunction(java.math.BigDecimal egfr, java.math.BigDecimal creatinine) { + if (egfr != null) { + if (egfr.compareTo(java.math.BigDecimal.valueOf(90)) >= 0) return "NORMAL"; + if (egfr.compareTo(java.math.BigDecimal.valueOf(60)) >= 0) return "MILD"; + if (egfr.compareTo(java.math.BigDecimal.valueOf(30)) >= 0) return "MODERATE"; + return "SEVERE"; + } + if (creatinine != null) { + if (creatinine.compareTo(java.math.BigDecimal.valueOf(133)) < 0) return "NORMAL"; + if (creatinine.compareTo(java.math.BigDecimal.valueOf(177)) < 0) return "MILD"; + if (creatinine.compareTo(java.math.BigDecimal.valueOf(442)) < 0) return "MODERATE"; + return "SEVERE"; + } + return "NORMAL"; + } + + private String assessLiverFunction(java.math.BigDecimal alt, java.math.BigDecimal ast) { + int level = 0; + if (alt != null) { + if (alt.compareTo(java.math.BigDecimal.valueOf(40)) >= 0) level = Math.max(level, 1); + if (alt.compareTo(java.math.BigDecimal.valueOf(120)) >= 0) level = Math.max(level, 2); + if (alt.compareTo(java.math.BigDecimal.valueOf(400)) >= 0) level = Math.max(level, 3); + } + if (ast != null) { + if (ast.compareTo(java.math.BigDecimal.valueOf(40)) >= 0) level = Math.max(level, 1); + if (ast.compareTo(java.math.BigDecimal.valueOf(120)) >= 0) level = Math.max(level, 2); + if (ast.compareTo(java.math.BigDecimal.valueOf(400)) >= 0) level = Math.max(level, 3); + } + return switch (level) { + case 1 -> "MILD"; + case 2 -> "MODERATE"; + case 3 -> "SEVERE"; + default -> "NORMAL"; + }; + } + + private String mergeLevel(String current, String candidate) { + int currentLevel = impairmentLevelValue(current); + int candidateLevel = impairmentLevelValue(candidate); + return candidateLevel > currentLevel ? candidate : current; + } + + private int impairmentLevelValue(String level) { + return switch (level) { + case "SEVERE" -> 3; + case "MODERATE" -> 2; + case "MILD" -> 1; + default -> 0; + }; + } + + private String formatOrganType(String organType) { + return switch (organType) { + case "LIVER" -> "肝功能"; + case "KIDNEY" -> "肾功能"; + case "BOTH" -> "肝肾功能"; + default -> organType; + }; + } + + private String formatImpairmentLevel(String level) { + return switch (level) { + case "MILD" -> "轻度损害"; + case "MODERATE" -> "中度损害"; + case "SEVERE" -> "重度损害"; + default -> "正常"; + }; + } + /** * 保存审核日志 */ diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/controller/RationalDrugController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/controller/RationalDrugController.java index 74d5e008c..bcc3a9bb7 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/controller/RationalDrugController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/rationaldrug/controller/RationalDrugController.java @@ -5,6 +5,7 @@ import com.healthlink.his.rationaldrug.domain.DrugDosageRange; import com.healthlink.his.rationaldrug.domain.DrugInteractionRule; import com.healthlink.his.rationaldrug.dto.AuditResultDto; import com.healthlink.his.rationaldrug.dto.AuditStatisticsDto; +import com.healthlink.his.rationaldrug.dto.DosageAdjustmentRequestDto; import com.healthlink.his.rationaldrug.dto.InteractionCheckResultDto; import com.healthlink.his.rationaldrug.dto.PrescriptionAuditDto; import com.healthlink.his.rationaldrug.service.IDrugDosageRangeService; @@ -14,6 +15,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; @@ -127,4 +129,12 @@ public class RationalDrugController { AuditResultDto result = rationalDrugAppService.checkDosage(drugCode, dosage, population); return AjaxResult.success(result); } + + @PostMapping("/adjust-dosage") + @Operation(summary = "肝肾功能自动调量") + @PreAuthorize("hasAuthority('infection:rationaldrug:edit')") + public AjaxResult adjustDosageByOrganFunction(@RequestBody DosageAdjustmentRequestDto request) { + AuditResultDto result = rationalDrugAppService.adjustDosageByOrganFunction(request); + return AjaxResult.success(result); + } } diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/rationaldrug/dto/DosageAdjustmentRequestDto.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/rationaldrug/dto/DosageAdjustmentRequestDto.java new file mode 100644 index 000000000..bbdc4862b --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/rationaldrug/dto/DosageAdjustmentRequestDto.java @@ -0,0 +1,43 @@ +package com.healthlink.his.rationaldrug.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +/** + * 肝肾功能调量请求DTO + * + * @author system + */ +@Data +@Accessors(chain = true) +public class DosageAdjustmentRequestDto { + + /** 药品编码 */ + private String drugCode; + + /** 当前剂量 */ + private BigDecimal currentDose; + + /** 剂量单位 */ + private String doseUnit; + + /** 肝肾功能类型: LIVER / KIDNEY / BOTH */ + private String organFunctionType; + + /** 肌酐 (μmol/L) — 肾功能 */ + private BigDecimal creatinine; + + /** 肾小球滤过率 eGFR (mL/min) — 肾功能 */ + private BigDecimal egfr; + + /** 谷丙转氨酶 ALT (U/L) — 肝功能 */ + private BigDecimal alt; + + /** 谷草转氨酶 AST (U/L) — 肝功能 */ + private BigDecimal ast; + + /** 人群类型: ADULT/CHILD/ELDERLY/PREGNANT */ + private String population; +} diff --git a/healthlink-his-ui/src/api/rationaldrug.js b/healthlink-his-ui/src/api/rationaldrug.js index e7a484ce2..abc3619f5 100644 --- a/healthlink-his-ui/src/api/rationaldrug.js +++ b/healthlink-his-ui/src/api/rationaldrug.js @@ -50,3 +50,8 @@ export function listDosageRules(params) { export function checkDosage(drugCode, dosage, population) { return request({ url: '/api/v1/rational-drug/check-dosage', method: 'get', params: { drugCode, dosage, population } }) } + +// ==================== 肝肾功能调量 ==================== +export function adjustDosageByOrganFunction(data) { + return request({ url: '/api/v1/rational-drug/adjust-dosage', method: 'post', data }) +} diff --git a/healthlink-his-ui/src/views/rationaldrug/dosage-adjustment/index.vue b/healthlink-his-ui/src/views/rationaldrug/dosage-adjustment/index.vue new file mode 100644 index 000000000..62ca8214f --- /dev/null +++ b/healthlink-his-ui/src/views/rationaldrug/dosage-adjustment/index.vue @@ -0,0 +1,295 @@ + + + + +