ソースを参照

重构承保规则、费用规则匹配校验

lixiaolong 2 週間 前
コミット
3a85ca35ed

+ 311 - 514
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/FactorMatch.java

@@ -15,7 +15,6 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.*;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /**
 /**
@@ -25,157 +24,99 @@ public class FactorMatch {
 
 
     private static final Logger log = LoggerFactory.getLogger(FactorMatch.class);
     private static final Logger log = LoggerFactory.getLogger(FactorMatch.class);
 
 
-    private static final Map<String, String> plateFourCodeMap = new HashMap<>();
+    private static final Map<String, String> PLATE_FOUR_CODE_MAP = new HashMap<>();
 
 
     static {
     static {
-        plateFourCodeMap.put("1404", "晋D");     plateFourCodeMap.put("1408", "晋M");
-        plateFourCodeMap.put("1411", "晋J");     plateFourCodeMap.put("1403", "晋C");
-        plateFourCodeMap.put("1407", "晋K");     plateFourCodeMap.put("1410", "晋L");
-        plateFourCodeMap.put("1402", "晋B");     plateFourCodeMap.put("1406", "晋F");
-        plateFourCodeMap.put("1401", "晋A");     plateFourCodeMap.put("1405", "晋E");
-        plateFourCodeMap.put("1409", "晋H");     plateFourCodeMap.put("14", "晋");
-        plateFourCodeMap.put("31", "沪");        plateFourCodeMap.put("53", "云");
-        plateFourCodeMap.put("15", "蒙");        plateFourCodeMap.put("11", "京");
-        plateFourCodeMap.put("22", "吉");        plateFourCodeMap.put("51", "川");
-        plateFourCodeMap.put("12", "津");        plateFourCodeMap.put("64", "宁");
-        plateFourCodeMap.put("34", "皖");        plateFourCodeMap.put("37", "鲁");
-        plateFourCodeMap.put("44", "粤");        plateFourCodeMap.put("45", "桂");
-        plateFourCodeMap.put("65", "新");        plateFourCodeMap.put("32", "苏");
-        plateFourCodeMap.put("36", "赣");        plateFourCodeMap.put("13", "冀");
-        plateFourCodeMap.put("41", "豫");        plateFourCodeMap.put("33", "浙");
-        plateFourCodeMap.put("46", "琼");        plateFourCodeMap.put("42", "鄂");
-        plateFourCodeMap.put("43", "湘");        plateFourCodeMap.put("62", "甘");
-        plateFourCodeMap.put("35", "闽");        plateFourCodeMap.put("54", "藏");
-        plateFourCodeMap.put("52", "贵");        plateFourCodeMap.put("21", "辽");
-        plateFourCodeMap.put("50", "渝");        plateFourCodeMap.put("61", "陕");
-        plateFourCodeMap.put("63", "青");        plateFourCodeMap.put("23", "黑");
+        PLATE_FOUR_CODE_MAP.put("1404", "晋D");  PLATE_FOUR_CODE_MAP.put("1408", "晋M");
+        PLATE_FOUR_CODE_MAP.put("1411", "晋J");  PLATE_FOUR_CODE_MAP.put("1403", "晋C");
+        PLATE_FOUR_CODE_MAP.put("1407", "晋K");  PLATE_FOUR_CODE_MAP.put("1410", "晋L");
+        PLATE_FOUR_CODE_MAP.put("1402", "晋B");  PLATE_FOUR_CODE_MAP.put("1406", "晋F");
+        PLATE_FOUR_CODE_MAP.put("1401", "晋A");  PLATE_FOUR_CODE_MAP.put("1405", "晋E");
+        PLATE_FOUR_CODE_MAP.put("1409", "晋H");  PLATE_FOUR_CODE_MAP.put("14",   "晋");
+        PLATE_FOUR_CODE_MAP.put("31",   "沪");   PLATE_FOUR_CODE_MAP.put("53",   "云");
+        PLATE_FOUR_CODE_MAP.put("15",   "蒙");   PLATE_FOUR_CODE_MAP.put("11",   "京");
+        PLATE_FOUR_CODE_MAP.put("22",   "吉");   PLATE_FOUR_CODE_MAP.put("51",   "川");
+        PLATE_FOUR_CODE_MAP.put("12",   "津");   PLATE_FOUR_CODE_MAP.put("64",   "宁");
+        PLATE_FOUR_CODE_MAP.put("34",   "皖");   PLATE_FOUR_CODE_MAP.put("37",   "鲁");
+        PLATE_FOUR_CODE_MAP.put("44",   "粤");   PLATE_FOUR_CODE_MAP.put("45",   "桂");
+        PLATE_FOUR_CODE_MAP.put("65",   "新");   PLATE_FOUR_CODE_MAP.put("32",   "苏");
+        PLATE_FOUR_CODE_MAP.put("36",   "赣");   PLATE_FOUR_CODE_MAP.put("13",   "冀");
+        PLATE_FOUR_CODE_MAP.put("41",   "豫");   PLATE_FOUR_CODE_MAP.put("33",   "浙");
+        PLATE_FOUR_CODE_MAP.put("46",   "琼");   PLATE_FOUR_CODE_MAP.put("42",   "鄂");
+        PLATE_FOUR_CODE_MAP.put("43",   "湘");   PLATE_FOUR_CODE_MAP.put("62",   "甘");
+        PLATE_FOUR_CODE_MAP.put("35",   "闽");   PLATE_FOUR_CODE_MAP.put("54",   "藏");
+        PLATE_FOUR_CODE_MAP.put("52",   "贵");   PLATE_FOUR_CODE_MAP.put("21",   "辽");
+        PLATE_FOUR_CODE_MAP.put("50",   "渝");   PLATE_FOUR_CODE_MAP.put("61",   "陕");
+        PLATE_FOUR_CODE_MAP.put("63",   "青");   PLATE_FOUR_CODE_MAP.put("23",   "黑");
     }
     }
 
 
+    // ============================================================
+    // 主入口
+    // ============================================================
     /**
     /**
-     * 因子比对
-     * @param ptlAgreementCostRuleList
-     * @param ruleAttrMappingVoList
-     * @param undwrtRulesAttrList
-     * @param logs
-     * @return
+     * 判断费用因子是否匹配
+     * @param ptlAgreementCostRuleList 协议规则列表
+     * @param ruleAttrMappingVoList 需要校验的规则属性映射列表
+     * @param undwrtRulesAttrList 协议费用因子元数据列表
+     * @param logs 日志
+     * @param undwrtRulesDictList 协议费用因子元数据字典列表
+     * @return 是否匹配
      */
      */
-    public static boolean judgements(List<PtlAgreementCostRule> ptlAgreementCostRuleList, List<RuleAttrMappingVo> ruleAttrMappingVoList,
-                                     List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList, StringBuilder logs,  List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
-        if (ptlAgreementCostRuleList == null || ptlAgreementCostRuleList.isEmpty()) {
+    public static boolean judgements(List<PtlAgreementCostRule> ptlAgreementCostRuleList,
+                                     List<RuleAttrMappingVo> ruleAttrMappingVoList,
+                                     List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList,
+                                     StringBuilder logs,
+                                     List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
+        if (CollUtil.isEmpty(ptlAgreementCostRuleList)) {
             return false;
             return false;
         }
         }
-        // 按batch分组:不同batch之间为OR关系,同batch内为AND关系
+        // 预建索引:避免内层循环反复 stream().filter() 造成 O(R*A) 扫描
+        Map<String, String> mappingIndex = buildMappingIndex(ruleAttrMappingVoList);
+        Map<String, PtlAgreementUndwrtRulesAttr> attrIndex = buildAttrIndex(undwrtRulesAttrList);
+        Map<Integer, Map<String, String>> dictIndex = buildDictIndex(undwrtRulesDictList);
+        Map<Integer, Map<String, String>> dictNameIndex = buildDictNameIndex(undwrtRulesDictList);
+
+        // 按 batch 分组:不同 batch 之间 OR,同 batch 内 AND
         Map<String, List<PtlAgreementCostRule>> batchMap = ptlAgreementCostRuleList.stream()
         Map<String, List<PtlAgreementCostRule>> batchMap = ptlAgreementCostRuleList.stream()
                 .collect(Collectors.groupingBy(rule -> rule.getBatch() != null ? rule.getBatch() : "default",
                 .collect(Collectors.groupingBy(rule -> rule.getBatch() != null ? rule.getBatch() : "default",
                         LinkedHashMap::new, Collectors.toList()));
                         LinkedHashMap::new, Collectors.toList()));
 
 
         StringBuilder errorLogs = new StringBuilder();
         StringBuilder errorLogs = new StringBuilder();
         for (Map.Entry<String, List<PtlAgreementCostRule>> entry : batchMap.entrySet()) {
         for (Map.Entry<String, List<PtlAgreementCostRule>> entry : batchMap.entrySet()) {
-            List<PtlAgreementCostRule> batchRules = entry.getValue();
             StringBuilder batchLogs = new StringBuilder();
             StringBuilder batchLogs = new StringBuilder();
-            boolean batchMatched = doJudgementsMatch(batchRules, ruleAttrMappingVoList, undwrtRulesAttrList,
-                    batchLogs, undwrtRulesDictList);
-            if (batchMatched) {
-                // OR关系:只要任一个batch匹配即通过
-                return true;
+            if (doJudgementsMatch(entry.getValue(), mappingIndex, attrIndex, dictIndex, dictNameIndex, batchLogs)) {
+                return true; // OR:任一 batch 匹配即通过
             }
             }
             errorLogs.append(batchLogs);
             errorLogs.append(batchLogs);
         }
         }
-        // 所有batch都未匹配,输出错误日志
         logs.append(errorLogs);
         logs.append(errorLogs);
         return false;
         return false;
     }
     }
 
 
