|
@@ -12,20 +12,21 @@ import com.diagbot.enums.IsDeleteEnum;
|
|
|
import com.diagbot.exception.CommonErrorCode;
|
|
|
import com.diagbot.exception.CommonException;
|
|
|
import com.diagbot.service.MappingConfigService;
|
|
|
-import com.diagbot.util.BeanUtil;
|
|
|
-import com.diagbot.util.ListUtil;
|
|
|
-import com.diagbot.util.RespDTOUtil;
|
|
|
-import com.diagbot.util.UserUtils;
|
|
|
+import com.diagbot.util.*;
|
|
|
import com.diagbot.vo.*;
|
|
|
import com.google.common.collect.Lists;
|
|
|
import lombok.val;
|
|
|
+import net.objecthunter.exp4j.ExpressionBuilder;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Date;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.BigInteger;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.util.*;
|
|
|
+import java.util.function.BiFunction;
|
|
|
+import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
@@ -150,6 +151,262 @@ public class KlConceptStaticFacade {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取量表计算公式结果
|
|
|
+ */
|
|
|
+ public CalculatorFormulaScoreDTO getCalculatorFormulaResult(CalculatorFormulaVO calculatorFormulaVO) {
|
|
|
+
|
|
|
+ // 1. 获取计算公式配置
|
|
|
+ KlCalculatorFormulaDTO formulaConfig = getFormulaConfig(calculatorFormulaVO.getConceptId());
|
|
|
+
|
|
|
+ // 2. 参数预处理
|
|
|
+ Map<String, Object> inputParams = Optional.ofNullable(calculatorFormulaVO.getParams())
|
|
|
+ .orElseGet(HashMap::new);
|
|
|
+
|
|
|
+ // 3. 匹配计算公式
|
|
|
+ String matchedFormula = matchFormula(formulaConfig, inputParams);
|
|
|
+
|
|
|
+ // 4. 转换计算参数
|
|
|
+ Map<String, Double> calcParams = convertParams(inputParams);
|
|
|
+
|
|
|
+ // 5. 执行计算并返回结果
|
|
|
+ String score = calculateResult(matchedFormula, calcParams);
|
|
|
+
|
|
|
+ // 6. 比较得分,并返回响应的结果
|
|
|
+ String result = getResult(formulaConfig, matchedFormula, score);
|
|
|
+
|
|
|
+ CalculatorFormulaScoreDTO scoreDTO = new CalculatorFormulaScoreDTO();
|
|
|
+ scoreDTO.setScore(score);
|
|
|
+ scoreDTO.setResult(result);
|
|
|
+ return scoreDTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getResult(KlCalculatorFormulaDTO formulaConfig, String matchedFormula, String score) {
|
|
|
+ String result = null;
|
|
|
+ KlCalculatorFormulaDTO.ScoreTableData scoreTableData = formulaConfig.getScoreTableData().stream().filter(e -> e.getFormula().equalsIgnoreCase(matchedFormula)).findFirst().get();
|
|
|
+ List<KlCalculatorFormulaDTO.ScoreRange> scoreRanges = scoreTableData.getScoreRange();
|
|
|
+ // 6. 匹配得分范围
|
|
|
+ double scoreValue;
|
|
|
+ try {
|
|
|
+ scoreValue = Double.parseDouble(score);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR,
|
|
|
+ "得分格式错误: " + score);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (KlCalculatorFormulaDTO.ScoreRange range : scoreRanges) {
|
|
|
+ BigDecimal ge = range.getGreaterEqual(); // 下限
|
|
|
+ BigDecimal le = range.getLessEqual(); // 上限
|
|
|
+
|
|
|
+ // 边界条件检查
|
|
|
+ boolean lowerBoundMet = ge == null || scoreValue >= ge.doubleValue();
|
|
|
+ boolean upperBoundMet = le == null || scoreValue <= le.doubleValue();
|
|
|
+
|
|
|
+ // 特殊场景处理
|
|
|
+ if (ge != null && le != null) {
|
|
|
+ // 当配置的上下限反序时自动校正
|
|
|
+ if (ge.doubleValue() > le.doubleValue()) {
|
|
|
+ lowerBoundMet = scoreValue >= le.doubleValue();
|
|
|
+ upperBoundMet = scoreValue <= ge.doubleValue();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 匹配条件判断
|
|
|
+ if (lowerBoundMet && upperBoundMet) {
|
|
|
+ result = range.getResult();
|
|
|
+ break; // 找到第一个匹配项即返回
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 后置校验
|
|
|
+ if (result == null) {
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR,
|
|
|
+ "得分" + score + "未匹配到有效范围");
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取计算公式配置
|
|
|
+ */
|
|
|
+ private KlCalculatorFormulaDTO getFormulaConfig(Long conceptId) {
|
|
|
+ return Optional.ofNullable(cdssCoreClient.getRecordById(new IdVO(conceptId)))
|
|
|
+ .filter(RespDTOUtil::respIsOK)
|
|
|
+ .map(resp -> resp.data.getCalculatorFormula())
|
|
|
+ .orElseThrow(() -> new CommonException(CommonErrorCode.SERVER_IS_ERROR, "计算公式配置不存在"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 匹配计算公式
|
|
|
+ */
|
|
|
+ private String matchFormula(KlCalculatorFormulaDTO formulaConfig, Map<String, Object> params) {
|
|
|
+ return formulaConfig.getScoreTableData().stream()
|
|
|
+ .collect(Collectors.groupingBy(KlCalculatorFormulaDTO.ScoreTableData::getGroupId))
|
|
|
+ .values().stream()
|
|
|
+ .filter(group -> checkGroupConditions(group, params))
|
|
|
+ .findFirst()
|
|
|
+ .map(group -> group.get(0).getFormula())
|
|
|
+ .orElseThrow(() -> new CommonException(CommonErrorCode.SERVER_IS_ERROR, "无匹配计算公式"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查组条件
|
|
|
+ */
|
|
|
+ private boolean checkGroupConditions(List<KlCalculatorFormulaDTO.ScoreTableData> group,
|
|
|
+ Map<String, Object> params) {
|
|
|
+ return group.stream()
|
|
|
+ .allMatch(tableData -> checkSingleCondition(tableData, params));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查单个条件
|
|
|
+ */
|
|
|
+ private boolean checkSingleCondition(KlCalculatorFormulaDTO.ScoreTableData tableData,
|
|
|
+ Map<String, Object> params) {
|
|
|
+ String conditionName = tableData.getConditionName();
|
|
|
+ Object inputValue = params.get(conditionName);
|
|
|
+
|
|
|
+ if (inputValue == null) {
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR, "缺少必要参数: " + conditionName);
|
|
|
+ }
|
|
|
+
|
|
|
+ return Optional.ofNullable(tableData.getConditionType())
|
|
|
+ .map(type -> {
|
|
|
+ if ("数值类型".equals(type)) {
|
|
|
+ return checkNumericCondition(inputValue, tableData.getConditionContent().getValueType());
|
|
|
+ } else if ("文本类型".equals(type)) {
|
|
|
+ return checkTextCondition(inputValue, tableData.getConditionContent().getTextType());
|
|
|
+ }
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR, "未知条件类型: " + type);
|
|
|
+ })
|
|
|
+ .orElseThrow(() -> new CommonException(CommonErrorCode.SERVER_IS_ERROR, "未指定条件类型"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 数值条件校验(使用JDK8的Function接口)
|
|
|
+ */
|
|
|
+ private boolean checkNumericCondition(Object inputValue, KlCalculatorFormulaDTO.ValueType condition) {
|
|
|
+ // 获取最小值校验函数(已绑定 threshold)
|
|
|
+ Function<Double, Boolean> minCheck = getNumericCheckFunction(
|
|
|
+ condition.getMinValueName(),
|
|
|
+ condition.getMinValue() // 直接传递阈值
|
|
|
+ );
|
|
|
+
|
|
|
+ // 获取最大值校验函数(已绑定 threshold)
|
|
|
+ Function<Double, Boolean> maxCheck = getNumericCheckFunction(
|
|
|
+ condition.getMaxValueName(),
|
|
|
+ condition.getMaxValue() // 直接传递阈值
|
|
|
+ );
|
|
|
+
|
|
|
+ try {
|
|
|
+ double value = Double.parseDouble(inputValue.toString());
|
|
|
+ return minCheck.apply(value) && maxCheck.apply(value);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR, "参数类型错误: " + inputValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成数值校验函数
|
|
|
+ */
|
|
|
+ private Function<Double, Boolean> getNumericCheckFunction(String operator, BigDecimal threshold) {
|
|
|
+ if (operator == null || threshold == null) {
|
|
|
+ return value -> true; // 默认通过校验
|
|
|
+ }
|
|
|
+
|
|
|
+ double thresholdValue = threshold.doubleValue();
|
|
|
+ switch (operator) {
|
|
|
+ case ">": return value -> value > thresholdValue;
|
|
|
+ case ">=": return value -> value >= thresholdValue;
|
|
|
+ case "<": return value -> value < thresholdValue;
|
|
|
+ case "<=": return value -> value <= thresholdValue;
|
|
|
+ default:
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR, "无效运算符: " + operator);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文本条件校验
|
|
|
+ */
|
|
|
+ private boolean checkTextCondition(Object inputValue, String expectedText) {
|
|
|
+ return Optional.ofNullable(expectedText)
|
|
|
+ .map(t -> t.equals(inputValue.toString()))
|
|
|
+ .orElseThrow(() -> new CommonException(CommonErrorCode.SERVER_IS_ERROR, "文本条件未配置"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 参数转换(使用Stream API)
|
|
|
+ */
|
|
|
+ private Map<String, Double> convertParams(Map<String, Object> params) {
|
|
|
+ return params.entrySet().stream()
|
|
|
+ .map(entry -> {
|
|
|
+ Optional<Double> valueOpt = tryParseDouble(entry.getValue());
|
|
|
+ return valueOpt.map(v -> new AbstractMap.SimpleEntry<>(entry.getKey(), v));
|
|
|
+ })
|
|
|
+ .filter(Optional::isPresent)
|
|
|
+ .map(Optional::get)
|
|
|
+ .collect(Collectors.toMap(
|
|
|
+ Map.Entry::getKey,
|
|
|
+ Map.Entry::getValue,
|
|
|
+ (existing, replacement) -> existing // 处理重复key的情况
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全转换数值方法(返回Optional)
|
|
|
+ */
|
|
|
+ private Optional<Double> tryParseDouble(Object value) {
|
|
|
+ if (value == null) return Optional.empty();
|
|
|
+
|
|
|
+ try {
|
|
|
+ return Optional.of(Double.parseDouble(value.toString()));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return Optional.empty();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行计算
|
|
|
+ */
|
|
|
+ private String calculateResult(String formula, Map<String, Double> params) {
|
|
|
+ try {
|
|
|
+ double result = new ExpressionBuilder(formula)
|
|
|
+ .variables(params.keySet())
|
|
|
+ .build()
|
|
|
+ .setVariables(params)
|
|
|
+ .evaluate();
|
|
|
+ return formatDouble(result);
|
|
|
+ } catch (IllegalArgumentException | ArithmeticException e) {
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR,
|
|
|
+ "公式计算失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 结果格式化
|
|
|
+ */
|
|
|
+ private String formatDouble(double value) {
|
|
|
+ return BigDecimal.valueOf(value)
|
|
|
+ .setScale(6, RoundingMode.HALF_UP)
|
|
|
+ .stripTrailingZeros()
|
|
|
+ .toPlainString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全数值转换
|
|
|
+ */
|
|
|
+ private Double convertToDouble(String paramName, Object value) {
|
|
|
+ try {
|
|
|
+ return Double.parseDouble(value.toString());
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new CommonException(CommonErrorCode.SERVER_IS_ERROR,
|
|
|
+ "参数[" + paramName + "]转换失败: " + value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 医院端获取静态知识(对接)
|
|
|
*
|