Przeglądaj źródła

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

lipf 3 miesięcy temu
rodzic
commit
879e6e8728
17 zmienionych plików z 224 dodań i 49 usunięć
  1. 2 0
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoice.java
  2. 3 3
      tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/filter/CarModelsFilterUtils.java
  3. 14 5
      tenant/insurance/quotation-dajia/src/main/java/com/jzg/quotation/dajia/crawler/service/impl/DaJiaCrawlerRequestImpl.java
  4. 4 0
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/api/service/Impl/GuoRenApiRequestImpl.java
  5. 4 0
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/service/impl/GuoRenCrawlerRequestImpl.java
  6. 12 6
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/entity/Request/HtCrawlerQuoteRequest.java
  7. 56 15
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/service/Impl/HuaTaiCrawlerRequestImpl.java
  8. 19 5
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestBuilder.java
  9. 2 0
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/entity/response/ZhongAnCrawlerPaymentResponse.java
  10. 17 6
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/service/Impl/ZhongAnCrawlerRequestImpl.java
  11. 1 1
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/service/ZhongAnAPIService.java
  12. 1 1
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/utils/JsonUtil.java
  13. 79 0
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/utils/QrBase64Util.java
  14. 1 1
      tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryCarInfoResultTest.java
  15. 1 1
      tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryJyytVehicleModelResultTest.java
  16. 1 1
      tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryPolicyDetailResultTest.java
  17. 7 4
      tenant/organization/src/main/java/com/jzg/organization/service/impl/SignInServiceImpl.java

+ 2 - 0
commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoice.java

@@ -59,4 +59,6 @@ public class InsPlyIncomeInvoice extends BaseModel {
     @Schema(description = "状态")
     private String status;
 
+    @Schema(description = "租户代码")
+    private String systemCode;
 }

+ 3 - 3
tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/filter/CarModelsFilterUtils.java

@@ -35,11 +35,11 @@ public final class CarModelsFilterUtils {
 
         carModelsFilterList = carModelsFilterList.stream().filter(car -> carInfo.getPurchasePrice().equals(String.valueOf(car.getPurchasePrice()))).collect(Collectors.toList());
 
-        if (!stadarCarDTOS.isEmpty()) {
-            return stadarCarDTOS.get(0);
+        if (!carModelsFilterList.isEmpty()) {
+            return carModelsFilterList.get(0);
         }
 
-        return carModelsFilterList.get(0);
+        return stadarCarDTOS.get(0);
     }
 
 }

+ 14 - 5
tenant/insurance/quotation-dajia/src/main/java/com/jzg/quotation/dajia/crawler/service/impl/DaJiaCrawlerRequestImpl.java

@@ -2,6 +2,8 @@ package com.jzg.quotation.dajia.crawler.service.impl;
 
 
 import com.alibaba.fastjson2.JSONObject;
+import com.jzg.commons.core.page.HttpResult;
+import com.jzg.commons.entity.po.SysUploadFiles;
 import com.jzg.commons.entity.quote.vo.AttributionInformationVo;
 import com.jzg.commons.entity.quote.vo.QuoteVo;
 import com.jzg.commons.entity.quote.vo.aggregated.*;
@@ -9,6 +11,7 @@ import com.jzg.commons.entity.quote.vo.base.LoginBase;
 import com.jzg.commons.entity.quote.vo.image.InsuranceImageVo;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.quotation.commons.annotation.QuoteCrawlerService;
+import com.jzg.quotation.commons.client.PlatformClient;
 import com.jzg.quotation.commons.constant.InsuranceCode;
 import com.jzg.quotation.commons.entity.DetectLoginRequest;
 import com.jzg.quotation.commons.entity.GetCaptchaRequest;
@@ -30,13 +33,16 @@ import com.jzg.quotation.dajia.crawler.entity.response.*;
 import com.jzg.quotation.dajia.crawler.service.DaJiaCrawlerOrderService;
 import com.jzg.quotation.dajia.crawler.service.DaJiaCrawlerQuoteProcessService;
 import com.jzg.quotation.dajia.crawler.service.DaJiaCrawlerRequest;
+import com.jzg.quotation.dajia.crawler.utils.ByteArrayMultipartFile;
 import com.jzg.quotation.dajia.crawler.utils.ImagesEncrypt;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
 import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpHeaders;
 import org.springframework.util.StringUtils;
 