-    /**
-     * 判断一个batch内的规则是否全部匹配(AND关系)
-     */
-    private static boolean doJudgementsMatch(List<PtlAgreementCostRule> batchRules, List<RuleAttrMappingVo> ruleAttrMappingVoList,
-                                             List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList, StringBuilder logs,
-                                             List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
-        for (PtlAgreementCostRule ptlAgreementProductCostsAttrLink : batchRules) {
-            //查找规则中的 传值属性
-            RuleAttrMappingVo ruleAttrMappingVo = ruleAttrMappingVoList.stream()
-                    .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                    .findFirst()
-                    .orElse(null);
-            //如果查找不到 传值属性 直接费用对比失败
-            if (ruleAttrMappingVo == null) {
-                List<PtlAgreementUndwrtRulesAttr> collect = undwrtRulesAttrList.stream()
-                        .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                        .toList();
-                if (!collect.isEmpty()) {
-                    logs.append("规则匹配失败:" + collect.get(0).getAttrName() + "\n");
-                }
-                return false;
-            }
-
-            //查找 费用因子规则
-            PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr = undwrtRulesAttrList.stream()
-                    .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                    .findFirst()
-                    .get();
+    // ============================================================
+    // 判断一个batch内的规则是否全部匹配(AND关系)
+    // ============================================================
 
 
-            boolean useFlag;
-            if(!Objects.isNull(ruleAttrMappingVo.getValue())) {
-                useFlag = attrEqual(ptlAgreementProductCostsAttrLink, ptlAgreementUndwrtRulesAttr, ruleAttrMappingVo.getValue(),
-                        false, undwrtRulesDictList);
-            }else{
-                useFlag = false;
+    private static boolean doJudgementsMatch(List<PtlAgreementCostRule> batchRules,
+                                             Map<String, String> mappingIndex,
+                                             Map<String, PtlAgreementUndwrtRulesAttr> attrIndex,
+                                             Map<Integer, Map<String, String>> dictIndex,
+                                             Map<Integer, Map<String, String>> dictNameIndex,
+                                             StringBuilder logs) {
+        for (PtlAgreementCostRule rule : batchRules) {
+            String value = mappingIndex.get(rule.getAttrCode());
+            if (value == null) {
+                // 找不到传值属性时的日志
+                appendMissingAttrLog(logs, attrIndex, rule.getAttrCode(), false);
+                return false;
             }
             }
+            // 协议费用因子元数据
+            PtlAgreementUndwrtRulesAttr meta = attrIndex.get(rule.getAttrCode());
+            boolean useFlag = attrEqual(rule, meta, value, false, dictIndex, dictNameIndex);
 
 
             if (!useFlag) {
             if (!useFlag) {
-                List<PtlAgreementUndwrtRulesAttr> collect = undwrtRulesAttrList.stream()
-                        .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                        .toList();
-
-                boolean isPlateAttr = "LicenseNo".equals(ptlAgreementProductCostsAttrLink.getAttrCode())
-                        || "LicenseArea".equals(ptlAgreementProductCostsAttrLink.getAttrCode());
-
-                // 检验的值
-                String ruleVal = ptlAgreementProductCostsAttrLink.getMin() != null
-                        ? ptlAgreementProductCostsAttrLink.getMin()
-                        : ptlAgreementProductCostsAttrLink.getMax();
-                if (isPlateAttr) {
-                    StringBuilder str = new StringBuilder();
-                    for (String s : ruleVal.split(",")) {
-                        str.append(plateFourCodeMap.getOrDefault(s, s)).append(",");
-                    }
-                    logs.append("检验的值:").append(str.toString()).append("\n");
-                } else {
-                    Integer dictId = collect.get(0).getDictId();
-                    Map<String, String> codeToNameMap = new HashMap<>();
-                    if (dictId != null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                        for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                            if (Objects.equals(label.getDictId(), dictId)) {
-                                codeToNameMap.put(label.getCode(), label.getName());
-                            }
-                        }
-                    }
-                    logs.append("检验的值:").append(displayRuleValue(ruleVal, codeToNameMap)).append("\n");
-                }
-
-                // 传入的值
-                String inVal = ruleAttrMappingVo.getValue();
-                if (isPlateAttr) {
-                    logs.append("传入的值:" + plateFourCodeMap.getOrDefault(inVal, inVal) + "\n");
-                } else {
-                    Integer dictId = collect.get(0).getDictId();
-                    Map<String, String> codeToNameMap = new HashMap<>();
-                    if (dictId != null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                        for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                            if (Objects.equals(label.getDictId(), dictId)) {
-                                codeToNameMap.put(label.getCode(), label.getName());
-                            }
-                        }
-                    }
-                    String name = codeToNameMap.getOrDefault(inVal, inVal);
-                    logs.append("传入的值:" + (codeToNameMap.isEmpty() ? inVal : name) + "\n");
-                }
-
-                if (CollUtil.isNotEmpty(collect)) {
-                    logs.append("规则匹配失败:" + collect.get(0).getAttrName() + "\n");
-                }
-
+                appendMismatchLog(logs, attrIndex, dictIndex, rule.getAttrCode(),
+                        rule, value, false);
                 return false;
                 return false;
             }
             }
         }
         }
@@ -183,22 +124,122 @@ public class FactorMatch {
     }
     }
 
 
     // ============================================================
     // ============================================================
-    // 字典映射
+    // 索引预构建:把 stream().filter() 的 O(A) 一次性摊成 O(1) 查找
     // ============================================================
     // ============================================================
 
 
