theFlyNeedProgressive 11 månader sedan
förälder
incheckning
8295506331
28 ändrade filer med 1876 tillägg och 24 borttagningar
  1. 3 0
      .gitignore
  2. 0 24
      .idea/GrepConsole.xml
  3. 382 0
      .qoder/quests/bohai-insurance-api-quotation-analysis.md
  4. 63 0
      tenant/insurance/quotation-bohai/CLAUDE.md
  5. 132 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/build/BoHaiCrawlerQuoteResultsVoBuild.java
  6. 265 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/component/BoHaiCrawlerRequestComponent.java
  7. 19 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/config/RestTemplateConfig.java
  8. 35 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/BoHaiCrawlerConfigureParameters.java
  9. 53 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/po/BoHaiCrawlerOrder.java
  10. 29 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/BoHaiCrawlerBaseRequest.java
  11. 51 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/BoHaiCrawlerQuotationRequest.java
  12. 20 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/BoHaiGenerateOrderNoRequest.java
  13. 25 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/PaymentRequest.java
  14. 23 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/UnderwritingRequest.java
  15. 40 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiCrawlerBaseResponse.java
  16. 21 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiGenerateOrderNoResponse.java
  17. 21 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiPaymentResponse.java
  18. 36 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiUnderwritingResponse.java
  19. 19 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/properties/BoHaiCrawlerProperties.java
  20. 15 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/repository/BoHaiCrawlerOrderRepository.java
  21. 18 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/BoHaiCrawlerOrderService.java
  22. 28 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/BoHaiCrawlerQuoteProcessService.java
  23. 12 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/BoHaiCrawlerRequest.java
  24. 56 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/impl/BoHaiCrawlerOrderServiceImpl.java
  25. 103 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/impl/BoHaiCrawlerQuoteProcessServiceImpl.java
  26. 249 0
      tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/impl/BoHaiCrawlerRequestImpl.java
  27. 82 0
      tenant/insurance/quotation-dajia/CLAUDE.md
  28. 76 0
      tenant/insurance/quotation-huanong/CLAUDE.md

+ 3 - 0
.gitignore

@@ -3,6 +3,9 @@ target/
 !**/src/main/**/target/
 !**/src/test/**/target/
 
+rebel.xml
+settings.local.json
+
 ### IntelliJ IDEA ###
 .idea
 *.iws

+ 0 - 24
.idea/GrepConsole.xml

@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="GrepConsole">
-    <option name="pinnedGrepConsolesState">
-      <PinnedGrepConsolesState>
-        <option name="map">
-          <map>
-            <entry>
-              <key>
-                <RunConfigurationRef>
-                  <option name="icon" value="springBoot.svg)" />
-                  <option name="name" value="PlatFormApplication" />
-                </RunConfigurationRef>
-              </key>
-              <value>
-                <Pins />
-              </value>
-            </entry>
-          </map>
-        </option>
-      </PinnedGrepConsolesState>
-    </option>
-  </component>
-</project>

+ 382 - 0
.qoder/quests/bohai-insurance-api-quotation-analysis.md

@@ -0,0 +1,382 @@
+# 渤海保险API报价功能设计分析
+
+## 概述
+
+渤海保险模块采用标准RESTful API方式与渤海保险外部系统进行集成,提供完整的车险报价服务。该模块位于 `tenant/insurance/quotation-bohai` 目录下,采用分层架构设计,支持交强险、商业险和驾意险的统一报价处理。
+
+## 技术架构
+
+### 模块结构
+
+```mermaid
+graph TB
+    A[quotation-bohai] --> B[api]
+    B --> C[service]
+    B --> D[component] 
+    B --> E[entity]
+    B --> F[build]
+    B --> G[repository]
+    B --> H[util]
+    
+    C --> C1[BoHaiApiRequest接口]
+    C --> C2[BoHaiApiRequestImpl实现]
+    
+    D --> D1[BoHaiRequestApiComponent]
+    
+    E --> E1[request请求实体]
+    E --> E2[response响应实体]
+    E --> E3[vo视图对象]
+    E --> E4[po持久化对象]
+    
+    F --> F1[BoHaiApiQuoteResultsVoBuild]
+    
+    G --> G1[BoHaiApiOrderRepository]
+    
+    H --> H1[加密工具类]
+    H --> H2[签名工具类]
+```
+
+### 核心组件
+
+| 组件 | 职责 | 说明 |
+|------|------|------|
+| BoHaiApiRequestImpl | 报价业务逻辑实现 | 核心服务类,协调整个报价流程 |
+| BoHaiRequestApiComponent | API通信组件 | 处理与渤海外部系统的HTTP通信 |
+| BoHaiApiQuoteResultsVoBuild | 结果构建器 | 将外部响应转换为内部统一数据格式 |
+| BoHaiApiOrderService | 订单服务 | 管理报价订单的持久化操作 |
+| SignatureUtils | 签名工具 | 处理请求签名和数据加解密 |
+
+## API端点定义
+
+### 核心接口方法
+
+| 方法 | 功能 | 入参 | 返回值 |
+|------|------|------|--------|
+| quote() | 车险报价 | QuoteVo | QuoteResultsVo |
+| underwriting() | 核保处理 | UnderwritingVo | UnderwritingResultsVo |
+| getPaymentLink() | 获取支付链接 | PaymentLinkVo | PaymentLinkResultsVo |
+| uploadImage() | 影像上传 | InsuranceUploadImageVo | void |
+| downloadPolicy() | 保单下载 | DownloadPolicyVo | DownloadPolicyResultVo |
+| queryOrderState() | 订单状态查询 | OrderStatusVo | OrderStatusResultVo |
+
+### 渤海外部API对接
+
+| 接口编码 | 接口名称 | 请求类型 | 功能描述 |
+|----------|----------|----------|----------|
+| Z00002 | 生成订单号 | GenerateOrderNoRequest | 生成渤海系统订单号 |
+| Z00004 | 车型查询 | VehicleTypeQueryRequest | 查询车辆型号信息 |
+| Z00005 | 车辆确认 | VehicleConfirmationRequest | 确认车辆信息 |
+| Z00006 | 报价接口 | QuotationRequest | 获取保险报价 |
+| Z00007 | 核保接口 | UnderwritingRequest | 提交核保申请 |
+| Z00008 | 支付接口 | PaymentRequest | 获取支付链接 |
+
+## 报价流程详细设计
+
+### 主要报价流程
+
+```mermaid
+sequenceDiagram
+    participant Client as 客户端
+    participant Service as BoHaiApiRequestImpl  
+    participant Component as BoHaiRequestApiComponent
+    participant Builder as BoHaiApiQuoteResultsVoBuild
+    participant OrderService as BoHaiApiOrderService
+    participant External as 渤海外部API
+
+    Client->>Service: quote(QuoteVo)
+    Service->>Service: 获取配置信息
+    Service->>Component: generateOrderNo()
+    Component->>External: 生成订单号请求
+    External-->>Component: 返回订单号
+    Component-->>Service: GenerateOrderNoResponse
+    
+    Service->>Service: 处理车辆品牌名称
+    Service->>Component: vehicleTypeQuery()
+    Component->>External: 车型查询请求
+    External-->>Component: 车型列表
+    Component-->>Service: VehicleModelsDTO
+    
+    Service->>Component: vehicleConfirmation()
+    Component->>External: 车辆确认请求
+    External-->>Component: 车辆确认结果
+    Component-->>Service: VehicleConfirmationResponse
+    
+    alt 车型编码错误
+        Service->>Service: 提取正确车型编码
+        Service->>Component: 重新车辆确认
+    end
+    
+    Service->>Service: 构建QuotationRequest
+    Service->>Service: repeatQuote()递归调用
+    Service->>Component: quotation()
+    Component->>External: 报价请求
+    External-->>Component: 报价结果
+    Component-->>Service: QuotationResponse
+    
+    alt 重复投保处理
+        Service->>Service: 调整起保日期
+        Service->>Service: 递归重新报价
+    end
+    
+    alt 报价成功
+        Service->>OrderService: 保存成功订单
+        Service->>Builder: buildQuoteResultsVo()
+        Builder-->>Service: QuoteResultsVo
+    else 报价失败
+        Service->>OrderService: 保存失败订单
+        Service->>Builder: buildQuoteFailResultsVo()
+        Builder-->>Service: QuoteResultsVo
+    end
+    
+    Service-->>Client: 返回报价结果
+```
+
+### 重复投保处理逻辑
+
+```mermaid
+flowchart TD
+    Start([接收报价响应]) --> CheckSuccess{报价是否成功?}
+    CheckSuccess --> |成功| CheckBusinessError{商业险有错误信息?}
+    CheckSuccess --> |失败| CheckTrafficError{交强险重复投保?}
+    
+    CheckBusinessError --> |有错误| CheckRepeatBusiness{包含重复投保?}
+    CheckBusinessError --> |无错误| End([处理完成])
+    
+    CheckRepeatBusiness --> |是| ExtractBusinessDate[提取终保日期]
+    CheckRepeatBusiness --> |否| End
+    
+    ExtractBusinessDate --> AdjustBusinessDate[调整商业险起保日期]
+    AdjustBusinessDate --> RecursiveQuoteBusiness[递归调用报价]
+    
+    CheckTrafficError --> |是| ExtractTrafficDate[提取交强险日期]
+    CheckTrafficError --> |否| End
+    
+    ExtractTrafficDate --> AdjustTrafficDate[调整交强险起保日期]
+    AdjustTrafficDate --> RecursiveQuoteTraffic[递归调用报价]
+    
+    RecursiveQuoteBusiness --> Start
+    RecursiveQuoteTraffic --> Start
+```
+
+## 数据模型设计
+
+### 核心请求实体
+
+```mermaid
+classDiagram
+    class QuotationRequest {
+        +String argueSolution
+        +TrafficPolicyDTO trafficPolicy
+        +BusinessPolicyDTO businessPolicy
+        +RealNameCheckDTO realNameCheck
+        +List~UnAutoDTO~ unAutos
+    }
+    
+    class TrafficPolicyDTO {
+        +String startDate
+        +String endDate
+        +VehicleTaxDTO vehicleTax
+    }
+    
+    class BusinessPolicyDTO {
+        +String startDate
+        +String endDate
+        +List~CoveragesDTO~ coverages
+        +FloatingRateOptionDTO floatingRateOption
+    }
+    
+    class CoveragesDTO {
+        +String coverageCode
+        +String coverageName
+        +Map~String,String~ extendInfos
+        +String sumInsurd
+    }
+    
+    class UnAutoDTO {
+        +String productCode
+        +String packageCode
+        +String copies
+    }
+    
+    QuotationRequest --> TrafficPolicyDTO
+    QuotationRequest --> BusinessPolicyDTO
+    QuotationRequest --> UnAutoDTO
+    BusinessPolicyDTO --> CoveragesDTO
+```
+
+### 响应数据模型
+
+```mermaid
+classDiagram
+    class QuotationResponse {
+        +String responseCode
+        +String responseMsg
+        +DataDTO data
+    }
+    
+    class DataDTO {
+        +TrafficPolicyDTO trafficPolicy
+        +BusinessPolicyDTO businessPolicy
+        +UnAutoPremiumDTO unAutoPremium
+    }
+    
+    class TrafficPolicyDTO {
+        +String premium
+        +String startDate
+        +String endDate
+        +VehicleTaxDTO vehicleTax
+        +String errorMsg
+    }
+    
+    class BusinessPolicyDTO {
+        +String premium
+        +String startDate  
+        +String endDate
+        +List~CoveragesDTO~ coverages
+        +String errorMsg
+    }
+    
+    QuotationResponse --> DataDTO
+    DataDTO --> TrafficPolicyDTO
+    DataDTO --> BusinessPolicyDTO
+```
+
+## 安全机制设计
+
+### 加密通信流程
+
+```mermaid
+sequenceDiagram
+    participant Client as 客户端
+    participant Component as BoHaiRequestApiComponent
+    participant Utils as SignatureUtils
+    participant External as 渤海API
+
+    Client->>Component: 发起API请求
+    Component->>Component: 构建请求JSON
+    Component->>Utils: encryptAndSign()
+    Utils->>Utils: AES加密请求内容
+    Utils->>Utils: RSA签名加密内容
+    Utils-->>Component: 返回加密内容+签名
+    
+    Component->>Component: 设置signature请求头
+    Component->>External: HTTP POST请求
+    External-->>Component: 加密响应
+    
+    Component->>Utils: decryptByPrivateKey()
+    Utils->>Utils: RSA解密响应
+    Utils-->>Component: 解密后的响应内容
+    Component-->>Client: 返回解析结果
+```
+
+### 配置参数管理
+
+| 配置项 | 说明 | 示例 |
+|--------|------|------|
+| sysSourceCode | 系统来源代码 | 标识接入系统 |
+| selfPrivateKey | 己方私钥 | RSA私钥用于解密 |
+| boHaiPublicKey | 渤海公钥 | RSA公钥用于加密 |
+| apiUrl | 接口地址 | 渤海API基础URL |
+
+## 错误处理机制
+
+### 响应码处理
+
+| 响应码 | 含义 | 处理策略 |
+|--------|------|----------|
+| R_Z00001 | 成功 | 正常处理业务逻辑 |
+| R_Z00002 | 数据正在处理中 | 自动重试机制 |
+| R_Z00004 | 业务失败 | 检查错误信息,特殊处理重复投保 |
+| R_Z00005 | 系统异常 | 抛出SystemException |
+
+### 特殊错误处理
+
+```mermaid
+flowchart TD
+    A[接收到错误响应] --> B{是否车型编码错误?}
+    B --> |是| C[正则提取正确编码]
+    B --> |否| D{是否重复投保?}
+    
+    C --> E[重新构建请求]
+    E --> F[递归调用接口]
+    
+    D --> |是| G[解析起保终保日期]
+    D --> |否| H[抛出异常或记录失败]
+    
+    G --> I[调整保险日期]
+    I --> J[递归重新报价]
+    
+    F --> K[返回处理结果]
+    J --> K
+    H --> K
+```
+
+## 性能与监控
+
+### 性能优化策略
+
+1. **请求重试机制**: 针对渤海系统处理中状态(R_Z00002)自动重试
+2. **递归调用控制**: 重复投保处理使用递归,需要控制递归深度
+3. **缓存机制**: 车型查询结果可考虑缓存以提升性能
+4. **连接池管理**: RestTemplate使用连接池管理HTTP连接
+
+### 监控指标
+
+| 指标类型 | 监控内容 | 阈值建议 |
+|----------|----------|----------|
+| 响应时间 | API调用响应时间 | <3秒 |
+| 成功率 | 报价成功率 | >95% |
+| 错误率 | 各类错误码占比 | <5% |
+| 重试次数 | 递归调用深度 | <5次 |
+
+### 日志记录
+
+```mermaid
+flowchart LR
+    A[请求开始] --> B[记录请求参数]
+    B --> C[记录加密前参数]
+    C --> D[记录HTTP请求]
+    D --> E[记录响应结果]
+    E --> F[记录解密后参数]
+    F --> G[记录业务处理结果]
+```
+
+## 完整报价流程图
+
+```mermaid
+flowchart TD
+    Start([开始报价]) --> GetConfig[获取配置信息]
+    GetConfig --> GenOrder[生成订单号]
+    GenOrder --> ProcessBrand[处理车辆品牌名称]
+    ProcessBrand --> QueryVehicle[车型查询]
+    QueryVehicle --> ConfirmVehicle[车辆确认]
+    
+    ConfirmVehicle --> CheckVehicleError{车型编码错误?}
+    CheckVehicleError --> |是| ExtractCode[提取正确编码]
+    CheckVehicleError --> |否| BuildRequest[构建报价请求]
+    
+    ExtractCode --> QueryVehicleAgain[重新车型查询确认]
+    QueryVehicleAgain --> BuildRequest
+    
+    BuildRequest --> CallQuote[调用报价接口]
+    CallQuote --> CheckResponse{报价响应检查}
+    
+    CheckResponse --> |成功无错误| SaveSuccess[保存成功订单]
+    CheckResponse --> |成功有错误| CheckRepeat{重复投保?}
+    CheckResponse --> |失败| HandleFailure[处理失败]
+    
+    CheckRepeat --> |商业险重复| AdjustBusiness[调整商业险日期]
+    CheckRepeat --> |交强险重复| AdjustTraffic[调整交强险日期] 
+    CheckRepeat --> |其他错误| HandleFailure
+    
+    AdjustBusiness --> RecursiveQuote[递归报价]
+    AdjustTraffic --> RecursiveQuote
+    RecursiveQuote --> CallQuote
+    
+    SaveSuccess --> BuildSuccessResult[构建成功结果]
+    HandleFailure --> SaveFailure[保存失败订单]
+    SaveFailure --> BuildFailResult[构建失败结果]
+    
+    BuildSuccessResult --> End([返回结果])
+    BuildFailResult --> End
+```

