|
@@ -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;
|
|
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
+}
|