-    /**
-     * 展示"检验的值":规则里存的是 code(如 04),翻成 name(如 家庭自用)方便对账。
-     * 支持多选逗号分隔,逐个翻译;翻译不到的保留原 code。
-     */
+    /** attrCode -> value 预建索引 */
+    private static Map<String, String> buildMappingIndex(List<RuleAttrMappingVo> list) {
+        Map<String, String> map = new HashMap<>();
+        if (CollUtil.isNotEmpty(list)) {
+            for (RuleAttrMappingVo v : list) map.put(v.getAttrCode(), v.getValue());
+        }
+        return map;
+    }
+
+    /** attrCode -> meta 预建索引 */
+    private static Map<String, PtlAgreementUndwrtRulesAttr> buildAttrIndex(List<PtlAgreementUndwrtRulesAttr> list) {
+        Map<String, PtlAgreementUndwrtRulesAttr> map = new HashMap<>();
+        if (CollUtil.isNotEmpty(list)) {
+            for (PtlAgreementUndwrtRulesAttr a : list) map.put(a.getAttrCode(), a);
+        }
+        return map;
+    }
+
+    /** dictId -> (code -> name) 预建索引,供日志展示使用 */
+    private static Map<Integer, Map<String, String>> buildDictIndex(List<PtlAgreementUndwrtRulesDictLabal> dictList) {
+        Map<Integer, Map<String, String>> map = new HashMap<>();
+        if (CollUtil.isNotEmpty(dictList)) {
+            for (PtlAgreementUndwrtRulesDictLabal l : dictList) {
+                Integer dictId = l.getDictId();
+                if (dictId == null) continue;
+                map.computeIfAbsent(dictId, k -> new HashMap<>()).put(l.getCode(), l.getName());
+            }
+        }
+        return map;
+    }
+
+    /** dictId -> (name -> code) 预建索引,供 normalizeToCode 使用 */
+    private static Map<Integer, Map<String, String>> buildDictNameIndex(List<PtlAgreementUndwrtRulesDictLabal> dictList) {
+        Map<Integer, Map<String, String>> map = new HashMap<>();
+        if (CollUtil.isNotEmpty(dictList)) {
+            for (PtlAgreementUndwrtRulesDictLabal l : dictList) {
+                Integer dictId = l.getDictId();
+                if (dictId == null) continue;
+                map.computeIfAbsent(dictId, k -> new HashMap<>()).put(l.getName(), l.getCode());
+            }
+        }
+        return map;
+    }
+
+    // ============================================================
+    // 工具方法
+    // ============================================================
+
+    private static PtlAgreementCostRule toCostRule(PtlAgreementUndwrtRulesAttrLink link) {
+        PtlAgreementCostRule r = new PtlAgreementCostRule();
+        r.setMin(link.getMin());
+        r.setMax(link.getMax());
+        r.setOperator(link.getOperator());
+        r.setAttrCode(link.getAttrCode());
+        return r;
+    }
+
+    /** 找不到传值属性时的日志(基于预建索引) */
+    private static void appendMissingAttrLog(StringBuilder logs,
+                                             Map<String, PtlAgreementUndwrtRulesAttr> attrIndex,
+                                             String attrCode, boolean html) {
+        PtlAgreementUndwrtRulesAttr a = attrIndex.get(attrCode);
+        if (a != null) {
+            logs.append((html ? "<span style=\"color:red;\">" : ""))
+                    .append("规则匹配失败:").append(a.getAttrName())
+                    .append(html ? "</span>" : "").append("\n");
+        }
+    }
+
+    /** 匹配失败时的"检验的值 / 传入的值"日志(基于预建索引) */
+    private static void appendMismatchLog(StringBuilder logs,
+                                          Map<String, PtlAgreementUndwrtRulesAttr> attrIndex,
+                                          Map<Integer, Map<String, String>> dictIndex,
+                                          String attrCode,
+                                          PtlAgreementCostRule rule,
+                                          String inVal,
+                                          boolean html) {
+        PtlAgreementUndwrtRulesAttr meta = attrIndex.get(attrCode);
+        if (meta == null) return;
+
+        boolean isPlate = "LicenseNo".equals(attrCode) || "LicenseArea".equals(attrCode);
+        String ruleVal = rule.getMin() != null ? rule.getMin() : rule.getMax();
+        Map<String, String> codeToNameMap = meta.getDictId() == null ? null : dictIndex.get(meta.getDictId());
+
+        // 检验的值
+        if (isPlate) {
+            StringBuilder sb = new StringBuilder();
+            for (String s : ruleVal.split(",")) sb.append(PLATE_FOUR_CODE_MAP.getOrDefault(s, s)).append(",");
+            logs.append("检验的值:").append(sb).append("\n");
+        } else {
+            logs.append("检验的值:").append(displayRuleValue(ruleVal, codeToNameMap)).append("\n");
+        }
+
+        // 传入的值
+        if (isPlate) {
+            logs.append("传入的值:").append(PLATE_FOUR_CODE_MAP.getOrDefault(inVal, inVal)).append("\n");
+        } else {
+            logs.append("传入的值:").append(codeToNameMap == null || codeToNameMap.isEmpty() ? inVal : codeToNameMap.getOrDefault(inVal, inVal)).append("\n");
+        }
+
+        logs.append((html ? "<span style=\"color:red;\">" : "") + "规则匹配失败:" + meta.getAttrName()
+                + (html ? "</span>" : "") + "\n");
+    }
+
+    /** 规则值 code -> name 展示(如 04 -> 家庭自用(04)) */
     private static String displayRuleValue(String ruleVal, Map<String, String> codeToNameMap) {
     private static String displayRuleValue(String ruleVal, Map<String, String> codeToNameMap) {
         if (ruleVal == null) return "";
         if (ruleVal == null) return "";
-        if (CollUtil.isEmpty(codeToNameMap)) return ruleVal;
-        String[] codes = Arrays.stream(ruleVal.split(","))
-                .map(String::trim).filter(s -> !s.isEmpty()).toArray(String[]::new);
-        if (codes.length == 0) return ruleVal;
+        if (codeToNameMap == null || codeToNameMap.isEmpty()) return ruleVal;
+        String[] codes = ruleVal.split(",");
         StringBuilder sb = new StringBuilder();
         StringBuilder sb = new StringBuilder();
         for (int i = 0; i < codes.length; i++) {
         for (int i = 0; i < codes.length; i++) {
-            String code = codes[i];
+            String code = codes[i].trim();
+            if (code.isEmpty()) continue;
             String name = codeToNameMap.getOrDefault(code, code);
             String name = codeToNameMap.getOrDefault(code, code);
             sb.append(name).append("(").append(code).append(")");
             sb.append(name).append("(").append(code).append(")");
             if (i < codes.length - 1) sb.append(",");
             if (i < codes.length - 1) sb.append(",");
@@ -206,422 +247,178 @@ public class FactorMatch {
         return sb.toString();
         return sb.toString();
     }
     }
 
 
-    public static boolean judgementsUnderwrite(List<PtlAgreementUndwrtRulesAttrLink> ptlAgreementCostRuleList, List<RuleAttrMappingVo> ruleAttrMappingVoList,
-                                     List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList, StringBuilder logs, List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
-        boolean useFlag = true;
+    public static boolean judgementsUnderwrite(List<PtlAgreementUndwrtRulesAttrLink> ptlAgreementCostRuleList,
+                                               List<RuleAttrMappingVo> ruleAttrMappingVoList,
+                                               List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList,
+                                               StringBuilder logs,
+                                               List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
+        // 预建索引:避免内层循环反复 stream().filter() 造成 O(R*A) 扫描
+        Map<String, String> mappingIndex = buildMappingIndex(ruleAttrMappingVoList);
+        Map<String, PtlAgreementUndwrtRulesAttr> attrIndex = buildAttrIndex(undwrtRulesAttrList);
+        Map<Integer, Map<String, String>> dictIndex = buildDictIndex(undwrtRulesDictList);
+        Map<Integer, Map<String, String>> dictNameIndex = buildDictNameIndex(undwrtRulesDictList);
+
         boolean groupUseFlag = false;
         boolean groupUseFlag = false;
-        for (PtlAgreementUndwrtRulesAttrLink ptlAgreementProductCostsAttrLink : ptlAgreementCostRuleList) {
-            //查找规则中的 传值属性
-            RuleAttrMappingVo ruleAttrMappingVo = ruleAttrMappingVoList.stream()
-                    .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                    .findFirst()
-                    .orElse(null);
-            //如果查找不到 传值属性 直接费用对比失败
-            if (ruleAttrMappingVo == null) {
-                log.info("承保规则校验-属性未找到: attrCode={}", ptlAgreementProductCostsAttrLink.getAttrCode());
-                log.info("承保规则校验-规则匹配失败: attrCode={}", ptlAgreementProductCostsAttrLink.getAttrCode());
-
-                List<PtlAgreementUndwrtRulesAttr> collect = undwrtRulesAttrList.stream()
-                        .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                        .toList();
-                if (!collect.isEmpty()) {
-                    logs.append("规则匹配失败:" + collect.get(0).getAttrName() + "\n");
-                }
+        for (PtlAgreementUndwrtRulesAttrLink link : ptlAgreementCostRuleList) {
+            String value = mappingIndex.get(link.getAttrCode());
+            if (value == null) {
+                log.info("承保规则校验-属性未找到: attrCode={}", link.getAttrCode());
+                appendMissingAttrLog(logs, attrIndex, link.getAttrCode(), true);
                 return false;
                 return false;
             }
             }
 
 
+            PtlAgreementUndwrtRulesAttr meta = attrIndex.get(link.getAttrCode());
+            PtlAgreementCostRule costRule = toCostRule(link);
 
 
-            //查找 费用因子规则
-            PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr = undwrtRulesAttrList.stream()
-                    .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                    .findFirst()
-                    .get();
-
-            if(!Objects.isNull(ruleAttrMappingVo.getValue())) {
-                PtlAgreementCostRule ptlAgreementCostRule = new PtlAgreementCostRule();
-                ptlAgreementCostRule.setMin(ptlAgreementProductCostsAttrLink.getMin());
-                ptlAgreementCostRule.setMax(ptlAgreementProductCostsAttrLink.getMax());
-                ptlAgreementCostRule.setOperator(ptlAgreementProductCostsAttrLink.getOperator());
-                ptlAgreementCostRule.setAttrCode(ptlAgreementProductCostsAttrLink.getAttrCode());
-                useFlag = attrEqual(ptlAgreementCostRule, ptlAgreementUndwrtRulesAttr, ruleAttrMappingVo.getValue(),
-                        true, undwrtRulesDictList);
+            boolean useFlag;
+            if (StrUtil.isNotBlank(value)) {
+                useFlag = attrEqual(costRule, meta, value, true, dictIndex, dictNameIndex);
                 log.info("承保规则校验-单属性匹配: attrCode={}, attrType={}, 规则值(min={}, max={}, operator={}), 传入值={}, useFlag={}",
                 log.info("承保规则校验-单属性匹配: attrCode={}, attrType={}, 规则值(min={}, max={}, operator={}), 传入值={}, useFlag={}",
-                        ptlAgreementProductCostsAttrLink.getAttrCode(), ptlAgreementUndwrtRulesAttr.getAttrType(),
-                        ptlAgreementProductCostsAttrLink.getMin(), ptlAgreementProductCostsAttrLink.getMax(),
-                        ptlAgreementProductCostsAttrLink.getOperator(), ruleAttrMappingVo.getValue(), useFlag);
-            }else{
+                        link.getAttrCode(), meta == null ? null : meta.getAttrType(), link.getMin(), link.getMax(),
+                        link.getOperator(), value, useFlag);
+            } else {
                 useFlag = false;
                 useFlag = false;
-                log.info("承保规则校验-传入值为null: attrCode={}, useFlag=false", ptlAgreementProductCostsAttrLink.getAttrCode());
-            }
-
-            if(useFlag){
-                groupUseFlag = useFlag;
+                log.info("承保规则校验-传入值为null: attrCode={}, useFlag=false", link.getAttrCode());
             }
             }
 
 
-            if (!useFlag) {
+            if (useFlag) {
+                groupUseFlag = true;
+            } else {
                 log.info("承保规则校验-条件命中! attrCode={}, 传入值={}, 规则min={}, max={}, operator={}",
                 log.info("承保规则校验-条件命中! attrCode={}, 传入值={}, 规则min={}, max={}, operator={}",
-                        ptlAgreementProductCostsAttrLink.getAttrCode(), ruleAttrMappingVo.getValue(),
-                        ptlAgreementProductCostsAttrLink.getMin(), ptlAgreementProductCostsAttrLink.getMax(),
-                        ptlAgreementProductCostsAttrLink.getOperator());
-
-                List<PtlAgreementUndwrtRulesAttr> collect = undwrtRulesAttrList.stream()
-                        .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
-                        .toList();
-                boolean isPlateAttr = "LicenseNo".equals(ptlAgreementProductCostsAttrLink.getAttrCode())
-                        || "LicenseArea".equals(ptlAgreementProductCostsAttrLink.getAttrCode());
-
-                // 检验的值
-                String ruleVal = ptlAgreementProductCostsAttrLink.getMin() != null
-                        ? ptlAgreementProductCostsAttrLink.getMin()
-                        : ptlAgreementProductCostsAttrLink.getMax();
-                if (isPlateAttr) {
-                    StringBuilder str = new StringBuilder();
-                    for (String s : ruleVal.split(",")) {
-                        str.append(plateFourCodeMap.getOrDefault(s, s)).append(",");
-                    }
-                    logs.append("检验的值:").append(str.toString()).append("\n");
-                } else {
-                    Integer dictId = collect.get(0).getDictId();
-                    Map<String, String> codeToNameMap = new HashMap<>();
-                    if (dictId != null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                        for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                            if (Objects.equals(label.getDictId(), dictId)) {
-                                codeToNameMap.put(label.getCode(), label.getName());
-                            }
-                        }
-                    }
-                    logs.append("检验的值:").append(displayRuleValue(ruleVal, codeToNameMap)).append("\n");
-                }
-
-                // 传入的值
-                String inVal = ruleAttrMappingVo.getValue();
-                if (isPlateAttr) {
-                    logs.append("传入的值:" + plateFourCodeMap.getOrDefault(inVal, inVal) + "\n");
-                } else {
-                    Integer dictId = collect.get(0).getDictId();
-                    Map<String, String> codeToNameMap = new HashMap<>();
-                    if (dictId != null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                        for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                            if (Objects.equals(label.getDictId(), dictId)) {
-                                codeToNameMap.put(label.getCode(), label.getName());
-                            }
-                        }
-                    }
-                    String name = codeToNameMap.getOrDefault(inVal, inVal);
-                    logs.append("传入的值:" + (codeToNameMap.isEmpty() ? inVal : name) + "\n");
-                }
-
-                if (CollUtil.isNotEmpty(collect)) {
-                    logs.append("<span style=\"color:red;\">规则匹配失败:" + collect.get(0).getAttrName() + "</span>\n");
-                }
+                        link.getAttrCode(), value, link.getMin(), link.getMax(), link.getOperator());
+                appendMismatchLog(logs, attrIndex, dictIndex, link.getAttrCode(),
+                        costRule, value, true);
             }
             }
         }
         }
-        log.info("承保规则校验-所有条件均未命中, 规则组通过, useFlag={}", useFlag);
+        log.info("承保规则校验-所有条件均未命中, 规则组通过, useFlag={}", groupUseFlag);
         return groupUseFlag;
         return groupUseFlag;
     }
     }
 
 
-    private static boolean attrEqual(PtlAgreementCostRule ptlAgreementCostRule,
-                              PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, String value, boolean isUnderwrite,
-                              List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
-
-        if (ptlAgreementUndwrtRulesAttr == null || ptlAgreementUndwrtRulesAttr.getAttrType() == null) {
-            // 没有元数据:无法得知 attrType,按"包含"语义做纯文本兜底比较
-            // (BrandandModel 等 inputTag 大多是 contains 语义,与原逻辑 inputTextEqual 默认分支一致)
-            log.warn("attrEqual 因子元数据缺失,按纯文本兜底比较, attrCode={}", ptlAgreementCostRule.getAttrCode());
-            if (ptlAgreementCostRule.getMin() == null || value == null) return false;
-            String finalNormalizedValue = value;
-            boolean contains = Arrays.stream(ptlAgreementCostRule.getMin().split(","))
-                    .anyMatch(o -> finalNormalizedValue.contains(o.trim()));
-            return isUnderwrite ? !contains : contains;
-        }
+    // ============================================================
+    // 比较分发(统一把传入 name 还原成 code)
+    // ============================================================
 
 
-        // 字典还原依据:参与比较的这条因子元数据 ptlAgreementUndwrtRulesAttr 自身挂的 dictId,
-        // 再到 undwrtRulesDictList 中按 name 找 code(如 "家庭自用" -> "04")。
-        Integer dictId = ptlAgreementUndwrtRulesAttr.getDictId();
-        // ===== 新增:把传入的 name 还原成 code 再参与比较 =====
-        // 只对依赖字典的 select/radio/checkbox 做还原;input/inputNumber/date 等保留原值
-        if (CollUtil.isNotEmpty(undwrtRulesDictList)
-                && dictId != null
-                && !ptlAgreementCostRule.getAttrCode().equals("LicenseArea") // 车牌归属地无字典,纯文本
-                && !ptlAgreementCostRule.getAttrCode().equals("LicenseNo")) {
-            String normalized = value;
-            for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                if (Objects.equals(label.getDictId(), dictId) && label.getName().equals(value)) {
-                    normalized = label.getCode();
-                    break;
-                }
-            }
-            value = normalized;
-        }
-        // ====================================================
-        boolean result = false;
-        if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("checkbox")) {
-            result = checkBoxEqual(ptlAgreementCostRule, value, dictId, undwrtRulesDictList);
-        } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("inputNumber")) {
-            result = inputNumberEqual(ptlAgreementCostRule, value);
-        } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("input") || ptlAgreementUndwrtRulesAttr.getAttrType().equals("inputTag")) {
-            //截取身份证号码
-            if (ptlAgreementCostRule.getAttrCode().equals("CarOwnersIDNumber") || ptlAgreementCostRule.getAttrCode().equals("ApplicantIDNumber") ||
-                    ptlAgreementCostRule.getAttrCode().equals("InsuredIDNumber")) {
-                value = value.substring(0, ptlAgreementCostRule.getMin().length());
-            }
-            result = inputTextEqual(ptlAgreementCostRule, value);
-        } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("select")) {
-            //截取车牌
-            if (ptlAgreementUndwrtRulesAttr.getAttrCode().equals("LicenseNo")) {
-                value = value.substring(0, 4);
-            }
-            //车龄走数值比较,不走字典
-            if (ptlAgreementUndwrtRulesAttr.getAttrCode().equals("VehicleAge")) {
-                result = inputNumberEqual(ptlAgreementCostRule, value);
-            // LicenseArea 是 select 但无 dictId,按 input 纯文本比较(不走字典还原)
-            } else if (ptlAgreementUndwrtRulesAttr.getAttrCode().equals("LicenseArea")) {
-                result = inputTextEqual(ptlAgreementCostRule, value);
-            } else {
-                result = selectEqual(ptlAgreementCostRule, value, dictId, undwrtRulesDictList);
-            }
-        } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("radio")) {
-            result = radioEqual(ptlAgreementCostRule, value, dictId, undwrtRulesDictList);
-        } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("datetime")){ // 时间
-            result = dateTimeEqual(ptlAgreementCostRule, value);
-        } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("date")){
-            result = dateEqual(ptlAgreementCostRule, value);
+    private static boolean attrEqual(PtlAgreementCostRule rule,
+                                     PtlAgreementUndwrtRulesAttr meta,
+                                     String value,
+                                     boolean isUnderwrite,
+                                     Map<Integer, Map<String, String>> dictIndex,
+                                     Map<Integer, Map<String, String>> dictNameIndex) {
+        if (meta == null || meta.getAttrType() == null) {
+            log.warn("attrEqual 因子元数据缺失,按纯文本兜底比较, attrCode={}", rule.getAttrCode());
+            if ((rule.getMin() == null && rule.getMax() == null) || value == null) return false;
+            return Arrays.stream(rule.getMin().split(","))
+                    .anyMatch(o -> value.contains(o.trim())) == !isUnderwrite;
         }
         }
-        // 判断是否是承保规则
-        if(isUnderwrite){
-            return !result;
-        }else {
-            return result;
+
+        // 统一还原:传入的是 name 就翻成 code(排除无字典的纯文本字段),O(1) 查表
+        final String cmpValue = normalizeToCode(value, meta, dictNameIndex);
+
+        boolean result;
+        switch (meta.getAttrType()) {
+            case "checkbox":    result = checkBoxEqual(rule, cmpValue); break;
+            case "inputNumber": result = inputNumberEqual(rule, cmpValue); break;
+            case "input":
+            case "inputTag":    result = inputTextEqual(rule, cmpValue); break;
+            case "select":      result = selectEqual(rule, cmpValue, meta); break;
+            case "radio":       result = radioEqual(rule, cmpValue); break;
+            case "datetime":    result = dateTimeEqual(rule, cmpValue); break;
+            case "date":        result = dateEqual(rule, cmpValue); break;
+            default:            result = false;
         }
         }
+        return isUnderwrite ? !result : result;
     }
     }
 
 