+ 63 - 0
tenant/insurance/quotation-bohai/CLAUDE.md

@@ -0,0 +1,63 @@
+# CLAUDE.md
+
+此文件为Claude Code (claude.ai/code) 提供在此代码库中工作的指导。
+
+## 项目概述
+
+这是一个**渤海保险报价API**模块,属于大型多模块Maven保险报价服务项目的一部分。该项目包含多个保险公司集成。
+
+## 架构
+
+- **父级结构**: 多级Maven层次结构 (jzg → tenant → insurance → quotation-bohai)
+- **框架**: Spring Boot 3.0.0 + WebFlux 响应式编程
+- **数据库**: MongoDB集成 (Spring Data MongoDB)
+- **API风格**: RESTful API,使用FastJSON进行JSON序列化
+- **安全**: RSA加密和数字签名用于API通信
+
+## 构建命令
+
+```bash
+# 编译项目
+mvn compile
+
+# 打包(跳过测试)
+mvn package -DskipTests
+
+# 清理构建
+mvn clean package
+
+# 安装到本地仓库
+mvn install
+```
+
+## 关键组件
+
+- **BoHaiRequestApiComponent**: 主API客户端组件,处理所有渤海保险API调用
+- **BoHaiApiOrderService**: MongoDB服务层,用于持久化报价数据
+- **BohaiApiConfigurationProperties**: 渤海API端点配置属性
+- **多个枚举类**: 丰富的保险领域模型枚举类型
+
+## 开发说明
+
+- **Java版本**: 17
+- **依赖**: 重度依赖quotation-commons模块的共享功能
+- **加密**: 使用自定义RSA加密工具 (BoHaiEncrypted, SignatureUtils)
+- **日志**: InsuranceLog用于结构化保险特定日志
+- **错误处理**: 自定义SystemException处理业务逻辑错误
+
+## API集成模式
+
+模块遵循一致的渤海API集成模式:
+1. 使用正确的枚举构建请求体
+2. RSA加密和签名生成
+3. 带自定义头的HTTP POST请求
+4. 响应解密和验证
+5. 成功报价的MongoDB持久化
+
+## 常见开发任务
+
+- 添加新的渤海API端点
+- 扩展新保险产品的枚举类型
+- 修改加密/解密逻辑
+- 更新MongoDB持久化模型
+- 与quotation-commons共享组件集成

+ 132 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/build/BoHaiCrawlerQuoteResultsVoBuild.java

