Procházet zdrojové kódy

fix 恒邦多次重复投保判定

liub před 3 měsíci
rodič
revize
12dc79ca2d

+ 11 - 5
tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/component/HengBangCrawlerRequestComponent.java

@@ -363,20 +363,26 @@ public class HengBangCrawlerRequestComponent {
     }
 
 
-    // 重复投保提取时间
-    private static final Pattern DUPLICATE_END_TIME_PATTERN = Pattern.compile("终保日期 (\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2})");
+    // 重复投保提取时间(兼容两种格式:终保日期 2025-12-14 16:00 / 终保日期:2026-07-23)
+    private static final Pattern DUPLICATE_END_TIME_PATTERN = Pattern.compile("终保日期[:: ]\\s*(\\d{4}-\\d{2}-\\d{2}(?:\\s+\\d{2}:\\d{2})?)");
 
     /**
      * 提取系统自动分配的起保时间
      *
      * @param calculationResult 核心提示
-     * @return 提取到的时间字符串,未提取到返回 null
+     * @return 提取到的时间字符串(yyyy-MM-dd HH:mm:ss),未提取到返回 null
      */
     public String extractDuplicateEndTime(String calculationResult) {
         Matcher matcher = DUPLICATE_END_TIME_PATTERN.matcher(calculationResult);
         if (matcher.find()) {
-            String shortEndTime = matcher.group(1); // 提取到 2025-12-14 16:00
-            return shortEndTime + ":00"; // 补全秒数为 2025-12-14 16:00:00
+            String time = matcher.group(1);
+            if (time.length() <= 10) {
+                return time + " 00:00:00";
+            }
+            if (time.length() == 16) {
+                return time + ":00";
+            }
+            return time;
         }
         return null;
     }

+ 90 - 32
tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/service/Impl/HengBangCrawlerRequestImpl.java

@@ -154,41 +154,45 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
         HbCrawlerCalculationRequest calculationRequest = new HbCrawlerCalculationRequest(quoteVo, plyappno, depreciationPrice);
         // 非车信息匹配核心
         matchingAccident(quoteVo, headers, plyappno, calculationRequest);