-
-    private static boolean checkBoxEqual(PtlAgreementCostRule ptlAgreementCostRule, String value, Integer dictId,
-                                  List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
-        if (ptlAgreementCostRule.getMin() == null || value == null) {
-            return false;
-        }
-        // dictId 为空时(无字典):把 min 里的 code 翻成 name,再用实际 value(汉字)与 name 比较
-        String[] split = ptlAgreementCostRule.getMin().split(",");
-        for (String sp : split) {
-            String spTrim = sp.trim();
-            if (dictId == null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                    if (label.getCode().equals(spTrim) && value.trim().equals(label.getName())) {
-                        return true;
-                    }
-                }
-            }
-            if (spTrim.equals(value.trim())) {
-                return true;
-            }
-        }
-        return false;
+    /** 把传入值统一还原成 code(name -> code),基于预建索引 O(1) */
+    private static String normalizeToCode(String value,
+                                          PtlAgreementUndwrtRulesAttr meta,
+                                          Map<Integer, Map<String, String>> dictNameIndex) {
+        if (value == null || dictNameIndex.isEmpty()) return value;
+        Integer dictId = meta.getDictId();
+        if (dictId == null) return value;
+        String code = meta.getAttrCode();
+        if ("LicenseNo".equals(code) || "LicenseArea".equals(code)) return value; // 无字典从PLATE_FOUR_CODE_MAP获取还原
+        Map<String, String> nameToCode = dictNameIndex.get(dictId);
+        if (nameToCode == null || nameToCode.isEmpty()) return value;
+        return nameToCode.getOrDefault(value, value);
     }
     }
 
 