@@ -0,0 +1,132 @@
+package com.jzg.quotation.bohai.crawler.build;
+
+import com.jzg.commons.constants.dict.InsOrderStatusEnum;
+import com.jzg.commons.entity.quote.vo.KindInfoVo;
+import com.jzg.commons.entity.quote.vo.QuoteVo;
+import com.jzg.commons.entity.quote.vo.RiskInfoVo;
+import com.jzg.commons.entity.quote.vo.aggregated.QuoteResultsVo;
+import com.jzg.quotation.bohai.api.entity.response.QuotationResponse;
+import com.jzg.quotation.bohai.crawler.entity.response.BoHaiCrawlerQuotationResponse;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @description: 渤海财险爬虫报价结果构建器
+ * @author:
+ * @date:
+ **/
+public class BoHaiCrawlerQuoteResultsVoBuild {
+
+    public static QuoteResultsVo buildQuoteResultsVo(String ordersNo, QuoteVo quoteVo, BoHaiCrawlerQuotationResponse boHaiCrawlerQuotationResponse) {
+        QuoteResultsVo quoteResultsVo = new QuoteResultsVo();
+        quoteResultsVo.setOrdersNo(ordersNo);
+
+        // 设置交强险信息
+        if (boHaiCrawlerQuotationResponse.getData().getTrafficPolicy() != null) {
+            quoteResultsVo.setCiRiskInfoVo(buildCiRiskInfoVo(boHaiCrawlerQuotationResponse));
+        }
+
+        // 设置商业险信息
+        if (boHaiCrawlerQuotationResponse.getData().getBusinessPolicy() != null) {
+            quoteResultsVo.setBiRiskInfoVo(buildBiRiskInfoVo(boHaiCrawlerQuotationResponse));
+            // 商业险险种信息
+            List<KindInfoVo> kindInfoVo = buildKindInfoVo(quoteVo.getKindInfoVo(), boHaiCrawlerQuotationResponse.getData().getBusinessPolicy().getCoverages());
+            quoteResultsVo.setKindInfoVo(kindInfoVo);
+        }
+
+        // 交强保费 车船税 商业保费
+        buildPremium(quoteResultsVo, boHaiCrawlerQuotationResponse);
+        //设置报价状态:带核保
+        quoteResultsVo.setQuoteStatusEnum(InsOrderStatusEnum.WAIT_AUDIT);
+        return quoteResultsVo;
+    }
+
+    /**
+     * 处理交强险数据
+     * @param quotationResponse
+     * @return RiskInfoVo
+     */
+    public static RiskInfoVo buildCiRiskInfoVo(BoHaiCrawlerQuotationResponse quotationResponse) {
+        BoHaiCrawlerQuotationResponse.DataDTO.TrafficPolicyDTO trafficPolicy = quotationResponse.getData().getTrafficPolicy();
+        RiskInfoVo riskInfoVo = new RiskInfoVo();
+        riskInfoVo.setStartDate(trafficPolicy.getStartDate());
+        riskInfoVo.setEndDate(trafficPolicy.getEndDate());
+        return riskInfoVo;
+    }
+
+    /**
+     * 处理商业险数据
+     * @param quotationResponse
+     * @return
+     */
+    public static RiskInfoVo buildBiRiskInfoVo(BoHaiCrawlerQuotationResponse quotationResponse) {
+        // 获取商业险数据
+        BoHaiCrawlerQuotationResponse.DataDTO.BusinessPolicyDTO businessPolicy = quotationResponse.getData().getBusinessPolicy();
+        RiskInfoVo riskInfoVo = new RiskInfoVo();
+        riskInfoVo.setStartDate(businessPolicy.getStartDate());
+        riskInfoVo.setEndDate(businessPolicy.getEndDate());
+        return riskInfoVo;
+    }
+
+    /**
+     * 处理商业险信息
+     * @param kindInfoVo
+     * @param coverageListDTOS
+     * @return
+     */
+    private static List<KindInfoVo> buildKindInfoVo(List<KindInfoVo> kindInfoVo, List<BoHaiCrawlerQuotationResponse.DataDTO.BusinessPolicyDTO.CoveragesDTO> coverageListDTOS) {
+        Map<String, BoHaiCrawlerQuotationResponse.DataDTO.BusinessPolicyDTO.CoveragesDTO> collect = coverageListDTOS.stream()
+                .collect(Collectors.toMap(BoHaiCrawlerQuotationResponse.DataDTO.BusinessPolicyDTO.CoveragesDTO::getCoverageCode, x -> x));
+        kindInfoVo.forEach(kind -> {
+            BoHaiCrawlerQuotationResponse.DataDTO.BusinessPolicyDTO.CoveragesDTO coverageListDTO = collect.get(kind.getKindCode());
+            if (coverageListDTO.getCoverageCode().equals(kind.getKindCode())) {
+                kind.setAmount(String.valueOf(coverageListDTO.getSumInsurd()));//设置保额
+                kind.setPremium(String.valueOf(coverageListDTO.getPremium()));//设置保费
+            }
+        });
+        return kindInfoVo;
+    }
+
+    /**
+     * 设置商业险,交强险保费
+     * @param quoteResultsVo
+     * @param quotationResponse
+     */
+    private static void buildPremium(QuoteResultsVo quoteResultsVo, BoHaiCrawlerQuotationResponse quotationResponse) {
+        // 设置商业险保费
+        if (Objects.nonNull(quotationResponse.getData().getBusinessPolicy())){
+            quoteResultsVo.getCosts().setBiPremium(String.valueOf(quotationResponse.getData().getBusinessPolicy().getPremium()));
+        }
+        // 设置交强险保费
+        if (Objects.nonNull(quotationResponse.getData().getTrafficPolicy())) {
+            quoteResultsVo.getCosts().setCiPremium(String.valueOf(quotationResponse.getData().getTrafficPolicy().getPremium()));
+        }
+        // 设置车船税
+        quoteResultsVo.getCosts().setVvTax(String.valueOf(quotationResponse.getData().getTrafficPolicy().getVehicleTax().getTotalAmount()));
+        // 设置非车险(驾意险)保费
+        BoHaiCrawlerQuotationResponse.DataDTO.UnAutoPremiumDTO unAutoPremium = quotationResponse.getData().getUnAutoPremium();
+        if (unAutoPremium != null) {
+            quoteResultsVo.getCosts().setNiPremium(unAutoPremium.getPremium());
+        }
+    }
+
+    /**
+     * 处理报价失败结果
+     * @param boHaiQuoteResponse
+     * @return QuoteResultsVo
+     */
+    public static QuoteResultsVo buildQuoteFailResultsVo(BoHaiCrawlerQuotationResponse boHaiQuoteResponse) {
+        QuoteResultsVo quoteResultsVo = new QuoteResultsVo();
+        // 交易流水号
+        quoteResultsVo.setCompanyOrderNo(boHaiQuoteResponse.getBaseParam().getRequestNo());
+        // 报价失败提示
+        quoteResultsVo.setErrorMessage(boHaiQuoteResponse.getResponseMsg());
+        //设置报价状态:
+        quoteResultsVo.setQuoteStatusEnum(InsOrderStatusEnum.QUOTE_FAIL);
+        return quoteResultsVo;
+    }
+
+}

+ 265 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/component/BoHaiCrawlerRequestComponent.java

