Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/master'

wanghao 2 settimane fa
parent
commit
fe6968b121

+ 4 - 1
commons/src/main/java/com/jzg/commons/entity/agreement/dispatch/dto/PtlAgreementDispatchAction.java

@@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 import java.io.Serializable;
-import java.util.Map;
 
 @Data
 @Schema(description = "协议调度动作实体")
@@ -34,6 +33,10 @@ public class PtlAgreementDispatchAction implements Serializable {
     @TableField(exist = false)
     private String actionName;
 
+    @ExcelIgnore
+    @Schema(description = "是否默认(0-否 1-是)")
+    private Integer isDefault;
+
     @ExcelIgnore
     @Schema(description = "是否删除")
     @TableLogic

+ 2 - 1
tenant/insurance/quotation-ansheng/src/main/java/com/jzg/quotation/ansheng/crawler/service/AnShengCrawlerLoginService.java

@@ -1,5 +1,6 @@
 package com.jzg.quotation.ansheng.crawler.service;
 
+import com.jzg.commons.entity.quote.vo.AttributionInformationVo;
 import com.jzg.quotation.ansheng.crawler.entity.request.DownloadTokenResult;
 
 public interface AnShengCrawlerLoginService {
@@ -13,6 +14,6 @@ public interface AnShengCrawlerLoginService {
     String login(String userName, String password);
 
 
-    DownloadTokenResult getObtainDownloadToken(String username, String password);
+    DownloadTokenResult getObtainDownloadToken(AttributionInformationVo attributionInformationVo);
 
 }

+ 7 - 3
tenant/insurance/quotation-ansheng/src/main/java/com/jzg/quotation/ansheng/crawler/service/impl/AnShengCrawlerLoginServiceImpl.java

@@ -1,6 +1,7 @@
 package com.jzg.quotation.ansheng.crawler.service.impl;
 
 import com.jzg.commons.constants.quote.enums.base.ExpenseRequestUrl;
+import com.jzg.commons.entity.quote.vo.AttributionInformationVo;
 import com.jzg.commons.entity.quote.vo.identify.SliderCaptchaVo;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.quotation.ansheng.crawler.component.AnShengCrawlerCacheComponent;
@@ -8,6 +9,7 @@ import com.jzg.quotation.ansheng.crawler.component.AnShengCrawlerRequestComponen
 import com.jzg.quotation.ansheng.crawler.entity.request.*;
 import com.jzg.quotation.ansheng.crawler.entity.response.AnShengCrawlerVerifyResponse;
 import com.jzg.quotation.ansheng.crawler.service.AnShengCrawlerLoginService;
+import com.jzg.quotation.ansheng.crawler.utils.RSAUtils;
 import com.jzg.quotation.commons.service.ExteriorUtilsRequest;
 import lombok.RequiredArgsConstructor;
 import org.springframework.http.HttpHeaders;
@@ -66,7 +68,9 @@ public class AnShengCrawlerLoginServiceImpl implements AnShengCrawlerLoginServic
     }
 
     @Override
-    public DownloadTokenResult getObtainDownloadToken(String username, String password) {
+    public DownloadTokenResult getObtainDownloadToken(AttributionInformationVo attributionInformationVo) {
+        String username = attributionInformationVo.getUsername();
+        String password = attributionInformationVo.getPassword();
         // 构建请求头
         HttpHeaders headers = buildCommonHeaders();
         headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
@@ -78,7 +82,7 @@ public class AnShengCrawlerLoginServiceImpl implements AnShengCrawlerLoginServic
         // 表单参数
         MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
         formData.add("account", username);
-        formData.add("password", password);
+        formData.add("password", RSAUtils.encrypt(password));
         formData.add("phone", "");
         formData.add("agentCode", "");
         formData.add("verificationCode", "");
@@ -88,7 +92,7 @@ public class AnShengCrawlerLoginServiceImpl implements AnShengCrawlerLoginServic
         formData.add("dragSliderCode", dragCode != null ? dragCode : "");
         // 发起POST表单请求,使用项目已有的form请求方法
         LoginResp loginResp = anShengCrawlerRequestComponent.downloadToken( formData, urlParams,headers);
-        if (loginResp == null) {
+        if (loginResp == null || 0 != loginResp.getCode()) {
             DownloadTokenResult result = new DownloadTokenResult();
             result.setCode(400);
             result.setMessage("登录响应为空");

+ 1 - 3
tenant/insurance/quotation-ansheng/src/main/java/com/jzg/quotation/ansheng/crawler/service/impl/AnShengCrawlerQuoteProcessServiceImpl.java

@@ -58,9 +58,7 @@ public class AnShengCrawlerQuoteProcessServiceImpl implements AnShengCrawlerQuot
 
     @Override
     public DownloadTokenResult obtainDownloadToken(AttributionInformationVo attributionInformationVo) {
-        String username = attributionInformationVo.getUsername();
-        String password = attributionInformationVo.getPassword();
-        return anShengCrawlerLoginService.getObtainDownloadToken(username, password);
+        return anShengCrawlerLoginService.getObtainDownloadToken(attributionInformationVo);
     }
 
 }

+ 3 - 0
tenant/insurance/quotation-ansheng/src/main/java/com/jzg/quotation/ansheng/crawler/service/impl/AnShengCrawlerRequestImpl.java

@@ -577,6 +577,9 @@ public class AnShengCrawlerRequestImpl implements AnShengCrawlerRequest {
         DownloadPolicyResultVo downloadPolicyResultVo = new DownloadPolicyResultVo();
         AnShengCrawlerOrder anShengCrawlerOrder = anShengCrawlerOrderService.getById(downloadPolicyVo.getOrder().getOrdersNo());
         DownloadTokenResult obtainDownloadToken = anShengCrawlerQuoteProcessService.obtainDownloadToken(downloadPolicyVo.getOrder().getAttributionInformationVo());
+        if (200 != obtainDownloadToken.getCode()){
+            throw new SystemException("获取token失败,请联系管理员");
+        }
         // 缓存token,避免重复调用
         String loginToken = obtainDownloadToken.getToken();
         String bdToken = obtainDownloadToken.getBdToken();

+ 112 - 0
tenant/insurance/quotation-ansheng/src/main/java/com/jzg/quotation/ansheng/crawler/utils/RSAUtils.java

@@ -0,0 +1,112 @@
+package com.jzg.quotation.ansheng.crawler.utils;
+
+import javax.crypto.Cipher;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * RSA加密工具类
+ *
+ * @author YourName
+ * @date 2026-08-25
+ */
+public class RSAUtils {
+
+    private static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy+4Uy6zba9JeGu7LR7krxkQ+Ivt7MIl+jR/F0rCoPUDPlqvrKIQAeRlJ8txkLSskLAU9f2F2bjbUmzlBdrxVPz/mPIXA866SAd4ELTAdF3oltCa9yLPERLCLMEwUAjX2lUSS3gxAuXbIuPuVJQi1xQeV+VRiJulMFNjhlq86bRpsc7SZ0rMF3Ahc9WHxulcXofaDRLx+sTxzeTsZC3zCh5Dk2hnHaj7o2C/n8U6ZvfifKeB2kWZyoFlmgX8/6agSfkmdxuTw5CZk9qU/1k0ykg2N5whBfnaMbUD5P08KuU/eBIEriS7U7yQruVfovsgN+c2h4XPEcMG0WrqWZbRNpQIDAQAB";
+
+
+    private static volatile PublicKey publicKey;
+
+    /**
+     * 获取公钥
+     */
+    public static PublicKey getPublicKey() {
+        if (publicKey == null) {
+            synchronized (RSAUtils.class) {
+                if (publicKey == null) {
+                    publicKey = parsePublicKey();
+                }
+            }
+        }
+        return publicKey;
+    }
+
+    /**
+     * 解析公钥
+     */
+    private static PublicKey parsePublicKey() {
+        try {
+            byte[] keyBytes = Base64.getDecoder().decode(PUBLIC_KEY);
+            X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            return keyFactory.generatePublic(spec);
+        } catch (Exception e) {
+            throw new RuntimeException("解析RSA公钥失败", e);
+        }
+    }
+
+    /**
+     * RSA加密
+     *
+     * @param plaintext 明文
+     * @return Base64编码的密文
+     * @throws RuntimeException 加密失败时抛出
+     */
+    public static String encrypt(String plaintext) {
+        if (plaintext == null) {
+            throw new IllegalArgumentException("明文不能为null");
+        }
+
+        try {
+            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+            cipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
+
+            byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
+            return Base64.getEncoder().encodeToString(encrypted);
+        } catch (Exception e) {
+            throw new RuntimeException("RSA加密失败", e);
+        }
+    }
+
+    /**
+     * RSA加密(带最大长度限制检查)
+     * RSA最大加密长度为 密钥长度/8 - 11(PKCS1Padding)
+     * 对于2048位密钥,最大加密长度为245字节
+     *
+     * @param plaintext 明文
+     * @return Base64编码的密文
+     * @throws IllegalArgumentException 明文过长时抛出
+     */
+    public static String encryptWithCheck(String plaintext) {
+        if (plaintext == null) {
+            throw new IllegalArgumentException("明文不能为null");
+        }
+
+        // 2048位密钥,PKCS1Padding最大加密长度为245字节
+        int maxLength = 245;
+        byte[] plainBytes = plaintext.getBytes(StandardCharsets.UTF_8);
+        if (plainBytes.length > maxLength) {
+            throw new IllegalArgumentException(
+                    String.format("明文长度超过最大加密长度限制:当前%d字节,最大%d字节",
+                            plainBytes.length, maxLength)
+            );
+        }
+
+        return encrypt(plaintext);
+    }
+
+    /**
+     * 验证公钥是否有效
+     */
+    public static boolean isPublicKeyValid() {
+        try {
+            return getPublicKey() != null;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+}

+ 28 - 89
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/FactorMatch.java

@@ -25,6 +25,22 @@ public class FactorMatch {
 
     private static final Logger log = LoggerFactory.getLogger(FactorMatch.class);
 
+    private static final Map<String, String> plateFourCodeMap = new HashMap<>();
+
+    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");
+    }
+
     /**
      * 因子比对
      * @param ptlAgreementCostRuleList
@@ -128,7 +144,7 @@ public class FactorMatch {
                 }
 
                 String val = codeToNameMap.getOrDefault(ruleAttrMappingVo.getValue(), StrUtil.EMPTY);
-                logs.append("传入的值:" + (StrUtil.isNotBlank(val) ? val : ruleAttrMappingVo.getValue()) + "\n");
+                logs.append("传入的值:" + displayRuleValue((StrUtil.isNotBlank(val) ? val : ruleAttrMappingVo.getValue()), codeToNameMap) + "\n");
 
                 if (CollUtil.isNotEmpty(collect)) {
                     logs.append("规则匹配失败:" + collect.get(0).getAttrName() + "\n");
@@ -282,11 +298,11 @@ public class FactorMatch {
 
                 } else {
                     String max = codeToNameMap.getOrDefault(ptlAgreementProductCostsAttrLink.getMax(), StrUtil.EMPTY);
-                    logs.append("检验的值:" + (StrUtil.isNotBlank(max) ? max : ptlAgreementProductCostsAttrLink.getMax()) + "\n");
+                    logs.append("检验的值:" + displayRuleValue((StrUtil.isNotBlank(max) ? max : ptlAgreementProductCostsAttrLink.getMax()), codeToNameMap) + "\n");
                 }
 
                 String val = codeToNameMap.getOrDefault(ruleAttrMappingVo.getValue(), StrUtil.EMPTY);
-                logs.append("传入的值:" + (StrUtil.isNotBlank(val) ? val : ruleAttrMappingVo.getValue()) + "\n");
+                logs.append("传入的值:" + displayRuleValue((StrUtil.isNotBlank(val) ? val : ruleAttrMappingVo.getValue()), codeToNameMap) + "\n");
 
                 if (CollUtil.isNotEmpty(collect)) {
                     logs.append("<span style=\"color:red;\">规则匹配失败:" + collect.get(0).getAttrName() + "</span>\n");
@@ -310,10 +326,6 @@ public class FactorMatch {
         if (CollUtil.isNotEmpty(nameToCodeMap) && nameToCodeMap.containsKey(v)) {
             return nameToCodeMap.get(v);
         }
-        String normalized = normalizeYesNo(v);
-        if (normalized != null && !normalized.equals(v)) {
-            return normalized;
-        }
         return v;
     }
 
@@ -381,12 +393,6 @@ public class FactorMatch {
             if (sp.trim().equals(value.trim())) {
                 return true;
             }
-            // 顺带支持是/否归一化
-            String n1 = normalizeYesNo(sp.trim());
-            String n2 = normalizeYesNo(value.trim());
-            if (!n1.equals(sp.trim()) && !n2.equals(value.trim()) && n1.equals(n2)) {
-                return true;
-            }
         }
         return false;
     }
@@ -462,24 +468,12 @@ public class FactorMatch {
             String [] options = ptlAgreementCostRule.getMin().split(",");
             for (String opt : options) {
                 if (opt.trim().equals(value.trim())) {
-                    return false;
-                }
-            }
-            return true;
-        } else {
-            // 修复:select 支持多选逗号分隔,所以正确做法是 split 后逐个 equals
-            String[] options = ptlAgreementCostRule.getMin().split(",");
-            for (String opt : options) {
-                if (opt.trim().equals(value.trim())) {
-                    return true;
-                }
-                // 顺带支持是/否归一化
-                String n1 = normalizeYesNo(opt.trim());
-                String n2 = normalizeYesNo(value.trim());
-                if (!n1.equals(opt.trim()) && !n2.equals(value.trim()) && n1.equals(n2)) {
                     return true;
                 }
             }
+            return false;
+        } else if (ptlAgreementCostRule.getMin() != null && ptlAgreementCostRule.getMin().contains(value)) {
+            return true;
         }
 
         return false;
@@ -487,73 +481,18 @@ public class FactorMatch {
 
     private static boolean radioEqual(PtlAgreementCostRule ptlAgreementCostRule, String value) {
         //新能源判断
-        if ("NewEnergy".equals(ptlAgreementCostRule.getAttrCode())) {
+        if ("NewEnergyMark".equals(ptlAgreementCostRule.getAttrCode())) {
             // 是否是新能源车
             boolean newEnergy = EnergyTypeCode.isNewEnergy(value);
-            if (newEnergy && "1".equals(normalizeYesNo(ptlAgreementCostRule.getMin()))) {
+            if (newEnergy && ptlAgreementCostRule.getMin().equals("1")) {
                 return true;
-            } else if (!newEnergy && "0".equals(normalizeYesNo(ptlAgreementCostRule.getMin()))) {
+            } else if (!newEnergy && ptlAgreementCostRule.getMin().equals("0")) {
                 return true;
             }
-            return false;
-        }
-
-        // ========== 修复:归一化 是/否 类的 value ==========
-        String ruleValue = (ptlAgreementCostRule.getMin() != null) ? ptlAgreementCostRule.getMin() : ptlAgreementCostRule.getMax();
-        String normalizedRule = normalizeYesNo(ruleValue);
-        String normalizedValue = normalizeYesNo(value);
-
-        String operator = ptlAgreementCostRule.getOperator();
-        if (operator == null) operator = "1"; // 默认 等于
-
-        switch (operator) {
-            case "1": // 等于(默认)
-                // 修复1:先归一化后比较(解决 N/0、Y/1 不一致的问题)
-                if (normalizedRule != null && normalizedValue != null
-                        && !normalizedRule.equals(ruleValue) && !normalizedValue.equals(value)) {
-                    // 两个都经过归一化翻译了,直接比归一化后的值
-                    return normalizedRule.equals(normalizedValue);
-                }
-                // 修复2:用 equals 代替 contains(避免 "10".contains("0") 的误判)
-                return (ruleValue != null && ruleValue.equals(value))
-                        || (normalizedRule != null && normalizedRule.equals(normalizedValue));
-
-            case "2": // 不等于
-                if (normalizedRule != null && normalizedValue != null
-                        && !normalizedRule.equals(ruleValue) && !normalizedValue.equals(value)) {
-                    return !normalizedRule.equals(normalizedValue);
-                }
-                return ruleValue == null || !ruleValue.equals(value);
-
-            case "3": case "4": case "5": // 其他运算符(radio 一般用不上,兜底)
-                return inputNumberEqual(ptlAgreementCostRule, value);
-
-            default:
-                return (ruleValue != null && ruleValue.equals(value));
-        }
-    }
-
-    /**
-     * 归一化"是/否"类的值,把各种表达方式统一成 1/0
-     * 是: "1", "Y", "y", "是", "true", "TRUE", "True" -> "1"
-     * 否: "0", "N", "n", "否", "false", "FALSE", "False" -> "0"
-     * 其他值原样返回
-     */
-    private static String normalizeYesNo(String value) {
-        if (value == null) return null;
-        String v = value.trim();
-        if (v.isEmpty()) return v;
-        // 是否的"是"
-        if ("1".equals(v) || "Y".equalsIgnoreCase(v) || "true".equalsIgnoreCase(v)
-                || "是".equals(v) || "T".equalsIgnoreCase(v) || "YES".equalsIgnoreCase(v)) {
-            return "1";
-        }
-        // 是否的"否"
-        if ("0".equals(v) || "N".equalsIgnoreCase(v) || "false".equalsIgnoreCase(v)
-                || "否".equals(v) || "F".equalsIgnoreCase(v) || "NO".equalsIgnoreCase(v)) {
-            return "0";
+        } else if (ptlAgreementCostRule.getMin().contains(value)) {
+            return true;
         }
-        return v;
+        return false;
     }
 
     public static boolean dateTimeEqual(PtlAgreementCostRule ptlAgreementCostRule, String value){

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

@@ -6,6 +6,8 @@ import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.jzg.commons.core.base.BaseController;
 import com.jzg.commons.core.page.HttpResult;
+import com.jzg.commons.entity.agreement.dispatch.dto.PtlAgreementDispatchRulesAttrLink;
+import com.jzg.commons.entity.agreement.dispatch.vo.ValidateDispatchRuleVo;
 import com.jzg.commons.entity.orders.po.*;
 import com.jzg.commons.entity.po.*;
 import com.jzg.commons.entity.quote.vo.aggregated.UnderwritingVo;
@@ -24,12 +26,11 @@ import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -62,6 +63,22 @@ public class AuditVerificationAspect {
 
     private final PtlNewAgreementClient ptlNewAgreementClient;
 
+    private static final Map<String, String> plateFourCodeMap = new HashMap<>();
+
+    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");
+    }
+
 
 
     @Before("@annotation(com.jzg.quotation.summary.aop.AuditVerification)")
@@ -201,6 +218,12 @@ public class AuditVerificationAspect {
 
         log.info("=== 承保规则校验开始, 规则组数={}, 传入属性数={} ===", ruleGroups == null ? 0 : ruleGroups.size(), ruleAttrMappingVoList.size());
 
+        ruleAttrMappingVoList.forEach(ruleAttrMappingVo -> {
+            if ("LicenseNo".equals(ruleAttrMappingVo.getAttrCode()) && ruleAttrMappingVo.getValue().startsWith("晋")) {
+                ruleAttrMappingVo.setValue(plateFourCodeMap.getOrDefault(ruleAttrMappingVo.getValue(), ruleAttrMappingVo.getValue()));
+            }
+        });
+
         // 多条规则间为OR关系:只要任一条规则匹配即通过
         StringBuilder allErrorLogs = new StringBuilder();
         boolean anyRuleMatched = false;

+ 10 - 0
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementDispatchServiceImpl.java

@@ -78,9 +78,19 @@ public class PtlAgreementDispatchServiceImpl extends ServiceImpl<PtlAgreementDis
         rulesAttrLinkMapper.insert(ptlAgreementDispatchVo.getAttrLinks());
 
         if(CollUtil.isNotEmpty(ptlAgreementDispatchVo.getActions())){
+            // 同一调度下仅允许一个默认动作:多个默认只保留第一个,其余及未标记的均置为否(0)
+            boolean defaultAssigned = false;
             for (PtlAgreementDispatchAction action : ptlAgreementDispatchVo.getActions()){
                 action.setId(IdGenerate.nextId());
                 action.setDispatchId(dispatch.getId());
+                if (action.getIsDefault() != null && action.getIsDefault() == 1) {
+                    if (defaultAssigned) {
+                        action.setIsDefault(1);
+                    }
+                    defaultAssigned = true;
+                } else {
+                    action.setIsDefault(0);
+                }
             }
             actionMapper.insert(ptlAgreementDispatchVo.getActions());
         }

+ 29 - 5
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlSchemeServiceImpl.java

@@ -8,14 +8,12 @@ import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.jzg.commons.constants.ProductsType;
 import com.jzg.commons.core.base.BaseController;
 import com.jzg.commons.core.page.HttpResult;
 import com.jzg.commons.entity.action.enums.ActionEnum;
 import com.jzg.commons.entity.agreement.dispatch.dto.PtlAgreementDispatchAction;
 import com.jzg.commons.entity.agreement.dispatch.dto.PtlAgreementDispatchRulesAttrLink;
-import com.jzg.commons.entity.agreement.dispatch.vo.PtlAgreementDispatchQuery;
 import com.jzg.commons.entity.agreement.dispatch.vo.PtlAgreementDispatchVo;
 import com.jzg.commons.entity.agreement.dispatch.vo.ValidateDispatchRuleVo;
 import com.jzg.commons.entity.agreement.po.PtlAgreementAttribution;
@@ -53,7 +51,6 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.util.*;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 
@@ -112,6 +109,22 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
     @Autowired
     PtlAreaLicenseService ptlAreaLicenseService;
 
+    private static final Map<String, String> plateFourCodeMap = new HashMap<>();
+
+    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");
+    }
+
     /**
      * 添加方案
      *
@@ -666,7 +679,14 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
                     if (attrLinks != null) {
                         attrLinks.forEach(attrLink -> {
                             if ("LicenseNo".equals(attrLink.getAttrCode()) && attrLink.getMin().substring(0, 1).matches("[\\u4e00-\\u9fa5]")) {
-                                attrLink.setMin(ptlAreaLicenseService.convertLicense2AreaNumber(attrLink.getMin()));
+                                // attrLink.setMin(ptlAreaLicenseService.convertLicense2AreaNumber(attrLink.getMin()));
+                                String minVal = attrLink.getMin();
+                                // 支持逗号分隔的多个车牌前缀,如 "晋A,晋B,晋C" → "1401,1402,1403"
+                                String result = Arrays.stream(minVal.split(","))
+                                        .map(String::trim)
+                                        .map(code -> plateFourCodeMap.getOrDefault(code, code))
+                                        .collect(Collectors.joining(","));
+                                attrLink.setMin(result);
                             }
                         });
                     }
@@ -697,7 +717,11 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
                 company.setIsDispatch(0);
                 PtlAgreementDispatchVo.DispatchVo ptlAgreementDispatchVo = dispatchByCompany.get(company.getId());
                 if (ptlAgreementDispatchVo != null) {
-                    PtlAgreementDispatchAction action1 = ptlAgreementDispatchVo.getActions().stream().filter(action -> action.getActionCode().equals(ActionEnum.SPECIFY_AGREEMENT.getCode())).findFirst().orElse(null);
+                    PtlAgreementDispatchAction action1 = ptlAgreementDispatchVo.getActions().stream().filter(action -> action.getActionCode().equals(ActionEnum.SPECIFY_AGREEMENT.getCode())).findFirst()
+                            // 未匹配到指定协议动作时,回退取默认动作(isDefault=1)
+                            .orElseGet(() -> ptlAgreementDispatchVo.getActions().stream()
+                                    .filter(action -> action.getIsDefault() != null && action.getIsDefault() == 1)
+                                    .findFirst().orElse(null));
                     if (action1 != null) {
                         //将调度字段设为1
                         company.setIsDispatch(1);

+ 1 - 0
tenant/organization/src/main/resources/mapper/PtlAgreementDispatchActionMapper.xml

@@ -10,6 +10,7 @@
         <result property="actionCode" column="action_code"/>
         <result property="actionJson" column="action_json"
                 typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
+        <result property="isDefault" column="is_default"/>
     </resultMap>
 
     <select id="getActions" resultMap="ActionMap">