-    private static boolean inputNumberEqual(PtlAgreementCostRule ptlAgreementCostRule, String value) {
-        double v = Double.parseDouble(value);
-        //统一处理数值
-        if ("1".equals(ptlAgreementCostRule.getOperator())) {
-            if (Double.parseDouble(ptlAgreementCostRule.getMin()) <= v &&
-                    Double.parseDouble(ptlAgreementCostRule.getMax()) >= v) {
-                return true;
-            }
-        } else if ("2".equals(ptlAgreementCostRule.getOperator())) {
-            if (Double.parseDouble(ptlAgreementCostRule.getMin()) > v) {
-                return true;
-            }
-        } else if ("3".equals(ptlAgreementCostRule.getOperator())) {
-            if (Double.parseDouble(ptlAgreementCostRule.getMin()) >= v) {
-                return true;
-            }
-        } else if ("4".equals(ptlAgreementCostRule.getOperator())) {
-            if (Double.parseDouble(ptlAgreementCostRule.getMax()) < v) {
-                return true;
-            }
-        } else if ("5".equals(ptlAgreementCostRule.getOperator())) {
-            if (Double.parseDouble(ptlAgreementCostRule.getMax()) <= v) {
-                return true;
-            }
-        }
-        return false;
+    private static boolean checkBoxEqual(PtlAgreementCostRule rule, String value) {
+        if (rule.getMin() == null || value == null) return false;
+        return Arrays.stream(rule.getMin().split(",")).anyMatch(o -> o.trim().equals(value.trim()));
     }
     }
 
 