@@ -0,0 +1,265 @@
+package com.jzg.quotation.bohai.crawler.component;
+
+import com.alibaba.fastjson2.JSON;
+import com.jzg.commons.exception.SystemException;
+import com.jzg.commons.util.AssertionUtils;
+import com.jzg.quotation.bohai.crawler.constant.enums.RequestUrl;
+import com.jzg.quotation.bohai.crawler.entity.request.*;
+import com.jzg.quotation.bohai.crawler.entity.response.*;
+import com.jzg.quotation.bohai.crawler.properties.BoHaiCrawlerProperties;
+import com.jzg.quotation.bohai.crawler.util.DESEncryption;
+import com.jzg.quotation.commons.component.RequestComponent;
+import com.jzg.quotation.commons.service.ExteriorUtilsRequest;
+import com.jzg.quotation.commons.standard.CrawlerRequestUrlStandard;
+import com.jzg.quotation.commons.utils.RequestObjectConversion;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @description: 渤海财险爬虫请求组件
+ * @author:
+ * @date:
+ **/
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class BoHaiCrawlerRequestComponent {
+
+    // 循环次数
+    public static final int CIRCULATE_NUMBER = 3;
+
+    private final RequestComponent requestComponent;
+
+    private final ExteriorUtilsRequest exteriorUtilsRequest;
+
+    private final BoHaiCrawlerProperties boHaiCrawlerProperties;
+
+    /**
+     * 认证
+     *
+     * @param username 用户名
+     * @param password 密码
+     * @return ResponseEntity<BoHaiCrawlerBaseResponse>
+     */
+    public ResponseEntity<BoHaiCrawlerBaseResponse> authenticate(String username, String password) {
+        // 获取验证码
+        BoHaiCrawlerCaptchaResponse captcha = captcha(CIRCULATE_NUMBER);
+        // 登录存入缓存当中
+        BoHaiCrawlerAuthenticateRequest request = new BoHaiCrawlerAuthenticateRequest(captcha, username, password);
+        BoHaiCrawlerBaseRequest crawlerBaseRequest = new BoHaiCrawlerBaseRequest(request);
+        return requestComponent.objectPost(RequestUrl.AUTHENTICATE.getRequestUrl(boHaiCrawlerProperties.getBaseUrl()), crawlerBaseRequest, BoHaiCrawlerBaseResponse.class);
+    }
+
+    /**
+     * 验证码
+     * @param frequency
+     * @return BoHaiCrawlerCaptchaResponse
+     */
+    public BoHaiCrawlerCaptchaResponse captcha(int frequency) {
+        if (frequency <= 0) {
+            throw new SystemException("验证码异常");
+        }
+
+        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
+        map.add("imgId", UUID.randomUUID().toString().replaceAll("-", ""));
+        // 请求
+        BoHaiCrawlerBaseResponse boHaiCrawlerBaseResponse = requestComponent.requestGET(RequestUrl.CAPTCHA.getRequestUrl(boHaiCrawlerProperties.getBaseUrl()), map, "", BoHaiCrawlerBaseResponse.class);
+        BoHaiCrawlerCaptchaResponse boHaiCrawlerCaptchaResponse = DESEncryption.decryptStrToObject(boHaiCrawlerBaseResponse.getResult(), BoHaiCrawlerCaptchaResponse.class);
+        // 识别验证码
+        String s = exteriorUtilsRequest.captchaIdentify(boHaiCrawlerCaptchaResponse.getImg());
+        try {
+            int i = processCaptcha(s);
+            s = String.valueOf(i);
+        } catch (Exception e) {
+            frequency = frequency - 1;
+            return captcha(frequency);
+        }
+        boHaiCrawlerCaptchaResponse.setImg(s);
+        return boHaiCrawlerCaptchaResponse;
+    }
+
+    /**
+     * 处理验证码
+     * @param res
+     * @return int
+     */
+    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("");
+    }
+
+    /**
+     * 创建token(查看token是否过期前一步)
+     *
+     * @param operatorCode
+     * @param headers      请求头
+     * @return
+     */
+    public String createToken(String operatorCode, HttpHeaders headers) {
+        String url = RequestUrl.CREATE_TOKEN.getRequestUrl(boHaiCrawlerProperties.getBaseUrl());
+        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
+        map.add("operatorCode", operatorCode);
+        BoHaiCrawlerBaseResponse daJiaCrawlerBaseResponse = requestComponent.get(url, map, "", BoHaiCrawlerBaseResponse.class, headers);
+        return DESEncryption.decrypt(daJiaCrawlerBaseResponse.getResult()).replace("\"", "");
+    }
+
+    /**
+     * 校验token(获取新的token)
+     *
+     * @param daJiaCrawlerCheckTokenRequest 校验token
+     * @param headers                       请求头
+     * @return 车型列表
+     */
+    public BoHaiCrawlerCheckTokenResponse checkToken(BoHaiCrawlerCheckTokenRequest boHaiCrawlerCheckTokenRequest, HttpHeaders headers) {
+        return request(RequestUrl.CHECK_TOKEN, boHaiCrawlerCheckTokenRequest, BoHaiCrawlerCheckTokenResponse.class, headers);
+    }
+
+    /**
+     * 请求
+     *
+     * @param crawlerRequestUrlStandard
+     * @param request 报价请求体
+     * @param responseClass
+     * @param headers
+     * @return BoHaiResponse
+     */
+    public <BoHaiResponse> BoHaiResponse request(CrawlerRequestUrlStandard crawlerRequestUrlStandard, Object request, Class<BoHaiResponse> responseClass, HttpHeaders headers) {
+        // 参数加密
+        BoHaiCrawlerBaseRequest crawlerBaseRequest = new BoHaiCrawlerBaseRequest(request);
+        ResponseEntity<BoHaiCrawlerBaseResponse> responseEntity = requestComponent.objectPost(crawlerRequestUrlStandard.getRequestUrl(boHaiCrawlerProperties.getBaseUrl()), crawlerBaseRequest, BoHaiCrawlerBaseResponse.class, headers);
+        BoHaiCrawlerBaseResponse boHaiCrawlerBaseResponse = responseEntity.getBody();
+        AssertionUtils.isEmpty(boHaiCrawlerBaseResponse, "请求异常请重试");
+        if (!"200".equals(boHaiCrawlerBaseResponse.getCode())) {
+            throw new SystemException(boHaiCrawlerBaseResponse.getMessage());
+        }
+        // 参数解密
+        String decrypt = DESEncryption.decrypt(boHaiCrawlerBaseResponse.getResult());
+        return JSON.parseObject(decrypt, responseClass);
+    }
+
+    /**
+     * 获取机构信息
+     *
+     * @param request 获取机构信息 请求实体
+     * @param cDptCde 机构编码
+     * @param headers 请求头
+     * @return 唯一机构列表
+     */
+    public BoHaiCrawlerCompaniesResponse.ListDTO companies(BoHaiCrawlerCompaniesRequest request, String cDptCde, HttpHeaders headers) {
+        MultiValueMap<String, String> multiValueMap = RequestObjectConversion.convertMultiValueStringMap(request);
+        BoHaiCrawlerBaseResponse response = requestComponent.get(RequestUrl.COMPANIES.getRequestUrl(boHaiCrawlerProperties.getBaseUrl()), multiValueMap, "", BoHaiCrawlerBaseResponse.class, headers);
+        BoHaiCrawlerCompaniesResponse daJiaCrawlerCompaniesResponse = DESEncryption.decryptStrToObject(response.getResult(), BoHaiCrawlerCompaniesResponse.class);
+
+        return daJiaCrawlerCompaniesResponse.getList()
+                .stream()
+                .filter(x -> cDptCde.equals(x.getCDptCde()))
+                .findFirst()
+                .orElseThrow(() -> new SystemException(""));
+
+    }
+
+
+
+    /**
+     * 获取渠道信息
+     *
+     * @param operatorCode
+     * @param headers      请求头
+     * @return 获取渠道信息响应
+     */
+    public BoHaiCrawlerGetSaleInfoResponse getSaleInfo(String operatorCode, HttpHeaders headers) {
+        String url = RequestUrl.GET_SALE_INFO.getRequestUrl(boHaiCrawlerProperties.getBaseUrl()) + operatorCode;
+        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
+        map.add("operatorCode", operatorCode);
+        BoHaiCrawlerBaseResponse daJiaCrawlerBaseResponse = requestComponent.get(url, map, "", BoHaiCrawlerBaseResponse.class, headers);
+        return DESEncryption.decryptStrToObject(daJiaCrawlerBaseResponse.getResult(), BoHaiCrawlerGetSaleInfoResponse.class);
+    }
+
+    /**
+     * 获取套餐信息
+     *
+     * @param request      获取套餐 请求实体
+     * @param packagesName 套餐名称
+     * @param headers      请求头
+     * @return BoHaiCrawlerChannelPackagesResponse 套餐信息
+     */
+    public BoHaiCrawlerChannelPackagesResponse channelPackages(BoHaiCrawlerChannelPackagesRequest request, String packagesName, HttpHeaders headers) {
+        MultiValueMap<String, String> describe = RequestObjectConversion.convertMultiValueStringMap(request);
+        BoHaiCrawlerBaseResponse daJiaCrawlerBaseResponse = requestComponent.get(RequestUrl.CHANNEL_PACKAGES.getRequestUrl(boHaiCrawlerProperties.getBaseUrl()), describe, "", BoHaiCrawlerBaseResponse.class, headers);
+        List<BoHaiCrawlerChannelPackagesResponse> responses = DESEncryption.decryptStrToObjectList(daJiaCrawlerBaseResponse.getResult(), BoHaiCrawlerChannelPackagesResponse.class);
+        return responses.stream()
+                .filter(x -> packagesName.equals(x.getName()))
+                .findFirst()
+                .orElseThrow(() -> new SystemException(""));
+    }
+
+    /**
+     * 车型查询
+     *
+     * @param boHaiCrawlerCarRequest 车型查询请求体
+     * @param headers                请求头
+     * @return BoHaiCrawlerCarResponse 车型列表
+     */
+    public BoHaiCrawlerCarResponse car(BoHaiCrawlerCarRequest boHaiCrawlerCarRequest, HttpHeaders headers) {
+        return request(RequestUrl.CAR, boHaiCrawlerCarRequest, BoHaiCrawlerCarResponse.class, headers);
+    }
+
+    /**
+     * 车辆确认
+     *
+     * @param request 车辆确认请求实体
+     * @param headers
+     * @return BoHaiCrawlerVehicleConfirmationResponse
+     */
+    public BoHaiCrawlerVehicleConfirmationResponse vehicleConfirmation(BoHaiCrawlerVehicleConfirmationReqeust boHaiCrawlerVehicleConfirmationReqeust, HttpHeaders headers) {
+        return request(RequestUrl.CAR, boHaiCrawlerVehicleConfirmationReqeust, BoHaiCrawlerVehicleConfirmationResponse.class, headers);
+    }
+
+
+    /**
+     * 报价
+     *
+     * @param boHaiCrawlerQuotationRequest
+     * @param headers
+     * @return BoHaiCrawlerQuotationResponse
+     */
+    public BoHaiCrawlerQuotationResponse quotation(BoHaiCrawlerQuotationRequest boHaiCrawlerQuotationRequest, HttpHeaders headers) {
+        return request(RequestUrl.PRICE, boHaiCrawlerQuotationRequest, BoHaiCrawlerQuotationResponse.class, headers);
+    }
+
+}

+ 19 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/config/RestTemplateConfig.java

@@ -0,0 +1,19 @@
+package com.jzg.quotation.bohai.crawler.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @description: RestTemplate配置
+ * @author:
+ * @date:
+ **/
+@Configuration
+public class RestTemplateConfig {
+
+    @Bean
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+}

+ 35 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/BoHaiCrawlerConfigureParameters.java

@@ -0,0 +1,35 @@
+package com.jzg.quotation.bohai.crawler.entity;
+
+import com.jzg.quotation.commons.standard.ConfigureParameters;
+import lombok.Data;
+
+import java.io.Serial;
+
+/**
+ * @description: 渤海财险爬虫配置参数
+ * @author:
+ * @date:
+ **/
+@Data
+public class BoHaiCrawlerConfigureParameters implements ConfigureParameters {
+
+    @Serial
+    private static final long serialVersionUID = -4576395685599605562L;
+    /**
+     * 产品组合
+     */
+    private String combineProductCode;
+    /**
+     * 登录机构编码
+     */
+    private String loginCompany;
+    /**
+     * 承保机构
+     */
+    private String cDptCde;
+    /**
+     * 渠道套餐
+     */
+    private String channelPackages;
+
+}

+ 53 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/po/BoHaiCrawlerOrder.java

@@ -0,0 +1,53 @@
+package com.jzg.quotation.bohai.crawler.entity.po;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.time.LocalDateTime;
+
+/**
+ * @description: 渤海财险爬虫订单实体
+ * @author:
+ * @date:
+ **/
+@Data
+@Document(collection = "bohai.crawler.order")
+@NoArgsConstructor
+@AllArgsConstructor
+public class BoHaiCrawlerOrder {
+
+    @Id
+    private String id;
+
+    private String ordersNo;
+
+    private String orderNo;
+
+    private String busUniqueId;
+
+    private String ciApplyNo;
+
+    private String syApplyNo;
+
+    private String ciPolicyNo;
+
+    private String biPolicyNo;
+
+    private String ciPremium;
+
+    private String biPremium;
+
+    private String totalPremium;
+
+    private Boolean quoteSuccess;
+
+    private String errorMsg;
+
+    private LocalDateTime createTime;
+
+    private LocalDateTime updateTime;
+
+}