+import java.util.Base64;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -324,7 +330,8 @@ public class DaJiaCrawlerRequestImpl implements DaJiaCrawlerRequest {
     public void queryClauseData(ClauseDataVo clauseDataVo) {
 
     }
-
+    @Autowired
+    private PlatformClient platformClient;
 
     /**
      * 下载电子保单
@@ -347,7 +354,6 @@ public class DaJiaCrawlerRequestImpl implements DaJiaCrawlerRequest {
         DaJiaCrawlerOrder daJiaCrawlerOrder = daJiaCrawlerOrderService.getById(downloadPolicyVo.getOrder()
                 .getOrdersNo());
         String userCode = daJiaCrawlerCacheComponent.getUserCode(attributionInformationVo.getUsername());
-
         DaJiaCrawlerDownloadRequest daJiaCrawlerDownloadRequest = new DaJiaCrawlerDownloadRequest(daJiaCrawlerOrder, userCode);
         DownloadPolicyResultVo downloadPolicyResultVo = new DownloadPolicyResultVo();
         DaJiaCrawlerDownloadResponse response;
@@ -356,21 +362,24 @@ public class DaJiaCrawlerRequestImpl implements DaJiaCrawlerRequest {
         if (StringUtils.hasLength(daJiaCrawlerOrder.getCiPolicyNo())) {
             daJiaCrawlerDownloadRequest.setPlyNo(daJiaCrawlerOrder.getCiPolicyNo());
             response = daJiaCrawlerRequestComponent.electronPolicyIODownload(daJiaCrawlerDownloadRequest, headers);
-            downloadPolicyResultVo.setJqUrl(response.getFile());
+            HttpResult<SysUploadFiles> upload = platformClient.upload(new ByteArrayMultipartFile("file", response.getFileName(), "application/pdf", Base64.getDecoder().decode(response.getFile())));
+            downloadPolicyResultVo.setJqUrl(upload.getData().getDownloadUrl());
         }
 
         // 下载商业险保单
         if (StringUtils.hasLength(daJiaCrawlerOrder.getBiPolicyNo())) {
             daJiaCrawlerDownloadRequest.setPlyNo(daJiaCrawlerOrder.getBiPolicyNo());
             response = daJiaCrawlerRequestComponent.electronPolicyIODownload(daJiaCrawlerDownloadRequest, headers);
-            downloadPolicyResultVo.setSyUrl(response.getFile());
+            HttpResult<SysUploadFiles> upload = platformClient.upload(new ByteArrayMultipartFile("file", response.getFileName(), "application/pdf", Base64.getDecoder().decode(response.getFile())));
+            downloadPolicyResultVo.setSyUrl(upload.getData().getDownloadUrl());
         }
 
         // 下载非车险保单
         if (StringUtils.hasLength(daJiaCrawlerOrder.getCiPolicyNo())) {
             daJiaCrawlerDownloadRequest.setPlyNo(daJiaCrawlerOrder.getCiPolicyNo());
             response = daJiaCrawlerRequestComponent.electronPolicyIODownload(daJiaCrawlerDownloadRequest, headers);
-            downloadPolicyResultVo.setJyUrl(response.getFile());
+            HttpResult<SysUploadFiles> upload = platformClient.upload(new ByteArrayMultipartFile("file", response.getFileName(), "application/pdf", Base64.getDecoder().decode(response.getFile())));
+            downloadPolicyResultVo.setJyUrl(upload.getData().getDownloadUrl());
         }
         return downloadPolicyResultVo;
     }

+ 4 - 0
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/api/service/Impl/GuoRenApiRequestImpl.java

@@ -584,6 +584,10 @@ public class GuoRenApiRequestImpl implements GuoRenApiRequest {
     public List<GuoRenSpecialAgreementVo> getSpecialAgreements(QuoteVo quoteVo) {
         List<InsCompanyClause> companyClauseVos = quoteVo.getSpecialInfoVo();
         List<GuoRenSpecialAgreementVo> list = new ArrayList<>();
+        if (CollUtil.isEmpty(companyClauseVos)) {
+            logger.warn("国任获取特约数据失败,公司条款为空");
+            return list;
+        }
         for (InsuranceRisk insuranceRisk : InsuranceRisk.getInsuranceRisk()) {
             List<GuoRenSpecialAgreementVo> collect = companyClauseVos.stream().map(x -> new GuoRenSpecialAgreementVo(x, insuranceRisk)).collect(Collectors.toList());
             list.addAll(collect);

+ 4 - 0
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/service/impl/GuoRenCrawlerRequestImpl.java

@@ -1,5 +1,6 @@
 package com.jzg.quotation.guoren.crawler.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.jzg.commons.constants.InsuranceRisk;
 import com.jzg.commons.constants.dict.InsOrderStatusEnum;
 import com.jzg.commons.entity.orders.po.InsOrders;
@@ -196,6 +197,9 @@ public class GuoRenCrawlerRequestImpl implements GuoRenCrawlerRequest {
     private List<GuoRenSpecialAgreementVo> getSpecialAgreements(QuoteVo quoteVo) {
         List<InsCompanyClause> companyClauseVos = quoteVo.getSpecialInfoVo();
         List<GuoRenSpecialAgreementVo> list = new ArrayList<>();
+        if (CollUtil.isEmpty(companyClauseVos)) {
+            return list;
+        }
         for (InsuranceRisk insuranceRisk : InsuranceRisk.getInsuranceRisk()) {
             List<GuoRenSpecialAgreementVo> collect = companyClauseVos.stream()
                     .map(x -> new GuoRenSpecialAgreementVo(x, insuranceRisk))

+ 12 - 6
tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/entity/Request/HtCrawlerQuoteRequest.java

@@ -73,6 +73,9 @@ public class HtCrawlerQuoteRequest implements Serializable {
             RiskInfoVo biRiskInfoVo = quoteVo.getBiRiskInfoVo();
             VehicleAndVesselTaxVo taxVo = quoteVo.getVehicleAndVesselTaxVo();
             this.isFirstCount = "false";
+            if (limitAmount.contains(",")) {
+                this.transferFlag = limitAmount.split(",")[1];
+            }
             this.isGlmPrice = 0;
             if (ciRiskInfoVo != null) {
                 this.isInsureTraffic = "true";
@@ -87,7 +90,7 @@ public class HtCrawlerQuoteRequest implements Serializable {
             this.afloatValue = "0.0";
             this.hastax = this.isInsureTraffic;
             this.mealid = StringUtils.isBlank(mealid) ? "" : mealid;
-            List<CovListDTO> covListDTOS = getCovListDTOS(quoteVo, limitAmount);
+            List<CovListDTO> covListDTOS = getCovListDTOS(quoteVo, limitAmount.contains(",") ? limitAmount.split(",")[0] : limitAmount);
             this.covListJSON = JSON.toJSONString(covListDTOS);
             //this.covListJSON = "[{'comcoveragecode':'B','limitamount':'2000000','insteadwalkdailylimitamount':'2000000','coverageclasscode':'1','startdate':'2026-06-26 00:00:00','enddate':'2027-06-25 23:59:59','coveragecode':'1251','isdedu':'1','independentvalue':'','comcoveragename':'机动车第三者责任保险','deductible':'','basedpremium':'','benchmarkpremium':'','kindpremiun':'','deductiblerate':'','billdate':'2026-06-26 00:00:00','serviceTimes':'','serviceCode':''},{'comcoveragecode':'D11','limitamount':'20000','insteadwalkdailylimitamount':'20000','coverageclasscode':'1','startdate':'2026-06-26 00:00:00','enddate':'2027-06-25 23:59:59','coveragecode':'1251','isdedu':'1','independentvalue':'','comcoveragename':'机动车车上人员责任险(司机)','deductible':'','basedpremium':'','benchmarkpremium':'','kindpremiun':'','deductiblerate':'','billdate':'2026-06-26 00:00:00','serviceTimes':'','serviceCode':''},{'comcoveragecode':'D12','limitamount':'20000','insteadwalkdailylimitamount':'20000','coverageclasscode':'1','startdate':'2026-06-26 00:00:00','enddate':'2027-06-25 23:59:59','coveragecode':'1251','isdedu':'1','independentvalue':'','comcoveragename':'机动车车上人员责任险(乘客)','deductible':'','basedpremium':'','benchmarkpremium':'','kindpremiun':'','deductiblerate':'','billdate':'2026-06-26 00:00:00','serviceTimes':'','serviceCode':''},{'comcoveragecode':'BZ','coveragecode':'1002','startdate':'2026-06-26 00:00:00','enddate':'2027-06-25 23:59:59','comcoveragename':'机动车交通事故责任强制保险','billdate':'2026-06-26 00:00:00','limitamount':'200000'}]";
             this.coveragecode = "1251";
@@ -221,10 +224,13 @@ public class HtCrawlerQuoteRequest implements Serializable {
                     covListDTO.setCoveragecode("1251");
                     covListDTO.setComcoveragecode(kindInfoVo.getKindCode());
                     covListDTO.setComcoveragename(kindInfoVo.getKindName());
+                    covListDTO.setStartdate(biRiskInfoVo.getStartDate());
+                    covListDTO.setEnddate(biRiskInfoVo.getEndDate());
+                    covListDTO.setBilldate(biRiskInfoVo.getStartDate());
                     // A机动车损失保险 B机动车第三者责任保险 D11机动车车上人员责任险(司机) D12机动车车上人员责任险(乘客)
                     if(kindInfoVo.getKindCode().equals("A")) {
-                        covListDTO.setLimitamount(limitAmount);
-                        covListDTO.setInsteadwalkdailylimitamount(limitAmount);
+                        covListDTO.setLimitamount(limitAmount.contains(",") ? limitAmount.split(",")[0] : limitAmount);
+                        covListDTO.setInsteadwalkdailylimitamount(limitAmount.contains(",") ? limitAmount.split(",")[0] : limitAmount);
                     } else if (kindInfoVo.getKindCode().equals("SR")){
                         covListDTO.setLimitamount("");
                     }else {
@@ -243,13 +249,10 @@ public class HtCrawlerQuoteRequest implements Serializable {
                     } else {
                         covListDTO.setCoverageclasscode("1");
                     }
-                    covListDTO.setStartdate(ciRiskInfoVo.getStartDate());
-                    covListDTO.setEnddate(ciRiskInfoVo.getEndDate());
                     covListDTO.setIsdedu("1");
                     covListDTO.setKindpremiun("");
                     covListDTO.setServiceTimes(kindInfoVo.getKindCode().equals("SR") ? kindInfoVo.getServiceTimes() : "");
                     covListDTO.setServiceCode("");
-                    covListDTO.setBilldate(ciRiskInfoVo.getStartDate());
 
                     covListDTOS.add(covListDTO);
                 });
@@ -327,6 +330,9 @@ public class HtCrawlerQuoteRequest implements Serializable {
         @JsonProperty("isFirstCount")
         private String isFirstCount;
 
+        @JsonProperty("transferFlag")
+        private String transferFlag;
+
         @JsonProperty("isGlmPrice")
         private Integer isGlmPrice;
 

+ 56 - 15
tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/service/Impl/HuaTaiCrawlerRequestImpl.java

@@ -108,6 +108,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
     private static final Pattern PAT_QNO_SPECIAL = Pattern.compile("name=\"quotationNo\" value=\"(.*?)\"");
     private static final Pattern PAT_MEAL_ID = Pattern.compile("\"mealid\":\"(.*?)\"");
     private static final Pattern LIMIT_AMOUNT = Pattern.compile("value=\"(.*?)\" id=\"js_actualvalue\"");
+    private static final Pattern TRANSFER_FLAG = Pattern.compile("\"erroeType\":\"(.*?)\"");
     private static final Pattern PAT_END_DATE = Pattern.compile("终保日期\\s*([^;]+)");
     private static final Pattern RESPONSE_ERR = Pattern.compile("(\"infoName\"\\s*:\\s*\"([^\"]*)\",\\s*\"infomsg\"\\s*:\\s*\"([^\"]*)\"|\"erroeInfo\"\\s*:\\s*\"([^\"]*)\")",Pattern.DOTALL);
 
@@ -507,9 +508,10 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
 
                         // 重新执行报价预处理流程
                         AttributionInformationVo attr = q.getOrder().getAttributionInformationVo();
+                        log.info(map.get("unMatchVehicleFrameMessage")+"已重新执行prepareQuote,新modelcodexh:{}", currentModelCodexh);
                         currentPre = prepareQuote(attr, gsInfo, q.getCarInfoVo(),
                                 currentModelCodexh, cookie, quotationNo);
-                        log.info("已重新执行prepareQuote,新modelcodexh:{}", currentModelCodexh);
+                        executeQuoteSteps(q, quotationNo, dto, cookie, currentPre, gsInfo);
 
                         // 跳过当前循环,重新执行报价流程
                         continue;
@@ -619,9 +621,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         q.getCiRiskInfoVo().setStartDate(start);
         try {
             LocalDateTime dt = LocalDateTime.parse(start.replace(" ", "T"), DateTimeFormatter.ofPattern(DATE_FMT_T));
-            String newStart = dt.format(DateTimeFormatter.ofPattern(DATE_FMT));
             String newEnd = dt.plusYears(1).minusDays(1).format(DateTimeFormatter.ofPattern(DATE_FMT));
-            q.getCiRiskInfoVo().setStartDate(newStart.substring(0, 10) + " 00:00:00");
             q.getCiRiskInfoVo().setEndDate(newEnd.substring(0, 10) + " 23:59:59");
             log.info("重复投保,重新设置 起保: {}, 终保: {}", q.getCiRiskInfoVo().getStartDate(), q.getCiRiskInfoVo().getEndDate());
             return q;
@@ -634,12 +634,13 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         log.info("报价结果: {}", convertResponseMsg(decryptData));
         if (decryptData.contains("未传送车辆过户标志")) {
             log.error("报价失败: {}", convertResponseMsg(decryptData));
-            throw new SystemException(ERR_PREFIX + convertResponseMsg(decryptData));
+            throw new SystemException(ERR_PREFIX + convertResponseMsg(decryptData)+(decryptData.contains("1251BI") ? ",请勾选'商业险是否过户'选项" : ""));
         }
         if (!decryptData.contains("录入的校验码有误") && decryptData.contains("erroeInfo") && !duplQuoteResult.dupl()) {
             log.error("报价失败: {}", convertResponseMsg(decryptData));
             throw new SystemException(ERR_PREFIX + convertResponseMsg(decryptData));
         }
+        //车损险保额过高  limitAmount
         if (decryptData.contains("车损险保额不能高于")) {
             Pattern pattern = Pattern.compile("车损险保额不能高于(\\d+\\.?\\d*)元");
             Matcher matcher = pattern.matcher(decryptData);
@@ -647,30 +648,70 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
             decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, "", personalInsuredJSON), h);
             return resolveQuoteResponse(duplQuoteResult, decryptData, mealid, limitAmount, h, personalInsuredJSON);
         }
-        HtCrawlerQuoteResponse.DataDTO data = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataDTO.class);
+        if (decryptData.contains("传送了车辆过户标志,请确认")) {
+            Pattern pattern = Pattern.compile(TRANSFER_FLAG.pattern());
+            Matcher matcher = pattern.matcher(decryptData);
+            String transferFlag =  "";
+            if (matcher.find()) transferFlag = matcher.group(1);
+            decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount+","+transferFlag, "", personalInsuredJSON), h);
+            return resolveQuoteResponse(duplQuoteResult, decryptData, mealid, limitAmount, h, personalInsuredJSON);
+        }
+        if (decryptData.contains("TRANS_MESSAGE") && decryptData.contains("终保日期")) {
+            Map<String, String> map = JSONObject.parseObject(decryptData, Map.class);
+            if (map.containsKey("TRANS_MESSAGE") && map.get("TRANS_MESSAGE").contains("交强")) {
+                Matcher m = PAT_END_DATE.matcher(decryptData);
+                if (!m.find()) {
+                    String jiaoqiang_startDate = m.group(1);
+                    duplQuoteResult.q().getCiRiskInfoVo().setStartDate(jiaoqiang_startDate + ":00");
+                    LocalDateTime dt = LocalDateTime.parse(jiaoqiang_startDate.replace(" ", "T"), DateTimeFormatter.ofPattern(DATE_FMT_T));
+                    String newEnd = dt.plusYears(1).minusDays(1).format(DateTimeFormatter.ofPattern(DATE_FMT));
+                    duplQuoteResult.q().getCiRiskInfoVo().setEndDate(newEnd.substring(0, 10) + " 23:59:59");
+                    decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, "", personalInsuredJSON), h);
+                    return resolveQuoteResponse(duplQuoteResult, decryptData, mealid, limitAmount, h, personalInsuredJSON);
+                }
+            }
+            if (map.containsKey("TRANS_MESSAGE") && map.get("TRANS_MESSAGE").contains("商业")) {
+                Matcher m = PAT_END_DATE.matcher(decryptData);
+                if (!m.find()) {
+                    String chesun_startDate = m.group(1);
+                    duplQuoteResult.q().getBiRiskInfoVo().setStartDate(chesun_startDate);
+                    LocalDateTime dt = LocalDateTime.parse(chesun_startDate.replace(" ", "T"), DateTimeFormatter.ofPattern(DATE_FMT_T));
+                    String newEnd = dt.plusYears(1).minusDays(1).format(DateTimeFormatter.ofPattern(DATE_FMT));
+                    duplQuoteResult.q().getBiRiskInfoVo().setEndDate(newEnd.substring(0, 10) + " 23:59:59");
+                    decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, "", personalInsuredJSON), h);
+                    return resolveQuoteResponse(duplQuoteResult, decryptData, mealid, limitAmount, h, personalInsuredJSON);
+                }
+            }
+        }
+        if (decryptData.contains("errrorFlag") && decryptData.contains("carChange")) {
+            throw new SystemException(decryptData);
+        }
+        HtCrawlerQuoteResponse.DataDTO data = null;
+        data = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataDTO.class);
+
         if (data.getApplicationdto() != null && !decryptData.contains("车损险保额不能高于")) return data;
 
-        List<HtCrawlerQuoteResponse.DataDTO.BasePartDTOListDTO.BasePartDTO> list =
+        List<HtCrawlerQuoteResponse.DataDTO.BasePartDTOListDTO.BasePartDTO> basePartDTO =
                 data.getBasepartdtolist() != null ? data.getBasepartdtolist().getBasepartdto() : null;
-        if ((list == null || list.isEmpty()) && !decryptData.contains("车损险保额不能高于")) return data;
+        if ((basePartDTO == null || basePartDTO.isEmpty()) && !decryptData.contains("车损险保额不能高于")) throw new SystemException(ERR_PREFIX + convertResponseMsg(decryptData));
 
         for (int i = 0; i < MAX_RETRIES; i++) {
-            String basePartJSON = buildBasePartJSON(list);
-            String response = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, basePartJSON, personalInsuredJSON), h);
-            log.info("resolveQuoteResponse attempt {}/{}: {}", i + 1, MAX_RETRIES, response);
+            String basePartJSON = buildBasePartJSON(basePartDTO);
+            decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, basePartJSON, personalInsuredJSON), h);
+            log.info("resolveQuoteResponse attempt {}/{}: {}", i + 1, MAX_RETRIES, decryptData);
 
             HtCrawlerQuoteResponse.DataDTO result = null;
             try {
-                result = JSONObject.parseObject(response, HtCrawlerQuoteResponse.DataDTO.class);
+                result = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataDTO.class);
             } catch (Exception e) {
-                throw new RuntimeException(response);
+                throw new RuntimeException(decryptData);
             }
             if (result != null && result.getApplicationdto() != null) {
                 return result;
             }
 
             // autocheckcode 可能不准确,重新获取 quote 刷新验证码
-            if (response.contains("录入的校验码有误") && i < MAX_RETRIES - 1) {
+            if (decryptData.contains("录入的校验码有误") && i < MAX_RETRIES - 1) {
                 log.warn("验证码 校验失败,重新获取 刷新验证码 (尝试 {}/{})", i + 1, MAX_RETRIES);
                 // 必须传空 basePart,接口才会返回新的 basePart 列表(含新 autocheckcode)
                 String fresh = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, "", personalInsuredJSON), h);
@@ -679,9 +720,9 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                     return result;
                 }
                 // 用新的 basePart 列表更新 list,下一轮循环会自动重新 buildBasePartJSON
-                list = result != null && result.getBasepartdtolist() != null
+                basePartDTO = result != null && result.getBasepartdtolist() != null
                         ? result.getBasepartdtolist().getBasepartdto() : null;
-                if (list == null || list.isEmpty()) {
+                if (basePartDTO == null || basePartDTO.isEmpty()) {
                     return result != null ? result : data;
                 }
                 continue;

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

@@ -1674,9 +1674,11 @@ public class RenBaoNewCrawlerRequestBuilder {
         prpCitemCar.put("runAreaName", "中华人民共和国境内(不含港澳台)");
         prpCitemCar.put("countryNature", "03");
         prpCitemCar.put("countryNature1", "03");
-        prpCitemCar.put("chgOwnerFlag", "0");
+        // 过户车标志(与 Python transferFlag 逻辑一致)
+        boolean isTransfer = carInfo.isTransfer();
+        prpCitemCar.put("chgOwnerFlag", isTransfer ? "1" : "0");
         prpCitemCar.put("loanVehicleFlag", "0");
-        prpCitemCar.put("transferVehicleFlag", "0");
+        prpCitemCar.put("transferVehicleFlag", isTransfer ? "1" : "0");
         prpCitemCar.put("newCarFlag", "0");
         prpCitemCar.put("nonLocalFlag", "0");
         prpCitemCar.put("queryArea", queryArea != null ? queryArea : "140000");
@@ -1767,7 +1769,9 @@ public class RenBaoNewCrawlerRequestBuilder {
         prpCitemCar.put("tonCountLastYear", "");
 
         // ========== 过户相关 ==========
-        prpCitemCar.put("chgOwnerDate", "");
+        // 过户日期(与 Python transferDate / chgOwnerDate 一致)
+        String transferDate = carInfo.getTransferDate();
+        prpCitemCar.put("chgOwnerDate", isTransfer && transferDate != null ? transferDate : "");
         prpCitemCar.put("lastYearTransferFlag", "");
         prpCitemCar.put("lastYearTransferDate", "");
 
@@ -1833,7 +1837,17 @@ public class RenBaoNewCrawlerRequestBuilder {
         prpCitemCar.put("startAreaName", "");
         prpCitemCar.put("endAreaName", "");
         prpCitemCar.put("invoiceNo", "");
-        prpCitemCar.put("usedCarInvoiceAmount", "");
+        // 过户车发票金额(与 Python usedCarInvoiceAmount 一致:actualValue * priceCoefficient)
+        String usedCarInvoiceAmount = "";
+        if (isTransfer && actualValue != null && !actualValue.isEmpty()) {
+            try {
+                double amount = Double.parseDouble(actualValue);
+                usedCarInvoiceAmount = String.format("%.2f", amount);
+            } catch (NumberFormatException e) {
+                log.warn("过户车发票金额计算失败, actualValue=[{}]", actualValue);
+            }
+        }
+        prpCitemCar.put("usedCarInvoiceAmount", usedCarInvoiceAmount);
         prpCitemCar.put("hkLicenseNo", "");
         prpCitemCar.put("hkLicenseNoDis", true);
         prpCitemCar.put("ciRateRloatFlag", "01");
@@ -1931,7 +1945,7 @@ public class RenBaoNewCrawlerRequestBuilder {
         ));
         prpCitemCar.put("brandNameInfoAck", false);
         prpCitemCar.put("piccCarInfoFlag", false);
-        prpCitemCar.put("transferDate", "");
+        prpCitemCar.put("transferDate", isTransfer && transferDate != null ? transferDate : "");
         prpCitemCar.put("modelDemandNo", "");
         prpCitemCar.put("vehicleStyleUniqueId", "");
         prpCitemCar.put("monopolyCode", "");

+ 2 - 0
tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/entity/response/ZhongAnCrawlerPaymentResponse.java

@@ -48,6 +48,8 @@ public class ZhongAnCrawlerPaymentResponse extends ZhongAnCrawlerBaseResponse{
             private String redictUrl;
             @JsonProperty("shareShortUrl")
             private String shareShortUrl;
+            @JsonProperty("addWechatQrCodeUrl")
+            private String addWechatQrCodeUrl;
 
             @NoArgsConstructor
             @Data

+ 17 - 6
tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/service/Impl/ZhongAnCrawlerRequestImpl.java

@@ -37,6 +37,7 @@ import com.jzg.quotation.zhongan.crawler.entity.request.*;
 import com.jzg.quotation.zhongan.crawler.entity.response.*;
 import com.jzg.quotation.zhongan.crawler.service.ZhongAnCrawlerOrderService;
 import com.jzg.quotation.zhongan.crawler.service.ZhongAnCrawlerRequest;
+import com.jzg.quotation.zhongan.crawler.utils.QrBase64Util;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;
@@ -46,8 +47,10 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpHeaders;
 
 import java.io.*;
+import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.Base64;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -335,11 +338,14 @@ public class ZhongAnCrawlerRequestImpl implements ZhongAnCrawlerRequest {
         ZhongAnCrawlerOrder zhongAnCrawlerOrder = zhongAnCrawlerOrderService.findByOrdersNo(underwritingVo.getOrder().getOrdersNo());
         ZhongAnCrawlerMarkRequest markRequest = new ZhongAnCrawlerMarkRequest(parameters, zhongAnCrawlerOrder.getOrderNo());
         String markCompleted = zhongAnCrawlerRequestComponent.markCompleted(markRequest, headers);
-        ZhongAnCrawlerPaymentRequest paymentRequest = new ZhongAnCrawlerPaymentRequest(zhongAnCrawlerOrder.getPolicyId(), zhongAnCrawlerOrder.getCheckCode());
-        ZhongAnCrawlerPaymentResponse payment = zhongAnCrawlerRequestComponent.payment(paymentRequest, headers);
+
+        PaymentLinkVo paymentLinkVo = new PaymentLinkVo();
+        paymentLinkVo.setOrder(underwritingVo.getOrder());
+        PaymentLinkResultsVo paymentLinkResultsVo = getPaymentLink(paymentLinkVo);
+
 
         UnderwritingResultsVo underwritingResultsVo = new UnderwritingResultsVo();
-        underwritingResultsVo.setPaymentLink(payment.getValue().getDatas().getShareShortUrl());
+        underwritingResultsVo.setPaymentLink(paymentLinkResultsVo.getPaymentLink());
         //核保结果
         underwritingResultsVo.setUnderwritingResult("核保成功");
         //核保状态
@@ -359,7 +365,7 @@ public class ZhongAnCrawlerRequestImpl implements ZhongAnCrawlerRequest {
      * @param paymentLinkVo
      */
     @Override
-    public PaymentLinkResultsVo getPaymentLink(PaymentLinkVo paymentLinkVo) {
+    public PaymentLinkResultsVo getPaymentLink(PaymentLinkVo paymentLinkVo) throws Exception {
         //获取配置信息
         AttributionInformationVo attributionInformationVo = paymentLinkVo.getOrder().getAttributionInformationVo();
         ZhongAnCrawlerConfigureParameters parameters = ConfigureParametersConversion.conversionEntity(ZhongAnCrawlerConfigureParameters.class, attributionInformationVo.getConfigInfo());
@@ -374,8 +380,13 @@ public class ZhongAnCrawlerRequestImpl implements ZhongAnCrawlerRequest {
         ZhongAnCrawlerUploadInitDateResponse.ValueDTO.DatasDTO initData = zhongAnCrawlerCacheComponent.getInitData(orderBase.getOrdersNo() + ZhongAnCrawlerCacheComponent.INITDATE);
         ZhongAnCrawlerPaymentRequest paymentRequest = new ZhongAnCrawlerPaymentRequest(zhongAnCrawlerOrder.getPolicyId(), initData.getCheckCode());
         ZhongAnCrawlerPaymentResponse payment = zhongAnCrawlerRequestComponent.payment(paymentRequest, headers);
-        paymentLinkResultsVo.setPaymentLink(payment.getValue().getDatas().getShareShortUrl());
-
+        if (payment.getValue().getDatas().getAddWechatQrCodeUrl() != null && !payment.getValue().getDatas().getAddWechatQrCodeUrl().isEmpty()) {
+            String addWechatQrCodeUrl = payment.getValue().getDatas().getAddWechatQrCodeUrl();
+            paymentLinkResultsVo.setPaymentLink(QrBase64Util.downloadImgToBase64(addWechatQrCodeUrl));
+        } else {
+            String shareShortUrl = payment.getValue().getDatas().getShareShortUrl();
+            paymentLinkResultsVo.setPaymentLink(QrBase64Util.createQrCodeBase64(shareShortUrl));
+        }
         return paymentLinkResultsVo;
     }
 

+ 1 - 1
tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/service/ZhongAnAPIService.java

@@ -1,7 +1,7 @@
 package com.jzg.quotation.zhongan.crawler.service;
 
 import com.jzg.quotation.commons.component.RequestComponent;
-import com.jzg.quotation.zhongan.crawler.JsonUtil;
+import com.jzg.quotation.zhongan.crawler.utils.JsonUtil;
 import com.jzg.quotation.zhongan.crawler.client.ZhongAnAPI;
 import com.jzg.quotation.zhongan.crawler.model.param.ZhongAnCacheCarInfoParam;
 import com.jzg.quotation.zhongan.crawler.model.param.ZhongAnPremiumTrialParamCoverageInfo;

+ 1 - 1
tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/JsonUtil.java → tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/utils/JsonUtil.java

@@ -1,4 +1,4 @@
-package com.jzg.quotation.zhongan.crawler;
+package com.jzg.quotation.zhongan.crawler.utils;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.JsonParser;

+ 79 - 0
tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/utils/QrBase64Util.java

@@ -0,0 +1,79 @@
+package com.jzg.quotation.zhongan.crawler.utils;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.common.BitMatrix;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author quchen
+ * @date 2026/6/18 9:50
+ */
+
+public class QrBase64Util {
+    public static String createQrCodeBase64(String content, int width, int height) throws Exception {
+        if (content == null || content.isBlank()) {
+            return null;
+        }
+        Map<EncodeHintType, Object> hints = new HashMap<>();
+        hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
+        hints.put(EncodeHintType.MARGIN, 1);
+
+        BitMatrix bitMatrix = new MultiFormatWriter()
+                .encode(content, BarcodeFormat.QR_CODE, width, height, hints);
+
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                image.setRGB(x, y, bitMatrix.get(x, y) ? Color.BLACK.getRGB() : Color.WHITE.getRGB());
+            }
+        }
+
+        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+            ImageIO.write(image, "png", bos);
+            byte[] imgBytes = bos.toByteArray();
+            return Base64.getEncoder().encodeToString(imgBytes);
+        }
+    }
+
+    public static String createQrCodeBase64(String content) throws Exception {
+        return createQrCodeBase64(content, 350, 350);
+    }
+
+    /**
+     * 远程URL下载图片,转Base64
+     */
+    public static String downloadImgToBase64(String url) throws Exception {
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet get = new HttpGet(url);
+            try (CloseableHttpResponse response = httpClient.execute(get)) {
+                int statusCode = response.getStatusLine().getStatusCode();
+                if (statusCode != 200) {
+                    throw new RuntimeException("图片下载失败,状态码:" + statusCode);
+                }
+                HttpEntity entity = response.getEntity();
+                if (entity == null) {
+                    return null;
+                }
+                byte[] imgBytes = EntityUtils.toByteArray(entity);
+                EntityUtils.consume(entity);
+                return Base64.getEncoder().encodeToString(imgBytes);
+            }
+        }
+    }
+}

