Sfoglia il codice sorgente

Merge branch 'master' of http://39.101.143.165:8090/785834757/jzg

lipf 3 mesi fa
parent
commit
ba38a02b25
13 ha cambiato i file con 507 aggiunte e 121 eliminazioni
  1. 64 0
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/component/RenBaoNewCrawlerRequestComponent.java
  2. 16 1
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/constant/enums/RequestUrl.java
  3. 7 7
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestBuilder.java
  4. 253 3
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestImpl.java
  5. 1 1
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/classifier/impl/PassengerCarClassifier.java
  6. 12 1
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsOrdersTaxServiceImpl.java
  7. 8 0
      tenant/insurance/quotation-taikang/src/main/java/com/jzg/quotation/taikang/crawler/service/TKSimpleService.java
  8. 1 1
      tenant/insurance/quotation-taikang/src/main/java/com/jzg/quotation/taikang/crawler/service/impl/TKQuotationBaseServiceImpl.java
  9. 34 0
      tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/component/ZiJinCrawlerCacheComponent.java
  10. 1 0
      tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/enums/ResponseCode.java
  11. 7 1
      tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/service/impl/ZiJinCrawlerOrderServiceImpl.java
  12. 56 6
      tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/service/impl/ZiJinCrawlerRequestImpl.java
  13. 47 100
      tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlSchemeServiceImpl.java

+ 64 - 0
tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/component/RenBaoNewCrawlerRequestComponent.java

@@ -1006,6 +1006,70 @@ public class RenBaoNewCrawlerRequestComponent {
         return requestComponent.formData(url, parts, String.class, headers);
         return requestComponent.formData(url, parts, String.class, headers);
     }
     }
 
 
+    /**
+     * 获取滑块验证信息
+     *
+     * <p>对应Python的get_spilder方法,获取滑块拼图图片和验证参数</p>
+     *
+     * @param headers 请求头
+     * @return 滑块信息(包含jigsawImageBase64、originalImageBase64、secretKey、token)
+     */
+    public Map<String, Object> getSpilder(HttpHeaders headers) {
+        String url = buildUrl(RequestUrl.SPILDER_GET.getUrl());
+
+        Map<String, Object> requestData = new HashMap<>();
+        requestData.put("captchaType", "blockPuzzle");
+        requestData.put("clientUid", "slider-" + java.util.UUID.randomUUID().toString());
+        requestData.put("ts", System.currentTimeMillis());
+
+        log.info("获取滑块验证信息");
+        ResponseEntity<String> responseEntity = requestComponent.stringPost(url, requestData, String.class, headers);
+        return JSON.parseObject(responseEntity.getBody(), new TypeReference<Map<String, Object>>() {});
+    }
+
+    /**
+     * 校验滑块验证
+     *
+     * <p>对应Python的check_spilder方法,提交滑块验证结果</p>
+     *
+     * @param data 滑块验证数据(包含captchaType、pointJson、token)
+     * @param headers 请求头
+     * @return 校验结果
+     */
+    public Map<String, Object> checkSpilder(Map<String, Object> data, HttpHeaders headers) {
+        String url = buildUrl(RequestUrl.SPILDER_CHECK.getUrl());
+
+        log.info("提交滑块验证结果");
+        ResponseEntity<String> responseEntity = requestComponent.stringPost(url, data, String.class, headers);
+        return JSON.parseObject(responseEntity.getBody(), new TypeReference<Map<String, Object>>() {});
+    }
+
+    /**
+     * 滑块二次验证接口
+     *
+     * <p>对应Python的verify_spilder方法,滑块初步验证通过后需要二次验证</p>
+     *
+     * @param data 验证数据(包含captchaVerification)
+     * @param validate0 来自报价响应的statusText
+     * @param validate1 触发滑块的接口路径
+     * @param headers 请求头
+     * @return 验证结果
+     */
+    public Map<String, Object> verifySpilder(Map<String, Object> data, String validate0, String validate1,
+                                            HttpHeaders headers) {
+        String url = buildUrl(RequestUrl.SPILDER_VERIFY.getUrl());
+
+        // 对应Python: headers2 = self.headers.copy(); headers2['validate0'] = validate0; headers2['validate1'] = validate1
+        HttpHeaders verifyHeaders = new HttpHeaders();
+        verifyHeaders.addAll(headers);
+        verifyHeaders.set("validate0", validate0);
+        verifyHeaders.set("validate1", validate1);
+
+        log.info("滑块二次验证: validate0={}, validate1={}", validate0, validate1);
+        ResponseEntity<String> responseEntity = requestComponent.stringPost(url, data, String.class, verifyHeaders);
+        return JSON.parseObject(responseEntity.getBody(), new TypeReference<Map<String, Object>>() {});
+    }
+
     /**
     /**
      * 查询投保单信息(获取评分)
      * 查询投保单信息(获取评分)
      *
      *

+ 16 - 1
tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/constant/enums/RequestUrl.java

@@ -135,7 +135,22 @@ public enum RequestUrl {
     /**
     /**
      * 查询投保单信息接口(获取评分)
      * 查询投保单信息接口(获取评分)
      */
      */