+ 29 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/BoHaiCrawlerBaseRequest.java

@@ -0,0 +1,29 @@
+package com.jzg.quotation.bohai.crawler.entity.request;
+
+import com.jzg.quotation.bohai.crawler.util.DESEncryption;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @description: 渤海财险 爬虫 基础请求体
+ * @author:
+ * @date:
+ **/
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class BoHaiCrawlerBaseRequest implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 2304961708114602898L;
+
+    private String data;
+
+    public BoHaiCrawlerBaseRequest(Object data) {
+        this.data = DESEncryption.encryptObjectToStr(data);
+    }
+}

+ 51 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/BoHaiCrawlerQuotationRequest.java

@@ -0,0 +1,51 @@
+package com.jzg.quotation.bohai.crawler.entity.request;
+
+import com.jzg.commons.entity.quote.vo.AccidentalDrivingVo;
+import com.jzg.commons.entity.quote.vo.QuoteVo;
+import com.jzg.quotation.bohai.crawler.entity.BoHaiCrawlerConfigureParameters;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @description: 渤海财险爬虫报价请求
+ * @author:
+ * @date:
+ **/
+@Data
+public class BoHaiCrawlerQuotationRequest extends BoHaiCrawlerBaseRequest {
+
+    private BoHaiCrawlerConfigureParameters parameters;
+    private QuoteVo quoteVo;
+    private String currentValue;
+    private String orderNO;
+    private List<AccidentalDrivingVo> accidentVo;
+    private String discount;
+
+    public BoHaiCrawlerQuotationRequest(BoHaiCrawlerConfigureParameters parameters, QuoteVo quoteVo,
+                                        String currentValue, String orderNO, List<AccidentalDrivingVo> accidentVo, String discount) {
+        super(buildRequestData(parameters, quoteVo, currentValue, orderNO, accidentVo, discount));
+        this.parameters = parameters;
+        this.quoteVo = quoteVo;
+        this.currentValue = currentValue;
+        //默认设置为空字符串
+        this.orderNO = "";
+        this.accidentVo = accidentVo;
+        this.discount = discount;
+    }
+
+    private static Object buildRequestData(BoHaiCrawlerConfigureParameters parameters, QuoteVo quoteVo,
+                                         String currentValue, String orderNO, List<AccidentalDrivingVo> accidentVo, String discount) {
+        // 这里构建渤海API需要的请求数据格式
+        // 实际实现需要根据渤海API的具体要求来构建
+        return Map.of(
+            "parameters", parameters,
+            "quoteVo", quoteVo,
+            "currentValue", currentValue,
+            "orderNO", orderNO,
+            "accidentVo", accidentVo,
+            "discount", discount
+        );
+    }
+}

+ 20 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/BoHaiGenerateOrderNoRequest.java

@@ -0,0 +1,20 @@
+package com.jzg.quotation.bohai.crawler.entity.request;
+
+import com.jzg.quotation.bohai.crawler.entity.BoHaiCrawlerConfigureParameters;
+import lombok.Data;
+
+/**
+ * @description: 渤海财险爬虫生成订单号请求
+ * @author:
+ * @date:
+ **/
+@Data
+public class BoHaiGenerateOrderNoRequest extends BoHaiCrawlerBaseRequest {
+
+    private BoHaiCrawlerConfigureParameters parameters;
+
+    public BoHaiGenerateOrderNoRequest(BoHaiCrawlerConfigureParameters parameters) {
+        super(parameters);
+        this.parameters = parameters;
+    }
+}

+ 25 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/PaymentRequest.java

@@ -0,0 +1,25 @@
+package com.jzg.quotation.bohai.crawler.entity.request;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * @description: 渤海财险爬虫支付请求
+ * @author:
+ * @date:
+ **/
+@Data
+public class PaymentRequest extends BoHaiCrawlerBaseRequest {
+
+    private Object configInfo;
+    private String orderNo;
+    private String busUniqueId;
+
+    public PaymentRequest(Object configInfo, String orderNo, String busUniqueId) {
+        super(Map.of("configInfo", configInfo, "orderNo", orderNo, "busUniqueId", busUniqueId));
+        this.configInfo = configInfo;
+        this.orderNo = orderNo;
+        this.busUniqueId = busUniqueId;
+    }
+}

+ 23 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/request/UnderwritingRequest.java

@@ -0,0 +1,23 @@
+package com.jzg.quotation.bohai.crawler.entity.request;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * @description: 渤海财险爬虫核保请求
+ * @author:
+ * @date:
+ **/
+@Data
+public class UnderwritingRequest extends BoHaiCrawlerBaseRequest {
+
+    private Object configInfo;
+    private String orderNo;
+
+    public UnderwritingRequest(Object configInfo, String orderNo) {
+        super(Map.of("configInfo", configInfo, "orderNo", orderNo));
+        this.configInfo = configInfo;
+        this.orderNo = orderNo;
+    }
+}

+ 40 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiCrawlerBaseResponse.java

@@ -0,0 +1,40 @@
+package com.jzg.quotation.bohai.crawler.entity.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+
+/**
+ * @description: 渤海财险爬虫基础响应
+ * @author:
+ * @date:
+ **/
+@Data
+public class BoHaiCrawlerBaseResponse {
+
+    @Serial
+    private static final long serialVersionUID = -7410523311993599008L;
+
+    /**
+     * 状态码
+     */
+    @JsonProperty("code")
+    private String code;
+
+    /**
+     * 返回信息
+     */
+    @JsonProperty("message")
+    private String message;
+
+    /**
+     * 结果
+     */
+    @JsonProperty("result")
+    private String result;
+
+    @JsonProperty("traceId")
+    private String traceId;
+
+}

+ 21 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiGenerateOrderNoResponse.java

@@ -0,0 +1,21 @@
+package com.jzg.quotation.bohai.crawler.entity.response;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @description: 渤海财险爬虫生成订单号响应
+ * @author:
+ * @date:
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class BoHaiGenerateOrderNoResponse extends BoHaiCrawlerBaseResponse {
+
+    private DataDTO data;
+
+    @Data
+    public static class DataDTO {
+        private String orderNo;
+    }
+}

+ 21 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiPaymentResponse.java

@@ -0,0 +1,21 @@
+package com.jzg.quotation.bohai.crawler.entity.response;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @description: 渤海财险爬虫支付响应
+ * @author:
+ * @date:
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class BoHaiPaymentResponse extends BoHaiCrawlerBaseResponse {
+
+    private DataDTO data;
+
+    @Data
+    public static class DataDTO {
+        private String payUrl;
+    }
+}

+ 36 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/entity/response/BoHaiUnderwritingResponse.java

@@ -0,0 +1,36 @@
+package com.jzg.quotation.bohai.crawler.entity.response;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @description: 渤海财险爬虫核保响应
+ * @author:
+ * @date:
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class BoHaiUnderwritingResponse extends BoHaiCrawlerBaseResponse {
+
+    private DataDTO data;
+
+    @Data
+    public static class DataDTO {
+        private String trafficResultCode;
+        private String businessResultCode;
+        private String trafficResultMsg;
+        private String businessResultMsg;
+        private TrafficPolicyDTO trafficPolicy;
+        private BusinessPolicyDTO businessPolicy;
+    }
+
+    @Data
+    public static class TrafficPolicyDTO {
+        private String applicationNo;
+    }
+
+    @Data
+    public static class BusinessPolicyDTO {
+        private String applicationNo;
+    }
+}

+ 19 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/properties/BoHaiCrawlerProperties.java

@@ -0,0 +1,19 @@
+package com.jzg.quotation.bohai.crawler.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @description: 渤海财险爬虫配置属性
+ * @author:
+ * @date:
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "bohai.crawler")
+public class BoHaiCrawlerProperties {
+
+    private String baseUrl;
+
+}

+ 15 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/repository/BoHaiCrawlerOrderRepository.java

@@ -0,0 +1,15 @@
+package com.jzg.quotation.bohai.crawler.repository;
+
+import com.jzg.quotation.bohai.crawler.entity.po.BoHaiCrawlerOrder;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+/**
+ * @description: 渤海财险爬虫订单仓库
+ * @author:
+ * @date:
+ **/
+public interface BoHaiCrawlerOrderRepository extends MongoRepository<BoHaiCrawlerOrder, String> {
+
+    BoHaiCrawlerOrder findByOrdersNo(String ordersNo);
+
+}

+ 18 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/BoHaiCrawlerOrderService.java

@@ -0,0 +1,18 @@
+package com.jzg.quotation.bohai.crawler.service;
+
+import com.jzg.commons.entity.quote.vo.QuoteVo;
+import com.jzg.quotation.bohai.crawler.entity.po.BoHaiCrawlerOrder;
+import com.jzg.quotation.bohai.crawler.entity.response.BoHaiCrawlerQuotationResponse;
+
+/**
+ * @description: 渤海财险爬虫订单服务
+ * @author:
+ * @date:
+ **/
+public interface BoHaiCrawlerOrderService {
+
+    void save(QuoteVo quoteVo, BoHaiCrawlerQuotationResponse boHaiCrawlerQuotationResponse, boolean success);
+
+    BoHaiCrawlerOrder getById(String ordersNo);
+
+}

+ 28 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/BoHaiCrawlerQuoteProcessService.java

@@ -0,0 +1,28 @@
+package com.jzg.quotation.bohai.crawler.service;
+
+import org.springframework.http.HttpHeaders;
+
+/**
+ * @description: 渤海财险爬虫报价流程服务
+ * @author :jinshihui
+ * @date: 2025年9月17日16:28:40
+ **/
+public interface BoHaiCrawlerQuoteProcessService {
+
+    /**
+     * 登录
+     * @param username 用户名
+     * @param password 密码
+     * @return String
+     */
+    String login(String username, String password);
+
+    /**
+     * 获取请求头
+     * @param username 用户名
+     * @param password 密码
+     * @return HttpHeaders
+     */
+    HttpHeaders getHeaders(String username, String password);
+
+}