+ 1 - 1
tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryCarInfoResultTest.java

@@ -1,6 +1,6 @@
 package com.jzg.quotation.zhongan.crawler.model.result;
 
-import com.jzg.quotation.zhongan.crawler.JsonUtil;
+import com.jzg.quotation.zhongan.crawler.utils.JsonUtil;
 import org.junit.jupiter.api.Test;
 
 import java.util.Map;

+ 1 - 1
tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryJyytVehicleModelResultTest.java

@@ -1,6 +1,6 @@
 package com.jzg.quotation.zhongan.crawler.model.result;
 
-import com.jzg.quotation.zhongan.crawler.JsonUtil;
+import com.jzg.quotation.zhongan.crawler.utils.JsonUtil;
 import org.junit.jupiter.api.Test;
 
 import java.util.Map;

+ 1 - 1
tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryPolicyDetailResultTest.java

@@ -1,6 +1,6 @@
 package com.jzg.quotation.zhongan.crawler.model.result;
 
-import com.jzg.quotation.zhongan.crawler.JsonUtil;
+import com.jzg.quotation.zhongan.crawler.utils.JsonUtil;
 
 import java.util.Map;
 

+ 7 - 4
tenant/organization/src/main/java/com/jzg/organization/service/impl/SignInServiceImpl.java