-    private static boolean inputTextEqual(PtlAgreementCostRule ptlAgreementCostRule, String value) {
-        //将值进行分割
-        String[] split = ptlAgreementCostRule.getMin().split(",");
-        AtomicBoolean returnBool = new AtomicBoolean(false);
-
-        List<String> equalsMap = new ArrayList<>();
-        //字符型评分
-        equalsMap.add("CharacterBasedRating");
-        //交强字符型评分
-        equalsMap.add("JqCharacterBasedRating");
-        //商业字符型评分
-        equalsMap.add("SyCharacterBasedRating");
-
-
-
-        //equest 等于判断
-        if(equalsMap.contains(ptlAgreementCostRule.getAttrCode())){
-            boolean b = Arrays.stream(split).anyMatch(value::equals);
-            returnBool.set(b);
-        }else if((ptlAgreementCostRule.getAttrCode().equals("BrandandModel") || ptlAgreementCostRule.getAttrCode().equals("CarOwnerName")
-                || ptlAgreementCostRule.getAttrCode().equals("CarOwnersAddress") || ptlAgreementCostRule.getAttrCode().equals("ApplicantAddress")
-                || ptlAgreementCostRule.getAttrCode().equals("InsuredAddress"))
-                && ptlAgreementCostRule.getOperator().equals("2")){
-            //处理品牌型号  不包含的情况
-            boolean anyMatch = Arrays.stream(split)
-                    .anyMatch(value::contains);
-            returnBool.set(!anyMatch);
-        }else {
-            boolean anyMatch = Arrays.stream(split)
-                    .anyMatch(value::contains);
-            returnBool.set(anyMatch);
-        }
-        return returnBool.get();
+    private static boolean selectEqual(PtlAgreementCostRule rule, String value, PtlAgreementUndwrtRulesAttr meta) {
+        if (rule.getMin() == null || value == null) return false;
+        if ("LicenseArea".equals(meta.getAttrCode()) || "LicenseNo".equals(meta.getAttrCode())) return inputTextEqual(rule, value);
+        if ("VehicleAge".equals(meta.getAttrCode())) return inputNumberEqual(rule, value);
+        return rule.getMin().contains(value);
     }
     }
 
 
-    private static boolean selectEqual(PtlAgreementCostRule ptlAgreementCostRule, String value, Integer dictId,
-                                List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
-        if (ptlAgreementCostRule.getMin() == null || value == null) {
-            return false;
-        }
-        // dictId 为空时(无字典):把 min 里的 code 翻成 name,再用实际 value(汉字)与 name 比较
-        if (ptlAgreementCostRule.getOperator().equals("2")) {
-            String [] options = ptlAgreementCostRule.getMin().split(",");
-            for (String opt : options) {
-                String optTrim = opt.trim();
-                if (optTrim.equals(value.trim())) {
-                    return true;
-                }
-                if (dictId == null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                    for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                        if (label.getCode().equals(optTrim) && value.trim().equals(label.getName())) {
-                            return true;
-                        }
-                    }
-                }
-            }
+    private static boolean radioEqual(PtlAgreementCostRule rule, String value) {
+        if ("NewEnergyMark".equals(rule.getAttrCode())) {
+            boolean newEnergy = EnergyTypeCode.isNewEnergy(value);
+            if (newEnergy && "1".equals(rule.getMin())) return true;
+            if (!newEnergy && "0".equals(rule.getMin())) return true;
             return false;
             return false;
-        } else if (ptlAgreementCostRule.getMin() != null) {
-            if (ptlAgreementCostRule.getMin().contains(value)) {
-                return true;
-            }
-            if (dictId == null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                    if (label.getCode().equals(ptlAgreementCostRule.getMin()) && value.equals(label.getName())) {
-                        return true;
-                    }
-                }
-            }
         }
         }
-
-        return false;
+        return rule.getMin().contains(value);
     }
     }
 
 
-    private static boolean radioEqual(PtlAgreementCostRule ptlAgreementCostRule, String value, Integer dictId,
-                               List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
-        //新能源判断
-        if ("NewEnergyMark".equals(ptlAgreementCostRule.getAttrCode())) {
-            // 是否是新能源车
-            boolean newEnergy = EnergyTypeCode.isNewEnergy(value);
-            if (newEnergy && ptlAgreementCostRule.getMin().equals("1")) {
-                return true;
-            } else if (!newEnergy && ptlAgreementCostRule.getMin().equals("0")) {
-                return true;
-            }
-        } else {
-            // dictId 为空时(无字典):把 min 里的 code 翻成 name,再用实际 value(汉字)与 name 比较
-            if (dictId == null && CollUtil.isNotEmpty(undwrtRulesDictList)) {
-                for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
-                    if (label.getCode().equals(ptlAgreementCostRule.getMin()) && value.equals(label.getName())) {
-                        return true;
-                    }
-                }
-            }
-            if (ptlAgreementCostRule.getMin().contains(value)) {
-                return true;
-            }
+    private static boolean inputNumberEqual(PtlAgreementCostRule rule, String value) {
+        if ((rule.getMin() == null && rule.getMax() == null) || value == null) return false;
+        double v = Double.parseDouble(value);
+        double min = rule.getMin() != null ? Double.parseDouble(rule.getMin()) : Double.MIN_VALUE;
+        double max = rule.getMax() != null ? Double.parseDouble(rule.getMax()) : Double.MAX_VALUE;
+        switch (rule.getOperator()) {
+            case "1": return min <= v && v <= max;
+            case "2": return min > v;
+            case "3": return min >= v;
+            case "4": return max < v;
+            case "5": return max <= v;
+            default:  return false;
         }
         }
-        return false;
     }
     }
 
 
-    public static boolean dateTimeEqual(PtlAgreementCostRule ptlAgreementCostRule, String value){
-        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-        if(Objects.isNull(value) || value.equals("")){
-            return false;
-        }
-        LocalDateTime v = LocalDateTime.parse(value,formatter);
-        LocalDateTime min = null,max = null;
-        if(!Objects.isNull(ptlAgreementCostRule.getMin())){
-            min = LocalDateTime.parse(ptlAgreementCostRule.getMin(),formatter);
-        }
-        if(!Objects.isNull(ptlAgreementCostRule.getMax())){
-            max = LocalDateTime.parse(ptlAgreementCostRule.getMax(),formatter);
+    private static boolean inputTextEqual(PtlAgreementCostRule rule, String value) {
+        if (rule.getMin() == null || value == null) return false;
+        String[] split = rule.getMin().split(",");
+        List<String> equalsMap = Arrays.asList("CharacterBasedRating", "JqCharacterBasedRating", "SyCharacterBasedRating");
+        if (equalsMap.contains(rule.getAttrCode())) {
+            return Arrays.stream(split).anyMatch(value::equals);
         }
         }
+        boolean contains = Arrays.stream(split).anyMatch(value::contains);
+        boolean notOp = ("BrandandModel".equals(rule.getAttrCode()) || "CarOwnerName".equals(rule.getAttrCode())
+                || "CarOwnersAddress".equals(rule.getAttrCode()) || "ApplicantAddress".equals(rule.getAttrCode())
+                || "InsuredAddress".equals(rule.getAttrCode())) && "2".equals(rule.getOperator());
+        return notOp ? !contains : contains;
+    }
 
 
-        //统一处理数值
-        if ("1".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isAfter(min) && v.isBefore(max)) {
-                return true;
-            }
-        } else if ("2".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isBefore(min)) {
-                return true;
-            }
-        } else if ("3".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isBefore(min) || v.isEqual(min)) {
-                return true;
-            }
-        } else if ("4".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isAfter(max)) {
-                return true;
-            }
-        } else if ("5".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isAfter(max) || v.isEqual(max)) {
-                return true;
-            }
-        }
-        return false;
+    public static boolean dateTimeEqual(PtlAgreementCostRule rule, String value) {
+        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        if (StrUtil.isBlank(value)) return false;
+        LocalDateTime v = LocalDateTime.parse(value, f);
+        LocalDateTime min = rule.getMin() != null ? LocalDateTime.parse(rule.getMin(), f) : null;
+        LocalDateTime max = rule.getMax() != null ? LocalDateTime.parse(rule.getMax(), f) : null;
+        return compareDateTime(v, min, max, rule.getOperator());
     }
     }
 
 