-        String resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-        JSONObject resJsonObj = JSONObject.parseObject(resJson);
+
+        return executeCalculationWithRetry(calculationRequest, headers, quoteVo, plyappno);
+    }
+
+    /**
+     * 执行报价计算并处理重试逻辑(脱保、重复投保、转保车验证码)
+     */
+    private QuoteResultsVo executeCalculationWithRetry(HbCrawlerCalculationRequest calculationRequest, HttpHeaders headers, QuoteVo quoteVo, String plyappno) {
+        JSONObject resJsonObj = callCalculation(calculationRequest, headers);
         if (resJsonObj == null) {
-            return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+            return buildErrorResult(quoteVo);
         }
         String errorMsg = resJsonObj.getString("message");
-        String status = resJsonObj.getString("status");
 
         HbCrawlerProposalResponse proposalResponse = null;
         // 脱保情况 重新赋值核心建议起保时间 二次报价
         if (errorMsg.contains("脱保")) {
             calculationRequest.setOutofwarrantytype("N");
-            resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-            resJsonObj = JSONObject.parseObject(resJson);
-            if (resJsonObj == null) {
-                return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+            errorMsg = retryCalculation(calculationRequest, headers);
+            if (errorMsg == null) {
+                return buildErrorResult(quoteVo);
             }
-            errorMsg = resJsonObj.getString("message");
-            status = resJsonObj.getString("status");
         }
 
-        if (errorMsg.contains("重复投保")) {
-            String time = hbCrawlerRequestComponent.extractDuplicateEndTime(errorMsg);
-            if (time != null) {
-                calculationRequest.setBegintimeCi(DateUtil.toUtcDateTime(time));
-                calculationRequest.setBegintimeBi(DateUtil.toUtcDateTime(time));
+        // 重复投保:循环重试,优先从responseBody.begintimeBi获取建议起保时间
+        int maxRetry = 3;
+        while (errorMsg.contains("重复投保") && maxRetry-- > 0) {
+            String time = getDuplicateBegintime(resJsonObj, errorMsg);
+            log.info("重复投保,重试次数:{}, 建议起保时间:{}", 3 - maxRetry, time);
+            if (time == null) {
+                break;
             }
-
-            resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-            resJsonObj = JSONObject.parseObject(resJson);
+            calculationRequest.setBegintimeCi(DateUtil.toUtcDateTime(time));
+            calculationRequest.setBegintimeBi(DateUtil.toUtcDateTime(time));
+            resJsonObj = callCalculation(calculationRequest, headers);
             if (resJsonObj == null) {
-                return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+                return buildErrorResult(quoteVo);
             }
             errorMsg = resJsonObj.getString("message");
-            status = resJsonObj.getString("status");
         }
 
         if (errorMsg.contains("该车是转保车,请输入对应的转保验证码")) {
@@ -196,27 +200,23 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
             HbCrawlerRecordResponse recordResponse = JSONObject.parseObject(responseBody, HbCrawlerRecordResponse.class);
             // 识别验证码
             String systemJqCode = exteriorUtilsRequest.captchaIdentify(recordResponse.getCheckcodeJqImg());
-            log.info( "系统识别的交强验证码为:" + systemJqCode);
+            log.info("系统识别的交强验证码为:{}", systemJqCode);
             recordResponse.setCheckcodeJq(systemJqCode);
             // 识别验证码
             String systemSyCode = exteriorUtilsRequest.captchaIdentify(recordResponse.getCheckcodeSyImg());
-            log.info( "系统识别的商业验证码为:" + systemSyCode);
+            log.info("系统识别的商业验证码为:{}", systemSyCode);
             recordResponse.setCheckcodeSy(systemSyCode);
-            recordResponse.getCheckcodeJq().replace("0","o");
-            recordResponse.getCheckcodeSy().replace("0","o");
+            recordResponse.setCheckcodeJq(recordResponse.getCheckcodeJq().replace("0", "o"));
+            recordResponse.setCheckcodeSy(recordResponse.getCheckcodeSy().replace("0", "o"));
             calculationRequest.setTransferVehicle(recordResponse);
 
-            resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-            resJsonObj = JSONObject.parseObject(resJson);
-            if (resJsonObj == null) {
-                return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+            errorMsg = retryCalculation(calculationRequest, headers);
+            if (errorMsg == null) {
+                return buildErrorResult(quoteVo);
             }
-            errorMsg = resJsonObj.getString("message");
-            status = resJsonObj.getString("status");
         }
 
-        if (errorMsg.equals("成功")) {
-            errorMsg = "成功";
+        if ("成功".equals(errorMsg)) {
             // 5.2 报价第三步,获取报价数据
             proposalResponse = hbCrawlerRequestComponent.carInsureItem(plyappno, headers);
             HbCrawlerOrder hbCrawlerOrder = new HbCrawlerOrder();
@@ -227,6 +227,64 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
         return HbCrawlerQuoteResultVoBuild.build(quoteVo, proposalResponse, errorMsg);
     }
 
+    /**
+     * 调用报价接口并返回解析结果
+     */
+    private JSONObject callCalculation(HbCrawlerCalculationRequest calculationRequest, HttpHeaders headers) {
+        String resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
+        return JSONObject.parseObject(resJson);
+    }
+
+    /**
+     * 重试报价并返回错误消息,null表示接口异常
+     */
+    private String retryCalculation(HbCrawlerCalculationRequest calculationRequest, HttpHeaders headers) {
+        JSONObject resJsonObj = callCalculation(calculationRequest, headers);
+        return resJsonObj != null ? resJsonObj.getString("message") : null;
+    }
+
+    /**
+     * 构建异常返回结果
+     */
+    private QuoteResultsVo buildErrorResult(QuoteVo quoteVo) {
+        return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+    }
+
+    /**
+     * 获取重复投保场景的建议起保时间(yyyy-MM-dd HH:mm:ss 格式)
+     * 优先从 responseBody.begintimeBi 获取,兜底从 message 正则提取
+     */
+    private String getDuplicateBegintime(JSONObject resJsonObj, String errorMsg) {
+        // 优先从 responseBody.begintimeBi 获取
+        JSONObject responseBody = resJsonObj.getJSONObject("responseBody");
+        if (responseBody != null) {
+            String begintimeBi = responseBody.getString("begintimeBi");
+            if (begintimeBi != null) {
+                return normalizeTimeFormat(begintimeBi);
+            }
+        }
+        // 兼容:从 message 正则提取终保日期
+        return hbCrawlerRequestComponent.extractDuplicateEndTime(errorMsg);
+    }
+
+    /**
+     * 统一时间格式为 yyyy-MM-dd HH:mm:ss
+     */
+    private String normalizeTimeFormat(String time) {
+        if (time == null) {
+            return null;
+        }
+        // "2026-07-23" → "2026-07-23 00:00:00"
+        if (time.length() <= 10) {
+            return time + " 00:00:00";
+        }
+        // "2025-12-14 16:00" → "2025-12-14 16:00:00"
+        if (time.length() == 16) {
+            return time + ":00";
+        }
+        return time;
+    }
+
     /**
      * 注释
      *