+ 12 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/BoHaiCrawlerRequest.java

@@ -0,0 +1,12 @@
+package com.jzg.quotation.bohai.crawler.service;
+
+import com.jzg.quotation.commons.service.QuotationCrawlerBaseService;
+
+/**
+ * @description: 渤海财险请求
+ * @author:
+ * @date:
+ **/
+public interface BoHaiCrawlerRequest extends QuotationCrawlerBaseService {
+
+}

+ 56 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/impl/BoHaiCrawlerOrderServiceImpl.java

@@ -0,0 +1,56 @@
+package com.jzg.quotation.bohai.crawler.service.impl;
+
+import com.jzg.commons.entity.quote.vo.QuoteVo;
+import com.jzg.quotation.bohai.crawler.entity.po.BoHaiCrawlerOrder;
+import com.jzg.quotation.bohai.crawler.entity.response.BoHaiCrawlerQuotationResponse;
+import com.jzg.quotation.bohai.crawler.repository.BoHaiCrawlerOrderRepository;
+import com.jzg.quotation.bohai.crawler.service.BoHaiCrawlerOrderService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+/**
+ * @description: 渤海财险爬虫订单服务实现
+ * @author:
+ * @date:
+ **/
+@Service
+@RequiredArgsConstructor
+public class BoHaiCrawlerOrderServiceImpl implements BoHaiCrawlerOrderService {
+
+    private final BoHaiCrawlerOrderRepository boHaiCrawlerOrderRepository;
+
+    @Override
+    public void save(QuoteVo quoteVo, BoHaiCrawlerQuotationResponse boHaiCrawlerQuotationResponse, boolean success) {
+        BoHaiCrawlerOrder order = new BoHaiCrawlerOrder();
+        order.setOrdersNo(quoteVo.getOrder().getOrdersNo());
+        order.setOrderNo(boHaiCrawlerQuotationResponse.getData().getBusUniqueId());
+        order.setBusUniqueId(boHaiCrawlerQuotationResponse.getData().getBusUniqueId());
+        order.setQuoteSuccess(success);
+
+        if (boHaiCrawlerQuotationResponse.getData().getTrafficPolicy() != null) {
+            order.setCiApplyNo(boHaiCrawlerQuotationResponse.getData().getTrafficPolicy().getApplicationNo());
+            order.setCiPremium(boHaiCrawlerQuotationResponse.getData().getTrafficPolicy().getPremium());
+        }
+
+        if (boHaiCrawlerQuotationResponse.getData().getBusinessPolicy() != null) {
+            order.setSyApplyNo(boHaiCrawlerQuotationResponse.getData().getBusinessPolicy().getApplicationNo());
+            order.setBiPremium(boHaiCrawlerQuotationResponse.getData().getBusinessPolicy().getPremium());
+        }
+
+        order.setTotalPremium(boHaiCrawlerQuotationResponse.getData().getPremium());
+        order.setCreateTime(LocalDateTime.now());
+        order.setUpdateTime(LocalDateTime.now());
+
+        if (!success) {
+            order.setErrorMsg(boHaiCrawlerQuotationResponse.getResponseMsg());
+        }
+        boHaiCrawlerOrderRepository.save(order);
+    }
+
+    @Override
+    public BoHaiCrawlerOrder getById(String ordersNo) {
+        return boHaiCrawlerOrderRepository.findByOrdersNo(ordersNo);
+    }
+}

+ 103 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/impl/BoHaiCrawlerQuoteProcessServiceImpl.java

@@ -0,0 +1,103 @@
+package com.jzg.quotation.bohai.crawler.service.impl;
+
+import com.jzg.commons.exception.SystemException;
+import com.jzg.quotation.bohai.crawler.component.BoHaiCrawlerCacheComponent;
+import com.jzg.quotation.bohai.crawler.component.BoHaiCrawlerRequestComponent;
+import com.jzg.quotation.bohai.crawler.entity.request.BoHaiCrawlerCheckTokenRequest;
+import com.jzg.quotation.bohai.crawler.entity.response.BoHaiCrawlerAuthenticateResponse;
+import com.jzg.quotation.bohai.crawler.entity.response.BoHaiCrawlerBaseResponse;
+import com.jzg.quotation.bohai.crawler.entity.response.BoHaiCrawlerCheckTokenResponse;
+import com.jzg.quotation.bohai.crawler.service.BoHaiCrawlerQuoteProcessService;
+import com.jzg.quotation.bohai.crawler.util.DESEncryption;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import java.util.Optional;
+
+/**
+ * @description: 渤海财险爬虫报价流程服务实现
+ * @author:
+ * @date:
+ **/
+@Service
+@RequiredArgsConstructor
+public class BoHaiCrawlerQuoteProcessServiceImpl implements BoHaiCrawlerQuoteProcessService {
+
+    private final BoHaiCrawlerCacheComponent boHaiCrawlerCacheComponent;
+
+    private final BoHaiCrawlerRequestComponent boHaiCrawlerRequestComponent;
+
+    // 登陆次数
+    public static final int LOGINS_NUMBER = 3;
+
+    /**
+     * 登录
+     * @param username 用户名
+     * @param password 密码
+     * @return String 返回token信息
+     */
+    @Override
+    public String login(String username, String password) {
+        // 渤海登录逻辑实现
+        // 这里需要实现渤海特定的登录流程
+        /*String token = "bohai_token_" + System.currentTimeMillis();
+        String userCode = "bohai_user_" + username;
+        boHaiCrawlerCacheComponent.cacheAuthorization(token, userCode, username);*/
+
+        for (int i = 0; i < LOGINS_NUMBER; i++) {
+            ResponseEntity<BoHaiCrawlerBaseResponse> responseEntity = boHaiCrawlerRequestComponent.authenticate(username, password);
+            BoHaiCrawlerBaseResponse response = Optional.of(responseEntity)
+                    .map(ResponseEntity::getBody)
+                    .orElseThrow();
+
+            if ("200".equals(response.getCode())) {
+                BoHaiCrawlerAuthenticateResponse daJiaCrawlerCaptchaResponse = DESEncryption.decryptStrToObject(response.getResult(), BoHaiCrawlerAuthenticateResponse.class);
+                // 校验token
+                String token = checkToken(daJiaCrawlerCaptchaResponse.getUserCode(), daJiaCrawlerCaptchaResponse.getIdToken());
+                return boHaiCrawlerCacheComponent.cacheAuthorization(token, daJiaCrawlerCaptchaResponse.getUserCode(), username);
+            }
+
+            String message = Optional.ofNullable(responseEntity.getBody())
+                    .map(BoHaiCrawlerBaseResponse::getMessage)
+                    .orElseThrow(() -> new SystemException("登录失败"));
+            if(!StringUtils.hasLength(message)) {
+                throw new SystemException(message);
+            }
+        }
+        throw new SystemException("登录失败");
+    }
+
+    @Override
+    public HttpHeaders getHeaders(String username, String password) {
+        String authorization = boHaiCrawlerCacheComponent.getAuthorization(username);
+        if (authorization == null) {
+            login(username, password);
+            authorization = boHaiCrawlerCacheComponent.getAuthorization(username);
+        }
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.set("Authorization", "Bearer " + authorization);
+        headers.set("Content-Type", "application/json");
+        headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
+        return headers;
+    }
+
+    /**
+     * 校验token
+     * @param userCode
+     * @param token
+     * @return String
+     */
+    public String checkToken(String userCode, String token) {
+        HttpHeaders httpHeaders = new HttpHeaders();
+        httpHeaders.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
+        // 创建token
+        String tokenId = boHaiCrawlerRequestComponent.createToken(userCode, httpHeaders);
+        BoHaiCrawlerCheckTokenRequest boHaiCrawlerCheckTokenRequest = new BoHaiCrawlerCheckTokenRequest(tokenId, userCode);
+        // 校验token
+        BoHaiCrawlerCheckTokenResponse daJiaCrawlerCheckTokenResponse = boHaiCrawlerRequestComponent.checkToken(boHaiCrawlerCheckTokenRequest, httpHeaders);
+        return  daJiaCrawlerCheckTokenResponse.getIdToken();
+    }
+}

+ 249 - 0
tenant/insurance/quotation-bohai/src/main/java/com/jzg/quotation/bohai/crawler/service/impl/BoHaiCrawlerRequestImpl.java