-    public static boolean dateEqual(PtlAgreementCostRule ptlAgreementCostRule, String value){
-//        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
-        if(Objects.isNull(value) || value.equals("")){
-            return false;
-        }
+    public static boolean dateEqual(PtlAgreementCostRule rule, String value) {
+        if (StrUtil.isBlank(value)) return false;
         LocalDateTime v = LocalDate.parse(value).atStartOfDay();
         LocalDateTime v = LocalDate.parse(value).atStartOfDay();
-        LocalDateTime min = null,max = null;
-        if(!Objects.isNull(ptlAgreementCostRule.getMin())){
-            min = LocalDate.parse(ptlAgreementCostRule.getMin()).atStartOfDay();
-        }
-        if(!Objects.isNull(ptlAgreementCostRule.getMax())){
-            max = LocalDate.parse(ptlAgreementCostRule.getMax()).atStartOfDay();
-        }
+        LocalDateTime min = rule.getMin() != null ? LocalDate.parse(rule.getMin()).atStartOfDay() : null;
+        LocalDateTime max = rule.getMax() != null ? LocalDate.parse(rule.getMax()).atStartOfDay() : null;
+        return compareDateTime(v, min, max, rule.getOperator());
+    }
 
 
-        //统一处理数值
-        if ("1".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isAfter(min) && v.isBefore(max)) {
-                return true;
-            }
-        } else if ("2".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isBefore(min)) {
-                return true;
-            }
-        } else if ("3".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isBefore(min) || v.isEqual(min)) {
-                return true;
-            }
-        } else if ("4".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isAfter(max)) {
-                return true;
-            }
-        } else if ("5".equals(ptlAgreementCostRule.getOperator())) {
-            if (v.isAfter(max) || v.isEqual(max)) {
-                return true;
-            }
+    private static boolean compareDateTime(LocalDateTime v, LocalDateTime min, LocalDateTime max, String op) {
+        switch (op) {
+            case "1": return v.isAfter(min) && v.isBefore(max);
+            case "2": return v.isBefore(min);
+            case "3": return !v.isAfter(min);
+            case "4": return v.isAfter(max);
+            case "5": return !v.isBefore(max);
+            default:  return false;
         }
         }
-        return false;
     }
     }
-}
+}

+ 27 - 16
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/aspect/AuditVerificationAspect.java

