瀏覽代碼

中煤爬虫报价相关

shenweidong 1 年之前
父節點
當前提交
c2608a06fd

+ 11 - 0
tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/constant/CacheConstant.java

@@ -38,4 +38,15 @@ public class CacheConstant {
      */
     public static final long GR_SPECIAL_AGREEMENT_TIME = 1L;
 
+    /**
+     * 中煤财险授权
+     */
+    public static final String ZHONGMEI_AUTHORIZATION = "zhongmei:authorization:";
+
+    /**
+     * 中煤财险授权 保存时间
+     * 暂时跟中煤时间一样,距离最后一次操作有效期30分钟
+     */
+    public static final long ZHONGMEI_AUTHORIZATION_TIME = 30L;
+
 }

+ 1 - 1
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/api/properties/ZmApiProperties.java

@@ -5,7 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 
 /**
- * @description: todo
+ * @description: 中煤财险 API配置信息
  * @author:shenweidong
  * @date:2025/2/24 11:04
  **/

+ 150 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/component/ZmCrawlerRequestComponent.java

@@ -0,0 +1,150 @@
+package com.jzg.quote.zhongmei.crawler.component;
+
+import com.jzg.commons.exception.SystemException;
+import com.jzg.quotation.commons.component.RequestComponent;
+import com.jzg.quotation.commons.constant.CacheConstant;
+import com.jzg.quotation.commons.entity.vo.base.LoginBase;
+import com.jzg.quotation.commons.entity.vo.base.R;
+import com.jzg.quotation.commons.service.ExteriorUtilsRequest;
+import com.jzg.quote.zhongmei.crawler.constant.enums.RequestUrl;
+import com.jzg.quote.zhongmei.crawler.entity.Request.ZmCrawlerAuthenticateRequest;
+import com.jzg.quote.zhongmei.crawler.entity.Response.ZmCrawlerAuthenticateResponse;
+import com.jzg.quote.zhongmei.crawler.entity.Response.ZmCrawlerCaptchaResponse;
+import com.jzg.quote.zhongmei.crawler.properties.ZmCrawlerProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RMap;
+import org.redisson.api.RedissonClient;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * @description:
+ * @author: shenwd
+ * @date: 2025/3/20 14:39
+ **/
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class ZmCrawlerRequestComponent {
+
+    private final WebClient webClient;
+
+    private final RequestComponent requestComponent;
+
+    private final RedissonClient redissonClient;
+
+    private final ExteriorUtilsRequest exteriorUtilsRequest;
+
+    private final ZmCrawlerProperties zmCrawlerProperties;
+
+    /**
+     * 请求次数
+    */
+    public static final int REQUEST_NUMBER = 3;
+
+    public static final String AUTHORIZATION = "AUTHORIZATION";
+
+    public static final String USER_CODE = "USER_CODE";
+
+    /**
+     * 验证码
+     * @return
+     */
+    public ZmCrawlerCaptchaResponse captcha(int frequency) {
+        if (frequency <= 0) {
+            throw new SystemException("验证码异常");
+        }
+        Map<String, Object> map = Map.of("imgId", UUID.randomUUID().toString().replaceAll("-", ""));
+        // 请求
+        ZmCrawlerCaptchaResponse zmCrawlerCaptchaResponse = requestComponent.requestGET(RequestUrl.CAPTCHA.getRequestUrl(zmCrawlerProperties.getBaseUrl()), map, "", ZmCrawlerCaptchaResponse.class);
+        // 识别验证码
+        String s = exteriorUtilsRequest.captchaIdentify(zmCrawlerCaptchaResponse.getVerifyCode());
+        try {
+            int i = processCaptcha(s);
+            s = String.valueOf(i);
+        } catch (Exception e) {
+            frequency = frequency - 1;
+            return captcha(frequency);
+        }
+        zmCrawlerCaptchaResponse.setVerifyStr(s);
+        return zmCrawlerCaptchaResponse;
+    }
+
+    private Map<String, String> buildCache(ZmCrawlerAuthenticateResponse authenticateResponse) {
+        Map<String, String> authorizationMap = new HashMap<>();
+        authorizationMap.put(AUTHORIZATION, authenticateResponse.getHead().getToken());
+        return authorizationMap;
+    }
+
+    /**
+     * 登录次数
+     * @param username     用户名
+     * @param password     密码
+     * @param loginsNumber 登录次数
+     * @return token
+     */
+    public String authenticate(String username, String password, int loginsNumber) {
+        // 登录验证码获取
+        ZmCrawlerCaptchaResponse captchaResponse = captcha(REQUEST_NUMBER);
+        // 登录存入缓存当中
+        ZmCrawlerAuthenticateRequest authenticateRequest = new ZmCrawlerAuthenticateRequest(captchaResponse, username, password);
+        ResponseEntity<ZmCrawlerAuthenticateResponse> responseEntity = requestComponent.objectPost(RequestUrl.AUTHENTICATE.getRequestUrl(zmCrawlerProperties.getBaseUrl()), authenticateRequest, ZmCrawlerAuthenticateResponse.class);
+        ZmCrawlerAuthenticateResponse authenticateResponse = Optional.of(responseEntity).map(ResponseEntity::getBody).orElseThrow();
+
+        if ("0".equals(authenticateResponse.getHead().getReturnCode())) {
+            Map<String, String> authorizationMap = buildCache(authenticateResponse);
+            RMap<Object, Object> map = redissonClient.getMap(CacheConstant.ZHONGMEI_AUTHORIZATION + username);
+            map.putAll(authorizationMap);
+            map.expire(Duration.ofMinutes(CacheConstant.ZHONGMEI_AUTHORIZATION_TIME));
+            return authorizationMap.get(AUTHORIZATION);
+        }
+        loginsNumber = loginsNumber - 1;
+        if (loginsNumber <= 0) {
+            throw new SystemException(authenticateResponse.getHead().getReturnMessage());
+        }
+        return authenticate(username, password, loginsNumber);
+    }
+
+    public int processCaptcha(String res) {
+        // 检查字符串长度是否足够,并且第三个字符不是'='
+        if (res.length() >= 3 && res.charAt(2) != '=') {
+            char operator = res.charAt(1);
+            try {
+                int operand1 = Integer.parseInt(res.substring(0, 1));
+                int operand2 = Integer.parseInt(res.substring(2, 3));
+                // 注意:这里假设操作符和操作数都是有效的,实际应用中应该添加更多的错误处理
+
+                switch (operator) {
+                    case '+':
+                    case 'f': // 通常'f'不是加法运算符,但根据原代码保留
+                        return operand1 + operand2;
+                    case 'x':
+                    case 'X':
+                    case '4': // 通常'4'不是乘法运算符,但保留以匹配原代码
+                    case '9': // 通常'9'也不是乘法运算符,但保留以匹配原代码
+                        return operand1 * operand2;
+                    case '-':
+                    case 'e': // 通常'e'不是减法运算符,但根据原代码保留
+                    case '=': // 通常'='不是减法运算符,但根据原代码保留
+                    case '2': // 通常'2'也不是减法运算符,但根据原代码保留
+                        return operand1 - operand2;
+                    // 如果操作符不匹配,则获取新的验证码图片(模拟)
+                }
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        throw new SystemException("");
+    }
+
+
+}

+ 51 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/constant/enums/RequestUrl.java

@@ -0,0 +1,51 @@
+package com.jzg.quote.zhongmei.crawler.constant.enums;
+
+import com.jzg.quotation.commons.standard.CrawlerRequestUrlStandard;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @description: todo
+ * @author: shenwd
+ * @date: 2025/3/20 16:22
+ **/
+@AllArgsConstructor
+@Getter
+public enum RequestUrl implements CrawlerRequestUrlStandard {
+    // 登录
+    CAPTCHA("VerifyCode.do", "验证码"),
+    AUTHENTICATE("Login.do", "登录");
+
+    private final String url;
+    private final String description;
+    /**
+     * 获取爬虫请求的 url
+     *
+     * @param url
+     * @return url
+     */
+    @Override
+    public String getRequestUrl(String url) {
+        return CrawlerRequestUrlStandard.super.getRequestUrl(url);
+    }
+
+    /**
+     * 获取爬虫请求的 描述
+     *
+     * @return 描述
+     */
+    @Override
+    public String getRequestDescription() {
+        return "";
+    }
+
+    /**
+     * 获取请求的uri
+     *
+     * @return uri
+     */
+    @Override
+    public String uri() {
+        return "";
+    }
+}

+ 52 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/entity/Request/ZmCrawlerAuthenticateRequest.java

@@ -0,0 +1,52 @@
+package com.jzg.quote.zhongmei.crawler.entity.Request;
+
+import com.jzg.quote.zhongmei.api.entity.request.BaseRequest;
+import com.jzg.quote.zhongmei.crawler.entity.Response.ZmCrawlerCaptchaResponse;
+import lombok.Data;
+
+import java.io.Serial;
+
+/**
+ * @description: 中煤财险 登录
+ * @author: shenwd
+ * @date: 2025/3/20 16:52
+ **/
+@Data
+public class ZmCrawlerAuthenticateRequest extends BaseRequest {
+
+    @Serial
+    private static final long serialVersionUID = -6857128647067009718L;
+
+    public ZmCrawlerAuthenticateRequest(ZmCrawlerCaptchaResponse captchaResponse, String username, String password) {
+        this.userCode = username;
+        this.pwd = password;
+        this.vckeyID = captchaResponse.getVckeyID();
+        this.verifyCode = captchaResponse.getVerifyStr();
+        this.sign = captchaResponse.getVerifyCode();
+    }
+
+    /**
+     * 用户名
+     */
+    private String userCode;
+
+    /**
+     * MD5(盐为空)加密后的密码
+     */
+    private String pwd;
+
+    /**
+     * 验证码id
+     */
+    private String vckeyID;
+
+    /**
+     * 验证码字符串
+     */
+    private String verifyCode;
+
+    /**
+     * 验证码图片
+     */
+    private String sign;
+}

+ 34 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/entity/Response/ZmCrawlerAuthenticateResponse.java

@@ -0,0 +1,34 @@
+package com.jzg.quote.zhongmei.crawler.entity.Response;
+
+import com.alibaba.fastjson.JSONArray;
+import com.jzg.quote.zhongmei.api.entity.response.BaseResponse;
+import lombok.Data;
+
+import java.io.Serial;
+
+/**
+ * @description: 中煤财险 登录返参
+ * @author: shenwd
+ * @date: 2025/3/20 17:02
+ **/
+@Data
+public class ZmCrawlerAuthenticateResponse extends BaseResponse {
+
+    @Serial
+    private static final long serialVersionUID = 7801281039478093622L;
+
+    /**
+     * 暂时先不管
+     */
+    private JSONArray loginCom;
+
+    private String pwdExpireFlag;
+
+    private String reCode;
+
+    private String time;
+
+    private String userCode;
+
+    private String userName;
+}

+ 37 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/entity/Response/ZmCrawlerCaptchaResponse.java

@@ -0,0 +1,37 @@
+package com.jzg.quote.zhongmei.crawler.entity.Response;
+
+import com.jzg.quote.zhongmei.api.entity.response.BaseResponse;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @description: 中煤财险 获取验证码
+ * @author: shenwd
+ * @date: 2025/3/20 16:02
+ **/
+@Data
+@NoArgsConstructor
+public class ZmCrawlerCaptchaResponse extends BaseResponse {
+
+    @Serial
+    private static final long serialVersionUID = 1241581713422482562L;
+
+    /**
+     * 验证码id
+     */
+    private String vckeyID;
+
+    /**
+     * 验证码图片
+     */
+    private String verifyCode;
+
+    /**
+     * 验证码
+     */
+    private String verifyStr;
+
+}

+ 20 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/properties/ZmCrawlerProperties.java

@@ -0,0 +1,20 @@
+package com.jzg.quote.zhongmei.crawler.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @description: 中煤财险 爬虫配置信息
+ * @author: shenwd
+ * @date: 2025/3/20 14:45
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "zhongmei.crawler")
+public class ZmCrawlerProperties {
+    /**
+     * 中煤快速出单系统url
+     */
+    private String baseUrl;
+}

+ 102 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/service/Impl/ZhongMeiCrawlerRequestImpl.java

@@ -0,0 +1,102 @@
+package com.jzg.quote.zhongmei.crawler.service.Impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.jzg.quotation.commons.constant.InsuranceCode;
+import com.jzg.quotation.commons.entity.vo.aggregated.InsuranceUploadImageVo;
+import com.jzg.quotation.commons.entity.vo.aggregated.QuoteResultsVo;
+import com.jzg.quotation.commons.entity.vo.aggregated.QuoteVo;
+import com.jzg.quotation.commons.entity.vo.aggregated.UnderwritingVo;
+import com.jzg.quotation.commons.entity.vo.base.LoginBase;
+import com.jzg.quotation.commons.entity.vo.base.OrderBase;
+import com.jzg.quotation.commons.entity.vo.quote.AttributionInformationVo;
+import com.jzg.quote.zhongmei.crawler.component.ZmCrawlerRequestComponent;
+import com.jzg.quote.zhongmei.crawler.service.ZhongMeiCrawlerRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * @description: 中煤财险 爬虫报价
+ * @author: shenwd
+ * @date: 2025/3/20 14:36
+ **/
+@Service(value = InsuranceCode.INSURANCE_ZMBX)
+@RequiredArgsConstructor
+public class ZhongMeiCrawlerRequestImpl implements ZhongMeiCrawlerRequest {
+
+    private final ZmCrawlerRequestComponent zmCrawlerRequestComponent;
+
+    /**
+     * 登录
+     * @param loginBase 登录信息
+     */
+    @Override
+    public void login(LoginBase loginBase) {
+        zmCrawlerRequestComponent.authenticate(loginBase.getUsername(), loginBase.getPassword(), ZmCrawlerRequestComponent.REQUEST_NUMBER);
+    }
+
+    /**
+     * 报价
+     * @param quoteVo 报价实体
+     * @return 报价信息
+     */
+    @Override
+    public QuoteResultsVo quote(QuoteVo quoteVo) throws JsonProcessingException {
+        // 1.0获取token的请求头
+        AttributionInformationVo attributionInformationVo = quoteVo
+                .getOrder()
+                .getAttributionInformationVo();
+
+
+        return null;
+    }
+
+    /**
+     * 提交核保 子订单
+     *
+     * @param underwritingVo
+     */
+    @Override
+    public void underwriting(UnderwritingVo underwritingVo) {
+
+    }
+
+    /**
+     * 获取支付链接
+     *
+     * @param orderBase
+     */
+    @Override
+    public String getPaymentLink(OrderBase orderBase) throws JsonProcessingException {
+        return "";
+    }
+
+    /**
+     * 上传影像
+     *
+     * @param insuranceUploadImageVo
+     */
+    @Override
+    public void uploadImage(InsuranceUploadImageVo insuranceUploadImageVo) {
+
+    }
+
+    /**
+     * 下载保单
+     *
+     * @param orderBase
+     */
+    @Override
+    public void downloadPolicy(OrderBase orderBase) {
+
+    }
+
+    /**
+     * 查询订单状态
+     *
+     * @param orderBase
+     */
+    @Override
+    public void queryOrderState(OrderBase orderBase) {
+
+    }
+}

+ 11 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/service/ZhongMeiCrawlerRequest.java

@@ -0,0 +1,11 @@
+package com.jzg.quote.zhongmei.crawler.service;
+
+import com.jzg.quotation.commons.service.QuotationCrawlerBaseService;
+
+/**
+ * @description: 中煤爬虫报价请求接口
+ * @author: shenwd
+ * @date: 2025/3/20 14:34
+ **/
+public interface ZhongMeiCrawlerRequest extends QuotationCrawlerBaseService {
+}

+ 59 - 0
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quote/zhongmei/crawler/util/Base64Tool.java

@@ -0,0 +1,59 @@
+package com.jzg.quote.zhongmei.crawler.util;
+
+import com.alibaba.fastjson2.JSON;
+
+import java.util.Base64;
+import java.util.Random;
+
+/**
+ * @description:
+ * @author: shenwd
+ * @date: 2025/3/20 15:18
+ **/
+public class Base64Tool {
+
+    public static String encode(String base64Str) {
+        String t = Base64.getEncoder().encodeToString(base64Str.getBytes());
+        int[] c = {2, 5, 8, 10, 15};
+        int r = (int) Math.floor(10 * Math.round(new Random().nextDouble() * 10) / 10);
+        String i = "jsfklashfkbklfdhslkfhlksfd";
+        i = String.valueOf(i.charAt(r));
+        StringBuilder e = new StringBuilder();
+        e.append(t, 0, c[0]).append(i).append(t, c[0], c[1]).append(i)
+                .append(t, c[1], c[2]).append(i).append(t, c[2], c[3]).append(i)
+                .append(t, c[3], c[4]).append(i).append(t.substring(c[4]));
+        return e.toString();
+    }
+
+    public static String decode(String base64Str) {
+        int[] c = {0, 2, 5, 8, 10, 15};
+        StringBuilder v = new StringBuilder();
+        for (int x = 0; x < c.length; x++) {
+            int start = c[x] + x;
+            int end = (x < c.length - 1) ? c[x + 1] + x : base64Str.length();
+            v.append(base64Str, start, end);
+        }
+        byte[] jsonBytes = Base64.getDecoder().decode(v.toString());
+        return new String(jsonBytes);
+    }
+
+    /**
+     * 转换数据
+     * @param data
+     * @param tClass
+     * @return
+     * @param <T>
+     */
+    public static <T> T transfer(String data, Class<T> tClass) {
+        return JSON.parseObject(data, tClass);
+    }
+
+    public static void main(String[] args) {
+        String original = "your_original_string_here";
+        String encoded = encode(original);
+        encoded = "eyaJoZaWFkaIjap7InRayYW5zQ29kZSI6IlZlcmlmeUNvZGUiLCJ0cmFuc1R5cGUiOiJSZXEiLCJ0b2tlbiI6IiIsInNvdXJjZSI6IjIifSwidXNlckNvZGUiOiIxNDAwMTM5MyJ9";
+        System.out.println("Encoded: " + encoded);
+        String decoded = decode(encoded);
+        System.out.println("Decoded: " + decoded);
+    }
+}