@@ -0,0 +1,249 @@
+package com.jzg.quotation.bohai.crawler.service.impl;
+
+import com.jzg.commons.entity.quote.vo.AttributionInformationVo;
+import com.jzg.commons.entity.quote.vo.CustomerInfoVo;
+import com.jzg.commons.entity.quote.vo.QuoteVo;
+import com.jzg.commons.entity.quote.vo.aggregated.*;
+import com.jzg.commons.entity.quote.vo.base.LoginBase;
+import com.jzg.quotation.bohai.api.constant.enums.ResponseCode;
+import com.jzg.quotation.bohai.api.entity.vo.PersonAreaVo;
+import com.jzg.quotation.bohai.crawler.component.BoHaiCrawlerCacheComponent;
+import com.jzg.quotation.bohai.crawler.component.BuildingAttributionInformation;
+import com.jzg.quotation.bohai.crawler.entity.request.BoHaiGenerateOrderNoRequest;
+import com.jzg.quotation.bohai.crawler.entity.request.BoHaiCrawlerQuotationRequest;
+import com.jzg.quotation.bohai.crawler.entity.request.quote.ProposalInfoDTO;
+import com.jzg.quotation.commons.annotation.QuoteCrawlerService;
+import com.jzg.quotation.commons.constant.InsuranceCode;
+import com.jzg.quotation.commons.standard.conversion.ConfigureParametersConversion;
+import com.jzg.quotation.bohai.crawler.build.BoHaiCrawlerQuoteResultsVoBuild;
+import com.jzg.quotation.bohai.crawler.component.BoHaiCrawlerRequestComponent;
+import com.jzg.quotation.bohai.crawler.entity.BoHaiCrawlerConfigureParameters;
+import com.jzg.quotation.bohai.crawler.entity.po.BoHaiCrawlerOrder;
+import com.jzg.quotation.bohai.crawler.entity.request.*;
+import com.jzg.quotation.bohai.crawler.entity.response.*;
+import com.jzg.quotation.bohai.crawler.service.BoHaiCrawlerOrderService;
+import com.jzg.quotation.bohai.crawler.service.BoHaiCrawlerQuoteProcessService;
+import com.jzg.quotation.bohai.crawler.service.BoHaiCrawlerRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpHeaders;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 渤海财险爬虫报价服务实现类
+ *
+ * <p>通过模拟浏览器操作的方式实现渤海财险的完整报价流程</p>
+ *
+ * @author
+ * @since
+ */
+@QuoteCrawlerService(value = InsuranceCode.INSURANCE_BPIC)
+@RequiredArgsConstructor
+public class BoHaiCrawlerRequestImpl implements BoHaiCrawlerRequest {
+
+    private final BoHaiCrawlerRequestComponent boHaiCrawlerRequestComponent;
+
+    /**
+     * 归属信息构建组件,用于构建机构、套餐、渠道等归属信息
+     */
+    private final BuildingAttributionInformation buildingAttributionInformation;
+
+    private final BoHaiCrawlerCacheComponent boHaiCrawlerCacheComponent;
+
+    private final BoHaiCrawlerOrderService boHaiCrawlerOrderService;
+
+    private final BoHaiCrawlerQuoteProcessService boHaiCrawlerQuoteProcessService;
+
+    /**
+     * 登录
+     *
+     * @param loginBase 登录信息
+     */
+    @Override
+    public void login(LoginBase loginBase) {
+        boHaiCrawlerQuoteProcessService.login(loginBase.getUsername(), loginBase.getPassword());
+    }
+
+    /**
+     * 报价
+     *
+     * @param quoteVo 报价实体
+     * @return QuoteResultsVo
+     */
+    @Override
+    public QuoteResultsVo quote(QuoteVo quoteVo) {
+        // 1.0 归属信息
+        AttributionInformationVo attributionInformationVo = quoteVo.getOrder().getAttributionInformationVo();
+        HttpHeaders headers = boHaiCrawlerQuoteProcessService.getHeaders(attributionInformationVo.getUsername(), attributionInformationVo.getPassword());
+        String userCode = boHaiCrawlerCacheComponent.getUserCode(attributionInformationVo.getUsername());
+
+        // 1.1 解析配置参数
+        BoHaiCrawlerConfigureParameters parameters = ConfigureParametersConversion.conversionEntity(BoHaiCrawlerConfigureParameters.class, attributionInformationVo.getConfigInfo());
+
+        // 1.2 构建归属信息(机构、套餐、渠道信息)
+        ProposalInfoDTO proposalInfoDTO = buildingAttributionInformation.build(parameters, userCode, headers);
+
+        // 2.0 生成订单号(没有这个逻辑)
+        //String orderNo = generateOrderNo(parameters, headers);
+
+        // 2.1 车辆查询
+        BoHaiCrawlerCarRequest daJiaCrawlerCarRequest = new BoHaiCrawlerCarRequest(quoteVo.getCarInfoVo(), parameters.getLoginCompany(), parameters.getCDptCde(), userCode);
+        BoHaiCrawlerCarResponse boHaiCrawlerCarResponse = boHaiCrawlerRequestComponent.car(daJiaCrawlerCarRequest, headers);
+
+        //2.2 车辆确认
+        Map<String, Object> vehicleConfirmationResult = getVehicleConfirmationResponse(quoteVo, boHaiCrawlerCarResponse, parameters, headers);
+        //获取车辆当前价值
+        String currentValue = (String) vehicleConfirmationResult.get("currentValue");
+
+        // 3.0 报价接口
+        BoHaiCrawlerQuotationRequest boHaiCrawlerQuotationRequest = new BoHaiCrawlerQuotationRequest(parameters, quoteVo, currentValue, "", quoteVo.getAccidentalDrivings(), "");
+        BoHaiCrawlerQuotationResponse boHaiCrawlerQuotationResponse = boHaiCrawlerRequestComponent.quotation(boHaiCrawlerQuotationRequest, headers);
+
+        // 4.0 处理报价失败
+        if (!ResponseCode.R_Z00001.equals(boHaiCrawlerQuotationResponse.getResponseCode())) {
+            boHaiCrawlerOrderService.save(quoteVo, boHaiCrawlerQuotationResponse, false);
+            return BoHaiCrawlerQuoteResultsVoBuild.buildQuoteFailResultsVo(boHaiCrawlerQuotationResponse);
+        } else {
+            //报价成功
+            boHaiCrawlerOrderService.save(quoteVo, boHaiCrawlerQuotationResponse, true);
+            return BoHaiCrawlerQuoteResultsVoBuild.buildQuoteResultsVo(quoteVo.getOrder().getOrdersNo(), quoteVo, boHaiCrawlerQuotationResponse);
+        }
+    }
+
+    @Override
+    public UnderwritingResultsVo underwriting(UnderwritingVo underwritingVo) {
+        /*AttributionInformationVo attributionInformationVo = underwritingVo.getOrder().getAttributionInformationVo();
+        BoHaiCrawlerOrder boHaiCrawlerOrder = boHaiCrawlerOrderService.getById(underwritingVo.getOrder().getOrdersNo());
+        HttpHeaders headers = boHaiCrawlerQuoteProcessService.getHeaders(attributionInformationVo.getUsername(), attributionInformationVo.getPassword());
+
+        UnderwritingRequest underwritingRequest = new UnderwritingRequest(attributionInformationVo.getConfigInfo(), boHaiCrawlerOrder.getOrderNo());
+        BoHaiUnderwritingResponse boHaiUnderwritingResponse = boHaiCrawlerRequestComponent.underwriting(underwritingRequest, headers);
+
+        UnderwritingResultsVo underwritingResultsVo = new UnderwritingResultsVo();
+        if ("R_Z00001".equals(boHaiUnderwritingResponse.getResponseCode())) {
+            String jqStatus = boHaiUnderwritingResponse.getData().getTrafficResultCode();
+            String syStatus = boHaiUnderwritingResponse.getData().getBusinessResultCode();
+
+            if ("A4".equals(jqStatus) || "A4".equals(syStatus)) {
+                underwritingResultsVo.setUnderwritingStatus(InsOrderStatusEnum.STAFF_PROCESS);
+                underwritingResultsVo.setUnderwritingResult("人工处理");
+            } else if ("A6".equals(jqStatus) || "A6".equals(syStatus)) {
+                underwritingResultsVo.setUnderwritingStatus(InsOrderStatusEnum.AUDIT_FAIL);
+                StringBuilder sb = new StringBuilder();
+                if (null != boHaiUnderwritingResponse.getData().getTrafficResultMsg()) {
+                    sb.append(String.format("交强险提示信息:s%", boHaiUnderwritingResponse.getData().getTrafficResultMsg()));
+                }
+                if (null != boHaiUnderwritingResponse.getData().getBusinessResultMsg()) {
+                    sb.append(String.format("商业险提示信息:s%", boHaiUnderwritingResponse.getData().getBusinessResultMsg()));
+                }
+                underwritingResultsVo.setUnderwritingResult(sb.toString());
+            } else if ("A0".equals(jqStatus) || "A0".equals(syStatus)) {
+                underwritingResultsVo.setUnderwritingStatus(InsOrderStatusEnum.PAY_PENDING);
+                underwritingResultsVo.setJqApplyNo(boHaiUnderwritingResponse.getData().getTrafficPolicy().getApplicationNo());
+                underwritingResultsVo.setSyApplyNo(boHaiUnderwritingResponse.getData().getBusinessPolicy().getApplicationNo());
+            }
+        }
+        return underwritingResultsVo;*/
+        return null;
+    }
+
+    @Override
+    public PaymentLinkResultsVo getPaymentLink(PaymentLinkVo paymentLinkVo) {
+        /*AttributionInformationVo attributionInformationVo = paymentLinkVo.getOrder().getAttributionInformationVo();
+        BoHaiCrawlerOrder boHaiCrawlerOrder = boHaiCrawlerOrderService.getById(paymentLinkVo.getOrder().getOrdersNo());
+        HttpHeaders headers = boHaiCrawlerQuoteProcessService.getHeaders(attributionInformationVo.getUsername(), attributionInformationVo.getPassword());
+
+        PaymentRequest paymentRequest = new PaymentRequest(attributionInformationVo.getConfigInfo(), boHaiCrawlerOrder.getOrderNo(), boHaiCrawlerOrder.getBusUniqueId());
+        BoHaiPaymentResponse boHaiPaymentResponse = boHaiCrawlerRequestComponent.payment(paymentRequest, headers);
+
+        PaymentLinkResultsVo paymentLinkResultsVo = new PaymentLinkResultsVo();
+        paymentLinkResultsVo.setPaymentLink(boHaiPaymentResponse.getData().getPayUrl());
+        return paymentLinkResultsVo;*/
+        return null;
+    }
+
+    @Override
+    public void uploadImage(InsuranceUploadImageVo insuranceUploadImageVo) {
+        // 渤海影像上传实现
+        /*AttributionInformationVo attributionInformationVo = insuranceUploadImageVo.getOrder().getAttributionInformationVo();
+        BoHaiCrawlerOrder boHaiCrawlerOrder = boHaiCrawlerOrderService.getById(insuranceUploadImageVo.getOrder().getOrdersNo());
+        HttpHeaders headers = boHaiCrawlerQuoteProcessService.getHeaders(attributionInformationVo.getUsername(), attributionInformationVo.getPassword());
+
+        ImageUploadRequest imageUploadRequest = new ImageUploadRequest(attributionInformationVo.getConfigInfo(),
+                boHaiCrawlerOrder.getOrderNo(), boHaiCrawlerOrder.getBusUniqueId(), insuranceUploadImageVo.getImageVoList());
+        boHaiCrawlerRequestComponent.imageUpload(imageUploadRequest, headers);*/
+    }
+
+    @Override
+    public DownloadPolicyResultVo downloadPolicy(DownloadPolicyVo downloadPolicyVo) {
+        /*AttributionInformationVo attributionInformationVo = downloadPolicyVo.getOrder().getAttributionInformationVo();
+        BoHaiCrawlerOrder boHaiCrawlerOrder = boHaiCrawlerOrderService.getById(downloadPolicyVo.getOrder().getOrdersNo());
+        HttpHeaders headers = boHaiCrawlerQuoteProcessService.getHeaders(attributionInformationVo.getUsername(), attributionInformationVo.getPassword());
+
+        ElectronicPolicyRequest electronicPolicyRequest = new ElectronicPolicyRequest(attributionInformationVo.getConfigInfo(),
+                boHaiCrawlerOrder.getOrderNo(), boHaiCrawlerOrder.getCiPolicyNo(), boHaiCrawlerOrder.getBiPolicyNo());
+        ElectronicPolicyResponse electronicPolicyResponse = boHaiCrawlerRequestComponent.electronicPolicy(electronicPolicyRequest, headers);
+
+        DownloadPolicyResultVo downloadPolicyResultVo = new DownloadPolicyResultVo();
+        if (StringUtils.hasLength(electronicPolicyResponse.getData().getTrafficEPolicyUrl())) {
+            downloadPolicyResultVo.setJqUrl(electronicPolicyResponse.getData().getTrafficEPolicyUrl());
+        }
+        if (StringUtils.hasLength(electronicPolicyResponse.getData().getBusinessEPolicyUrl())) {
+            downloadPolicyResultVo.setSyUrl(electronicPolicyResponse.getData().getBusinessEPolicyUrl());
+        }
+        return downloadPolicyResultVo;*/
+        return null;
+    }
+
+    /**
+     *查询订单状态
+     * @param orderStatusVo
+     * @return
+     */
+    @Override
+    public OrderStatusResultVo queryOrderState(OrderStatusVo orderStatusVo) {
+        AttributionInformationVo attributionInformationVo = orderStatusVo.getOrder().getAttributionInformationVo();
+        BoHaiCrawlerOrder boHaiCrawlerOrder = boHaiCrawlerOrderService.getById(orderStatusVo.getOrder().getOrdersNo());
+        HttpHeaders headers = boHaiCrawlerQuoteProcessService.getHeaders(attributionInformationVo.getUsername(), attributionInformationVo.getPassword());
+
+        /*StatusDocumentsRequest statusDocumentsRequest = new StatusDocumentsRequest(attributionInformationVo.getConfigInfo(),
+                boHaiCrawlerOrder.getOrderNo(), boHaiCrawlerOrder.getCiApplyNo());
+        StatusDocumentsResponse response = boHaiCrawlerRequestComponent.statusDocuments(statusDocumentsRequest, headers);*/
+
+        OrderStatusResultVo orderStatusResultVo = new OrderStatusResultVo();
+        // 处理订单状态逻辑
+        return orderStatusResultVo;
+    }
+
+    /**
+     * 车辆确认
+     *
+     * @param quoteVo
+     * @param boHaiCrawlerCarResponse
+     * @param parameters
+     * @param orderNo
+     * @param headers
+     * @return Map<String, Object>
+     */
+    private Map<String, Object> getVehicleConfirmationResponse(QuoteVo quoteVo, BoHaiCrawlerCarResponse boHaiCrawlerCarResponse, BoHaiCrawlerConfigureParameters parameters,HttpHeaders headers) {
+        // 车主信息
+        CustomerInfoVo ownerInfo = quoteVo.getOwnersInfo();
+        PersonAreaVo ownerInfoArea = new PersonAreaVo(ownerInfo);
+        // 被保人信息
+        CustomerInfoVo insuredPersonInfo = quoteVo.getInsuredPersonInfo();
+        PersonAreaVo insuredPersonArea = new PersonAreaVo(insuredPersonInfo);
+        // 投保人信息
+        CustomerInfoVo policyHolderInfo = quoteVo.getPolicyHolderInfo();
+        PersonAreaVo policyHolderArea = new PersonAreaVo(policyHolderInfo);
+
+        BoHaiCrawlerVehicleConfirmationReqeust vehicleConfirmationReqeust = new BoHaiCrawlerVehicleConfirmationReqeust(parameters, quoteVo, boHaiCrawlerCarResponse, ownerInfoArea, insuredPersonArea, policyHolderArea);
+
+        BoHaiCrawlerVehicleConfirmationResponse boHaiCrawlerVehicleConfirmationResponse = boHaiCrawlerRequestComponent.vehicleConfirmation(vehicleConfirmationReqeust,headers);
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("vehicleModelsDTO", boHaiCrawlerCarResponse.getList().get(0));
+        map.put("vehicleConfirmationResponse", boHaiCrawlerVehicleConfirmationResponse);
+        return map;
+    }
+}