@@ -64,20 +64,29 @@ public class AuditVerificationAspect {
 
 
     private final PtlNewAgreementClient ptlNewAgreementClient;
     private final PtlNewAgreementClient ptlNewAgreementClient;
 
 
-    private static final Map<String, String> plateFourCodeMap = new HashMap<>();
-
+    private static final Map<String, String> PLATE_FOUR_CODE_MAP = new HashMap<>();
     static {
     static {
-        plateFourCodeMap.put("晋D", "1404");
-        plateFourCodeMap.put("晋M", "1408");
-        plateFourCodeMap.put("晋J", "1411");
-        plateFourCodeMap.put("晋C", "1403");
-        plateFourCodeMap.put("晋K", "1407");
-        plateFourCodeMap.put("晋L", "1410");
-        plateFourCodeMap.put("晋B", "1402");
-        plateFourCodeMap.put("晋F", "1406");
-        plateFourCodeMap.put("晋A", "1401");
-        plateFourCodeMap.put("晋E", "1405");
-        plateFourCodeMap.put("晋H", "1409");
+        PLATE_FOUR_CODE_MAP.put("晋D", "1404");  PLATE_FOUR_CODE_MAP.put("晋M", "1408");
+        PLATE_FOUR_CODE_MAP.put("晋J", "1411");  PLATE_FOUR_CODE_MAP.put("晋C", "1403");
+        PLATE_FOUR_CODE_MAP.put("晋K", "1407");  PLATE_FOUR_CODE_MAP.put("晋L", "1410");
+        PLATE_FOUR_CODE_MAP.put("晋B", "1402");  PLATE_FOUR_CODE_MAP.put("晋F", "1406");
+        PLATE_FOUR_CODE_MAP.put("晋A", "1401");  PLATE_FOUR_CODE_MAP.put("晋E", "1405");
+        PLATE_FOUR_CODE_MAP.put("晋H", "1409");  PLATE_FOUR_CODE_MAP.put("晋",   "14");
+        PLATE_FOUR_CODE_MAP.put("沪",   "31");   PLATE_FOUR_CODE_MAP.put("云",   "53");
+        PLATE_FOUR_CODE_MAP.put("蒙",   "15");   PLATE_FOUR_CODE_MAP.put("京",   "11");
+        PLATE_FOUR_CODE_MAP.put("吉",   "22");   PLATE_FOUR_CODE_MAP.put("川",   "51");
+        PLATE_FOUR_CODE_MAP.put("津",   "12");   PLATE_FOUR_CODE_MAP.put("宁",   "64");
+        PLATE_FOUR_CODE_MAP.put("皖",   "34");   PLATE_FOUR_CODE_MAP.put("鲁",   "37");
+        PLATE_FOUR_CODE_MAP.put("粤",   "44");   PLATE_FOUR_CODE_MAP.put("桂",   "45");
+        PLATE_FOUR_CODE_MAP.put("新",   "65");   PLATE_FOUR_CODE_MAP.put("苏",   "32");
+        PLATE_FOUR_CODE_MAP.put("赣",   "36");   PLATE_FOUR_CODE_MAP.put("冀",   "13");
+        PLATE_FOUR_CODE_MAP.put("豫",   "41");   PLATE_FOUR_CODE_MAP.put("浙",   "33");
+        PLATE_FOUR_CODE_MAP.put("琼",   "46");   PLATE_FOUR_CODE_MAP.put("鄂",   "42");
+        PLATE_FOUR_CODE_MAP.put("湘",   "43");   PLATE_FOUR_CODE_MAP.put("甘",   "62");
+        PLATE_FOUR_CODE_MAP.put("闽",   "35");   PLATE_FOUR_CODE_MAP.put("藏",   "54");
+        PLATE_FOUR_CODE_MAP.put("贵",   "52");   PLATE_FOUR_CODE_MAP.put("辽",   "21");
+        PLATE_FOUR_CODE_MAP.put("渝",   "50");   PLATE_FOUR_CODE_MAP.put("陕",   "61");
+        PLATE_FOUR_CODE_MAP.put("青",   "63");   PLATE_FOUR_CODE_MAP.put("黑",   "23");
     }
     }
 
 
 
 
@@ -222,8 +231,11 @@ public class AuditVerificationAspect {
         log.info("=== 承保规则校验开始, 规则组数={}, 传入属性数={} ===", ruleGroups == null ? 0 : ruleGroups.size(), ruleAttrMappingVoList.size());
         log.info("=== 承保规则校验开始, 规则组数={}, 传入属性数={} ===", ruleGroups == null ? 0 : ruleGroups.size(), ruleAttrMappingVoList.size());
 
 
         ruleAttrMappingVoList.forEach(ruleAttrMappingVo -> {
         ruleAttrMappingVoList.forEach(ruleAttrMappingVo -> {
-            if ("LicenseNo".equals(ruleAttrMappingVo.getAttrCode()) && ruleAttrMappingVo.getValue().startsWith("晋")) {
-                ruleAttrMappingVo.setValue(plateFourCodeMap.getOrDefault(ruleAttrMappingVo.getValue().substring(0, 2), ruleAttrMappingVo.getValue()));
+            if ("LicenseNo".equals(ruleAttrMappingVo.getAttrCode())) {
+                ruleAttrMappingVo.setValue(PLATE_FOUR_CODE_MAP.getOrDefault(ruleAttrMappingVo.getValue().substring(0, 2), ruleAttrMappingVo.getValue()));
+            }
+            if ("LicenseArea".equals(ruleAttrMappingVo.getAttrCode())) {
+                ruleAttrMappingVo.setValue(PLATE_FOUR_CODE_MAP.getOrDefault(ruleAttrMappingVo.getValue().substring(0, 1), ruleAttrMappingVo.getValue()));
             }
             }
         });
         });
 
 
@@ -247,7 +259,6 @@ public class AuditVerificationAspect {
             }
             }
 
 
             // 直接判断该规则组是否匹配,无需循环调用
             // 直接判断该规则组是否匹配,无需循环调用
-            // 有一个匹配上  或者都没匹配上 都返回true
             boolean ruleMatched = FactorMatch.judgementsUnderwrite(ptlAgreementUndwrtRulesNews, ruleAttrMappingVoList, undwrtRulesAttrList, ruleLogs, undwrtRulesDictList);
             boolean ruleMatched = FactorMatch.judgementsUnderwrite(ptlAgreementUndwrtRulesNews, ruleAttrMappingVoList, undwrtRulesAttrList, ruleLogs, undwrtRulesDictList);
 
 
             log.info("规则组[{}]匹配结果: ruleMatched={}, 错误日志=[{}]", groupIndex, ruleMatched, ruleLogs);
             log.info("规则组[{}]匹配结果: ruleMatched={}, 错误日志=[{}]", groupIndex, ruleMatched, ruleLogs);

+ 34 - 9
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/aspect/QuoteVerificationAspect.java

@@ -73,6 +73,31 @@ public class QuoteVerificationAspect {
     private final BaseController baseController;
     private final BaseController baseController;
     private final PtlNewAgreementClient ptlNewAgreementClient;
     private final PtlNewAgreementClient ptlNewAgreementClient;
 
 
+    private static final Map<String, String> PLATE_FOUR_CODE_MAP = new HashMap<>();
+    static {
+        PLATE_FOUR_CODE_MAP.put("晋D", "1404");  PLATE_FOUR_CODE_MAP.put("晋M", "1408");
+        PLATE_FOUR_CODE_MAP.put("晋J", "1411");  PLATE_FOUR_CODE_MAP.put("晋C", "1403");
+        PLATE_FOUR_CODE_MAP.put("晋K", "1407");  PLATE_FOUR_CODE_MAP.put("晋L", "1410");
+        PLATE_FOUR_CODE_MAP.put("晋B", "1402");  PLATE_FOUR_CODE_MAP.put("晋F", "1406");
+        PLATE_FOUR_CODE_MAP.put("晋A", "1401");  PLATE_FOUR_CODE_MAP.put("晋E", "1405");
+        PLATE_FOUR_CODE_MAP.put("晋H", "1409");  PLATE_FOUR_CODE_MAP.put("晋",   "14");
+        PLATE_FOUR_CODE_MAP.put("沪",   "31");   PLATE_FOUR_CODE_MAP.put("云",   "53");
+        PLATE_FOUR_CODE_MAP.put("蒙",   "15");   PLATE_FOUR_CODE_MAP.put("京",   "11");
+        PLATE_FOUR_CODE_MAP.put("吉",   "22");   PLATE_FOUR_CODE_MAP.put("川",   "51");
+        PLATE_FOUR_CODE_MAP.put("津",   "12");   PLATE_FOUR_CODE_MAP.put("宁",   "64");
+        PLATE_FOUR_CODE_MAP.put("皖",   "34");   PLATE_FOUR_CODE_MAP.put("鲁",   "37");
+        PLATE_FOUR_CODE_MAP.put("粤",   "44");   PLATE_FOUR_CODE_MAP.put("桂",   "45");
+        PLATE_FOUR_CODE_MAP.put("新",   "65");   PLATE_FOUR_CODE_MAP.put("苏",   "32");
+        PLATE_FOUR_CODE_MAP.put("赣",   "36");   PLATE_FOUR_CODE_MAP.put("冀",   "13");
+        PLATE_FOUR_CODE_MAP.put("豫",   "41");   PLATE_FOUR_CODE_MAP.put("浙",   "33");
+        PLATE_FOUR_CODE_MAP.put("琼",   "46");   PLATE_FOUR_CODE_MAP.put("鄂",   "42");
+        PLATE_FOUR_CODE_MAP.put("湘",   "43");   PLATE_FOUR_CODE_MAP.put("甘",   "62");
+        PLATE_FOUR_CODE_MAP.put("闽",   "35");   PLATE_FOUR_CODE_MAP.put("藏",   "54");
+        PLATE_FOUR_CODE_MAP.put("贵",   "52");   PLATE_FOUR_CODE_MAP.put("辽",   "21");
+        PLATE_FOUR_CODE_MAP.put("渝",   "50");   PLATE_FOUR_CODE_MAP.put("陕",   "61");
+        PLATE_FOUR_CODE_MAP.put("青",   "63");   PLATE_FOUR_CODE_MAP.put("黑",   "23");
+    }
+
     @AfterReturning(pointcut = "@annotation(com.jzg.quotation.summary.aop.QuoteVerification)", returning = "result")
     @AfterReturning(pointcut = "@annotation(com.jzg.quotation.summary.aop.QuoteVerification)", returning = "result")
     public void doAfter(HttpResult<QuoteResultsVo> result) {
     public void doAfter(HttpResult<QuoteResultsVo> result) {
         String loginUserId = baseController.getUserId();
         String loginUserId = baseController.getUserId();
@@ -130,10 +155,6 @@ public class QuoteVerificationAspect {
             log.info("过滤的费用列表:{}",JSONObject.toJSONString(agreementProductCosts));
             log.info("过滤的费用列表:{}",JSONObject.toJSONString(agreementProductCosts));
             if (CollUtil.isNotEmpty(agreementProductCosts)) {
             if (CollUtil.isNotEmpty(agreementProductCosts)) {
                 //遍历费用因子
                 //遍历费用因子
-                HttpResult<String> convertLicense2AreaNumber = orgClient.convertLicense2AreaNumber(insOrdersCarInfo.getLicenseNo().substring(0, 2));
-                if(convertLicense2AreaNumber.isSuccess()){
-                    insOrdersCarInfo.setLicenseNo(convertLicense2AreaNumber.getData());//车牌转code
-                }
                 String costId = verificationCosts(agreementProductCosts, logs, insOrdersOther, insOrdersCarInfo, insOrdersRisks,
                 String costId = verificationCosts(agreementProductCosts, logs, insOrdersOther, insOrdersCarInfo, insOrdersRisks,
                         insOrdersCarUserInfos, kindList, insOrdersCosts, insOrdersAccidentalDriving, insOrders);
                         insOrdersCarUserInfos, kindList, insOrdersCosts, insOrdersAccidentalDriving, insOrders);
                 if (costId != null) {
                 if (costId != null) {
@@ -249,16 +270,20 @@ public class QuoteVerificationAspect {
                                      InsOrdersAccidentalDriving insOrdersAccidentalDriving, InsOrders insOrders) {
                                      InsOrdersAccidentalDriving insOrdersAccidentalDriving, InsOrders insOrders) {
         //获取所有费用属性
         //获取所有费用属性
         List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList = orgClient.list().getData();
         List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList = orgClient.list().getData();
-//        HttpResult comparenew = orgClient.compareNewCar(insOrdersCarInfo.getRegisterDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), insOrders.getCompanyId(), insOrdersCarInfo.getVehicleUseCode());
         boolean isNewCar = false;
         boolean isNewCar = false;
-//        if(comparenew.getCode()== 200) {
-//            // 新旧车判断
-//             isNewCar = comparenew.getData().equals("true") ? true : false;
-//        }
         //获取所有费用属性关联
         //获取所有费用属性关联
         List<RuleAttrMappingVo> ruleAttrMappingVoList = RuleAttrMappingVo.getRuleAttrMappingVoList(insOrdersOther,insOrdersCarInfo,
         List<RuleAttrMappingVo> ruleAttrMappingVoList = RuleAttrMappingVo.getRuleAttrMappingVoList(insOrdersOther,insOrdersCarInfo,
                 riskList, insOrdersCarUserInfos, kindList, insOrdersCosts, insOrdersAccidentalDriving, isNewCar, insOrders.getCompanyId());
                 riskList, insOrdersCarUserInfos, kindList, insOrdersCosts, insOrdersAccidentalDriving, isNewCar, insOrders.getCompanyId());
 
 
+        ruleAttrMappingVoList.forEach(ruleAttrMappingVo -> {
+            if ("LicenseNo".equals(ruleAttrMappingVo.getAttrCode())) {
+                ruleAttrMappingVo.setValue(PLATE_FOUR_CODE_MAP.getOrDefault(ruleAttrMappingVo.getValue().substring(0, 2), ruleAttrMappingVo.getValue()));
+            }
+            if ("LicenseArea".equals(ruleAttrMappingVo.getAttrCode())) {
+                ruleAttrMappingVo.setValue(PLATE_FOUR_CODE_MAP.getOrDefault(ruleAttrMappingVo.getValue().substring(0, 1), ruleAttrMappingVo.getValue()));
+            }
+        });
+
         // 字典数据在一次报价内是只读不变的,提到循环外避免N+1远程调用
         // 字典数据在一次报价内是只读不变的,提到循环外避免N+1远程调用
         // 修复批量报价时该方法被 organization 服务打爆的问题
         // 修复批量报价时该方法被 organization 服务打爆的问题
         List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList = ptlNewAgreementClient.getUndwrtRulesDictList();
         List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList = ptlNewAgreementClient.getUndwrtRulesDictList();