-    QUERY_PROPOSAL_INFO("/front/019K0000003/ipdi/queryProposalInfo");
+    QUERY_PROPOSAL_INFO("/front/019K0000003/ipdi/queryProposalInfo"),
+
+    /**
+     * 获取滑块验证信息接口
+     */
+    SPILDER_GET("/front/019K0000003/spilder/get"),
+
+    /**
+     * 滑块验证校验接口
+     */
+    SPILDER_CHECK("/front/019K0000003/spilder/check"),
+
+    /**
+     * 滑块验证二次校验接口
+     */
+    SPILDER_VERIFY("/front/019K0000003/spilder/verify");
 
 
     /**
     /**
      * 接口URL路径
      * 接口URL路径

+ 7 - 7
tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestBuilder.java

@@ -3102,10 +3102,10 @@ public class RenBaoNewCrawlerRequestBuilder {
         carShipTax.put("prePolicyEndDate", "");
         carShipTax.put("prePolicyEndDate", "");
 
 
         // ========== 税款金额 ==========
         // ========== 税款金额 ==========
-        carShipTax.put("thisPayTax", "");
-        carShipTax.put("prePayTax", "");
-        carShipTax.put("delayPayTax", "");
-        carShipTax.put("sumPayTax", "");
+        carShipTax.put("thisPayTax", "300.00");
+        carShipTax.put("prePayTax", "0.00");
+        carShipTax.put("delayPayTax", "0.00");
+        carShipTax.put("sumPayTax", "300.00");
         carShipTax.put("sumRefundTax", "");
         carShipTax.put("sumRefundTax", "");
         carShipTax.put("taxUnitAmount", "");
         carShipTax.put("taxUnitAmount", "");
         carShipTax.put("taxUnit", "");
         carShipTax.put("taxUnit", "");
@@ -3114,9 +3114,9 @@ public class RenBaoNewCrawlerRequestBuilder {
         carShipTax.put("payEndDate", "");
         carShipTax.put("payEndDate", "");
 
 
         // ========== 禁用标志 - req2.json 为空字符串 ==========
         // ========== 禁用标志 - req2.json 为空字符串 ==========
-        carShipTax.put("disableThisPayTax", "");
-        carShipTax.put("disablePrePayTax", "");
-        carShipTax.put("disableDelayPayTax", "");
+        carShipTax.put("disableThisPayTax", true);
+        carShipTax.put("disablePrePayTax", true);
+        carShipTax.put("disableDelayPayTax", true);
 
 
         // ========== 显示标志 ==========
         // ========== 显示标志 ==========
         carShipTax.put("jeShow", false);
         carShipTax.put("jeShow", false);

+ 253 - 3
tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestImpl.java

@@ -7,6 +7,7 @@ import com.jzg.commons.entity.quote.vo.aggregated.*;
 import com.jzg.commons.entity.quote.vo.base.LoginBase;
 import com.jzg.commons.entity.quote.vo.base.LoginBase;
 import com.jzg.commons.entity.quote.vo.image.InsuranceImageVo;
 import com.jzg.commons.entity.quote.vo.image.InsuranceImageVo;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.commons.exception.SystemException;
+import com.jzg.commons.util.AESUtils;
 import com.jzg.quotation.commons.annotation.QuoteCrawlerService;
 import com.jzg.quotation.commons.annotation.QuoteCrawlerService;
 import com.jzg.quotation.commons.constant.InsuranceCode;
 import com.jzg.quotation.commons.constant.InsuranceCode;
 import com.jzg.quotation.commons.entity.DetectLoginRequest;
 import com.jzg.quotation.commons.entity.DetectLoginRequest;
@@ -30,13 +31,18 @@ import org.redisson.api.RMap;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpHeaders;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
 import java.net.URLEncoder;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
+import java.util.UUID;
 
 
 /**
 /**
  * 人保车险爬虫服务实现 * 
  * 人保车险爬虫服务实现 * 
@@ -149,9 +155,36 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
                                                         LoginInfoBO loginInfo, String username) {
                                                         LoginInfoBO loginInfo, String username) {
         int modelCodeRetryCount = 0;
         int modelCodeRetryCount = 0;
         int duplicateInsuranceRetryCount = 0;
         int duplicateInsuranceRetryCount = 0;
+        int hkcs = 0; // 滑块风控重试计数器
         for (int retry = 0; retry < 6; retry++) {
         for (int retry = 0; retry < 6; retry++) {
             RenBaoNewCrawlerQuoteResponse response = requestComponent.quote(request, headers);
             RenBaoNewCrawlerQuoteResponse response = requestComponent.quote(request, headers);
 
 
+            // 判断触发滑块风控
+            if (response.getStatus() != null && response.getStatus() == -2) {
+                boolean passSpilderMainRes = false;
+                // 对应Python: validate0 = caculatePremium_res['statusText']; validate1 = '/bizSave/caculatePremium'
+                String validate0 = response.getStatusText();
+                String validate1 = "/bizSave/caculatePremium";
+                log.error("检测到滑块: statusText={}, {}", validate0, JSONUtil.toJsonStr(response));
+                if (hkcs < 2) {
+                    while (hkcs < 2) {
+                        hkcs++;
+                        passSpilderMainRes = passSpilderMain(headers, username, validate0, validate1);
+                        if (passSpilderMainRes) {
+                            break;
+                        }
+                    }
+                    if (passSpilderMainRes) {
+                        log.info("滑块验证通过,重新发起报价");
+                        continue;
+                    } else {
+                        throw new SystemException("触发滑块风控,未通过,请稍后报价");
+                    }
+                } else {
+                    throw new SystemException("触发滑块风控,未通过,请稍后报价");
+                }
+            }
+
             if (isSuccess(response)) {
             if (isSuccess(response)) {
                 return response;
                 return response;
             }
             }
@@ -463,6 +496,221 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
         return "未知错误";
         return "未知错误";
     }
     }
 
 
+    /**
+     * 滑块验证主程序(对应Python的pass_spilder_main方法)
+     *
+     * <p>获取滑块图片、计算滑块位置、提交验证,通过后还需二次验证</p>
+     *
+     * @param headers   请求头
+     * @param username  用户名
+     * @param validate0 来自报价响应的statusText(对应Python: caculatePremium_res['statusText'])
+     * @param validate1 触发滑块的接口路径(对应Python: '/bizSave/caculatePremium')
+     * @return true-滑块通过,false-未通过
+     */
+    private boolean passSpilderMain(HttpHeaders headers, String username, String validate0, String validate1) {
+        try {
+            // 1. 获取滑块验证信息(对应Python的get_spilder)
+            Map<String, Object> spilderInfo = requestComponent.getSpilder(headers);
+
+            // 检查响应是否有效
+            if (spilderInfo == null || !spilderInfo.containsKey("repData")) {
+                log.error("获取滑块信息失败: {}", JSONUtil.toJsonStr(spilderInfo));
+                return false;
+            }
+
+            @SuppressWarnings("unchecked")
+            Map<String, Object> repData = (Map<String, Object>) spilderInfo.get("repData");
+            if (repData == null) {
+                log.error("滑块信息repData为空");
+                return false;
+            }
+
+            String jigsawImageBase64 = (String) repData.get("jigsawImageBase64");
+            String originalImageBase64 = (String) repData.get("originalImageBase64");
+            String secretKey = (String) repData.get("secretKey");
+            String token = (String) repData.get("token");
+
+            if (jigsawImageBase64 == null || originalImageBase64 == null
+                    || secretKey == null || token == null) {
+                log.error("滑块信息不完整: jigsaw={}, original={}, secretKey={}, token={}",
+                        jigsawImageBase64 != null, originalImageBase64 != null,
+                        secretKey != null, token != null);
+                return false;
+            }
+
+            // 2. 解码图片
+            byte[] targetBytes = Base64.getDecoder().decode(jigsawImageBase64);
+            byte[] backgroundBytes = Base64.getDecoder().decode(originalImageBase64);
+
+            // 3. 获取背景图宽度
+            BufferedImage backgroundImage = ImageIO.read(new ByteArrayInputStream(backgroundBytes));
+            if (backgroundImage == null) {
+                log.error("无法解码背景图");
+                return false;
+            }
+            int width = backgroundImage.getWidth();
+
+            // 4. 计算滑块X位置(对应Python的get_X)
+            int X = getX(targetBytes, backgroundBytes);
+
+            // 5. 计算归一化坐标(对应Python: t = 320 * t / setSize_imgWidth)
+            double t = 320.0 * X / width;
+            String pointJson = "{\"x\":" + t + ",\"y\":5}";
+
+            // 6. AES加密坐标(对应Python的aes_encrypt)
+            String encryptedPointJson = aesEncryptForSpilder(pointJson, secretKey);
+
+            // 7. 提交滑块验证(对应Python的check_spilder)
+            Map<String, Object> checkData = new HashMap<>();
+            checkData.put("captchaType", "blockPuzzle");
+            checkData.put("pointJson", encryptedPointJson);
+            checkData.put("token", token);
+
+            Map<String, Object> checkResult = requestComponent.checkSpilder(checkData, headers);
+
+            // 8. 判断初步验证结果
+            if (checkResult == null || !"0000".equals(checkResult.get("repCode"))) {
+                String repMsg = checkResult != null ? (String) checkResult.get("repMsg") : "滑块验证失败";
+                log.error("滑块初步验证失败: {}", repMsg);
+                return false;
+            }
+
+            log.info("滑块初步验证通过,进行二次验证");
+
+            // 9. 二次验证(Python新增步骤)
+            // 对应Python: rstoken = check_spilder_res['repData']['token']; captchaVerification = aes_encrypt(rstoken + '---' + point_json, secretKey)
+            @SuppressWarnings("unchecked")
+            Map<String, Object> checkRepData = (Map<String, Object>) checkResult.get("repData");
+            if (checkRepData == null) {
+                log.error("滑块初步验证repData为空");
+                return false;
+            }
+            String rsToken = (String) checkRepData.get("token");
+            if (rsToken == null) {
+                log.error("滑块初步验证返回token为空");
+                return false;
+            }
+            String captchaVerification = aesEncryptForSpilder(rsToken + "---" + pointJson, secretKey);
+
+            Map<String, Object> verifyData = new HashMap<>();
+            verifyData.put("captchaVerification", captchaVerification);
+
+            Map<String, Object> verifyResult = requestComponent.verifySpilder(verifyData, validate0, validate1, headers);
+
+            // 10. 判断二次验证结果(对应Python: verify_spilder_res['repCode'] == '0000')
+            if (verifyResult != null && "0000".equals(verifyResult.get("repCode"))) {
+                log.info("滑块二次验证通过");
+                return true;
+            } else {
+                String repMsg = verifyResult != null ? (String) verifyResult.get("repMsg") : "滑块二次验证失败";
+                log.error("滑块二次验证失败: {}", repMsg);
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("滑块验证异常: {}", e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 计算滑块匹配位置(对应Python的get_X方法,使用模板匹配算法)
+     *
+     * <p>通过比较拼图块与背景图的像素差异,找到拼图块在背景图中的最佳匹配位置</p>
+     *
+     * @param jigsawBytes    拼图块图片字节(带透明通道的滑块图)
+     * @param backgroundBytes 背景图图片字节
+     * @return 滑块X坐标
+     */
+    private int getX(byte[] jigsawBytes, byte[] backgroundBytes) {
+        try {
+            BufferedImage jigsaw = ImageIO.read(new ByteArrayInputStream(jigsawBytes));
+            BufferedImage background = ImageIO.read(new ByteArrayInputStream(backgroundBytes));
+
+            if (jigsaw == null || background == null) {
+                log.warn("无法解码滑块图片,使用默认X=0");
+                return 0;
+            }
+
+            int bgWidth = background.getWidth();
+            int bgHeight = background.getHeight();
+            int jgWidth = jigsaw.getWidth();
+            int jgHeight = jigsaw.getHeight();
+
+            // 搜索范围:只在X方向滑动(滑块验证通常只需要水平匹配)
+            int searchEnd = bgWidth - jgWidth;
+            if (searchEnd <= 0) {
+                return 0;
+            }
+
+            double bestMatch = Double.MAX_VALUE;
+            int bestX = 0;
+
+            // 遍历X方向搜索最佳匹配位置
+            for (int x = 0; x <= searchEnd; x++) {
+                double diff = 0;
+                int count = 0;
+
+                // 比较拼图块非透明像素与背景对应位置的像素差异
+                for (int jx = 0; jx < jgWidth; jx++) {
+                    for (int jy = 0; jy < jgHeight; jy++) {
+                        int jigsawPixel = jigsaw.getRGB(jx, jy);
+                        int jigsawAlpha = (jigsawPixel >> 24) & 0xff;
+
+                        // 只比较非透明的拼图像素
+                        if (jigsawAlpha > 128) {
+                            int bgPixel = background.getRGB(x + jx, jy);
+
+                            int jr = (jigsawPixel >> 16) & 0xff;
+                            int jg = (jigsawPixel >> 8) & 0xff;
+                            int jb = jigsawPixel & 0xff;
+                            int br = (bgPixel >> 16) & 0xff;
+                            int bg = (bgPixel >> 8) & 0xff;
+                            int bb = bgPixel & 0xff;
+
+                            // 使用颜色差异的平方和作为相似度度量
+                            diff += Math.pow(jr - br, 2) + Math.pow(jg - bg, 2) + Math.pow(jb - bb, 2);
+                            count++;
+                        }
+                    }
+                }
+
+                if (count > 0) {
+                    diff /= count;
+                    if (diff < bestMatch) {
+                        bestMatch = diff;
+                        bestX = x;
+                    }
+                }
+            }
+
+            log.info("滑块匹配结果: X={}, diff={}", bestX, bestMatch);
+            return bestX;
+        } catch (Exception e) {
+            log.error("滑块图片匹配异常: {}", e.getMessage(), e);
+            return 0;
+        }
+    }
+
+    /**
+     * AES加密(对应Python的aes_encrypt方法)
+     *
+     * <p>使用AES/ECB/PKCS5Padding模式加密,密钥不足16位时右侧补空格,超出16位截断</p>
+     *
+     * @param plainText 明文
+     * @param key       密钥
+     * @return Base64编码的密文
+     */
+    private String aesEncryptForSpilder(String plainText, String key) {
+        // 确保密钥长度为16字节(对应Python: key.ljust(16)[:16])
+        String paddedKey;
+        if (key.length() < 16) {
+            paddedKey = String.format("%-16s", key);
+        } else {
+            paddedKey = key.substring(0, 16);
+        }
+        return AESUtils.encryptECBPK5Base64(plainText, paddedKey);
+    }
+
     /**
     /**
      * 构建报价结果VO
      * 构建报价结果VO
      *
      *
@@ -543,10 +791,12 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
                                 costs.setCiPremium(planFee);
                                 costs.setCiPremium(planFee);
                                 totalPremium += fee;
                                 totalPremium += fee;
                             } else if ("RM9".equals(payReason)) {
                             } else if ("RM9".equals(payReason)) {
-                                vvTax = fee;
-                                costs.setVvTax(planFee);
+                                vvTax = vvTax +fee;
+                                totalPremium += fee;
+                            } else if ("RM8".equals(payReason)) {
+                                vvTax = vvTax +fee;
                                 totalPremium += fee;
                                 totalPremium += fee;
-                            } else if (proposalNo != null && proposalNo.contains("DAA")) {
+                            }else if (proposalNo != null && proposalNo.contains("DAA")) {
                                 biPremium += fee;
                                 biPremium += fee;
                                 totalPremium += fee;
                                 totalPremium += fee;
                             } else if (proposalNo != null && proposalNo.contains("DZA")) {
                             } else if (proposalNo != null && proposalNo.contains("DZA")) {

+ 1 - 1
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/classifier/impl/PassengerCarClassifier.java

@@ -22,7 +22,7 @@ public class PassengerCarClassifier implements CarKindClassifier {
     @Override
     @Override
     public CarKindDetailIdCode getKind(CarInfoVo car) {
     public CarKindDetailIdCode getKind(CarInfoVo car) {
         int seats = Integer.parseInt(car.getSeatCount());
         int seats = Integer.parseInt(car.getSeatCount());
-        if (seats <= 6) {
+        if (seats < 6) {
             return CarKindDetailIdCode.K01;
             return CarKindDetailIdCode.K01;
         }
         }
         if (seats <= 10) {
         if (seats <= 10) {

+ 12 - 1
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsOrdersTaxServiceImpl.java

@@ -31,7 +31,18 @@ public class InsOrdersTaxServiceImpl extends ServiceImpl<InsOrdersTaxMapper, Ins
     public void createTax(InsOrders insOrders, QuoteVo quoteVo) {
     public void createTax(InsOrders insOrders, QuoteVo quoteVo) {
         InsOrdersTax insOrdersTax = new InsOrdersTax();
         InsOrdersTax insOrdersTax = new InsOrdersTax();
         insOrdersTax.setOrderNo(insOrders.getId());
         insOrdersTax.setOrderNo(insOrders.getId());
-        insOrdersTax.setTaxRelifFlag(Integer.parseInt(quoteVo.getVehicleAndVesselTaxVo().getTaxRelifFlag()));
+        Integer taxFlag = Integer.parseInt(quoteVo.getVehicleAndVesselTaxVo().getTaxRelifFlag());
+        switch(taxFlag){
+            case 1:
+                // 正常缴税赋值字段
+                insOrdersTax.setTaxpayerName(quoteVo.getVehicleAndVesselTaxVo().getTaxpayerName());
+                insOrdersTax.setTaxpayerIdentifier(quoteVo.getVehicleAndVesselTaxVo().getTaxpayerIdentifier());
+                insOrdersTax.setIdentifyNumber(quoteVo.getVehicleAndVesselTaxVo().getIdentifyNumber());
+                break;
+            default:
+                break;
+        }
+        insOrdersTax.setTaxRelifFlag(taxFlag);
         this.save(insOrdersTax);
         this.save(insOrdersTax);
     }
     }
 
 

+ 8 - 0
tenant/insurance/quotation-taikang/src/main/java/com/jzg/quotation/taikang/crawler/service/TKSimpleService.java

@@ -1,16 +1,19 @@
 package com.jzg.quotation.taikang.crawler.service;
 package com.jzg.quotation.taikang.crawler.service;
 
 
+import com.alibaba.fastjson.JSONObject;
 import com.jzg.commons.constants.quote.enums.quote.CarKindCode;
 import com.jzg.commons.constants.quote.enums.quote.CarKindCode;
 import com.jzg.commons.constants.quote.enums.quote.VehicleUseCode;
 import com.jzg.commons.constants.quote.enums.quote.VehicleUseCode;
 import com.jzg.commons.entity.orders.po.InsOrdersCarUserInfo;
 import com.jzg.commons.entity.orders.po.InsOrdersCarUserInfo;
 import com.jzg.commons.entity.quote.vo.*;
 import com.jzg.commons.entity.quote.vo.*;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.quotation.commons.filter.CarModelsFilterUtils;
 import com.jzg.quotation.commons.filter.CarModelsFilterUtils;
+import com.jzg.quotation.commons.utils.TimeProcessing;
 import com.jzg.quotation.taikang.crawler.CodeUtil;
 import com.jzg.quotation.taikang.crawler.CodeUtil;
 import com.jzg.quotation.taikang.crawler.model.TKLoginSession;
 import com.jzg.quotation.taikang.crawler.model.TKLoginSession;
 import com.jzg.quotation.taikang.crawler.model.api.*;
 import com.jzg.quotation.taikang.crawler.model.api.*;
 import com.jzg.quotation.taikang.crawler.model.entity.*;
 import com.jzg.quotation.taikang.crawler.model.entity.*;
 import jakarta.annotation.Resource;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
@@ -18,8 +21,11 @@ import org.springframework.stereotype.Service;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.util.*;
 import java.util.*;
 import java.util.function.Function;
 import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
+@Slf4j
 @Service
 @Service
 public class TKSimpleService {
 public class TKSimpleService {
 
 
@@ -1139,6 +1145,8 @@ public class TKSimpleService {
             nonCarProductList.forEach(nonCarProductListDTO -> {
             nonCarProductList.forEach(nonCarProductListDTO -> {
                 nonCarProductListDTO.setIsSelect("0");
                 nonCarProductListDTO.setIsSelect("0");
                 nonCarProductListDTO.setPremium("0");
                 nonCarProductListDTO.setPremium("0");
+                nonCarProductListDTO.setStartDate(null);
+                nonCarProductListDTO.setEndDate(null);
             });
             });
         }
         }
 
 

+ 1 - 1
tenant/insurance/quotation-taikang/src/main/java/com/jzg/quotation/taikang/crawler/service/impl/TKQuotationBaseServiceImpl.java

@@ -345,7 +345,7 @@ public class TKQuotationBaseServiceImpl implements TKQuotationBaseService {
                     }
                     }
                 });
                 });
 
 
-            } else if ("C02".equals(imageCode)) {
+            } else if ("C02".equals(imageCode) || "D02".equals(imageCode)) {
                 List<InsuranceImageVo.ImageDTO> imageBase64List = s.getImageBase64List();
                 List<InsuranceImageVo.ImageDTO> imageBase64List = s.getImageBase64List();
                 imageBase64List.forEach(c -> {
                 imageBase64List.forEach(c -> {
                     String imageId = c.getImageId();
                     String imageId = c.getImageId();

+ 34 - 0
tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/component/ZiJinCrawlerCacheComponent.java

@@ -3,6 +3,7 @@ package com.jzg.quotation.zijin.crawler.component;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.JSONObject;
+import com.jzg.quotation.zijin.crawler.entity.po.ZiJinCrawlerOrder;
 import com.jzg.quotation.zijin.crawler.entity.request.ZiJinCrawlerSaveQuoteInfoRequest;
 import com.jzg.quotation.zijin.crawler.entity.request.ZiJinCrawlerSaveQuoteInfoRequest;
 import com.jzg.quotation.zijin.crawler.entity.request.ZiJinCrawlerUploadImageInfoListRequest;
 import com.jzg.quotation.zijin.crawler.entity.request.ZiJinCrawlerUploadImageInfoListRequest;
 import com.jzg.quotation.zijin.crawler.entity.response.ZiJinCrawlerSaveResponse;
 import com.jzg.quotation.zijin.crawler.entity.response.ZiJinCrawlerSaveResponse;
@@ -12,6 +13,7 @@ import org.redisson.api.RBucket;
 import org.redisson.api.RMap;
 import org.redisson.api.RMap;
 import org.redisson.api.RedissonClient;
 import org.redisson.api.RedissonClient;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
 
 
 import java.time.Duration;
 import java.time.Duration;
 import java.util.HashMap;
 import java.util.HashMap;
@@ -51,6 +53,10 @@ public class ZiJinCrawlerCacheComponent {
 
 
     public static final long IMAGES_INFO_TIME = 1L;
     public static final long IMAGES_INFO_TIME = 1L;
 
 
+    public static String ZIJIN_ORDER_RESPONSE = "zijin:order:response:";
+
+    public static final long ZIJIN_ORDER_RESPONSE_TIME = 1L;
+
     /**
     /**
      * 缓存认证信息
      * 缓存认证信息
      * @param token
      * @param token
@@ -131,4 +137,32 @@ public class ZiJinCrawlerCacheComponent {
         String imagesInfo = (String) redissonClient.getBucket(IMAGES_INFO + orderNo).get();
         String imagesInfo = (String) redissonClient.getBucket(IMAGES_INFO + orderNo).get();
         return JSONArray.parseArray(imagesInfo, ZiJinCrawlerSaveQuoteInfoRequest.ItemFileData.FileInfo.class);
         return JSONArray.parseArray(imagesInfo, ZiJinCrawlerSaveQuoteInfoRequest.ItemFileData.FileInfo.class);
     }
     }
+
+
+    /**
+     * 缓存订单信息
+     *
+     * @param orderNo   订单号
+     * @param order 报价请求信息
+     */
+    public void cacheOrderRequest(String orderNo, ZiJinCrawlerOrder order) {
+        String responseStr = JSONObject.toJSONString(order);
+        redissonClient.getBucket(ZIJIN_ORDER_RESPONSE + orderNo).set(responseStr, Duration.ofDays(ZIJIN_ORDER_RESPONSE_TIME));
+    }
+
+    /**
+     * 获取订单报价信息
+     *
+     * @param orderNo 订单号
+     * @return 报价订单返回
+     */
+    public ZiJinCrawlerOrder getOrderResponse(String orderNo) {
+        String o = (String) redissonClient.getBucket(ZIJIN_ORDER_RESPONSE + orderNo).get();
+        ZiJinCrawlerOrder ziJinCrawlerOrder = JSON.parseObject(o, ZiJinCrawlerOrder.class);
+        if (ObjectUtils.isEmpty(ziJinCrawlerOrder)) {
+            throw new RuntimeException("订单信息不存在");
+        }
+        return ziJinCrawlerOrder;
+    }
+
 }
 }

+ 1 - 0
tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/enums/ResponseCode.java

@@ -14,6 +14,7 @@ public enum ResponseCode {
 
 
     R_0000("0000", "成功"),
     R_0000("0000", "成功"),
     R_0001("9980", "转保标志!"),
     R_0001("9980", "转保标志!"),
+    R_0002("4001", "重复投保"),
     R_Z00002("Z00002", "再次调用此接口确认");
     R_Z00002("Z00002", "再次调用此接口确认");
 
 
     private final String code;
     private final String code;

+ 7 - 1
tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/service/impl/ZiJinCrawlerOrderServiceImpl.java

@@ -1,6 +1,8 @@
 package com.jzg.quotation.zijin.crawler.service.impl;
 package com.jzg.quotation.zijin.crawler.service.impl;
 
 
 import com.jzg.commons.entity.quote.vo.QuoteVo;
 import com.jzg.commons.entity.quote.vo.QuoteVo;
+import com.jzg.commons.util.ObjectUtil;
+import com.jzg.quotation.zijin.crawler.component.ZiJinCrawlerCacheComponent;
 import com.jzg.quotation.zijin.crawler.entity.po.ZiJinCrawlerOrder;
 import com.jzg.quotation.zijin.crawler.entity.po.ZiJinCrawlerOrder;
 import com.jzg.quotation.zijin.crawler.entity.response.ZiJinCrawlerQuotationResponse;
 import com.jzg.quotation.zijin.crawler.entity.response.ZiJinCrawlerQuotationResponse;
 import com.jzg.quotation.zijin.crawler.entity.response.ZiJinCrawlerSaveProposalResponse;
 import com.jzg.quotation.zijin.crawler.entity.response.ZiJinCrawlerSaveProposalResponse;
@@ -8,6 +10,7 @@ import com.jzg.quotation.zijin.crawler.repository.ZiJinCrawlerOrderRepository;
 import com.jzg.quotation.zijin.crawler.service.ZiJinCrawlerOrderService;
 import com.jzg.quotation.zijin.crawler.service.ZiJinCrawlerOrderService;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 
 
@@ -21,6 +24,7 @@ import java.time.LocalDateTime;
 public class ZiJinCrawlerOrderServiceImpl implements ZiJinCrawlerOrderService {
 public class ZiJinCrawlerOrderServiceImpl implements ZiJinCrawlerOrderService {
 
 
     private final ZiJinCrawlerOrderRepository ziJinCrawlerOrderRepository;
     private final ZiJinCrawlerOrderRepository ziJinCrawlerOrderRepository;
+    private final ZiJinCrawlerCacheComponent ziJinCrawlerCacheComponent;
 
 
     /**
     /**
      * 保存紫金财险爬虫订单
      * 保存紫金财险爬虫订单
@@ -33,7 +37,7 @@ public class ZiJinCrawlerOrderServiceImpl implements ZiJinCrawlerOrderService {
     public void save(QuoteVo quoteVo, ZiJinCrawlerQuotationResponse ziJinCrawlerQuotationResponse, ZiJinCrawlerSaveProposalResponse ziJinCrawlerSaveProposalResponse, String orderId, boolean success) {
     public void save(QuoteVo quoteVo, ZiJinCrawlerQuotationResponse ziJinCrawlerQuotationResponse, ZiJinCrawlerSaveProposalResponse ziJinCrawlerSaveProposalResponse, String orderId, boolean success) {
         ZiJinCrawlerOrder order = new ZiJinCrawlerOrder();
         ZiJinCrawlerOrder order = new ZiJinCrawlerOrder();
         order.setOrdersNo(quoteVo.getOrder().getOrdersNo());
         order.setOrdersNo(quoteVo.getOrder().getOrdersNo());
-        order.setOrderNo(ziJinCrawlerQuotationResponse.getOrderNo());
+        order.setOrderNo(ObjectUtils.isEmpty(ziJinCrawlerQuotationResponse.getOrderNo()) ? ziJinCrawlerSaveProposalResponse.getData().getOrderNo() : ziJinCrawlerQuotationResponse.getOrderNo());
         order.setBusUniqueId(orderId);
         order.setBusUniqueId(orderId);
         order.setQuoteSuccess(success);
         order.setQuoteSuccess(success);
         if (success && ziJinCrawlerSaveProposalResponse != null) {
         if (success && ziJinCrawlerSaveProposalResponse != null) {
@@ -53,6 +57,8 @@ public class ZiJinCrawlerOrderServiceImpl implements ZiJinCrawlerOrderService {
             order.setErrorMsg(ziJinCrawlerQuotationResponse.getRtnMsg());
             order.setErrorMsg(ziJinCrawlerQuotationResponse.getRtnMsg());
         }
         }
         ziJinCrawlerOrderRepository.save(order);
         ziJinCrawlerOrderRepository.save(order);
+        // 缓存数据
+        ziJinCrawlerCacheComponent.cacheOrderRequest(quoteVo.getOrder().getOrdersNo(), order);
     }
     }
 
 
     @Override
     @Override

+ 56 - 6
tenant/insurance/quotation-zijin/src/main/java/com/jzg/quotation/zijin/crawler/service/impl/ZiJinCrawlerRequestImpl.java

@@ -19,6 +19,7 @@ import com.jzg.quotation.commons.entity.GetCaptchaRequest;
 import com.jzg.quotation.commons.entity.GetCaptchaResponse;
 import com.jzg.quotation.commons.entity.GetCaptchaResponse;
 import com.jzg.quotation.commons.standard.conversion.ConfigureParametersConversion;
 import com.jzg.quotation.commons.standard.conversion.ConfigureParametersConversion;
 import com.jzg.quotation.commons.utils.DateTimeUtil;
 import com.jzg.quotation.commons.utils.DateTimeUtil;
+import com.jzg.quotation.commons.utils.TimeProcessing;
 import com.jzg.quotation.zijin.api.constant.enums.MotorVehicleInsuranceEnum;
 import com.jzg.quotation.zijin.api.constant.enums.MotorVehicleInsuranceEnum;
 import com.jzg.quotation.zijin.api.constant.enums.RiskCodeEnum;
 import com.jzg.quotation.zijin.api.constant.enums.RiskCodeEnum;
 import com.jzg.quotation.zijin.api.entity.request.UploadImageInfoListRequest;
 import com.jzg.quotation.zijin.api.entity.request.UploadImageInfoListRequest;
@@ -47,6 +48,8 @@ import org.springframework.util.StringUtils;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.util.*;
 import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /**
 /**
@@ -196,6 +199,7 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
             ziJinCrawlerQuotationRequest.setVehicleInfo(buildVehicleInfo(quoteVo, actualValue, modelCode, iacVehicleCode, ziJinCrawlerCarResponse));
             ziJinCrawlerQuotationRequest.setVehicleInfo(buildVehicleInfo(quoteVo, actualValue, modelCode, iacVehicleCode, ziJinCrawlerCarResponse));
 
 
             // 设置盈利数据列表
             // 设置盈利数据列表
+            // todo: 完了看是否要调整
             ziJinCrawlerQuotationRequest.setProfitDataList(buildProfitDataList(quoteVo));
             ziJinCrawlerQuotationRequest.setProfitDataList(buildProfitDataList(quoteVo));
 
 
             // 设置税务字段
             // 设置税务字段
@@ -211,6 +215,24 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
             // 设置紫金财险爬虫订单号,后面保存到数据库中
             // 设置紫金财险爬虫订单号,后面保存到数据库中
             ziJinCrawlerQuotationResponse.setOrderNo(ziJinCrawlerGetOrderNoResponse.getOrderNo());
             ziJinCrawlerQuotationResponse.setOrderNo(ziJinCrawlerGetOrderNoResponse.getOrderNo());
 
 
+            // 重复投保,延保
+            if (ResponseCode.R_0002.getCode().equals(ziJinCrawlerQuotationResponse.getRtnCode()) && ziJinCrawlerQuotationResponse.getRtnMsg() != null && ziJinCrawlerQuotationResponse.getRtnMsg().contains("重复投保")) {
+                String endTime = extractDuplicateEndTime(ziJinCrawlerQuotationResponse.getRtnMsg());
+                if (StringUtils.hasLength(endTime)) {
+                    String startTime = endTime;
+                    String newEndTime = TimeProcessing.minusOneSecondAfterYear(startTime);
+                    // 修改商业险起保时间(后端接口期望日期格式为 yyyy-MM-dd)
+                    ziJinCrawlerQuotationRequest.getBaseInfo().setStartDate(TimeProcessing.dateTimeToDate(startTime));
+                    ziJinCrawlerQuotationRequest.getBaseInfo().setEndDate(TimeProcessing.minusDays(TimeProcessing.dateTimeToDate(newEndTime), 1));
+                    // 修改交强险起保时间
+                    ziJinCrawlerQuotationRequest.getBaseInfo().setStartDateCI(TimeProcessing.dateTimeToDate(startTime));
+                    ziJinCrawlerQuotationRequest.getBaseInfo().setEndDateCI(TimeProcessing.minusDays(TimeProcessing.dateTimeToDate(newEndTime), 1));
+                    log.info("紫金报价重复投保参数: startTime={}, endTime={}", startTime, newEndTime);
+                    ziJinCrawlerQuotationResponse = ziJinCrawlerRequestComponent.quotation(ziJinCrawlerQuotationRequest, headers);
+                    log.info("紫金报价重复投保后响应: {}", JSON.toJSONString(ziJinCrawlerQuotationResponse));
+                }
+            }
+
             // 转保标志
             // 转保标志
             if (ResponseCode.R_0001.getCode().equals(ziJinCrawlerQuotationResponse.getRtnCode())) {
             if (ResponseCode.R_0001.getCode().equals(ziJinCrawlerQuotationResponse.getRtnCode())) {
                 if (ziJinCrawlerQuotationResponse.getBasePart() != null) {
                 if (ziJinCrawlerQuotationResponse.getBasePart() != null) {
@@ -223,6 +245,7 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
                     }
                     }
                 }
                 }
             }
             }
+
             // 4.0 处理报价失败
             // 4.0 处理报价失败
             if (!ResponseCode.R_0000.getCode().equals(ziJinCrawlerQuotationResponse.getRtnCode())) {
             if (!ResponseCode.R_0000.getCode().equals(ziJinCrawlerQuotationResponse.getRtnCode())) {
                 ziJinCrawlerOrderService.save(quoteVo, ziJinCrawlerQuotationResponse, null, ziJinCrawlerQuotationRequest.getOrderId(), false);
                 ziJinCrawlerOrderService.save(quoteVo, ziJinCrawlerQuotationResponse, null, ziJinCrawlerQuotationRequest.getOrderId(), false);
@@ -353,7 +376,7 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
      */
      */
     @Override
     @Override
     public void uploadImage(InsuranceUploadImageVo insuranceUploadImageVo) {
     public void uploadImage(InsuranceUploadImageVo insuranceUploadImageVo) {
-        log.info("uploadImage入参的值:{}", JSONObject.toJSONString(insuranceUploadImageVo));
+        // log.info("uploadImage入参的值:{}", JSONObject.toJSONString(insuranceUploadImageVo));
         try {
         try {
 
 
             // 获取归属信息
             // 获取归属信息
@@ -373,6 +396,14 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
                 throw new SystemException("未找到订单信息,订单号:" + order.getOrdersNo());
                 throw new SystemException("未找到订单信息,订单号:" + order.getOrdersNo());
             }
             }
 
 
+            String orderNo = null;
+            if (ObjectUtils.isEmpty(ziJinCrawlerOrder.getOrderNo())) {
+                // 获取订单信息
+                ZiJinCrawlerOrder ziJinCrawlerOrder1 = ziJinCrawlerCacheComponent.getOrderResponse(order.getOrdersNo());
+                log.info("缓存中获取订单信息,订单号:{}, 订单信息:{}", order.getOrdersNo(), JSONObject.toJSONString(ziJinCrawlerOrder1));
+                orderNo = ziJinCrawlerOrder1.getOrderNo();
+            }
+
             // 检查影像列表是否为空
             // 检查影像列表是否为空
             if (CollectionUtils.isEmpty(insuranceUploadImageVo.getImageVoList())) {
             if (CollectionUtils.isEmpty(insuranceUploadImageVo.getImageVoList())) {
                 log.warn("影像列表为空,订单号:{}", order.getOrdersNo());
                 log.warn("影像列表为空,订单号:{}", order.getOrdersNo());
@@ -410,7 +441,7 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
                 // 设置文件类型,转换为紫金对应的文件类型
                 // 设置文件类型,转换为紫金对应的文件类型
                 String fileType = getFileTypeByImageCode(insuranceImageVo.getImageCode());
                 String fileType = getFileTypeByImageCode(insuranceImageVo.getImageCode());
                 ziJinCrawlerImageUploadRequest.setFileType(fileType);
                 ziJinCrawlerImageUploadRequest.setFileType(fileType);
-                ziJinCrawlerImageUploadRequest.setOrderNo(ziJinCrawlerOrder.getOrderNo());
+                ziJinCrawlerImageUploadRequest.setOrderNo(ziJinCrawlerOrder.getOrderNo() == null ? orderNo : ziJinCrawlerOrder.getOrderNo());
                 ZiJinCrawlerImageUploadResponse ziJinCrawlerImageUploadResponse = ziJinCrawlerRequestComponent.uploadImage(ziJinCrawlerImageUploadRequest, headers);
                 ZiJinCrawlerImageUploadResponse ziJinCrawlerImageUploadResponse = ziJinCrawlerRequestComponent.uploadImage(ziJinCrawlerImageUploadRequest, headers);
                 log.info("影像上传响应,订单号:{},响应:{}", ziJinCrawlerOrder.getOrderNo(), JSON.toJSONString(ziJinCrawlerImageUploadResponse));
                 log.info("影像上传响应,订单号:{},响应:{}", ziJinCrawlerOrder.getOrderNo(), JSON.toJSONString(ziJinCrawlerImageUploadResponse));
 
 
@@ -1388,8 +1419,8 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
             carPolicySubInfo.setPlanName(accidentalDriving.getProductName());
             carPolicySubInfo.setPlanName(accidentalDriving.getProductName());
             carPolicySubInfo.setInsureGradeNo("");
             carPolicySubInfo.setInsureGradeNo("");
             carPolicySubInfo.setProductCode(accidentalDriving.getProductCode());
             carPolicySubInfo.setProductCode(accidentalDriving.getProductCode());
-            carPolicySubInfo.setRiskCode("0736");
-            carPolicySubInfo.setRiskName("全家安行保");
+            carPolicySubInfo.setRiskCode("0736"); // 完了要调整编码,是否要转换
+            carPolicySubInfo.setRiskName("全家安行保"); // 完了要调整成accidentalDriving.getRiskName(),看名称是否传了
             carPolicySubInfo.setSumAmount(ObjectUtils.isEmpty(accidentalDriving.getSumAmount()) ? null : (int) Double.parseDouble(accidentalDriving.getSumAmount()));
             carPolicySubInfo.setSumAmount(ObjectUtils.isEmpty(accidentalDriving.getSumAmount()) ? null : (int) Double.parseDouble(accidentalDriving.getSumAmount()));
             carPolicySubInfo.setQuantity(accidentalDriving.getQuantity() == 0 ? "0" : String.valueOf(accidentalDriving.getQuantity()));
             carPolicySubInfo.setQuantity(accidentalDriving.getQuantity() == 0 ? "0" : String.valueOf(accidentalDriving.getQuantity()));
             carPolicySubInfo.setIsUseInsured("1");
             carPolicySubInfo.setIsUseInsured("1");
@@ -1738,7 +1769,26 @@ public class ZiJinCrawlerRequestImpl implements ZiJinCrawlerRequest {
         return profitDataList;
         return profitDataList;
     }
     }
 
 
+    /**
+     * 截取重复投保终保日期的正则表达式
+     */
+    private static final Pattern DUPLICATE_END_TIME_PATTERN = Pattern.compile("终保日期 (\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2})");
 
 
-
-
+    /**
+     * 提取重复投保消息中的终保日期
+     *
+     * @param returnMessage 返回消息
+     * @return 提取到的时间字符串,格式为 yyyy-MM-dd HH:mm:ss,未提取到返回 null
+     */
+    private String extractDuplicateEndTime(String returnMessage) {
+        if (returnMessage == null) {
+            return null;
+        }
+        Matcher matcher = DUPLICATE_END_TIME_PATTERN.matcher(returnMessage);
+        if (matcher.find()) {
+            String shortEndTime = matcher.group(1);
+            return shortEndTime + ":00";
+        }
+        return null;
+    }
 }
 }

+ 47 - 100
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlSchemeServiceImpl.java

@@ -35,6 +35,7 @@ import com.jzg.organization.client.OrgClient;
 import com.jzg.organization.mapper.*;
 import com.jzg.organization.mapper.*;
 import com.jzg.organization.service.*;
 import com.jzg.organization.service.*;
 import io.seata.spring.annotation.GlobalTransactional;
 import io.seata.spring.annotation.GlobalTransactional;
+import org.apache.http.util.TextUtils;
 import org.redisson.api.RedissonClient;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
@@ -98,6 +99,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
 
 
     /**
     /**
      * 添加方案
      * 添加方案
+     *
      * @param ptlSchemeSaveVo 车险报价的方案
      * @param ptlSchemeSaveVo 车险报价的方案
      * @return HttpResult<Boolean>
      * @return HttpResult<Boolean>
      */
      */
@@ -124,8 +126,8 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
         List<PtlSchemeInfo> ptlSchemeInfoList = ptlSchemeSaveVo.getPtlSchemeInfoList();
         List<PtlSchemeInfo> ptlSchemeInfoList = ptlSchemeSaveVo.getPtlSchemeInfoList();
         // modify by lipf, 解决描述为空时,修改失效的bug 2026年4月21日17:04:15
         // modify by lipf, 解决描述为空时,修改失效的bug 2026年4月21日17:04:15
         for (PtlSchemeInfo ptlSchemeInfo : ptlSchemeInfoList) {
         for (PtlSchemeInfo ptlSchemeInfo : ptlSchemeInfoList) {
-            var desc =  ptlSchemeInfo.getDescription();
-            if(desc == null){
+            var desc = ptlSchemeInfo.getDescription();
+            if (desc == null) {
                 ptlSchemeInfo.setDescription("");
                 ptlSchemeInfo.setDescription("");
             }
             }
         }
         }
@@ -162,8 +164,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
      */
      */
     private void cleanDrivingIntention(List<PtlSchemeInfo> ptlSchemeInfoList) {
     private void cleanDrivingIntention(List<PtlSchemeInfo> ptlSchemeInfoList) {
         List<String> ids = ptlSchemeInfoList.stream().map(info -> info.getId()).collect(Collectors.toList());
         List<String> ids = ptlSchemeInfoList.stream().map(info -> info.getId()).collect(Collectors.toList());
-        ptlSchemeInsuranceDrivingIntentionMapper.delete(new LambdaQueryWrapper<PtlSchemeInsuranceDrivingIntention>()
-                .in(PtlSchemeInsuranceDrivingIntention::getSchemeInfoId, ids));
+        ptlSchemeInsuranceDrivingIntentionMapper.delete(new LambdaQueryWrapper<PtlSchemeInsuranceDrivingIntention>().in(PtlSchemeInsuranceDrivingIntention::getSchemeInfoId, ids));
     }
     }
 
 
     /**
     /**
@@ -173,8 +174,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
      */
      */
     private void cleanInsurance(List<PtlSchemeInfo> ptlSchemeInfoList) {
     private void cleanInsurance(List<PtlSchemeInfo> ptlSchemeInfoList) {
         List<String> ids = ptlSchemeInfoList.stream().map(PtlSchemeInfo::getId).collect(Collectors.toList());
         List<String> ids = ptlSchemeInfoList.stream().map(PtlSchemeInfo::getId).collect(Collectors.toList());
-        ptlSchemeInsuranceMapper.delete(new LambdaQueryWrapper<PtlSchemeInsurance>()
-                .in(PtlSchemeInsurance::getSchemeInfoId, ids));
+        ptlSchemeInsuranceMapper.delete(new LambdaQueryWrapper<PtlSchemeInsurance>().in(PtlSchemeInsurance::getSchemeInfoId, ids));
     }
     }
 
 
     /**
     /**
@@ -183,20 +183,17 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
      * @return
      * @return
      */
      */
     private void cleanRules(PtlScheme ptlScheme) {
     private void cleanRules(PtlScheme ptlScheme) {
-        ptlSchemeRulesAttrLinkMapper.delete(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>()
-                .eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
+        ptlSchemeRulesAttrLinkMapper.delete(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>().eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
     }
     }
 
 
     @Override
     @Override
     public PtlSchemeQueryVo getScheme(String id) {
     public PtlSchemeQueryVo getScheme(String id) {
-        log.info("根据方案id=[{}]获取方案信息(ptl_scheme_info)",id);
+        log.info("根据方案id=[{}]获取方案信息(ptl_scheme_info)", id);
         //获取方案信息
         //获取方案信息
-        PtlSchemeInfo ptlSchemeInfo = ptlSchemeInfoMapper.selectOne(new LambdaQueryWrapper<PtlSchemeInfo>()
-                .eq(PtlSchemeInfo::getId, id));
+        PtlSchemeInfo ptlSchemeInfo = ptlSchemeInfoMapper.selectOne(new LambdaQueryWrapper<PtlSchemeInfo>().eq(PtlSchemeInfo::getId, id));
         AssertionUtils.isFail(Objects.isNull(ptlSchemeInfo), "方案不存在");
         AssertionUtils.isFail(Objects.isNull(ptlSchemeInfo), "方案不存在");
         //获取方案信息
         //获取方案信息
-        PtlScheme ptlScheme = ptlSchemeMapper.selectOne(new LambdaQueryWrapper<PtlScheme>()
-                .eq(PtlScheme::getId, ptlSchemeInfo.getSchemeId()));
+        PtlScheme ptlScheme = ptlSchemeMapper.selectOne(new LambdaQueryWrapper<PtlScheme>().eq(PtlScheme::getId, ptlSchemeInfo.getSchemeId()));
 
 
         AssertionUtils.isFail(Objects.isNull(ptlScheme), "方案不存在");
         AssertionUtils.isFail(Objects.isNull(ptlScheme), "方案不存在");
 
 
@@ -234,8 +231,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
         List<PtlSchemeInsuranceDrivingIntention> ptlSchemeInsuranceDrivingIntentions = ptlSchemeInsuranceDrivingIntentionMapper.getSchemeDrivingIntention(ptlSchemeInfo.getId());
         List<PtlSchemeInsuranceDrivingIntention> ptlSchemeInsuranceDrivingIntentions = ptlSchemeInsuranceDrivingIntentionMapper.getSchemeDrivingIntention(ptlSchemeInfo.getId());
 
 
         //获取规则
         //获取规则
-        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>()
-                .eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
+        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>().eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
 
 
         List<PtlAgreementUndwrtRulesAttr> agreementRulesVo = rulesGet(ptlScheme.getCompanyId(), ptlScheme.getId());
         List<PtlAgreementUndwrtRulesAttr> agreementRulesVo = rulesGet(ptlScheme.getCompanyId(), ptlScheme.getId());
 
 
@@ -246,21 +242,14 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
     @Override
     @Override
     public PtlSchemeQueryVo getSchemeAll(String companyId, String productId, String schemeType, String energyType, String vehicleType, String carnature) {
     public PtlSchemeQueryVo getSchemeAll(String companyId, String productId, String schemeType, String energyType, String vehicleType, String carnature) {
         //获取方案信息
         //获取方案信息
-        PtlScheme ptlScheme = ptlSchemeMapper.selectOne(new LambdaQueryWrapper<PtlScheme>()
-                .eq(PtlScheme::getCompanyId, companyId)
-                .eq(PtlScheme::getVehicleType, vehicleType)
-                .eq(PtlScheme::getCarnature, carnature)
-                .eq(PtlScheme::getEnergyType, energyType)
-                .eq(PtlScheme::getProductId, productId));
+        PtlScheme ptlScheme = ptlSchemeMapper.selectOne(new LambdaQueryWrapper<PtlScheme>().eq(PtlScheme::getCompanyId, companyId).eq(PtlScheme::getVehicleType, vehicleType).eq(PtlScheme::getCarnature, carnature).eq(PtlScheme::getEnergyType, energyType).eq(PtlScheme::getProductId, productId));
 
 
         if (Objects.isNull(ptlScheme)) {
         if (Objects.isNull(ptlScheme)) {
             throw new NoneException("方案不存在");
             throw new NoneException("方案不存在");
         }
         }
 
 
         //获取方案信息
         //获取方案信息
-        List<PtlSchemeInfo> ptlSchemeInfos = ptlSchemeInfoMapper.selectList(new LambdaQueryWrapper<PtlSchemeInfo>()
-                .eq(PtlSchemeInfo::getSchemeId, ptlScheme.getId())
-                .eq(PtlSchemeInfo::getSchemeType, schemeType));
+        List<PtlSchemeInfo> ptlSchemeInfos = ptlSchemeInfoMapper.selectList(new LambdaQueryWrapper<PtlSchemeInfo>().eq(PtlSchemeInfo::getSchemeId, ptlScheme.getId()).eq(PtlSchemeInfo::getSchemeType, schemeType));
 
 
         List<PtlSchemeQueryVo.Scheme> schemes = new ArrayList<>();
         List<PtlSchemeQueryVo.Scheme> schemes = new ArrayList<>();
         ptlSchemeInfos.forEach(ptlSchemeInfo -> {
         ptlSchemeInfos.forEach(ptlSchemeInfo -> {
@@ -284,8 +273,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
         });
         });
 
 
         //获取规则
         //获取规则
-        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>()
-                .eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
+        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>().eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
 
 
         List<PtlAgreementUndwrtRulesAttr> agreementRulesVo = rulesGet(ptlScheme.getCompanyId(), ptlScheme.getId());
         List<PtlAgreementUndwrtRulesAttr> agreementRulesVo = rulesGet(ptlScheme.getCompanyId(), ptlScheme.getId());
 
 
@@ -310,12 +298,10 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
 
 
     @Override
     @Override
     public List<PtlAgreementUndwrtRulesAttr> rulesGet(String companyId, String schemeId) {
     public List<PtlAgreementUndwrtRulesAttr> rulesGet(String companyId, String schemeId) {
-        List<PtlSchemeRulesAttrLink> linkList = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>()
-                .eq(PtlSchemeRulesAttrLink::getSchemeId, schemeId));
+        List<PtlSchemeRulesAttrLink> linkList = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>().eq(PtlSchemeRulesAttrLink::getSchemeId, schemeId));
         // 获取费用因子属性
         // 获取费用因子属性
         List<PtlAgreementUndwrtRulesAttr> attrList = agreementUndwrtRulesAttrService.getUndwrtRulesAttrList(companyId);
         List<PtlAgreementUndwrtRulesAttr> attrList = agreementUndwrtRulesAttrService.getUndwrtRulesAttrList(companyId);
-        Map<String, PtlAgreementUndwrtRulesAttr> attrMap = attrList.stream()
-                .collect(Collectors.toMap(PtlAgreementUndwrtRulesAttr::getAttrCode, attr -> attr));
+        Map<String, PtlAgreementUndwrtRulesAttr> attrMap = attrList.stream().collect(Collectors.toMap(PtlAgreementUndwrtRulesAttr::getAttrCode, attr -> attr));
         List<PtlAgreementUndwrtRulesAttr> newAttrList = new ArrayList<>();
         List<PtlAgreementUndwrtRulesAttr> newAttrList = new ArrayList<>();
         linkList.forEach(link -> {
         linkList.forEach(link -> {
             if (attrMap.containsKey(link.getAttrCode())) {
             if (attrMap.containsKey(link.getAttrCode())) {
@@ -364,8 +350,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
     public List<PtlAgreementInsCompanyVo> ruleFilterCompany(SchemeRuleFilterVo schemeRuleFilterVo) {
     public List<PtlAgreementInsCompanyVo> ruleFilterCompany(SchemeRuleFilterVo schemeRuleFilterVo) {
         String userSystemCode = baseController.getUserSystemCode();
         String userSystemCode = baseController.getUserSystemCode();
         String systemCode = baseController.getSystemCode();
         String systemCode = baseController.getSystemCode();
-        log.info("租户匹配保司以及保司下的协议等信息。 schemeRuleFilterVo=[{}],userSystemCode=[{}],systemCode=[{}]",
-                JSONUtil.toJsonStr(schemeRuleFilterVo),userSystemCode,systemCode);
+        log.info("租户匹配保司以及保司下的协议等信息。 schemeRuleFilterVo=[{}],userSystemCode=[{}],systemCode=[{}]", JSONUtil.toJsonStr(schemeRuleFilterVo), userSystemCode, systemCode);
         //根据权限获取所有保司
         //根据权限获取所有保司
         HttpResult<List<EsmInsCompany>> companyListResult = esmInsCompanyClient.getUserCompany(systemCode);
         HttpResult<List<EsmInsCompany>> companyListResult = esmInsCompanyClient.getUserCompany(systemCode);
         log.info("租户[{}]有权限的保司有[{}]个", systemCode, CollUtil.size(companyListResult.getData()));
         log.info("租户[{}]有权限的保司有[{}]个", systemCode, CollUtil.size(companyListResult.getData()));
@@ -374,30 +359,22 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
         // 业务员的归属权限是可选参数,不选的话不进行过滤
         // 业务员的归属权限是可选参数,不选的话不进行过滤
         companyList = salespersonFilterCompanyList(schemeRuleFilterVo, companyList);
         companyList = salespersonFilterCompanyList(schemeRuleFilterVo, companyList);
 
 
-        log.info("根据权限获取到的保司个数是[{}].systemCode=[{}]", CollUtil.size(companyList),systemCode);
-        if(CollUtil.isEmpty(companyList)){
-            log.info("租户[{}]没有可报价的保险公司,请到平台系统的【租户管理】-【功能配置】页面进行配置并且查看对应公司的协议,费用是否维护好",systemCode);
+        log.info("根据权限获取到的保司个数是[{}].systemCode=[{}]", CollUtil.size(companyList), systemCode);
+        if (CollUtil.isEmpty(companyList)) {
+            log.info("租户[{}]没有可报价的保险公司,请到平台系统的【租户管理】-【功能配置】页面进行配置并且查看对应公司的协议,费用是否维护好", systemCode);
             AssertionUtils.isFail(companyList.isEmpty(), "没有可报价的保险公司");
             AssertionUtils.isFail(companyList.isEmpty(), "没有可报价的保险公司");
         }
         }
 
 
         //根据基本规则 能源、车辆类型、使用性质获取方案
         //根据基本规则 能源、车辆类型、使用性质获取方案
-        List<PtlScheme> ptlSchemes = ptlSchemeMapper.selectList(new LambdaQueryWrapper<PtlScheme>()
-                .eq(PtlScheme::getEnergyType, schemeRuleFilterVo.getCarInfo().getEnergyTypeCode().equals("0") ? "0" : "1")
-                .like(PtlScheme::getCarnature, schemeRuleFilterVo.getCarInfo().getCarNatureCode())
-                .like(PtlScheme::getVehicleType, schemeRuleFilterVo.getCarInfo().getCarKindCode())
-                .eq(PtlScheme::getProductId, schemeRuleFilterVo.getProductId())
-                .eq(PtlScheme::getIsDelete, 0));
-        log.info("根据车辆信息匹配保险方案:匹配到[{}]个方案。 energyType(能源类型)=[{}],carNatureCode(使用性质)=[{}],carKindCode(车辆性质)=[{}], productId(产品id)=[{}]",
-                CollUtil.size(ptlSchemes), schemeRuleFilterVo.getCarInfo().getEnergyTypeCode(), schemeRuleFilterVo.getCarInfo().getCarNatureCode(),schemeRuleFilterVo.getCarInfo().getCarKindCode(),
-                schemeRuleFilterVo.getProductId());
+        List<PtlScheme> ptlSchemes = ptlSchemeMapper.selectList(new LambdaQueryWrapper<PtlScheme>().eq(PtlScheme::getEnergyType, schemeRuleFilterVo.getCarInfo().getEnergyTypeCode().equals("0") ? "0" : "1").like(PtlScheme::getCarnature, schemeRuleFilterVo.getCarInfo().getCarNatureCode()).like(PtlScheme::getVehicleType, schemeRuleFilterVo.getCarInfo().getCarKindCode()).eq(PtlScheme::getProductId, schemeRuleFilterVo.getProductId()).eq(PtlScheme::getIsDelete, 0));
+        log.info("根据车辆信息匹配保险方案:匹配到[{}]个方案。 energyType(能源类型)=[{}],carNatureCode(使用性质)=[{}],carKindCode(车辆性质)=[{}], productId(产品id)=[{}]", CollUtil.size(ptlSchemes), schemeRuleFilterVo.getCarInfo().getEnergyTypeCode(), schemeRuleFilterVo.getCarInfo().getCarNatureCode(), schemeRuleFilterVo.getCarInfo().getCarKindCode(), schemeRuleFilterVo.getProductId());
 
 
         // 查找规则
         // 查找规则
         List<String> ptlSchemeIds = ptlSchemes.stream().map(PtlScheme::getId).collect(Collectors.toList());
         List<String> ptlSchemeIds = ptlSchemes.stream().map(PtlScheme::getId).collect(Collectors.toList());
 
 
         AssertionUtils.isFail(ptlSchemeIds.isEmpty(), "没有可报价的保险公司");
         AssertionUtils.isFail(ptlSchemeIds.isEmpty(), "没有可报价的保险公司");
 
 
-        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>()
-                .in(PtlSchemeRulesAttrLink::getSchemeId, ptlSchemeIds));
+        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>().in(PtlSchemeRulesAttrLink::getSchemeId, ptlSchemeIds));
 
 
         // 将规则根据schemeId聚合转换成Map
         // 将规则根据schemeId聚合转换成Map
 //        Map<String, List<PtlSchemeRulesAttrLink>> ptlSchemeRulesAttrLinkMap = ptlSchemeRulesAttrLinks.stream()
 //        Map<String, List<PtlSchemeRulesAttrLink>> ptlSchemeRulesAttrLinkMap = ptlSchemeRulesAttrLinks.stream()
@@ -447,13 +424,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
                 // 根据section 分组 进行拆分 规则
                 // 根据section 分组 进行拆分 规则
                 for (PtlAgreementDispatchVo.DispatchVo vo : vos) {
                 for (PtlAgreementDispatchVo.DispatchVo vo : vos) {
                     List<PtlAgreementDispatchRulesAttrLink> attrLinks = vo.getAttrLinks();
                     List<PtlAgreementDispatchRulesAttrLink> attrLinks = vo.getAttrLinks();
-                    Map<String, List<PtlAgreementDispatchRulesAttrLink>> grouped =
-                            attrLinks.stream()
-                                    .collect(Collectors.groupingBy(
-                                            link -> Optional.ofNullable(link.getSection()).orElse("0"),
-                                            LinkedHashMap::new,
-                                            Collectors.toList()
-                                    ));
+                    Map<String, List<PtlAgreementDispatchRulesAttrLink>> grouped = attrLinks.stream().collect(Collectors.groupingBy(link -> Optional.ofNullable(link.getSection()).orElse("0"), LinkedHashMap::new, Collectors.toList()));
                     ValidateDispatchRuleVo.DispatchAttrVo dispatchAttrVo = new ValidateDispatchRuleVo.DispatchAttrVo();
                     ValidateDispatchRuleVo.DispatchAttrVo dispatchAttrVo = new ValidateDispatchRuleVo.DispatchAttrVo();
                     dispatchAttrVo.setDispatchId(vo.getDispatchId());
                     dispatchAttrVo.setDispatchId(vo.getDispatchId());
                     dispatchAttrVo.setDispatchName(vo.getDispatchName());
                     dispatchAttrVo.setDispatchName(vo.getDispatchName());
@@ -491,14 +462,12 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
     }
     }
 
 
     @Override
     @Override
-    public SchemeResultVo getSchemeResult(String companyId, String productId,String seatCount) {
+    public SchemeResultVo getSchemeResult(String companyId, String productId, String seatCount) {
         log.info("根据保司Id和产品id查询协议信息。 companyId=[{}],productId=[{}]", companyId, productId);
         log.info("根据保司Id和产品id查询协议信息。 companyId=[{}],productId=[{}]", companyId, productId);
         List<SchemeResultVo.SchemeVo> schemeInsuranceInfo = ptlSchemeMapper.getSchemeInsuranceInfo(companyId, productId);
         List<SchemeResultVo.SchemeVo> schemeInsuranceInfo = ptlSchemeMapper.getSchemeInsuranceInfo(companyId, productId);
         List<SchemeResultVo.SchemeVo> schemeDrivingInfo = ptlSchemeMapper.getSchemeDrivingInfo(companyId, productId);
         List<SchemeResultVo.SchemeVo> schemeDrivingInfo = ptlSchemeMapper.getSchemeDrivingInfo(companyId, productId);
 
 
 
 
-
-
         // 创建一个 Map,Key: info.id, Value: List<Driving>
         // 创建一个 Map,Key: info.id, Value: List<Driving>
         Map<String, List<SchemeResultVo.Driving>> drivingMap = new HashMap<>();
         Map<String, List<SchemeResultVo.Driving>> drivingMap = new HashMap<>();
 
 
@@ -513,11 +482,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
             int seats = Integer.parseInt(seatCount);
             int seats = Integer.parseInt(seatCount);
             drivingMap.forEach((infoId, drivings) -> {
             drivingMap.forEach((infoId, drivings) -> {
                 drivings.forEach(driving -> {
                 drivings.forEach(driving -> {
-                    jyxSetting setting = jyxSettingMapper.selectOne(
-                            new LambdaQueryWrapper<jyxSetting>()
-                                    .eq(jyxSetting::getJyxConfigId, driving.getId())
-                                    .eq(jyxSetting::getSeats, seats)
-                                    .last("limit 1"));
+                    jyxSetting setting = jyxSettingMapper.selectOne(new LambdaQueryWrapper<jyxSetting>().eq(jyxSetting::getJyxConfigId, driving.getId()).eq(jyxSetting::getSeats, seats).last("limit 1"));
                     if (setting != null) {
                     if (setting != null) {
                         driving.setProductCode(setting.getProductCode());
                         driving.setProductCode(setting.getProductCode());
                         driving.setPremium(setting.getPremium() != null ? setting.getPremium().toString() : null);
                         driving.setPremium(setting.getPremium() != null ? setting.getPremium().toString() : null);
@@ -528,7 +493,6 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
         }
         }
 
 
 
 
-
         // 遍历 schemeInsuranceInfo,把 drivings 合并
         // 遍历 schemeInsuranceInfo,把 drivings 合并
         for (SchemeResultVo.SchemeVo insuranceScheme : schemeInsuranceInfo) {
         for (SchemeResultVo.SchemeVo insuranceScheme : schemeInsuranceInfo) {
             for (SchemeResultVo.SchemeInfoVo info : insuranceScheme.getInfos()) {
             for (SchemeResultVo.SchemeInfoVo info : insuranceScheme.getInfos()) {
@@ -554,7 +518,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
 
 
         SchemeResultVo schemeResultVo = new SchemeResultVo();
         SchemeResultVo schemeResultVo = new SchemeResultVo();
         //基础方案
         //基础方案
-        List<SchemeResultVo.SchemeVo> schemeVo = schemeInsuranceInfo.stream().filter(x -> x.getSchemeType().equals("1")).toList();
+        List<SchemeResultVo.SchemeVo> schemeVo = schemeInsuranceInfo.stream().filter(x -> !TextUtils.isEmpty(x.getSchemeType()) && x.getSchemeType().equals("1")).toList();
 
 
         List<SchemeResultVo.SchemeInfoVo> allInfo = new ArrayList<>();
         List<SchemeResultVo.SchemeInfoVo> allInfo = new ArrayList<>();
 
 
@@ -574,7 +538,7 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
         });
         });
 
 
         //自选方案
         //自选方案
-        SchemeResultVo.SchemeVo opSchemeVo = schemeInsuranceInfo.stream().filter(x -> x.getSchemeType().equals("2")).findFirst().orElse(null);
+        SchemeResultVo.SchemeVo opSchemeVo = schemeInsuranceInfo.stream().filter(x -> !TextUtils.isEmpty(x.getSchemeType()) && x.getSchemeType().equals("2")).findFirst().orElse(null);
         if (Objects.nonNull(opSchemeVo)) {
         if (Objects.nonNull(opSchemeVo)) {
             schemeResultVo.setOptionalScheme(opSchemeVo.getInfos().get(0));
             schemeResultVo.setOptionalScheme(opSchemeVo.getInfos().get(0));
         }
         }
@@ -605,16 +569,11 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
     public Boolean copyScheme(String schemeInfoId, String productCode) {
     public Boolean copyScheme(String schemeInfoId, String productCode) {
 
 
 
 
-        PtlSchemeInfo ptlSchemeInfo = ptlSchemeInfoMapper.selectOne(new LambdaQueryWrapper<PtlSchemeInfo>()
-                .eq(PtlSchemeInfo::getId, schemeInfoId));
-        PtlScheme ptlScheme = ptlSchemeMapper.selectOne(new LambdaQueryWrapper<PtlScheme>()
-                .eq(PtlScheme::getId, ptlSchemeInfo.getSchemeId()));
-        List<PtlSchemeInsurance> ptlSchemeInsurances = ptlSchemeInsuranceMapper.selectList(new LambdaQueryWrapper<PtlSchemeInsurance>()
-                .eq(PtlSchemeInsurance::getSchemeInfoId, ptlSchemeInfo.getId()));
-        List<PtlSchemeInsuranceDrivingIntention> ptlSchemeInsuranceDrivingIntentions = ptlSchemeInsuranceDrivingIntentionMapper.selectList(new LambdaQueryWrapper<PtlSchemeInsuranceDrivingIntention>()
-                .eq(PtlSchemeInsuranceDrivingIntention::getSchemeInfoId, ptlSchemeInfo.getId()));
-        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>()
-                .eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
+        PtlSchemeInfo ptlSchemeInfo = ptlSchemeInfoMapper.selectOne(new LambdaQueryWrapper<PtlSchemeInfo>().eq(PtlSchemeInfo::getId, schemeInfoId));
+        PtlScheme ptlScheme = ptlSchemeMapper.selectOne(new LambdaQueryWrapper<PtlScheme>().eq(PtlScheme::getId, ptlSchemeInfo.getSchemeId()));
+        List<PtlSchemeInsurance> ptlSchemeInsurances = ptlSchemeInsuranceMapper.selectList(new LambdaQueryWrapper<PtlSchemeInsurance>().eq(PtlSchemeInsurance::getSchemeInfoId, ptlSchemeInfo.getId()));
+        List<PtlSchemeInsuranceDrivingIntention> ptlSchemeInsuranceDrivingIntentions = ptlSchemeInsuranceDrivingIntentionMapper.selectList(new LambdaQueryWrapper<PtlSchemeInsuranceDrivingIntention>().eq(PtlSchemeInsuranceDrivingIntention::getSchemeInfoId, ptlSchemeInfo.getId()));
+        List<PtlSchemeRulesAttrLink> ptlSchemeRulesAttrLinks = ptlSchemeRulesAttrLinkMapper.selectList(new LambdaQueryWrapper<PtlSchemeRulesAttrLink>().eq(PtlSchemeRulesAttrLink::getSchemeId, ptlScheme.getId()));
 
 
         String newSchemeId = IdGenerate.nextId();
         String newSchemeId = IdGenerate.nextId();
         String newSchemeInfoId = IdGenerate.nextId();
         String newSchemeInfoId = IdGenerate.nextId();
@@ -652,24 +611,21 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
 
 
     /**
     /**
      * 根据业务员的deptId过滤保险公司列表
      * 根据业务员的deptId过滤保险公司列表
+     *
      * @param schemeRuleFilterVo
      * @param schemeRuleFilterVo
      * @param companyList
      * @param companyList
      * @return
      * @return
      */
      */
     private List<EsmInsCompany> salespersonFilterCompanyList(SchemeRuleFilterVo schemeRuleFilterVo, List<EsmInsCompany> companyList) {
     private List<EsmInsCompany> salespersonFilterCompanyList(SchemeRuleFilterVo schemeRuleFilterVo, List<EsmInsCompany> companyList) {
         SalespersonInfoVo salespersonInfo = schemeRuleFilterVo.getSalespersonInfo();
         SalespersonInfoVo salespersonInfo = schemeRuleFilterVo.getSalespersonInfo();
-        if (CollUtil.isNotEmpty(companyList) &&
-                ObjectUtil.isNotEmpty(salespersonInfo) && StrUtil.isNotBlank(salespersonInfo.getId())){
+        if (CollUtil.isNotEmpty(companyList) && ObjectUtil.isNotEmpty(salespersonInfo) && StrUtil.isNotBlank(salespersonInfo.getId())) {
             // 根据业务员ID获取业务员信息(含部门ID)
             // 根据业务员ID获取业务员信息(含部门ID)
             SysUserJzgInfo sysUserJzgInfo = sysUserJzgInfoService.getUserById(salespersonInfo.getId());
             SysUserJzgInfo sysUserJzgInfo = sysUserJzgInfoService.getUserById(salespersonInfo.getId());
             String userDeptId = sysUserJzgInfo.getDeptId(); // 业务员所属部门ID
             String userDeptId = sysUserJzgInfo.getDeptId(); // 业务员所属部门ID
 
 
             // ========== 核心逻辑开始 ==========
             // ========== 核心逻辑开始 ==========
             // 1. 拿到所有协议ID(来自有权限的保司)
             // 1. 拿到所有协议ID(来自有权限的保司)
-            List<String> allAgreementIds = companyList.stream()
-                    .flatMap(company -> Optional.ofNullable(company.getAgreements()).orElse(Collections.emptyList()).stream())
-                    .map(PtlAgreement::getId)
-                    .collect(Collectors.toList());
+            List<String> allAgreementIds = companyList.stream().flatMap(company -> Optional.ofNullable(company.getAgreements()).orElse(Collections.emptyList()).stream()).map(PtlAgreement::getId).collect(Collectors.toList());
 
 
             if (CollUtil.isNotEmpty(allAgreementIds)) {
             if (CollUtil.isNotEmpty(allAgreementIds)) {
                 // 2. 查询【协议-部门】关联表中,协议在 allAgreementIds 范围内的记录
                 // 2. 查询【协议-部门】关联表中,协议在 allAgreementIds 范围内的记录
@@ -678,33 +634,24 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
                 List<PtlAgreementDept> agreementDepts = ptlAgreementDeptService.list(wrapper);
                 List<PtlAgreementDept> agreementDepts = ptlAgreementDeptService.list(wrapper);
 
 
                 // 3. 筛选:只保留部门ID = 业务员部门ID 的关联记录
                 // 3. 筛选:只保留部门ID = 业务员部门ID 的关联记录
-                List<PtlAgreementDept> userValidAgreementDepts = agreementDepts.stream()
-                        .filter(ad -> StrUtil.equals(ad.getDeptId(), userDeptId))
-                        .collect(Collectors.toList());
+                List<PtlAgreementDept> userValidAgreementDepts = agreementDepts.stream().filter(ad -> StrUtil.equals(ad.getDeptId(), userDeptId)).collect(Collectors.toList());
 
 
                 // 4. 提取出:当前业务员【有权限的协议ID】
                 // 4. 提取出:当前业务员【有权限的协议ID】
-                Set<String> validAgreementIds = userValidAgreementDepts.stream()
-                        .map(PtlAgreementDept::getAgreementId)
-                        .collect(Collectors.toSet());
+                Set<String> validAgreementIds = userValidAgreementDepts.stream().map(PtlAgreementDept::getAgreementId).collect(Collectors.toSet());
 
 
                 // 5. 过滤保司列表:只保留【包含有效协议】的保司
                 // 5. 过滤保司列表:只保留【包含有效协议】的保司
-                companyList = companyList.stream()
-                        .filter(company -> {
-                            List<PtlAgreement> agreements = company.getAgreements();
-                            if (CollUtil.isEmpty(agreements)) {
-                                return false;
-                            }
-                            // 只要保司下有一个协议在 validAgreementIds 里,就保留
-                            return agreements.stream()
-                                    .anyMatch(agreement -> validAgreementIds.contains(agreement.getId()));
-                        })
-                        .collect(Collectors.toList());
+                companyList = companyList.stream().filter(company -> {
+                    List<PtlAgreement> agreements = company.getAgreements();
+                    if (CollUtil.isEmpty(agreements)) {
+                        return false;
+                    }
+                    // 只要保司下有一个协议在 validAgreementIds 里,就保留
+                    return agreements.stream().anyMatch(agreement -> validAgreementIds.contains(agreement.getId()));
+                }).collect(Collectors.toList());
 
 
                 // 6. 每个保司只保留【业务员有权限的协议】
                 // 6. 每个保司只保留【业务员有权限的协议】
                 companyList.forEach(company -> {
                 companyList.forEach(company -> {
-                    List<PtlAgreement> filteredAgreements = company.getAgreements().stream()
-                            .filter(agreement -> validAgreementIds.contains(agreement.getId()))
-                            .collect(Collectors.toList());
+                    List<PtlAgreement> filteredAgreements = company.getAgreements().stream().filter(agreement -> validAgreementIds.contains(agreement.getId())).collect(Collectors.toList());
                     company.setAgreements(filteredAgreements);
                     company.setAgreements(filteredAgreements);
                 });
                 });
             }
             }