+ 82 - 0
tenant/insurance/quotation-dajia/CLAUDE.md

@@ -0,0 +1,82 @@
+# CLAUDE.md
+
+本文档为Claude Code (claude.ai/code) 在此代码库中工作提供指导。
+
+## 项目结构
+
+这是一个基于Maven的Java Spring Boot微服务项目,用于保险报价服务。项目组织如下:
+
+- **根目录**: `D:\project\jzg\tenant\insurance\` - 父级Maven模块
+- **模块**: 多个保险报价服务(quotation-dajia、quotation-taikang、quotation-summary等)
+- **公共模块**: 共享模块(quotation-commons、commons核心库)
+
+## 关键技术
+
+- **框架**: Spring Boot 3.0.0 with WebFlux
+- **构建工具**: Maven with Java 17
+- **数据库**: MongoDB with Spring Data MongoDB
+- **API**: RESTful APIs with OpenFeign服务通信
+- **文档**: Swagger/OpenAPI注解
+- **工具库**: Hutool crypto、Apache Commons Lang
+
+## 开发命令
+
+### 构建和运行
+```bash
+# 构建特定模块(例如quotation-dajia)
+mvn clean compile -pl quotation-dajia
+
+# 包含测试的构建
+mvn clean package -pl quotation-dajia
+
+# 运行Spring Boot应用(从模块目录)
+mvn spring-boot:run
+
+# 安装到本地仓库
+mvn clean install -pl quotation-dajia
+```
+
+### 测试
+```bash
+# 运行特定模块的测试
+mvn test -pl quotation-dajia
+
+# 运行特定测试类
+mvn test -pl quotation-dajia -Dtest=ClassName
+
+# 运行测试覆盖率检查
+mvn test -pl quotation-dajia -Djacoco.skip=false
+```
+
+### 依赖管理
+```bash
+# 检查依赖更新
+mvn versions:display-dependency-updates -pl quotation-dajia
+
+# 分析依赖关系
+mvn dependency:tree -pl quotation-dajia
+```
+
+## 架构模式
+
+1. **微服务架构**: 每个保险提供商都有独立的报价模块
+2. **共享公共模块**: 通用功能在`quotation-commons`模块中
+3. **MongoDB集成**: 使用Spring Data MongoDB仓库和模式验证
+4. **Feign客户端**: 通过OpenFeign进行服务间通信
+5. **REST控制器**: 标准的Spring MVC控制器,带有Swagger文档
+
+## 模块结构
+
+每个报价模块通常包含:
+- `src/main/java/com/jzg/quotation/[provider]/` - Java源代码
+- API组件、常量、构建器
+- MongoDB仓库接口
+- 配置属性
+- 与外部保险API的集成
+
+## 重要说明
+
+- **证书管理**: 某些模块需要安装HTTPS证书(参见文档)
+- **MongoDB模式**: 使用复杂的模式验证来管理会话和API数据
+- **保险集成**: 模块与各种保险提供商API集成
+- **公共依赖**: 依赖`quotation-commons`和`com.jzg.commons`模块

+ 76 - 0
tenant/insurance/quotation-huanong/CLAUDE.md

@@ -0,0 +1,76 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## 项目概述
+
+这是一个基于Maven的Java Spring Boot微服务模块,专门用于华农保险的报价服务集成。
+
+## 技术栈
+
+- **框架**: Spring Boot 3.0.0
+- **Java版本**: Java 17
+- **构建工具**: Maven
+- **数据存储**: MongoDB (通过Spring Data MongoDB)
+- **网络通信**: WebFlux reactive programming
+- **工具库**: Hutool, Apache Commons
+
+## 开发命令
+
+### 构建命令
+```bash
+# 构建当前模块
+mvn clean compile
+
+# 包含测试的完整构建
+mvn clean package
+
+# 安装到本地Maven仓库
+mvn clean install
+
+# 运行Spring Boot应用
+mvn spring-boot:run
+```
+
+### 模块特定构建
+```bash
+# 从根目录构建特定模块
+mvn clean compile -pl quotation-huanong
+
+# 构建并跳过测试
+mvn clean package -pl quotation-huanong -DskipTests
+```
+
+## 项目结构
+
+- `src/main/java/com/jzg/quotation/huanong/` - 主要Java源代码
+  - `crawler/` - 网络爬虫和API集成组件
+  - `component/` - 业务组件
+  - `entity/` - 数据实体和DTO
+  - `constant/` - 常量和枚举
+  - `service/` - 业务服务层
+  - `controller/` - REST API控制器
+
+## 架构特点
+
+1. **爬虫集成**: 与华农保险API的深度集成
+2. **响应式编程**: 使用WebFlux进行非阻塞IO操作
+3. **MongoDB存储**: 会话数据和报价结果的持久化
+4. **配置驱动**: 通过配置参数控制API行为
+5. **错误处理**: 完善的异常处理和状态码管理
+
+## 重要依赖
+
+- `quotation-commons` - 共享报价功能模块
+- `com.jzg.commons` - 核心公共库
+- Spring Data MongoDB - 数据库操作
+- Spring WebFlux - 响应式Web框架
+- Hutool - 工具类库
+
+## 开发注意事项
+
+- 遵循现有的代码规范和包结构
+- 使用响应式编程模式(Mono/Flux)
+- 保持与`quotation-commons`模块的兼容性
+- 配置参数通过`HuaNongCrawlerConfigureParameters`管理
+- API调用使用`HuaNongCrawlerRequestComponent`组件