@@ -60,6 +60,8 @@ public class SignInServiceImpl implements SignInService {
 
     //本地生活在一账通的固定平台ID
     private static final String CLIENT_ID = "jzg-7kP9xL2mN5vQ8zR1jW";
+    // 本地生活平台id
+    private static final String BDSH_CLIENT_ID = "bdsh-X9yZ1wV3uT5sR7qP9o";
 
     private static final Integer ACTIVITY_TYPE = 7;
     @Autowired
@@ -94,7 +96,7 @@ public class SignInServiceImpl implements SignInService {
         } catch (Exception e) {
             log.error("签到抽奖,远程调抽奖次数增加接口异常", e);
         }
-        if (lotteryCountVOHttpResult != null && lotteryCountVOHttpResult.getData().getSuccess()) {
+        if (lotteryCountVOHttpResult != null && lotteryCountVOHttpResult.getCode() == 200) {
             UpdateWrapper<SysUserActivityCompleteInRule> updateWrapper = new UpdateWrapper<>();
             updateWrapper.eq(StrUtil.isNotBlank(dto.getSystemCode()), "system_code", dto.getSystemCode());
             updateWrapper.eq(StrUtil.isNotBlank(dto.getUserId()), "user_id", dto.getUserId());
@@ -259,9 +261,9 @@ public class SignInServiceImpl implements SignInService {
                                     .stream()
                                     .collect(Collectors.groupingBy(UnifiedUserClientVO::getClientId));
                             // 首先查业务员是否在一账通绑定过本地生活  【已绑定过 远程调抽奖次数增加接口 标记已同步
-                            if (userClientGroupMap.containsKey(CLIENT_ID)) {
+                            if (userClientGroupMap.containsKey(BDSH_CLIENT_ID)) {
                                 LotteryCounDTO lotteryCounDTO = new LotteryCounDTO();
-                                lotteryCounDTO.setAppUserId(getUserId(userClientGroupMap.get(CLIENT_ID), CLIENT_ID));
+                                lotteryCounDTO.setAppUserId(getUserId(userClientGroupMap.get(BDSH_CLIENT_ID), BDSH_CLIENT_ID));
                                 lotteryCounDTO.setActivityId(activity.getId());
                                 lotteryCounDTO.setCount(Integer.parseInt(rule.getSignLotteryNum()));
                                 HttpResult<LotteryCountVO> lotteryCountVOHttpResult = null;
@@ -271,7 +273,8 @@ public class SignInServiceImpl implements SignInService {
                                     log.error("签到抽奖,远程调抽奖次数增加接口异常,userId:{},clientId:{},tenantId:{}", userId, CLIENT_ID, tenantId);
                                     log.error("签到抽奖,远程调抽奖次数增加接口异常", e);
                                 }
-                                if (lotteryCountVOHttpResult != null && lotteryCountVOHttpResult.getData().getSuccess()) {
+
+                                if (lotteryCountVOHttpResult != null && lotteryCountVOHttpResult.getCode() == 200) {
                                     // 记录规则完成  标记已同步
                                     completeSignRule(tenantId, userId, activity.getId(),
                                             table.getId(), rule, YesNoEnum.YES.getCode(), ACTIVITY_TYPE);