Sfoglia il codice sorgente

Merge branch 'master' into wrq_dev

wangrq 3 mesi fa
parent
commit
83db43cb24
47 ha cambiato i file con 1661 aggiunte e 160 eliminazioni
  1. 2 0
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoice.java
  2. 4 0
      commons/src/main/java/com/jzg/commons/entity/scheme/vo/PtlAgreementInsCompanyVo.java
  3. 7 0
      commons/src/main/java/com/jzg/commons/util/BigDecimalUtil.java
  4. 185 0
      platform/src/main/java/com/jzg/controller/MigrateDateController.java
  5. 49 0
      platform/src/main/java/com/jzg/entity/dto/MigrateDataParam.java
  6. 101 0
      platform/src/main/java/com/jzg/mapper/MigrateDataMapper.java
  7. 121 0
      platform/src/main/java/com/jzg/service/MigrateDataService.java
  8. 463 0
      platform/src/main/java/com/jzg/service/impl/MigrateDataServiceImpl.java
  9. 102 0
      platform/src/main/resources/mapper/MigrateDataMapper.xml
  10. 26 2
      tenant/insurance/quotation-chengtai/src/main/java/com/jzg/quotation/chengtai/api/build/ChengTaiAPIQuoteResultsVoBuild.java
  11. 18 0
      tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/constant/enums/quote/RenewalEnum.java
  12. 3 3
      tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/filter/CarModelsFilterUtils.java
  13. 13 5
      tenant/insurance/quotation-dajia/src/main/java/com/jzg/quotation/dajia/crawler/service/impl/DaJiaCrawlerRequestImpl.java
  14. 66 0
      tenant/insurance/quotation-dajia/src/main/java/com/jzg/quotation/dajia/crawler/utils/ByteArrayMultipartFile.java
  15. 3 1
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/component/HengBangCrawlerRequestComponent.java
  16. 24 9
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/service/Impl/HengBangCrawlerRequestImpl.java
  17. 26 0
      tenant/insurance/quotation-huanong/src/main/java/com/jzg/quotation/huanong/crawler/build/HuaNongCrawlerQuoteResultsVoBuild.java
  18. 4 0
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/build/HuaTaiCrawlerQuoteResultsVoBuild.java
  19. 3 3
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/entity/Request/HtCrawlerQuoteRequest.java
  20. 45 15
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/service/Impl/HuaTaiCrawlerRequestImpl.java
  21. 22 0
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/component/RenBaoNewCrawlerRequestComponent.java
  22. 6 1
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/constant/enums/RequestUrl.java
  23. 42 7
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestBuilder.java
  24. 1 12
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/aspect/QuoteVerificationAspect.java
  25. 4 5
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/InsFeeOrderController.java
  26. 14 0
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/InsOrdersController.java
  27. 10 9
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/InsOrdersService.java
  28. 36 43
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsOrdersServiceImpl.java
  29. 1 9
      tenant/insurance/quotation-summary/src/main/resources/mapper/InsOrdersMapper.xml
  30. 2 0
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/entity/response/ZhongAnCrawlerPaymentResponse.java
  31. 17 6
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/service/Impl/ZhongAnCrawlerRequestImpl.java
  32. 1 1
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/service/ZhongAnAPIService.java
  33. 1 1
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/utils/JsonUtil.java
  34. 79 0
      tenant/insurance/quotation-zhongan/src/main/java/com/jzg/quotation/zhongan/crawler/utils/QrBase64Util.java
  35. 1 1
      tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryCarInfoResultTest.java
  36. 1 1
      tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryJyytVehicleModelResultTest.java
  37. 1 1
      tenant/insurance/quotation-zhongan/src/test/java/com/jzg/quotation/zhongan/crawler/model/result/ZhongAnQueryPolicyDetailResultTest.java
  38. 0 2
      tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quotation/zhongmei/crawler/constant/enums/ZmCrawlerFloatFlag.java
  39. 10 10
      tenant/organization/src/main/java/com/jzg/organization/client/InsFeeOrdersClient.java
  40. 11 1
      tenant/organization/src/main/java/com/jzg/organization/client/InsOrdersClient.java
  41. 12 0
      tenant/organization/src/main/java/com/jzg/organization/client/PlatformClient.java
  42. 9 0
      tenant/organization/src/main/java/com/jzg/organization/mapper/ReceivableMapper.java
  43. 18 0
      tenant/organization/src/main/java/com/jzg/organization/service/ReceivableService.java
  44. 29 0
      tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlSchemeServiceImpl.java
  45. 45 8
      tenant/organization/src/main/java/com/jzg/organization/service/impl/ReceivableServiceImpl.java
  46. 7 4
      tenant/organization/src/main/java/com/jzg/organization/service/impl/SignInServiceImpl.java
  47. 16 0
      tenant/organization/src/main/resources/mapper/ReceivableMapper.xml

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

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

+ 4 - 0
commons/src/main/java/com/jzg/commons/entity/scheme/vo/PtlAgreementInsCompanyVo.java

@@ -4,6 +4,7 @@ import com.jzg.commons.entity.po.EsmInsCompany;
 import com.jzg.commons.entity.po.PtlAgreement;
 import com.jzg.commons.entity.quote.vo.aggregated.QuoteEchoVo;
 import com.jzg.commons.entity.quote.vo.aggregated.QuoteResultsVo;
+import com.jzg.commons.entity.vo.InsCompanyClauseVo;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -47,4 +48,7 @@ public class PtlAgreementInsCompanyVo extends EsmInsCompany implements Serializa
     @Schema(description = "费用")
     private QuoteEchoVo.OrdersCostsVo costs;
 
+    @Schema(description = "国任特约选中列表(车主与投保人不一致时返回车主约定T0028)")
+    private List<InsCompanyClauseVo> specialSelectedList;
+
 }

+ 7 - 0
commons/src/main/java/com/jzg/commons/util/BigDecimalUtil.java

@@ -159,4 +159,11 @@ public class BigDecimalUtil {
         }
         return new BigDecimal(v);
     }
+
+    public static BigDecimal toZero(BigDecimal amount) {
+        if(amount == null){
+            return BigDecimal.ZERO;
+        }
+        return  amount;
+    }
 }

+ 185 - 0
platform/src/main/java/com/jzg/controller/MigrateDateController.java

@@ -0,0 +1,185 @@
+package com.jzg.controller;
+
+import com.jzg.commons.core.base.BaseController;
+import com.jzg.commons.core.page.HttpResult;
+import com.jzg.entity.dto.MigrateDataParam;
+import com.jzg.service.MigrateDataService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数据迁移控制器
+ */
+@RestController
+@RequestMapping("/migrate/data")
+@Tag(name = "数据迁移管理")
+@Slf4j
+public class MigrateDateController extends BaseController {
+
+    @Autowired
+    private MigrateDataService migrateDataService;
+
+    /**
+     * 从源表迁移数据到目标表
+     * @param param 迁移参数
+     * @return 迁移结果
+     */
+    @Operation(summary = "迁移数据")
+    @PostMapping("/migrate")
+    public HttpResult migrateData(@Validated @RequestBody MigrateDataParam param) {
+        try {
+            // 如果需要先清空目标表
+            if (Boolean.TRUE.equals(param.getClearTargetBeforeMigrate())) {
+                String clearCondition = param.getClearTargetCondition();
+                int deleteCount = migrateDataService.deleteTargetData(
+                    param.getTargetTableName(),
+                    clearCondition != null ? clearCondition : "1=1"
+                );
+                log.info("已清空目标表 {} 中 {} 条数据", param.getTargetTableName(), deleteCount);
+            }
+
+            // 执行数据迁移
+            int migratedCount;
+            String primaryKeyField = param.getPrimaryKeyField();
+            boolean generateSnowflakeId = Boolean.TRUE.equals(param.getGenerateSnowflakeId());
+            String sourcePrimaryKeyField = param.getSourcePrimaryKeyField();
+            boolean saveOriginalId = Boolean.TRUE.equals(param.getSaveOriginalId());
+
+            if (param.getFieldMapping() != null && !param.getFieldMapping().isEmpty()) {
+                // 使用字段映射迁移
+                migratedCount = migrateDataService.migrateDataWithMapping(
+                    param.getSourceTableName(),
+                    param.getTargetTableName(),
+                    param.getFieldMapping(),
+                    param.getWhereCondition(),
+                    primaryKeyField,
+                    generateSnowflakeId,
+                    sourcePrimaryKeyField,
+                    saveOriginalId
+                );
+            } else if (param.getFields() != null && !param.getFields().isEmpty()) {
+                // 迁移指定字段
+                migratedCount = migrateDataService.migrateDataWithFields(
+                    param.getSourceTableName(),
+                    param.getTargetTableName(),
+                    param.getFields(),
+                    param.getWhereCondition(),
+                    primaryKeyField,
+                    generateSnowflakeId,
+                    sourcePrimaryKeyField,
+                    saveOriginalId
+                );
+            } else {
+                // 迁移所有字段
+                migratedCount = migrateDataService.migrateData(
+                    param.getSourceTableName(),
+                    param.getTargetTableName(),
+                    param.getWhereCondition(),
+                    primaryKeyField,
+                    generateSnowflakeId,
+                    sourcePrimaryKeyField,
+                    saveOriginalId
+                );
+            }
+
+            return HttpResult.ok("数据迁移成功,共迁移 " + migratedCount + " 条记录");
+        } catch (Exception e) {
+            log.error("数据迁移失败", e);
+            return HttpResult.error("数据迁移失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 更新目标表数据
+     * @param targetTableName 目标表名
+     * @param dataMap 需要更新的数据
+     * @param whereCondition 更新条件
+     * @return 更新结果
+     */
+    @Operation(summary = "更新目标表数据")
+    @PutMapping("/update/{targetTableName}")
+    public HttpResult updateTargetData(
+            @PathVariable String targetTableName,
+            @RequestBody Map<String, Object> dataMap,
+            @RequestParam(required = false) String whereCondition) {
+        try {
+            if (whereCondition == null || whereCondition.trim().isEmpty()) {
+                return HttpResult.error("更新条件不能为空");
+            }
+
+            int count = migrateDataService.updateTargetData(targetTableName, dataMap, whereCondition);
+            return HttpResult.ok("更新成功,影响 " + count + " 条记录");
+        } catch (Exception e) {
+            log.error("更新数据失败", e);
+            return HttpResult.error("更新失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除目标表数据
+     * @param targetTableName 目标表名
+     * @param whereCondition 删除条件
+     * @return 删除结果
+     */
+    @Operation(summary = "删除目标表数据")
+    @DeleteMapping("/delete/{targetTableName}")
+    public HttpResult deleteTargetData(
+            @PathVariable String targetTableName,
+            @RequestParam String whereCondition) {
+        try {
+            if (whereCondition == null || whereCondition.trim().isEmpty()) {
+                return HttpResult.error("删除条件不能为空");
+            }
+
+            int count = migrateDataService.deleteTargetData(targetTableName, whereCondition);
+            return HttpResult.ok("删除成功,影响 " + count + " 条记录");
+        } catch (Exception e) {
+            log.error("删除数据失败", e);
+            return HttpResult.error("删除失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取表结构信息
+     * @param tableName 表名
+     * @return 表结构信息
+     */
+    @Operation(summary = "获取表结构信息")
+    @GetMapping("/structure/{tableName}")
+    public HttpResult getTableStructure(@PathVariable String tableName) {
+        try {
+            List<Map<String, Object>> structure = migrateDataService.getTableStructure(tableName);
+            return HttpResult.ok(structure);
+        } catch (Exception e) {
+            log.error("获取表结构失败", e);
+            return HttpResult.error("获取表结构失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 检查表是否存在
+     * @param tableName 表名
+     * @return 检查结果
+     */
+    @Operation(summary = "检查表是否存在")
+    @GetMapping("/exists/{tableName}")
+    public HttpResult checkTableExists(@PathVariable String tableName) {
+        try {
+            boolean exists = migrateDataService.checkTableExists(tableName);
+            return HttpResult.ok(Map.of(
+                "tableName", tableName,
+                "exists", exists
+            ));
+        } catch (Exception e) {
+            log.error("检查表是否存在失败", e);
+            return HttpResult.error("检查失败: " + e.getMessage());
+        }
+    }
+}

+ 49 - 0
platform/src/main/java/com/jzg/entity/dto/MigrateDataParam.java

@@ -0,0 +1,49 @@
+
+package com.jzg.entity.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数据迁移参数
+ */
+@Data
+@Schema(description = "数据迁移参数")
+public class MigrateDataParam {
+
+    @Schema(description = "源表名", required = true)
+    private String sourceTableName;
+
+    @Schema(description = "目标表名", required = true)
+    private String targetTableName;
+
+    @Schema(description = "查询条件(SQL WHERE子句,不包含WHERE关键字)")
+    private String whereCondition;
+
+    @Schema(description = "需要迁移的字段列表(为空则迁移所有字段)")
+    private List<String> fields;
+
+    @Schema(description = "字段映射关系(源字段名 -> 目标字段名)")
+    private Map<String, String> fieldMapping;
+
+    @Schema(description = "是否先清空目标表数据")
+    private Boolean clearTargetBeforeMigrate = false;
+
+    @Schema(description = "清空目标表的条件(SQL WHERE子句,不包含WHERE关键字)")
+    private String clearTargetCondition;
+
+    @Schema(description = "目标表的主键字段名")
+    private String primaryKeyField;
+
+    @Schema(description = "是否为主键生成雪花ID(默认false)")
+    private Boolean generateSnowflakeId = false;
+
+    @Schema(description = "是否保存原始ID到original_id字段(默认false)")
+    private Boolean saveOriginalId = false;
+
+    @Schema(description = "源表的主键字段名(用于保存到original_id)")
+    private String sourcePrimaryKeyField;
+}

+ 101 - 0
platform/src/main/java/com/jzg/mapper/MigrateDataMapper.java

@@ -0,0 +1,101 @@
+
+package com.jzg.mapper;
+
+import com.jzg.commons.annotation.IgnoreTenant;
+import org.apache.ibatis.annotations.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数据迁移Mapper
+ */
+@Mapper
+public interface MigrateDataMapper {
+
+    /**
+     * 从源表查询数据(使用JDBC直接查询,避免MyBatis Plus类型处理器干扰)
+     * @param sourceTableName 源表名
+     * @param whereCondition 查询条件(可为空)
+     * @return 查询结果
+     */
+    @IgnoreTenant
+    List<Map<String, Object>> selectFromSourceTable(@Param("sourceTableName") String sourceTableName, 
+                                                     @Param("whereCondition") String whereCondition);
+
+    /**
+     * 插入数据到目标表
+     * @param targetTableName 目标表名
+     * @param dataMap 数据Map
+     * @return 影响行数
+     */
+    @IgnoreTenant
+    int insertToTargetTable(@Param("targetTableName") String targetTableName, 
+                           @Param("dataMap") Map<String, Object> dataMap);
+
+    /**
+     * 批量插入数据到目标表
+     * @param targetTableName 目标表名
+     * @param dataList 数据列表
+     * @return 影响行数
+     */
+    @IgnoreTenant
+    int batchInsertToTargetTable(@Param("targetTableName") String targetTableName, 
+                                @Param("dataList") List<Map<String, Object>> dataList);
+
+    /**
+     * 更新目标表数据
+     * @param targetTableName 目标表名
+     * @param dataMap 数据Map
+     * @param whereCondition 更新条件
+     * @return 影响行数
+     */
+    @IgnoreTenant
+    int updateTargetTable(@Param("targetTableName") String targetTableName,
+                         @Param("dataMap") Map<String, Object> dataMap,
+                         @Param("whereCondition") String whereCondition);
+
+    /**
+     * 删除目标表数据
+     * @param targetTableName 目标表名
+     * @param whereCondition 删除条件
+     * @return 影响行数
+     */
+    @IgnoreTenant
+    int deleteFromTargetTable(@Param("targetTableName") String targetTableName,
+                             @Param("whereCondition") String whereCondition);
+
+    /**
+     * 检查目标表是否存在
+     * @param tableName 表名
+     * @return 存在返回true
+     */
+    @IgnoreTenant
+    boolean checkTableExists(@Param("tableName") String tableName);
+
+    /**
+     * 检查表中是否存在指定列
+     * @param tableName 表名
+     * @param columnName 列名
+     * @return 存在返回true
+     */
+    @IgnoreTenant
+    boolean checkColumnExists(@Param("tableName") String tableName, @Param("columnName") String columnName);
+
+    /**
+     * 为表添加original_id列
+     * @param tableName 表名
+     * @return 影响行数
+     */
+    @IgnoreTenant
+    int addOriginalIdColumn(@Param("tableName") String tableName);
+
+    /**
+     * 获取表结构信息
+     * @param tableName 表名
+     * @return 表字段信息
+     */
+    @IgnoreTenant
+    List<Map<String, Object>> getTableStructure(@Param("tableName") String tableName);
+}

+ 121 - 0
platform/src/main/java/com/jzg/service/MigrateDataService.java

@@ -0,0 +1,121 @@
+package com.jzg.service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数据迁移服务接口
+ */
+public interface MigrateDataService {
+
+    /**
+     * 从源表迁移数据到目标表
+     * @param sourceTableName 源表名
+     * @param targetTableName 目标表名
+     * @param whereCondition 查询条件(可为空)
+     * @return 迁移的记录数
+     */
+    int migrateData(String sourceTableName, String targetTableName, String whereCondition);
+
+    /**
+     * 从源表迁移数据到目标表(支持雪花ID生成)
+     * @param sourceTableName 源表名
+     * @param targetTableName 目标表名
+     * @param whereCondition 查询条件(可为空)
+     * @param primaryKeyField 主键字段名
+     * @param generateSnowflakeId 是否生成雪花ID
+     * @param sourcePrimaryKeyField 源表主键字段名
+     * @param saveOriginalId 是否保存原始ID
+     * @return 迁移的记录数
+     */
+    int migrateData(String sourceTableName, String targetTableName, String whereCondition,
+                    String primaryKeyField, boolean generateSnowflakeId,
+                    String sourcePrimaryKeyField, boolean saveOriginalId);
+
+    /**
+     * 从源表迁移数据到目标表(支持字段映射)
+     * @param sourceTableName 源表名
+     * @param targetTableName 目标表名
+     * @param fieldMapping 字段映射关系(源字段->目标字段)
+     * @param whereCondition 查询条件(可为空)
+     * @return 迁移的记录数
+     */
+    int migrateDataWithMapping(String sourceTableName, String targetTableName,
+                              Map<String, String> fieldMapping, String whereCondition);
+
+    /**
+     * 从源表迁移数据到目标表(支持字段映射和雪花ID生成)
+     * @param sourceTableName 源表名
+     * @param targetTableName 目标表名
+     * @param fieldMapping 字段映射关系(源字段->目标字段)
+     * @param whereCondition 查询条件(可为空)
+     * @param primaryKeyField 主键字段名
+     * @param generateSnowflakeId 是否生成雪花ID
+     * @param sourcePrimaryKeyField 源表主键字段名
+     * @param saveOriginalId 是否保存原始ID
+     * @return 迁移的记录数
+     */
+    int migrateDataWithMapping(String sourceTableName, String targetTableName,
+                              Map<String, String> fieldMapping, String whereCondition,
+                              String primaryKeyField, boolean generateSnowflakeId,
+                              String sourcePrimaryKeyField, boolean saveOriginalId);
+
+    /**
+     * 从源表迁移指定字段的数据到目标表
+     * @param sourceTableName 源表名
+     * @param targetTableName 目标表名
+     * @param fields 需要迁移的字段列表
+     * @param whereCondition 查询条件(可为空)
+     * @return 迁移的记录数
+     */
+    int migrateDataWithFields(String sourceTableName, String targetTableName,
+                             List<String> fields, String whereCondition);
+
+    /**
+     * 从源表迁移指定字段的数据到目标表(支持雪花ID生成)
+     * @param sourceTableName 源表名
+     * @param targetTableName 目标表名
+     * @param fields 需要迁移的字段列表
+     * @param whereCondition 查询条件(可为空)
+     * @param primaryKeyField 主键字段名
+     * @param generateSnowflakeId 是否生成雪花ID
+     * @param sourcePrimaryKeyField 源表主键字段名
+     * @param saveOriginalId 是否保存原始ID
+     * @return 迁移的记录数
+     */
+    int migrateDataWithFields(String sourceTableName, String targetTableName,
+                             List<String> fields, String whereCondition,
+                             String primaryKeyField, boolean generateSnowflakeId,
+                             String sourcePrimaryKeyField, boolean saveOriginalId);
+
+    /**
+     * 更新目标表数据
+     * @param targetTableName 目标表名
+     * @param dataMap 需要更新的数据
+     * @param whereCondition 更新条件
+     * @return 影响的记录数
+     */
+    int updateTargetData(String targetTableName, Map<String, Object> dataMap, String whereCondition);
+
+    /**
+     * 删除目标表数据
+     * @param targetTableName 目标表名
+     * @param whereCondition 删除条件
+     * @return 影响的记录数
+     */
+    int deleteTargetData(String targetTableName, String whereCondition);
+
+    /**
+     * 获取表结构信息
+     * @param tableName 表名
+     * @return 表结构信息
+     */
+    List<Map<String, Object>> getTableStructure(String tableName);
+
+    /**
+     * 检查表是否存在
+     * @param tableName 表名
+     * @return 存在返回true
+     */
+    boolean checkTableExists(String tableName);
+}

+ 463 - 0
platform/src/main/java/com/jzg/service/impl/MigrateDataServiceImpl.java

@@ -0,0 +1,463 @@
+package com.jzg.service.impl;
+
+import com.jzg.commons.util.idgen.IdGenerate;
+import com.jzg.mapper.MigrateDataMapper;
+import com.jzg.service.MigrateDataService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 数据迁移服务实现类
+ */
+@Service
+@Slf4j
+public class MigrateDataServiceImpl implements MigrateDataService {
+
+    @Autowired
+    private MigrateDataMapper migrateDataMapper;
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    /**
+     * 为数据生成雪花ID
+     * @param dataMap 数据Map
+     * @param primaryKeyField 主键字段名
+     */
+    private void generateSnowflakeId(Map<String, Object> dataMap, String primaryKeyField) {
+        if (primaryKeyField != null && !primaryKeyField.isEmpty()) {
+            String snowflakeId = IdGenerate.nextId();
+            dataMap.put(primaryKeyField, snowflakeId);
+            log.debug("为主键字段 {} 生成雪花ID: {}", primaryKeyField, snowflakeId);
+        }
+    }
+
+    /**
+     * 将数据列表中的值转换为合适的类型,避免JSON类型转换问题
+     * 保留布尔值和数字类型的原始值,只将其他类型转换为字符串
+     * @param dataList 数据列表
+     * @return 转换后的数据列表
+     */
+    private List<Map<String, Object>> convertValuesToString(List<Map<String, Object>> dataList) {
+        return dataList.stream()
+                .map(row -> {
+                    Map<String, Object> newRow = new HashMap<>();
+                    row.forEach((key, value) -> {
+                        if (value == null) {
+                            newRow.put(key, null);
+                        } else if (value instanceof Boolean || value instanceof Number) {
+                            // 保留布尔值和数字类型的原始值
+                            newRow.put(key, value);
+                        } else {
+                            // 其他类型转换为字符串
+                            newRow.put(key, value.toString());
+                        }
+                    });
+                    return newRow;
+                })
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int migrateData(String sourceTableName, String targetTableName, String whereCondition) {
+        return migrateData(sourceTableName, targetTableName, whereCondition, null, false, null, false);
+    }
+
+    /**
+     * 带雪花ID生成的数据迁移方法
+     */
+    public int migrateData(String sourceTableName, String targetTableName, String whereCondition,
+                           String primaryKeyField, boolean generateSnowflakeId,
+                           String sourcePrimaryKeyField, boolean saveOriginalId) {
+        log.info("开始迁移数据: 源表={}, 目标表={}, 条件={}, 主键字段={}, 生成雪花ID={}, 源主键字段={}, 保存原始ID={}",
+                sourceTableName, targetTableName, whereCondition, primaryKeyField, generateSnowflakeId,
+                sourcePrimaryKeyField, saveOriginalId);
+
+        // 检查源表和目标表是否存在
+        if (!migrateDataMapper.checkTableExists(sourceTableName)) {
+            throw new RuntimeException("源表 " + sourceTableName + " 不存在");
+        }
+        if (!migrateDataMapper.checkTableExists(targetTableName)) {
+            throw new RuntimeException("目标表 " + targetTableName + " 不存在");
+        }
+
+        // 如果需要保存原始ID,检查并添加original_id列
+        if (saveOriginalId && sourcePrimaryKeyField != null && !sourcePrimaryKeyField.isEmpty()) {
+            if (!migrateDataMapper.checkColumnExists(targetTableName, "original_id")) {
+                migrateDataMapper.addOriginalIdColumn(targetTableName);
+                log.info("已为目标表 {} 添加 original_id 列", targetTableName);
+            }
+        }
+
+        // 从源表查询数据
+        List<Map<String, Object>> sourceData = migrateDataMapper.selectFromSourceTable(sourceTableName, whereCondition);
+        if (sourceData == null || sourceData.isEmpty()) {
+            log.info("源表中没有符合条件的数据");
+            return 0;
+        }
+
+        // 获取源表结构,确保所有字段都被包含
+        List<Map<String, Object>> sourceTableStructure = migrateDataMapper.getTableStructure(sourceTableName);
+        Set<String> allColumns = sourceTableStructure.stream()
+                .map(col -> col.get("columnName").toString())
+                .collect(Collectors.toSet());
+
+        // 确保每条记录都包含所有字段(即使值为null)
+        for (Map<String, Object> row : sourceData) {
+            for (String column : allColumns) {
+                if (!row.containsKey(column)) {
+                    row.put(column, null);
+                }
+            }
+        }
+
+        // 将所有值转换为String类型,避免JSON类型转换问题
+        sourceData = convertValuesToString(sourceData);
+
+        // 如果需要保存原始ID,为每条记录添加original_id字段
+        if (saveOriginalId && sourcePrimaryKeyField != null && !sourcePrimaryKeyField.isEmpty()) {
+            sourceData.forEach(row -> {
+                Object originalId = row.get(sourcePrimaryKeyField);
+                if (originalId != null) {
+                    row.put("original_id", originalId.toString());
+                }
+            });
+        }
+
+        // 如果需要生成雪花ID,则为每条记录生成
+        if (generateSnowflakeId && primaryKeyField != null && !primaryKeyField.isEmpty()) {
+            sourceData.forEach(row -> generateSnowflakeId(row, primaryKeyField));
+        }
+
+        // 批量插入到目标表
+        int batchSize = 1000;
+        int totalMigrated = 0;
+
+        for (int i = 0; i < sourceData.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, sourceData.size());
+            List<Map<String, Object>> batch = sourceData.subList(i, end);
+
+            // 使用JdbcTemplate执行批量插入
+            if (!batch.isEmpty()) {
+                // 获取列名
+                Set<String> columns = batch.get(0).keySet();
+                String columnNames = String.join(", ", columns);
+
+                // 构建占位符
+                String placeholders = String.join(", ", Collections.nCopies(columns.size(), "?"));
+
+                // 构建SQL语句
+                String sql = "INSERT INTO " + targetTableName + " (" + columnNames + ") VALUES (" + placeholders + ")";
+
+                // 准备参数
+                List<Object[]> args = batch.stream()
+                    .map(row -> columns.stream()
+                        .map(col -> row.get(col))
+                        .toArray())
+                    .collect(Collectors.toList());
+
+                // 执行批量插入
+                int[] results = jdbcTemplate.batchUpdate(sql, args);
+                totalMigrated += Arrays.stream(results).sum();
+                log.info("已迁移 {}/{} 条记录", totalMigrated, sourceData.size());
+            }
+        }
+
+        log.info("数据迁移完成: 共迁移 {} 条记录", totalMigrated);
+        return totalMigrated;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int migrateDataWithMapping(String sourceTableName, String targetTableName,
+                                     Map<String, String> fieldMapping, String whereCondition) {
+        return migrateDataWithMapping(sourceTableName, targetTableName, fieldMapping, whereCondition, null, false, null, false);
+    }
+
+    /**
+     * 带雪花ID生成的字段映射迁移方法
+     */
+    public int migrateDataWithMapping(String sourceTableName, String targetTableName,
+                                      Map<String, String> fieldMapping, String whereCondition,
+                                      String primaryKeyField, boolean generateSnowflakeId,
+                                      String sourcePrimaryKeyField, boolean saveOriginalId) {
+        log.info("开始迁移数据(带字段映射): 源表={}, 目标表={}, 映射关系={}, 条件={}, 主键字段={}, 生成雪花ID={}, 源主键字段={}, 保存原始ID={}",
+                sourceTableName, targetTableName, fieldMapping, whereCondition, primaryKeyField, generateSnowflakeId,
+                sourcePrimaryKeyField, saveOriginalId);
+
+        // 检查源表和目标表是否存在
+        if (!migrateDataMapper.checkTableExists(sourceTableName)) {
+            throw new RuntimeException("源表 " + sourceTableName + " 不存在");
+        }
+        if (!migrateDataMapper.checkTableExists(targetTableName)) {
+            throw new RuntimeException("目标表 " + targetTableName + " 不存在");
+        }
+
+        // 如果需要保存原始ID,检查并添加original_id列
+        if (saveOriginalId && sourcePrimaryKeyField != null && !sourcePrimaryKeyField.isEmpty()) {
+            if (!migrateDataMapper.checkColumnExists(targetTableName, "original_id")) {
+                migrateDataMapper.addOriginalIdColumn(targetTableName);
+                log.info("已为目标表 {} 添加 original_id 列", targetTableName);
+            }
+        }
+
+        // 从源表查询数据
+        List<Map<String, Object>> sourceData = migrateDataMapper.selectFromSourceTable(sourceTableName, whereCondition);
+        if (sourceData == null || sourceData.isEmpty()) {
+            log.info("源表中没有符合条件的数据");
+            return 0;
+        }
+
+        // 获取源表结构,确保所有字段都被包含
+        List<Map<String, Object>> sourceTableStructure = migrateDataMapper.getTableStructure(sourceTableName);
+        Set<String> allColumns = sourceTableStructure.stream()
+                .map(col -> col.get("columnName").toString())
+                .collect(Collectors.toSet());
+
+        // 确保每条记录都包含所有字段(即使值为null)
+        for (Map<String, Object> row : sourceData) {
+            for (String column : allColumns) {
+                if (!row.containsKey(column)) {
+                    row.put(column, null);
+                }
+            }
+        }
+
+        // 应用字段映射
+        List<Map<String, Object>> targetData = sourceData.stream()
+                .map(sourceRow -> {
+                    Map<String, Object> targetRow = new HashMap<>();
+                    fieldMapping.forEach((sourceField, targetField) -> {
+                        // 直接获取值,包括null值
+                        targetRow.put(targetField, sourceRow.get(sourceField));
+                    });
+                    return targetRow;
+                })
+                .collect(Collectors.toList());
+
+        // 将所有值转换为String类型,避免JSON类型转换问题
+        targetData = convertValuesToString(targetData);
+
+        // 如果需要保存原始ID,为每条记录添加original_id字段
+        if (saveOriginalId && sourcePrimaryKeyField != null && !sourcePrimaryKeyField.isEmpty()) {
+            targetData.forEach(row -> {
+                Object originalId = row.get(sourcePrimaryKeyField);
+                if (originalId != null) {
+                    row.put("original_id", originalId.toString());
+                }
+            });
+        }
+
+        // 如果需要生成雪花ID,则为每条记录生成
+        if (generateSnowflakeId && primaryKeyField != null && !primaryKeyField.isEmpty()) {
+            targetData.forEach(row -> generateSnowflakeId(row, primaryKeyField));
+        }
+
+        // 批量插入到目标表
+        int batchSize = 1000;
+        int totalMigrated = 0;
+
+        for (int i = 0; i < targetData.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, targetData.size());
+            List<Map<String, Object>> batch = targetData.subList(i, end);
+
+            // 使用JdbcTemplate执行批量插入
+            if (!batch.isEmpty()) {
+                // 获取列名
+                Set<String> columns = batch.get(0).keySet();
+                String columnNames = String.join(", ", columns);
+
+                // 构建占位符
+                String placeholders = String.join(", ", Collections.nCopies(columns.size(), "?"));
+
+                // 构建SQL语句
+                String sql = "INSERT INTO " + targetTableName + " (" + columnNames + ") VALUES (" + placeholders + ")";
+
+                // 准备参数
+                List<Object[]> args = batch.stream()
+                    .map(row -> columns.stream()
+                        .map(col -> row.get(col))
+                        .toArray())
+                    .collect(Collectors.toList());
+
+                // 执行批量插入
+                int[] results = jdbcTemplate.batchUpdate(sql, args);
+                totalMigrated += Arrays.stream(results).sum();
+                log.info("已迁移 {}/{} 条记录", totalMigrated, targetData.size());
+            }
+        }
+
+        log.info("数据迁移完成: 共迁移 {} 条记录", totalMigrated);
+        return totalMigrated;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int migrateDataWithFields(String sourceTableName, String targetTableName,
+                                    List<String> fields, String whereCondition) {
+        return migrateDataWithFields(sourceTableName, targetTableName, fields, whereCondition, null, false, null, false);
+    }
+
+    /**
+     * 带雪花ID生成的指定字段迁移方法
+     */
+    public int migrateDataWithFields(String sourceTableName, String targetTableName,
+                                     List<String> fields, String whereCondition,
+                                     String primaryKeyField, boolean generateSnowflakeId,
+                                     String sourcePrimaryKeyField, boolean saveOriginalId) {
+        log.info("开始迁移数据(指定字段): 源表={}, 目标表={}, 字段={}, 条件={}, 主键字段={}, 生成雪花ID={}, 源主键字段={}, 保存原始ID={}",
+                sourceTableName, targetTableName, fields, whereCondition, primaryKeyField, generateSnowflakeId,
+                sourcePrimaryKeyField, saveOriginalId);
+
+        // 检查源表和目标表是否存在
+        if (!migrateDataMapper.checkTableExists(sourceTableName)) {
+            throw new RuntimeException("源表 " + sourceTableName + " 不存在");
+        }
+        if (!migrateDataMapper.checkTableExists(targetTableName)) {
+            throw new RuntimeException("目标表 " + targetTableName + " 不存在");
+        }
+
+        // 如果需要保存原始ID,检查并添加original_id列
+        if (saveOriginalId && sourcePrimaryKeyField != null && !sourcePrimaryKeyField.isEmpty()) {
+            if (!migrateDataMapper.checkColumnExists(targetTableName, "original_id")) {
+                migrateDataMapper.addOriginalIdColumn(targetTableName);
+                log.info("已为目标表 {} 添加 original_id 列", targetTableName);
+            }
+        }
+
+        // 从源表查询数据
+        List<Map<String, Object>> sourceData = migrateDataMapper.selectFromSourceTable(sourceTableName, whereCondition);
+        if (sourceData == null || sourceData.isEmpty()) {
+            log.info("源表中没有符合条件的数据");
+            return 0;
+        }
+
+        // 获取源表结构,确保所有字段都被包含
+        List<Map<String, Object>> sourceTableStructure = migrateDataMapper.getTableStructure(sourceTableName);
+        Set<String> allColumns = sourceTableStructure.stream()
+                .map(col -> col.get("columnName").toString())
+                .collect(Collectors.toSet());
+
+        // 确保每条记录都包含所有字段(即使值为null)
+        for (Map<String, Object> row : sourceData) {
+            for (String column : allColumns) {
+                if (!row.containsKey(column)) {
+                    row.put(column, null);
+                }
+            }
+        }
+
+        // 过滤字段
+        List<Map<String, Object>> targetData = sourceData.stream()
+                .map(sourceRow -> {
+                    Map<String, Object> targetRow = new HashMap<>();
+                    fields.forEach(field -> {
+                        // 直接获取值,包括null值
+                        targetRow.put(field, sourceRow.get(field));
+                    });
+                    return targetRow;
+                })
+                .collect(Collectors.toList());
+
+        // 将所有值转换为String类型,避免JSON类型转换问题
+        targetData = convertValuesToString(targetData);
+
+        // 如果需要保存原始ID,为每条记录添加original_id字段
+        if (saveOriginalId && sourcePrimaryKeyField != null && !sourcePrimaryKeyField.isEmpty()) {
+            targetData.forEach(row -> {
+                Object originalId = row.get(sourcePrimaryKeyField);
+                if (originalId != null) {
+                    row.put("original_id", originalId.toString());
+                }
+            });
+        }
+
+        // 如果需要生成雪花ID,则为每条记录生成
+        if (generateSnowflakeId && primaryKeyField != null && !primaryKeyField.isEmpty()) {
+            targetData.forEach(row -> generateSnowflakeId(row, primaryKeyField));
+        }
+
+        // 批量插入到目标表
+        int batchSize = 1000;
+        int totalMigrated = 0;
+
+        for (int i = 0; i < targetData.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, targetData.size());
+            List<Map<String, Object>> batch = targetData.subList(i, end);
+
+            // 使用JdbcTemplate执行批量插入
+            if (!batch.isEmpty()) {
+                // 获取列名
+                Set<String> columns = batch.get(0).keySet();
+                String columnNames = String.join(", ", columns);
+
+                // 构建占位符
+                String placeholders = String.join(", ", Collections.nCopies(columns.size(), "?"));
+
+                // 构建SQL语句
+                String sql = "INSERT INTO " + targetTableName + " (" + columnNames + ") VALUES (" + placeholders + ")";
+
+                // 准备参数
+                List<Object[]> args = batch.stream()
+                    .map(row -> columns.stream()
+                        .map(col -> row.get(col))
+                        .toArray())
+                    .collect(Collectors.toList());
+
+                // 执行批量插入
+                int[] results = jdbcTemplate.batchUpdate(sql, args);
+                totalMigrated += Arrays.stream(results).sum();
+                log.info("已迁移 {}/{} 条记录", totalMigrated, targetData.size());
+            }
+        }
+
+        log.info("数据迁移完成: 共迁移 {} 条记录", totalMigrated);
+        return totalMigrated;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int updateTargetData(String targetTableName, Map<String, Object> dataMap, String whereCondition) {
+        log.info("更新目标表数据: 表={}, 数据={}, 条件={}", targetTableName, dataMap, whereCondition);
+
+        if (!migrateDataMapper.checkTableExists(targetTableName)) {
+            throw new RuntimeException("目标表 " + targetTableName + " 不存在");
+        }
+
+        return migrateDataMapper.updateTargetTable(targetTableName, dataMap, whereCondition);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int deleteTargetData(String targetTableName, String whereCondition) {
+        log.info("删除目标表数据: 表={}, 条件={}", targetTableName, whereCondition);
+
+        if (!migrateDataMapper.checkTableExists(targetTableName)) {
+            throw new RuntimeException("目标表 " + targetTableName + " 不存在");
+        }
+
+        return migrateDataMapper.deleteFromTargetTable(targetTableName, whereCondition);
+    }
+
+    @Override
+    public List<Map<String, Object>> getTableStructure(String tableName) {
+        log.info("获取表结构: 表={}", tableName);
+
+        if (!migrateDataMapper.checkTableExists(tableName)) {
+            throw new RuntimeException("表 " + tableName + " 不存在");
+        }
+
+        return migrateDataMapper.getTableStructure(tableName);
+    }
+
+    @Override
+    public boolean checkTableExists(String tableName) {
+        return migrateDataMapper.checkTableExists(tableName);
+    }
+}

+ 102 - 0
platform/src/main/resources/mapper/MigrateDataMapper.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jzg.mapper.MigrateDataMapper">
+
+    <!-- 从源表查询数据 -->
+    <select id="selectFromSourceTable" resultType="java.util.HashMap">
+        SELECT * FROM ${sourceTableName}
+        <if test="whereCondition != null and whereCondition != ''">
+            WHERE ${whereCondition}
+        </if>
+    </select>
+
+    <!-- 插入数据到目标表 -->
+    <insert id="insertToTargetTable">
+        INSERT INTO ${targetTableName}
+        <foreach collection="dataMap.keys" item="key" open="(" separator="," close=")">
+            ${key}
+        </foreach>
+        VALUES
+        <foreach collection="dataMap.keys" item="key" open="(" separator="," close=")">
+            #{dataMap[${key}]}
+        </foreach>
+    </insert>
+
+    <!-- 批量插入数据到目标表 -->
+    <insert id="batchInsertToTargetTable">
+        INSERT INTO ${targetTableName}
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <foreach collection="dataList[0].keys" item="key">
+                ${key},
+            </foreach>
+        </trim>
+        VALUES
+        <foreach collection="dataList" item="item" separator=",">
+            <trim prefix="(" suffix=")" suffixOverrides=",">
+                <foreach collection="item.keys" item="key">
+                    #{item[${key}]},
+                </foreach>
+            </trim>
+        </foreach>
+    </insert>
+
+    <!-- 更新目标表数据 -->
+    <update id="updateTargetTable">
+        UPDATE ${targetTableName}
+        <set>
+            <foreach collection="dataMap.keys" item="key" separator=",">
+                ${key} = #{dataMap[${key}]}
+            </foreach>
+        </set>
+        <if test="whereCondition != null and whereCondition != ''">
+            WHERE ${whereCondition}
+        </if>
+    </update>
+
+    <!-- 删除目标表数据 -->
+    <delete id="deleteFromTargetTable">
+        DELETE FROM ${targetTableName}
+        <if test="whereCondition != null and whereCondition != ''">
+            WHERE ${whereCondition}
+        </if>
+    </delete>
+
+    <!-- 检查表是否存在 -->
+    <select id="checkTableExists" resultType="boolean">
+        SELECT COUNT(*) > 0
+        FROM information_schema.tables
+        WHERE table_schema = DATABASE()
+        AND table_name = #{tableName}
+    </select>
+
+    <!-- 获取表结构信息 -->
+    <select id="getTableStructure" resultType="java.util.HashMap">
+        SELECT 
+            COLUMN_NAME as columnName,
+            DATA_TYPE as dataType,
+            IS_NULLABLE as isNullable,
+            COLUMN_KEY as columnKey,
+            COLUMN_DEFAULT as columnDefault,
+            COLUMN_COMMENT as columnComment
+        FROM information_schema.columns
+        WHERE table_schema = DATABASE()
+        AND table_name = #{tableName}
+        ORDER BY ORDINAL_POSITION
+    </select>
+
+    <!-- 检查表中是否存在指定列 -->
+    <select id="checkColumnExists" resultType="boolean">
+        SELECT COUNT(*) > 0
+        FROM information_schema.columns
+        WHERE table_schema = DATABASE()
+        AND table_name = #{tableName}
+        AND column_name = #{columnName}
+    </select>
+
+    <!-- 为表添加original_id列 -->
+    <update id="addOriginalIdColumn">
+        ALTER TABLE ${tableName}
+        ADD COLUMN original_id VARCHAR(64) COMMENT '原始数据ID'
+    </update>
+
+</mapper>

+ 26 - 2
tenant/insurance/quotation-chengtai/src/main/java/com/jzg/quotation/chengtai/api/build/ChengTaiAPIQuoteResultsVoBuild.java

@@ -1,6 +1,7 @@
 package com.jzg.quotation.chengtai.api.build;
 
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.jzg.commons.constants.dict.InsOrderStatusEnum;
 import com.jzg.commons.entity.quote.vo.KindInfoVo;
 import com.jzg.commons.entity.quote.vo.RiskInfoVo;
@@ -96,7 +97,12 @@ public final class ChengTaiAPIQuoteResultsVoBuild {
     private static QuoteResultsVo.ScoreVo buildScoreVo(PremiumCalculationResponse premiumCalculationResponse) {
         QuoteResultsVo.ScoreVo scoreVo = new QuoteResultsVo.ScoreVo();
         if (null != premiumCalculationResponse.getCompRisk() && null != premiumCalculationResponse.getCompRisk().getBasePart()){
-            scoreVo.setJqRenewal(premiumCalculationResponse.getCompRisk().getBasePart().getRenewalFlag());
+            String jqRenewal = premiumCalculationResponse.getCompRisk().getBasePart().getRenewalFlag();
+            switch (jqRenewal) {
+                case "-1" -> scoreVo.setJqRenewal("3");
+                case "0" -> scoreVo.setJqRenewal("1");
+                case "2" -> scoreVo.setJqRenewal("2");
+            }
             scoreVo.setCiClaims(premiumCalculationResponse.getCompRisk().getBasePart().getClaimsNum());
             if (null != premiumCalculationResponse.getCompRisk().getBasePart().getDiscount()) {
                 scoreVo.setJqAdjustRate(premiumCalculationResponse.getCompRisk().getBasePart().getDiscount().toString());
@@ -104,14 +110,32 @@ public final class ChengTaiAPIQuoteResultsVoBuild {
             }
         }
         if (null != premiumCalculationResponse.getCommRisk() && null != premiumCalculationResponse.getCommRisk().getBasePart()) {
+            String syRenewal = premiumCalculationResponse.getCommRisk().getBasePart().getRenewalFlag();
+            switch (syRenewal) {
+                case "-1" -> scoreVo.setSyRenewal("3");
+                case "0" -> scoreVo.setSyRenewal("1");
+                case "2" -> scoreVo.setSyRenewal("2");
+            }
             scoreVo.setBiClaims(premiumCalculationResponse.getCommRisk().getBasePart().getClaimsNum());
-            scoreVo.setSyRenewal(premiumCalculationResponse.getCommRisk().getBasePart().getRenewalFlag());
             if (null != premiumCalculationResponse.getCommRisk().getBasePart().getDiscount()){
                 scoreVo.setSyAdjustRate(premiumCalculationResponse.getCommRisk().getBasePart().getDiscount().toString());
                 scoreVo.setBiClaims(premiumCalculationResponse.getCommRisk().getBasePart().getClaimsNum());
             }
         }
         scoreVo.setLossRation(premiumCalculationResponse.getYztScore().getCentile().toString());
+        JSONArray scoreJson = new JSONArray();
+        if (premiumCalculationResponse.getOcftInfo().getCoefficientList().size() > 0) {
+            BigDecimal coefficientVal = premiumCalculationResponse.getOcftInfo().getCoefficientList().stream()
+                    .filter(item -> "BI".equals(item.getDataType()))
+                    .map(PremiumCalculationResponse.OcftInfo.Coefficient::getCoefficient)
+                    .findFirst()
+                    .orElse(null);
+            scoreJson.add(new JSONObject()
+                    .fluentPut("scoreName", "自主定价系数")
+                    .fluentPut("scoreKey","coefficient")
+                    .fluentPut("score", coefficientVal));
+        }
+        scoreVo.setScoreJson(scoreJson);
         return scoreVo;
     }
 

+ 18 - 0
tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/constant/enums/quote/RenewalEnum.java

@@ -0,0 +1,18 @@
+package com.jzg.quotation.commons.constant.enums.quote;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum RenewalEnum {
+
+    New(1,"新保"),
+    Renewal(2,"续保"),
+    Transfer(3,"转保");
+
+    private final Integer code;
+    private final String desc;
+
+}

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

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

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

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

+ 66 - 0
tenant/insurance/quotation-dajia/src/main/java/com/jzg/quotation/dajia/crawler/utils/ByteArrayMultipartFile.java

@@ -0,0 +1,66 @@
+package com.jzg.quotation.dajia.crawler.utils;
+
+import org.springframework.web.multipart.MultipartFile;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ByteArrayMultipartFile implements MultipartFile {
+
+    public static  String PDF_CONTENT_TYPE = "application/pdf";
+
+    public static  String FILE = "file";
+
+    private final byte[] content;
+    private final String name;
+    private final String originalFilename;
+    private final String contentType;
+
+    public ByteArrayMultipartFile(String name, String originalFilename, String contentType, byte[] content) {
+        this.name = name;
+        this.originalFilename = originalFilename;
+        this.contentType = contentType;
+        this.content = content;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getOriginalFilename() {
+        return originalFilename;
+    }
+
+    @Override
+    public String getContentType() {
+        return contentType;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return content == null || content.length == 0;
+    }
+
+    @Override
+    public long getSize() {
+        return content.length;
+    }
+
+    @Override
+    public byte[] getBytes() throws IOException {
+        return content;
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        return new ByteArrayInputStream(content);
+    }
+
+    @Override
+    public void transferTo(File dest) throws IOException, IllegalStateException {
+        java.nio.file.Files.write(dest.toPath(), content);
+    }
+}

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

@@ -1,6 +1,7 @@
 package com.jzg.quotation.hengbang.crawler.component;
 
 import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fasterxml.jackson.annotation.JsonInclude;
@@ -309,7 +310,8 @@ public class HengBangCrawlerRequestComponent {
             log.error("恒邦calculation接口调用失败[{}]: {}", e.getStatusCode(), e.getResponseBodyAsString());
             final JSONObject jsonObject = JSONObject.parseObject(e.getResponseBodyAsString());
             if (jsonObject.getInteger("status") == 500) {
-                return jsonObject.getString("detail");
+                String detail = jsonObject.getString("detail");
+                return StrUtil.isNotBlank(detail) ? detail : e.getResponseBodyAsString();
             } else if (jsonObject.getInteger("status") == 400) {
                 return jsonObject.getString("title");
             } else {

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

@@ -152,16 +152,24 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
         // 非车信息匹配核心
         matchingAccident(quoteVo, headers, plyappno, calculationRequest);
         String resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-        String errorMsg = JSONObject.parseObject(resJson).getString("message");
-        String status = JSONObject.parseObject(resJson).getString("status");
+        JSONObject resJsonObj = JSONObject.parseObject(resJson);
+        if (resJsonObj == null) {
+            return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+        }
+        String errorMsg = resJsonObj.getString("message");
+        String status = resJsonObj.getString("status");
 
         HbCrawlerProposalResponse proposalResponse = null;
         // 脱保情况 重新赋值核心建议起保时间 二次报价
         if (errorMsg.contains("脱保")) {
             calculationRequest.setOutofwarrantytype("N");
             resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-            errorMsg = JSONObject.parseObject(resJson).getString("message");
-            status = JSONObject.parseObject(resJson).getString("status");
+            resJsonObj = JSONObject.parseObject(resJson);
+            if (resJsonObj == null) {
+                return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+            }
+            errorMsg = resJsonObj.getString("message");
+            status = resJsonObj.getString("status");
         }
 
         if (errorMsg.contains("重复投保")) {
@@ -172,19 +180,26 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
             }
 
             resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-            errorMsg = JSONObject.parseObject(resJson).getString("message");
-            status = JSONObject.parseObject(resJson).getString("status");
+            resJsonObj = JSONObject.parseObject(resJson);
+            if (resJsonObj == null) {
+                return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+            }
+            errorMsg = resJsonObj.getString("message");
+            status = resJsonObj.getString("status");
         }
 
         if (errorMsg.contains("该车是转保车,请输入对应的转保验证码")) {
-            JSONObject resJsonObj = JSONObject.parseObject(resJson);
             String responseBody = resJsonObj.getString("responseBody");
             HbCrawlerRecordResponse recordResponse = JSONObject.parseObject(responseBody, HbCrawlerRecordResponse.class);
             calculationRequest.setTransferVehicle(recordResponse);
 
             resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-            errorMsg = JSONObject.parseObject(resJson).getString("message");
-            status = JSONObject.parseObject(resJson).getString("status");
+            resJsonObj = JSONObject.parseObject(resJson);
+            if (resJsonObj == null) {
+                return HbCrawlerQuoteResultVoBuild.build(quoteVo, null, "恒邦calculation接口返回异常");
+            }
+            errorMsg = resJsonObj.getString("message");
+            status = resJsonObj.getString("status");
         }
 
         if (errorMsg.equals("成功")) {

+ 26 - 0
tenant/insurance/quotation-huanong/src/main/java/com/jzg/quotation/huanong/crawler/build/HuaNongCrawlerQuoteResultsVoBuild.java

@@ -10,6 +10,7 @@ import com.jzg.commons.entity.quote.vo.aggregated.QuoteResultsVo;
 import com.jzg.quotation.commons.component.ConversionOfQuoteInformation;
 import com.jzg.quotation.huanong.crawler.entity.response.HuaNongCrawlerQuotedPriceResponse;
 import com.jzg.quotation.huanong.crawler.entity.response.HuaNongCrawlerSaveResponse;
+import com.jzg.quotation.commons.constant.enums.quote.RenewalEnum;
 import com.jzg.quotation.huanong.enums.dictionary.Risk;
 import org.springframework.util.ObjectUtils;
 
@@ -60,12 +61,16 @@ public final class HuaNongCrawlerQuoteResultsVoBuild {
                 quoteResultsVo.getCosts().setCiPremium(String.valueOf(contractDTO.getContractMain().getTotalPremium()));
                 quoteResultsVo.getCosts().setVvTax(String.valueOf(contractDTO.getTax().getSumTax()));
                 quoteResultsVo.setCiPolicyNumber(contractDTO1.getContractMain().getContractNo());
+                // 交强转续保标识(按RenewalEnum映射)
+                quoteResultsVo.getScoreVo().setJqRenewal(convertRenewalFlag(contractDTO.getContractMain().getRenewalFlag()));
             } else {
                 // 商业险
                 buildKindInfo(quoteVo.getKindInfoVo(), contractDTO.getCoverage());
                 buildRiskInfoVo(quoteVo.getBiRiskInfoVo(), contractDTO.getContractMain(), contractDTO.getPlatFormMessage(), false);
                 quoteResultsVo.getCosts().setBiPremium(String.valueOf(contractDTO.getContractMain().getTotalPremium()));
                 quoteResultsVo.setBiPolicyNumber(contractDTO1.getContractMain().getContractNo());
+                // 商业转续保标识(按RenewalEnum映射)
+                quoteResultsVo.getScoreVo().setSyRenewal(convertRenewalFlag(contractDTO.getContractMain().getRenewalFlag()));
             }
         });
         //设置评分
@@ -144,4 +149,25 @@ public final class HuaNongCrawlerQuoteResultsVoBuild {
         );
     }
 
+    /**
+     * 将华农接口返回的renewalFlag值按RenewalEnum枚举映射为code字符串
+     * @param renewalFlag 接口原始值
+     * @return RenewalEnum对应的code字符串,匹配失败返回null
+     */
+    private static String convertRenewalFlag(String renewalFlag) {
+        if (!org.springframework.util.StringUtils.hasText(renewalFlag)) {
+            return null;
+        }
+        //    New(1,"新保"),
+        //    Renewal(2,"续保"),
+        //    Transfer(3,"转保");
+        if(renewalFlag.equals("0")){
+            // 0 为转保
+            return "3";
+        }else{
+            return "2";
+        }
+
+    }
+
 }

+ 4 - 0
tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/build/HuaTaiCrawlerQuoteResultsVoBuild.java

@@ -51,6 +51,10 @@ public class HuaTaiCrawlerQuoteResultsVoBuild {
                 .fluentPut("score",quoteResponseData.getApplicationdto().getBicategory()));
         quoteResultsVo.getScoreVo().setScoreJson(scoreJson);
 
+        //续保标志0否1是 (跟python 一致)
+        quoteResultsVo.getScoreVo().setJqRenewal("转入".equals(quoteResponseData.getApplicationdto().getCustombusinesstype_ci()) ? "0" : "1");
+        quoteResultsVo.getScoreVo().setSyRenewal("转入".equals(quoteResponseData.getApplicationdto().getCustombusinesstype_bi()) ? "0" : "1");
+
         quoteResultsVo.setQuoteStatusEnum(InsOrderStatusEnum.WAIT_AUDIT);
         return quoteResultsVo;
     }

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

@@ -224,6 +224,9 @@ public class HtCrawlerQuoteRequest implements Serializable {
                     covListDTO.setCoveragecode("1251");
                     covListDTO.setComcoveragecode(kindInfoVo.getKindCode());
                     covListDTO.setComcoveragename(kindInfoVo.getKindName());
+                    covListDTO.setStartdate(biRiskInfoVo.getStartDate());
+                    covListDTO.setEnddate(biRiskInfoVo.getEndDate());
+                    covListDTO.setBilldate(biRiskInfoVo.getStartDate());
                     // A机动车损失保险 B机动车第三者责任保险 D11机动车车上人员责任险(司机) D12机动车车上人员责任险(乘客)
                     if(kindInfoVo.getKindCode().equals("A")) {
                         covListDTO.setLimitamount(limitAmount.contains(",") ? limitAmount.split(",")[0] : limitAmount);
@@ -246,13 +249,10 @@ public class HtCrawlerQuoteRequest implements Serializable {
                     } else {
                         covListDTO.setCoverageclasscode("1");
                     }
-                    covListDTO.setStartdate(ciRiskInfoVo.getStartDate());
-                    covListDTO.setEnddate(ciRiskInfoVo.getEndDate());
                     covListDTO.setIsdedu("1");
                     covListDTO.setKindpremiun("");
                     covListDTO.setServiceTimes(kindInfoVo.getKindCode().equals("SR") ? kindInfoVo.getServiceTimes() : "");
                     covListDTO.setServiceCode("");
-                    covListDTO.setBilldate(ciRiskInfoVo.getStartDate());
 
                     covListDTOS.add(covListDTO);
                 });

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

@@ -508,9 +508,10 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
 
                         // 重新执行报价预处理流程
                         AttributionInformationVo attr = q.getOrder().getAttributionInformationVo();
+                        log.info(map.get("unMatchVehicleFrameMessage")+"已重新执行prepareQuote,新modelcodexh:{}", currentModelCodexh);
                         currentPre = prepareQuote(attr, gsInfo, q.getCarInfoVo(),
                                 currentModelCodexh, cookie, quotationNo);
-                        log.info("已重新执行prepareQuote,新modelcodexh:{}", currentModelCodexh);
+                        executeQuoteSteps(q, quotationNo, dto, cookie, currentPre, gsInfo);
 
                         // 跳过当前循环,重新执行报价流程
                         continue;
@@ -620,10 +621,8 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         q.getCiRiskInfoVo().setStartDate(start);
         try {
             LocalDateTime dt = LocalDateTime.parse(start.replace(" ", "T"), DateTimeFormatter.ofPattern(DATE_FMT_T));
-            String newStart = dt.format(DateTimeFormatter.ofPattern(DATE_FMT));
-            String newEnd = dt.plusYears(1).minusDays(1).format(DateTimeFormatter.ofPattern(DATE_FMT));
-            q.getCiRiskInfoVo().setStartDate(newStart.substring(0, 10) + " 00:00:00");
-            q.getCiRiskInfoVo().setEndDate(newEnd.substring(0, 10) + " 23:59:59");
+            String newEnd = dt.plusYears(1).format(DateTimeFormatter.ofPattern(DATE_FMT));
+            q.getCiRiskInfoVo().setEndDate(newEnd);
             log.info("重复投保,重新设置 起保: {}, 终保: {}", q.getCiRiskInfoVo().getStartDate(), q.getCiRiskInfoVo().getEndDate());
             return q;
         } catch (Exception e) { log.error("日期解析失败: {}", e.getMessage()); }
@@ -641,6 +640,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
             log.error("报价失败: {}", convertResponseMsg(decryptData));
             throw new SystemException(ERR_PREFIX + convertResponseMsg(decryptData));
         }
+        //车损险保额过高  limitAmount
         if (decryptData.contains("车损险保额不能高于")) {
             Pattern pattern = Pattern.compile("车损险保额不能高于(\\d+\\.?\\d*)元");
             Matcher matcher = pattern.matcher(decryptData);
@@ -656,32 +656,62 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
             decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount+","+transferFlag, "", personalInsuredJSON), h);
             return resolveQuoteResponse(duplQuoteResult, decryptData, mealid, limitAmount, h, personalInsuredJSON);
         }
+        if (decryptData.contains("TRANS_MESSAGE") && decryptData.contains("终保日期")) {
+            Map<String, String> map = JSONObject.parseObject(decryptData, Map.class);
+            if (map.containsKey("TRANS_MESSAGE") && map.get("TRANS_MESSAGE").contains("交强")) {
+                Matcher m = PAT_END_DATE.matcher(decryptData);
+                if (!m.find()) {
+                    String jiaoqiang_startDate = m.group(1);
+                    duplQuoteResult.q().getCiRiskInfoVo().setStartDate(jiaoqiang_startDate + ":00");
+                    LocalDateTime dt = LocalDateTime.parse(jiaoqiang_startDate.replace(" ", "T"), DateTimeFormatter.ofPattern(DATE_FMT_T));
+                    String newEnd = dt.plusYears(1).format(DateTimeFormatter.ofPattern(DATE_FMT));
+                    duplQuoteResult.q().getCiRiskInfoVo().setEndDate(newEnd);
+                    decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, "", personalInsuredJSON), h);
+                    return resolveQuoteResponse(duplQuoteResult, decryptData, mealid, limitAmount, h, personalInsuredJSON);
+                }
+            }
+            if (map.containsKey("TRANS_MESSAGE") && map.get("TRANS_MESSAGE").contains("商业")) {
+                Matcher m = PAT_END_DATE.matcher(decryptData);
+                if (!m.find()) {
+                    String chesun_startDate = m.group(1);
+                    duplQuoteResult.q().getBiRiskInfoVo().setStartDate(chesun_startDate);
+                    LocalDateTime dt = LocalDateTime.parse(chesun_startDate.replace(" ", "T"), DateTimeFormatter.ofPattern(DATE_FMT_T));
+                    String newEnd = dt.plusYears(1).format(DateTimeFormatter.ofPattern(DATE_FMT));
+                    duplQuoteResult.q().getBiRiskInfoVo().setEndDate(newEnd);
+                    decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, "", personalInsuredJSON), h);
+                    return resolveQuoteResponse(duplQuoteResult, decryptData, mealid, limitAmount, h, personalInsuredJSON);
+                }
+            }
+        }
+        if (decryptData.contains("errrorFlag") && decryptData.contains("carChange")) {
+            throw new SystemException(decryptData);
+        }
         HtCrawlerQuoteResponse.DataDTO data = null;
         data = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataDTO.class);
 
         if (data.getApplicationdto() != null && !decryptData.contains("车损险保额不能高于")) return data;
 
-        List<HtCrawlerQuoteResponse.DataDTO.BasePartDTOListDTO.BasePartDTO> list =
+        List<HtCrawlerQuoteResponse.DataDTO.BasePartDTOListDTO.BasePartDTO> basePartDTO =
                 data.getBasepartdtolist() != null ? data.getBasepartdtolist().getBasepartdto() : null;
-        if ((list == null || list.isEmpty()) && !decryptData.contains("车损险保额不能高于")) throw new SystemException(ERR_PREFIX + convertResponseMsg(decryptData));
+        if ((basePartDTO == null || basePartDTO.isEmpty()) && !decryptData.contains("车损险保额不能高于")) throw new SystemException(ERR_PREFIX + convertResponseMsg(decryptData));
 
         for (int i = 0; i < MAX_RETRIES; i++) {
-            String basePartJSON = buildBasePartJSON(list);
-            String response = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, basePartJSON, personalInsuredJSON), h);
-            log.info("resolveQuoteResponse attempt {}/{}: {}", i + 1, MAX_RETRIES, response);
+            String basePartJSON = buildBasePartJSON(basePartDTO);
+            decryptData = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, basePartJSON, personalInsuredJSON), h);
+            log.info("resolveQuoteResponse attempt {}/{}: {}", i + 1, MAX_RETRIES, decryptData);
 
             HtCrawlerQuoteResponse.DataDTO result = null;
             try {
-                result = JSONObject.parseObject(response, HtCrawlerQuoteResponse.DataDTO.class);
+                result = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataDTO.class);
             } catch (Exception e) {
-                throw new RuntimeException(response);
+                throw new RuntimeException(decryptData);
             }
             if (result != null && result.getApplicationdto() != null) {
                 return result;
             }
 
             // autocheckcode 可能不准确,重新获取 quote 刷新验证码
-            if (response.contains("录入的校验码有误") && i < MAX_RETRIES - 1) {
+            if (decryptData.contains("录入的校验码有误") && i < MAX_RETRIES - 1) {
                 log.warn("验证码 校验失败,重新获取 刷新验证码 (尝试 {}/{})", i + 1, MAX_RETRIES);
                 // 必须传空 basePart,接口才会返回新的 basePart 列表(含新 autocheckcode)
                 String fresh = req.quote(new HtCrawlerQuoteRequest(duplQuoteResult.q(), mealid, limitAmount, "", personalInsuredJSON), h);
@@ -690,9 +720,9 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                     return result;
                 }
                 // 用新的 basePart 列表更新 list,下一轮循环会自动重新 buildBasePartJSON
-                list = result != null && result.getBasepartdtolist() != null
+                basePartDTO = result != null && result.getBasepartdtolist() != null
                         ? result.getBasepartdtolist().getBasepartdto() : null;
-                if (list == null || list.isEmpty()) {
+                if (basePartDTO == null || basePartDTO.isEmpty()) {
                     return result != null ? result : data;
                 }
                 continue;

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

@@ -1107,4 +1107,26 @@ public class RenBaoNewCrawlerRequestComponent {
 
         return JSON.parseObject(responseEntity.getBody(), new TypeReference<Map<String, Object>>() {});
     }
+
+    /**
+     * 获取推荐送修码列表(对应 Python getMonopoly_list)
+     *
+     * <p>查询指定归属部门下的所有可用送修码,用于校验配置中的 monopolyCode 是否有效</p>
+     *
+     * @param gscomCode 归属部门代码
+     * @param headers   请求头(含 Authorization)
+     * @return 送修码列表响应
+     */
+    public Map<String, Object> getMonopoly(String gscomCode, HttpHeaders headers) {
+        String url = buildUrl(RequestUrl.GET_MONOPOLY.getUrl());
+
+        Map<String, Object> requestData = new HashMap<>();
+        requestData.put("pageNo", 1);
+        requestData.put("pageSize", 1000);
+        requestData.put("storeCodeOrName", "");
+        requestData.put("comCode", gscomCode);
+
+        ResponseEntity<String> responseEntity = requestComponent.stringPost(url, requestData, String.class, headers);
+        return JSON.parseObject(responseEntity.getBody(), new TypeReference<Map<String, Object>>() {});
+    }
 }

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

@@ -150,7 +150,12 @@ public enum RequestUrl {
     /**
      * 滑块验证二次校验接口
      */
-    SPILDER_VERIFY("/front/019K0000003/spilder/verify");
+    SPILDER_VERIFY("/front/019K0000003/spilder/verify"),
+
+    /**
+     * 获取推荐送修码列表接口(对应 Python getMonopoly_list)
+     */
+    GET_MONOPOLY("/front/019K0000003/zsjzx/getMonopoly");
 
     /**
      * 接口URL路径

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

@@ -411,14 +411,47 @@ public class RenBaoNewCrawlerRequestBuilder {
         // 构建 prpCmainCommon 和 prpCmainChannel(需要 handler1CodeUni 和 queryArea)
         String queryArea = calculateQueryArea(loginInfo.getComCode());
 
+        // =====================================================================
+        // 送修码处理(与 Python monopolyCode 逻辑一致)
+        // =====================================================================
+        String monopolyCode = "";
+        String monopolyName = "";
+        String configMonopolyCode = loginInfo.getParameters() != null ? loginInfo.getParameters().getMonopolyCode() : null;
+        if (configMonopolyCode != null && !configMonopolyCode.isEmpty()) {
+            try {
+                Map<String, Object> monopolyResult = requestComponent.getMonopoly(gscomCode, headers);
+                if (isResponseSuccess(monopolyResult)) {
+                    @SuppressWarnings("unchecked")
+                    List<Map<String, Object>> monopolyList = (List<Map<String, Object>>) getNestedValue(monopolyResult, "data", "data");
+                    if (monopolyList != null) {
+                        for (Map<String, Object> item : monopolyList) {
+                            if (configMonopolyCode.equals(getStringValue(item, "storeCode"))) {
+                                monopolyCode = configMonopolyCode;
+                                monopolyName = getStringValue(item, "storeName");
+                                log.info("匹配到送修码: code={}, name={}", monopolyCode, monopolyName);
+                                break;
+                            }
+                        }
+                    }
+                }
+                if (monopolyCode.isEmpty()) {
+                    log.warn("送修码 [{}] 未在 PICC 系统中匹配到,将使用空值", configMonopolyCode);
+                }
+            } catch (Exception e) {
+                log.error("送修码验证失败,将使用空值。error=[{}]", e.getMessage(), e);
+            }
+        }
+
         // 构建核心业务数据(更新为包含完整参数)
         request.setPrpCmain(buildPrpCmain(loginInfo, startDate, endDate, businessNature,
                 agentCode, agentName, handler1Code, handler1CodeDes, handlerCode, handlerCodeDes,
                 carChecker, carCheckerTranslate, gscomCode, comCodeDes, jwtComCode,
-                handlerCodeUni, handler1CodeUni, useYear, cqpCount, linkerName));
+                handlerCodeUni, handler1CodeUni, useYear, cqpCount, linkerName,
+                monopolyCode, monopolyName));
         request.setPrpCitemCar(buildPrpCitemCar(carInfo, actualValueStr, vehicleModelCode,
                 vehicleId, vehicleModelCode, useNatureCode, vehicleClassPicc, fuelType, clauseType, vehicleFgwCode, purchasePrice,
-                gscomCode, loginInfo.getUserCode(), headers, username, queryArea, isEnergyCar, energyTypePlat));
+                gscomCode, loginInfo.getUserCode(), headers, username, queryArea, isEnergyCar, energyTypePlat,
+                monopolyCode));
         request.setPrpCinsureds(buildPrpCinsureds(quoteVo,customerResponse));
 
         // 价格系数默认使用1.0(与Python保持一致)
@@ -1174,7 +1207,8 @@ public class RenBaoNewCrawlerRequestBuilder {
                                              String carChecker, String carCheckerTranslate,
                                              String gscomCode, String comCodeDes, String jwtComCode,
                                              String handlerCodeUni, String handler1CodeUni,
-                                             String useYear, String cqpCount, String linkerName) {
+                                             String useYear, String cqpCount, String linkerName,
+                                             String monopolyCode, String monopolyName) {
         Map<String, Object> prpCmain = new HashMap<>();
 
         // ========== 基础字段 ==========
@@ -1187,8 +1221,8 @@ public class RenBaoNewCrawlerRequestBuilder {
         prpCmain.put("clauseFlag", "1");
         prpCmain.put("com_table", new ArrayList<>());
         prpCmain.put("upperPath", "");
-        prpCmain.put("monopolyCode", "");
-        prpCmain.put("monopolyName", "");
+        prpCmain.put("monopolyCode", monopolyCode != null ? monopolyCode : "");
+        prpCmain.put("monopolyName", monopolyName != null ? monopolyName : "");
         prpCmain.put("salesmanNum", "");
         prpCmain.put("cqpCount", cqpCount != null ? cqpCount : "");
         prpCmain.put("otherBase_table", new ArrayList<>());
@@ -1598,7 +1632,8 @@ public class RenBaoNewCrawlerRequestBuilder {
                                                 String carKindCode, String fuelType, String clauseType,
                                                 String vehicleFgwCode, BigDecimal purchasePrice,
                                                 String comCode, String userCode, HttpHeaders headers, String username,
-                                                String queryArea, String isEnergyCar, String energyTypePlat) {
+                                                String queryArea, String isEnergyCar, String energyTypePlat,
+                                                String monopolyCode) {
         Map<String, Object> prpCitemCar = new HashMap<>();
         String formattedRegisterDate = formatDateWithTimezone(carInfo.getRegisterDate());
 
@@ -1948,7 +1983,7 @@ public class RenBaoNewCrawlerRequestBuilder {
         prpCitemCar.put("transferDate", isTransfer && transferDate != null ? transferDate : "");
         prpCitemCar.put("modelDemandNo", "");
         prpCitemCar.put("vehicleStyleUniqueId", "");
-        prpCitemCar.put("monopolyCode", "");
+        prpCitemCar.put("monopolyCode", monopolyCode != null ? monopolyCode : "");
 
         return prpCitemCar;
     }

+ 1 - 12
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/aspect/QuoteVerificationAspect.java

@@ -230,9 +230,6 @@ public class QuoteVerificationAspect {
         insFeeOrders.setSyPremium(insOrdersCosts.getSyPremium());
         insFeeOrders.setJyPremium(insOrdersCosts.getJyPremium());
         insFeeOrders.setTotalPremium(insOrdersCosts.getSumPremium());
-        // 获取协议是平台协议还是私有协议 1 平台协议 2 私有协议
-        AgreementVo agreement = agreementInfoVo.getAgreement();
-        String agreementType = agreementInfoVo.getAgreement().getAgreementType();
         BigDecimal jqEntranceFeeRatio = convertPercent(agreementInfoVo.getAgreement().getJqEntranceFeeRatio());
         BigDecimal syEntranceFeeRatio = convertPercent(agreementInfoVo.getAgreement().getSyEntranceFeeRatio());
         BigDecimal jyEntranceFeeRatio = convertPercent(agreementInfoVo.getAgreement().getJyEntranceFeeRatio());
@@ -250,16 +247,8 @@ public class QuoteVerificationAspect {
         BigDecimal jqFollowRatio = calculateFollowRatio(jqCostType, jqEntranceFeeRatio);
         BigDecimal syFollowRatio = calculateFollowRatio(syCostType, syEntranceFeeRatio);
         BigDecimal jyFollowRatio = calculateFollowRatio(jyCostType, jyEntranceFeeRatio);
-
         // modify by lipf ,2026年6月17日15:30:54 是否除以1.06,不再根据平台协议还是私有协议进行判断,而是根据协议的是否含税来进行判断
-        String withTax = agreement.getWithTax();
-        BigDecimal taxFactor = new BigDecimal("1");
-        if ("0".equals(withTax)) {
-            // 不含税:保费 / 1.06
-            taxFactor =  new BigDecimal("1.06");
-        }
-
-
+        BigDecimal taxFactor = insOrdersService.obtainTaxRate(insOrders.getId());
         // 6. 统一处理各险种的费用计算(应收、应付、各类费用)
         processInsuranceType(
                 insOrdersCosts.getJqPremium(), jqCostType, jqEntranceFeeRatio, jqFollowRatio, taxFactor,

+ 4 - 5
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/InsFeeOrderController.java

@@ -11,7 +11,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
-import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -68,10 +67,10 @@ public class InsFeeOrderController {
      * @author lipf
      * @date 2026/6/4 10:17
      */
-    @PostMapping("/queryAllTotalReceivablePremiumByOrderNo")
-    BigDecimal queryAllTotalReceivablePremiumByOrderNo(@RequestBody List<String> orderNos){
-        return insFeeOrdersService.queryAllTotalReceivablePremiumByOrderNo(orderNos);
-    }
+//    @PostMapping("/queryAllTotalReceivablePremiumByOrderNo")
+//    BigDecimal queryAllTotalReceivablePremiumByOrderNo(@RequestBody List<String> orderNos){
+//        return insFeeOrdersService.queryAllTotalReceivablePremiumByOrderNo(orderNos);
+//    }
 
     @Operation(summary = "修改权益可用金额")
     @PostMapping("updateAvailableAmount")

+ 14 - 0
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/InsOrdersController.java

@@ -41,6 +41,7 @@ import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 
 import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
 import java.util.*;
 
 @RestController
@@ -111,6 +112,19 @@ public class InsOrdersController extends BaseController {
         return HttpResult.ok(insOrdersVo);
     }
 
+    /**
+     * 获取订单关联协议的税率
+     *
+     * @param orderNo 订单号
+     * @author lipf
+     * @date 2026/6/18 9:46
+     */
+    @GetMapping("/obtainTaxRate")
+    @Operation(summary = "获取订单关联协议的税率")
+    public HttpResult<BigDecimal> obtainTaxRate(@RequestParam("orderNo") String orderNo){
+        return HttpResult.ok(insOrdersService.obtainTaxRate(orderNo));
+    }
+
     @PostMapping("pageList")
     @Operation(summary = "订单分页列表")
     public HttpResult pageList(@RequestBody InsOrdersParam insOrdersParam) {

+ 10 - 9
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/InsOrdersService.java

@@ -28,6 +28,7 @@ import com.jzg.commons.entity.quote.vo.orders.InsOrdersVo;
 import com.jzg.quotation.summary.entity.vo.*;
 
 import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.List;
 
@@ -82,15 +83,6 @@ public interface InsOrdersService extends IService<InsOrders> {
      */
     void setUserAreaCode(QuoteVo quoteVo);
 
-
-    /**
-     * 保存报价的订单
-     *
-     * @return
-     */
-    boolean saveQuoteOrders(QuoteVo quoteVo) throws InvocationTargetException, IllegalAccessException;
-
-
     /**
      * 订单报价更新后处理
      *
@@ -292,4 +284,13 @@ public interface InsOrdersService extends IService<InsOrders> {
      * @date 2026/5/28 10:45
      */
     HttpResult<String> updateOrdersBatch(BatchModifyVo batchModifyVo);
+
+    /**
+     * 获取订单关联协议的税率
+     *
+     * @param orderNo 订单号
+     * @author lipf
+     * @date 2026/6/18 9:49
+     */
+    BigDecimal obtainTaxRate(String orderNo);
 }

+ 36 - 43
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsOrdersServiceImpl.java

@@ -41,6 +41,7 @@ import com.jzg.commons.entity.quote.vo.orders.InsOrdersEnd;
 import com.jzg.commons.entity.quote.vo.orders.InsOrdersVo;
 import com.jzg.commons.entity.rights.vo.InsOrderRightsInfoVo;
 import com.jzg.commons.entity.vo.*;
+import com.jzg.commons.exception.ServiceException;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.commons.util.*;
 import com.jzg.commons.util.idgen.IdGenerate;
@@ -648,33 +649,6 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
         }
     }
 
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public boolean saveQuoteOrders(QuoteVo quoteVo) throws InvocationTargetException, IllegalAccessException {
-        InsOrders insOrders = new InsOrders();
-        insOrders.setId(IdGenerate.nextId());
-        insOrders.setQuoteNo(quoteVo.getOrder().getQuoteNo());
-        insOrders.setOrderStatus(InsOrderStatusEnum.QUOTE_ING.getCode());
-        insOrders.setCompanyId(quoteVo.getOrder().getInsuranceCompanyCode());
-        insOrders.setProductId(quoteVo.getOrder().getProductId());
-//        insOrdersMapper.insert(insOrders);
-
-        InsOrdersCarInfo carInfo = new InsOrdersCarInfo();
-        BeanUtils.copyProperties(carInfo, quoteVo.getCarInfoVo());
-        carInfo.setOrderNo(insOrders.getId());
-
-        InsOrdersCarUserInfo ownersInfo = new InsOrdersCarUserInfo();
-        BeanUtils.copyProperties(ownersInfo, quoteVo.getOwnersInfo());
-
-        InsOrdersCarUserInfo insuredPersonInfo = new InsOrdersCarUserInfo();
-        BeanUtils.copyProperties(insuredPersonInfo, quoteVo.getInsuredPersonInfo());
-
-        InsOrdersCarUserInfo policyHolderInfo = new InsOrdersCarUserInfo();
-        BeanUtils.copyProperties(policyHolderInfo, quoteVo.getPolicyHolderInfo());
-
-        return true;
-    }
-
     @Override
     public void setUserAreaCode(QuoteVo quoteVo) {
         HttpResult<PersonAreaVo> ownerAddress = orgClient.queryByAddress(
@@ -1369,7 +1343,7 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
         List<InsOrdersQrcode> qrcodes = insOrdersQrcodeMapper.selectList(new LambdaQueryWrapper<InsOrdersQrcode>()
                 .eq(InsOrdersQrcode::getOrderno, ordersNo)
                 .orderByDesc(InsOrdersQrcode::getCreatetime));
-        if (qrcodes.size() > 0) {
+        if (!qrcodes.isEmpty()) {
             resultVo.setQrcode(qrcodes.get(0));
         }
         // 费用信息
@@ -1396,15 +1370,6 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
         return resultVo;
     }
 
-    private String getSystemCode() {
-        //如果是APP登录的话 则传递SystemCode的头部字段
-        if (baseController.getUserSystemCode().equals("APP")) {
-            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
-            return request.getHeader("system_code");
-        }
-        return baseController.getUserSystemCode();
-    }
-
     /**
      * 获取历史报价信息
      *
@@ -1668,13 +1633,41 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
         return insOrdersMapper.selectByRuleAndUserPhone(activityId, userPhone, qutNum, activityType);
     }
 
-
-    public BigDecimal multiply(BigDecimal premium, BigDecimal ratio) {
-        if(premium==null||ratio==null) {
-            return BigDecimal.ZERO;
-        }
-        return premium.multiply(ratio);
+    /**
+     * 获取订单关联协议的税率
+     *
+     * @param orderNo 订单号
+     * @author lipf
+     * @date 2026/6/18 9:49
+     */
+    @Override
+    public BigDecimal obtainTaxRate(String orderNo) {
+        InsOrders insOrders = insOrdersMapper.selectById(orderNo);
+        if(insOrders == null){
+            throw new ServiceException(String.format("订单不存在,订单号是%s",orderNo));
+        }
+        String agreementId = insOrders.getAgreementId();
+        if(agreementId==null){
+            throw new ServiceException(String.format("订单未绑定协议,订单号是%s",orderNo));
+        }
+        // 获取协议信息
+        PtlAgreement ptlAgreement = orgClient.getByAgreementId(agreementId);
+        if(ptlAgreement==null){
+            throw new ServiceException(String.format("订单关联的协议不存在,订单号是%s,协议编号是%s",orderNo,agreementId));
+        }
+        // 关键:获取是否含税标识
+        Integer withTax = ptlAgreement.getWithTax(); // 0-否 1-是
+        if(withTax==null){
+            throw new ServiceException(String.format("协议未配置是否含税,协议编号是%s",agreementId));
+        }
+        BigDecimal taxFactor = new BigDecimal("1");
+        if (withTax == 0) {
+            // 不含税:保费 / 1.06
+            taxFactor =  new BigDecimal("1.06");
+        }
+        return taxFactor;
     }
+
     /**
      * 车险报价批量修改联系人等信息
      *

+ 1 - 9
tenant/insurance/quotation-summary/src/main/resources/mapper/InsOrdersMapper.xml

@@ -900,19 +900,11 @@
         <where>
             and io.is_delete = 0  and io.parent_id != '0'
             <if test="insOrdersParam.jzgInfoId != null and insOrdersParam.jzgInfoId != ''">
-                and io.real_quote_user_id in (select username from  sys_user bbb where bbb.id in (
-                select ss.user_id from sys_user_jzg_info ss where ss.leader_id = #{insOrdersParam.jzgInfoId} and ss.is_delete = 0
-                ) and bbb.is_delete = 0 )
+                and io.real_quote_user_id in (select username from  sys_user bbb where bbb.id in ( select ss.user_id from sys_user_jzg_info ss where 1 =1 and  (ss.leader_id = #{insOrdersParam.jzgInfoId} or ss.id =#{insOrdersParam.jzgInfoId}) and ss.is_delete = 0 ) and bbb.is_delete = 0 )
             </if>
             <if test="insOrdersParam.orderNo != null and insOrdersParam.orderNo != ''">
                 AND io.id = #{insOrdersParam.orderNo}
             </if>
-            <if test="insOrdersParam.parentId == 0 ">
-                AND io.parent_id = 0
-            </if>
-            <if test="insOrdersParam.parentId == 1 ">
-                AND io.parent_id != 0
-            </if>
             <if test="insOrdersParam.licenseNo != null and insOrdersParam.licenseNo != ''">
                 AND ioci.license_no = #{insOrdersParam.licenseNo}
             </if>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 0 - 2
tenant/insurance/quotation-zhongmei/src/main/java/com/jzg/quotation/zhongmei/crawler/constant/enums/ZmCrawlerFloatFlag.java

@@ -15,7 +15,6 @@ public class ZmCrawlerFloatFlag {
     private static final Map<String, String> NO_ADJUST_REASON_CODE_MAP = new HashMap<>();
 
     static {
-        // 初始化 jqrateFloatFlagList
         JQ_RATE_FLOAT_FLAG_MAP.put("0", "费率浮动");
         JQ_RATE_FLOAT_FLAG_MAP.put("01", "新车,不浮动");
         JQ_RATE_FLOAT_FLAG_MAP.put("02", "首次投保,不浮动");
@@ -29,7 +28,6 @@ public class ZmCrawlerFloatFlag {
         JQ_RATE_FLOAT_FLAG_MAP.put("10", "脱保,不下浮");
         JQ_RATE_FLOAT_FLAG_MAP.put("99", "不在浮动时间范围内,不浮动");
 
-        // 初始化 synoAjustReasonCodeList
         NO_ADJUST_REASON_CODE_MAP.put("01", "过户车,不浮动");
         NO_ADJUST_REASON_CODE_MAP.put("02", "脱保6个月以上,不浮动");
         NO_ADJUST_REASON_CODE_MAP.put("03", "本次投保为短期单,不下浮");

+ 10 - 10
tenant/organization/src/main/java/com/jzg/organization/client/InsFeeOrdersClient.java

@@ -30,14 +30,14 @@ public interface InsFeeOrdersClient {
     @PostMapping("/supplementOrder/updateOrdersBatch")
     @Operation(summary = "车险报价批量修改联系人等信息")
     HttpResult<String> updateOrdersBatch(@RequestBody BatchModifyVo batchModifyVo);
-
-    /**
-     * 查询指定订单的应收总额
-     *
-     * @param orderNos 订单编号
-     * @author lipf
-     * @date 2026/6/4 10:17
-     */
-    @PostMapping("/insFeeOrder/queryAllTotalReceivablePremiumByOrderNo")
-    BigDecimal queryAllTotalReceivablePremiumByOrderNo(@RequestBody List<String> orderNos);
+//
+//    /**
+//     * 查询指定订单的应收总额
+//     *
+//     * @param orderNos 订单编号
+//     * @author lipf
+//     * @date 2026/6/4 10:17
+//     */
+//    @PostMapping("/insFeeOrder/queryAllTotalReceivablePremiumByOrderNo")
+//    BigDecimal queryAllTotalReceivablePremiumByOrderNo(@RequestBody List<String> orderNos);
 }

+ 11 - 1
tenant/organization/src/main/java/com/jzg/organization/client/InsOrdersClient.java

@@ -8,6 +8,8 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import java.math.BigDecimal;
+
 /**
  * 订单接口
  */
@@ -22,5 +24,13 @@ public interface InsOrdersClient {
     @PostMapping("/supplementOrder/selectInsOrdersById/{id}")
     HttpResult<InsOrdersVo> selectById(@PathVariable String id);
 
-
+    /**
+     * 获取订单关联协议的税率
+     *
+     * @param orderNo 订单号
+     * @author lipf
+     * @date 2026/6/18 9:46
+     */
+    @GetMapping("/supplementOrder/obtainTaxRate")
+    HttpResult<BigDecimal> obtainTaxRate(@RequestParam("orderNo") String orderNo);
 }

+ 12 - 0
tenant/organization/src/main/java/com/jzg/organization/client/PlatformClient.java

@@ -3,11 +3,16 @@ package com.jzg.organization.client;
 
 import com.jzg.commons.core.page.HttpResult;
 import com.jzg.commons.entity.agreement.po.PtlAgreementAttribution;
+import com.jzg.commons.entity.vo.CompanyClauseVo;
+import com.jzg.commons.entity.vo.InsCompanyClauseVo;
 import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import java.util.List;
+
 
 @FeignClient(name = "jzg-platform")
 public interface PlatformClient {
@@ -22,4 +27,11 @@ public interface PlatformClient {
     @PostMapping(value = "/manager/attribution/getAgreementAttribution2")
     public HttpResult<PtlAgreementAttribution> getAgreementAttribution2(@RequestParam(value = "id") String id);
 
+    /**
+     * 特别约定列表查询
+     */
+    @Operation(summary = "特别约定列表查询")
+    @PostMapping("/manager/clause/getClauseList")
+    HttpResult<List<InsCompanyClauseVo>> queryClauseData(@RequestBody CompanyClauseVo companyClauseVo);
+
 }

+ 9 - 0
tenant/organization/src/main/java/com/jzg/organization/mapper/ReceivableMapper.java

@@ -15,6 +15,7 @@ import com.jzg.organization.entity.dto.CalRes;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -72,6 +73,14 @@ public interface ReceivableMapper extends BaseMapper<InsPlyIncome> {
     List<InsPlyIncome> queryIncomeDatasByOrderIds(@Param("orderNos") List<String> orderNos,
                                                   @Param("companyId") String companyId);
 
+    /**
+     * 查询应收总额
+     *
+     * @author lipf
+     * @date 2026/6/4 10:22
+     */
+    BigDecimal queryAllTotalReceivablePremiumByOrderNo(@Param("orders") List<String> orders);
+
     List<InsPlyIncome> getReceivableList(@Param("receivableQueryVo") ReceivableQueryVo receivableQueryVo);
 
     /**

+ 18 - 0
tenant/organization/src/main/java/com/jzg/organization/service/ReceivableService.java

@@ -9,6 +9,7 @@ import com.jzg.commons.entity.finance.po.*;
 import com.jzg.commons.entity.finance.vo.*;
 import com.jzg.commons.entity.orders.po.InsOrderAdjust;
 import com.jzg.commons.entity.po.EsmInsCompany;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
@@ -180,6 +181,15 @@ public interface ReceivableService extends IService<InsPlyIncome> {
      */
     boolean batchModify(BatchModifyVo batchModifyVo);
 
+    /**
+     * 获取订单对应的税率
+     *
+     * @param orderNo
+     * @author lipf
+     * @date 2026/6/18 9:45
+     */
+    BigDecimal obtainTaxRate(String orderNo);
+
     /**
      * 获取平台结算列表
      */
@@ -329,6 +339,14 @@ public interface ReceivableService extends IService<InsPlyIncome> {
      */
     HttpResult<List<InsPlyIncome>> queryIncomeDatas(IncomeDataVO incomeDataVO);
 
+    /**
+     * 查询应收总额
+     *
+     * @author lipf
+     * @date 2026/6/4 10:22
+     */
+    BigDecimal queryAllTotalReceivablePremiumByOrderNo(@Param("orders") List<String> orders);
+
     /**
      * 删除发票信息(便于重新开票)
      *

+ 29 - 0
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlSchemeServiceImpl.java

@@ -21,10 +21,13 @@ import com.jzg.commons.entity.agreement.dispatch.vo.ValidateDispatchRuleVo;
 import com.jzg.commons.entity.costs.vo.ValidateSchemeRuleVo;
 import com.jzg.commons.entity.po.*;
 import com.jzg.commons.entity.po.jyxSetting;
+import com.jzg.commons.entity.quote.vo.CustomerInfoVo;
 import com.jzg.commons.entity.quote.vo.SalespersonInfoVo;
 import com.jzg.commons.entity.scheme.po.*;
 import com.jzg.commons.entity.scheme.vo.*;
+import com.jzg.commons.entity.vo.CompanyClauseVo;
 import com.jzg.commons.entity.vo.EsmInsCompanyResVo;
+import com.jzg.commons.entity.vo.InsCompanyClauseVo;
 import com.jzg.commons.exception.NoneException;
 import com.jzg.commons.util.AssertionUtils;
 import com.jzg.commons.util.MinioUtils;
@@ -32,6 +35,7 @@ import com.jzg.commons.util.idgen.IdGenerate;
 import com.jzg.organization.client.CostsRuleClient;
 import com.jzg.organization.client.EsmInsCompanyClient;
 import com.jzg.organization.client.OrgClient;
+import com.jzg.organization.client.PlatformClient;
 import com.jzg.organization.mapper.*;
 import com.jzg.organization.service.*;
 import io.seata.spring.annotation.GlobalTransactional;
@@ -88,6 +92,9 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
     @Autowired
     PtlAgreementDeptService ptlAgreementDeptService;
 
+    @Autowired
+    PlatformClient platformClient;
+
     @Autowired
     OrgClient orgClient;
 
@@ -460,6 +467,28 @@ public class PtlSchemeServiceImpl extends ServiceImpl<PtlSchemeMapper, PtlScheme
                 }
             });
         }
+        // 国任车主与投保人不一致时,返回车主约定T0028特约
+        for (PtlAgreementInsCompanyVo company : resVo) {
+            if ("XDCX".equals(company.getCode())) {
+                CustomerInfoVo ownerInfo = schemeRuleFilterVo.getOwnerInfo();
+                CustomerInfoVo applyInfo = schemeRuleFilterVo.getApplyInfo();
+                if (ObjectUtil.isNotNull(ownerInfo) && ObjectUtil.isNotNull(applyInfo)
+                        && !StrUtil.equals(ownerInfo.getIdentifyNumber(), applyInfo.getIdentifyNumber())) {
+                    CompanyClauseVo clauseVo = new CompanyClauseVo();
+                    clauseVo.setCompanyId(company.getId());
+                    clauseVo.setClauseCode("T0028");
+                    try {
+                        HttpResult<List<InsCompanyClauseVo>> clauseResult = platformClient.queryClauseData(clauseVo);
+                        if (clauseResult.getCode() == 200 && CollUtil.isNotEmpty(clauseResult.getData())) {
+                            company.setSpecialSelectedList(clauseResult.getData());
+                        }
+                    } catch (Exception e) {
+                        log.warn("查询国任特约T0028失败,companyId:{}", company.getId(), e);
+                    }
+                }
+                break;
+            }
+        }
         return resVo;
     }
 

+ 45 - 8
tenant/organization/src/main/java/com/jzg/organization/service/impl/ReceivableServiceImpl.java

@@ -40,6 +40,7 @@ import com.jzg.organization.excel.listener.*;
 import com.jzg.organization.mapper.*;
 import com.jzg.organization.service.ReceivableService;
 import com.jzg.organization.service.SysUserJzgInfoSalesmanService;
+import org.apache.ibatis.annotations.Param;
 import org.jetbrains.annotations.NotNull;
 import org.redisson.api.RBucket;
 import org.redisson.api.RKeys;
@@ -240,6 +241,17 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         return res;
     }
 
+    /**
+     * 查询应收总额
+     *
+     * @author lipf
+     * @date 2026/6/4 10:22
+     */
+    @Override
+    public BigDecimal queryAllTotalReceivablePremiumByOrderNo(@Param("orders") List<String> orders){
+        return baseMapper.queryAllTotalReceivablePremiumByOrderNo(orders);
+    }
+
     /**
      * 应收数据二次过滤
      *
@@ -1502,7 +1514,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
      */
     @Override
     public BigDecimal queryAllTotalReceivablePremium(List<String> orderNos) {
-        return insFeeOrdersClient.queryAllTotalReceivablePremiumByOrderNo(orderNos);
+        return this.queryAllTotalReceivablePremiumByOrderNo(orderNos);
     }
 
     @Override
@@ -2128,6 +2140,22 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         return importExcelResultVo;
     }
 
+    /**
+     * 获取订单对应的税率
+     *
+     * @param orderNo 订单号
+     * @author lipf
+     * @date 2026/6/18 9:45
+     */
+    @Override
+    public BigDecimal obtainTaxRate(String orderNo) {
+        HttpResult<BigDecimal> result = insOrdersClient.obtainTaxRate(orderNo);
+        if(result.isSuccess()){
+            return result.getData();
+        }
+        throw new ServiceException(result.getMsg());
+    }
+
     /**
      * 私有协议批量修改
      *      修改手续费比例(商业险手续费比例, 交强险手续费比例)
@@ -2153,18 +2181,27 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                 continue;
             }
             insOrders.add(insPlyIncomeSource.getOrderNo());
+            BigDecimal taxRate = this.obtainTaxRate(insPlyIncomeSource.getOrderNo());
             if (Objects.nonNull(batchModifyVo.getJqSuperviseCostsProportion()) && batchModifyVo.getJqSuperviseCostsProportion().compareTo(BigDecimal.ZERO) > 0) {
-                BigDecimal superviseReceivablePremium = BigDecimalUtil.multiply(new BigDecimal(Objects.toString(insPlyIncomeSource.getJqPremium(),"0") ), batchModifyVo.getJqSuperviseCostsProportion());
+                // modify by lipf 2026年6月18日09:36:01 转换为百分比
+                BigDecimal jqSuperviseCostsProportion = BigDecimalUtil.divide(batchModifyVo.getJqSuperviseCostsProportion(), new BigDecimal(100)) ;
+                BigDecimal superviseReceivablePremium = BigDecimalUtil.divide(BigDecimalUtil.multiply(new BigDecimal(Objects.toString(insPlyIncomeSource.getJqPremium(),"0") ), jqSuperviseCostsProportion), taxRate) ;
+                BigDecimal jqReceivePremiun = BigDecimalUtil.add(superviseReceivablePremium, BigDecimalUtil.toZero(insPlyIncomeSource.getInsFeeOrders()  == null? null:  insPlyIncomeSource.getInsFeeOrders().getJqFollowPremium()));
                 baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
-                        .set(InsPlyIncome::getJqSuperviseCostsProportion, batchModifyVo.getJqSuperviseCostsProportion())
+                        .set(InsPlyIncome::getJqSuperviseCostsProportion, jqSuperviseCostsProportion)
                         .set(InsPlyIncome::getJqSuperviseCostsPremiums, superviseReceivablePremium)
+                        .set(InsPlyIncome::getJqReceivablePremium,jqReceivePremiun)
                         .in(InsPlyIncome::getId, batchModifyVo.getIncomeIds()));
             }
             if (Objects.nonNull(batchModifyVo.getSySuperviseCostsProportion()) && batchModifyVo.getSySuperviseCostsProportion().compareTo(BigDecimal.ZERO) > 0) {
-                BigDecimal otherReceivablePremium = BigDecimalUtil.multiply(new BigDecimal(Objects.toString(insPlyIncomeSource.getSyPremium(),"0")), batchModifyVo.getSySuperviseCostsProportion());
+                // modify by lipf 2026年6月18日09:36:01 转换为百分比
+                BigDecimal sySuperviseCostsProportion = BigDecimalUtil.divide(batchModifyVo.getSySuperviseCostsProportion(), new BigDecimal(100)) ;
+                BigDecimal sySuperviseReceivablePremium = BigDecimalUtil.divide(BigDecimalUtil.multiply(new BigDecimal(Objects.toString(insPlyIncomeSource.getSyPremium(),"0")), sySuperviseCostsProportion), taxRate) ;
+                BigDecimal syReceivePremiun = BigDecimalUtil.add(sySuperviseReceivablePremium, BigDecimalUtil.toZero(insPlyIncomeSource.getInsFeeOrders() != null? insPlyIncomeSource.getInsFeeOrders().getSyFollowPremium() : null));
                 baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
-                        .set(InsPlyIncome::getSySuperviseCostsProportion, batchModifyVo.getSySuperviseCostsProportion())
-                        .set(InsPlyIncome::getSySuperviseCostsPremiums, otherReceivablePremium)
+                        .set(InsPlyIncome::getSySuperviseCostsProportion, sySuperviseCostsProportion)
+                        .set(InsPlyIncome::getSySuperviseCostsPremiums, sySuperviseReceivablePremium)
+                        .set(InsPlyIncome::getSyReceivablePremium,syReceivePremiun)
                         .in(InsPlyIncome::getId, batchModifyVo.getIncomeIds()));
             }
             if (batchModifyVo.getSigningTime() != null){
@@ -2495,8 +2532,8 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                                 BigDecimal.ZERO,
                                 item -> {
                                     // 优化点:将字符串转换为BigDecimal,避免在循环中频繁new对象
-                                    BigDecimal jqPremium = new BigDecimal(item.getJqReceivablePremium());
-                                    BigDecimal syPremium = new BigDecimal(item.getSyReceivablePremium());
+                                    BigDecimal jqPremium = BigDecimalUtil.valueOf(item.getJqOtherCostsPremiums());
+                                    BigDecimal syPremium = BigDecimalUtil.valueOf(item.getSyOtherCostsPremiums());
                                     return jqPremium.add(syPremium);
                                 },
                                 BigDecimal::add

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

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

+ 16 - 0
tenant/organization/src/main/resources/mapper/ReceivableMapper.xml

@@ -233,6 +233,9 @@
         ipi.jq_supervise_costs_premiums,
         ipi.sy_supervise_costs_premiums,
         ipi.jy_supervise_costs_premiums,
+        ipi.jq_other_costs_premiums,
+        ipi.sy_other_costs_premiums,
+        ipi.jy_other_costs_premiums,
         io.product_name,
         iocui.name AS insuredPerson,
         ipi.jq_premium,
@@ -277,6 +280,19 @@
             ipi.is_delete  = 0
             -- and ipi.settlement_status = '0'  -- 已经结算的车辆,在应收订单里面,还需要能查询到
     </sql>
+    <!-- 查询应收订单总额 lipf 2026年6月4日10:22:57 -->
+    <select id="queryAllTotalReceivablePremiumByOrderNo" resultType="java.math.BigDecimal">
+        SELECT
+        sum(COALESCE(ipi.jq_receivable_premium, 0) + COALESCE(ipi.sy_receivable_premium, 0) + COALESCE(ipi.jy_receivable_premium, 0) )
+        from
+        ins_ply_income ipi
+        <where>
+            ipi.order_no in
+            <foreach collection="orders" item="order" open="(" separator="," close=")">
+                #{order}
+            </foreach>
+        </where>
+    </select>
 
     <!--根据保司id和订单号进行过滤 2026年5月12日15:06:40  lipf  -->
     <select id="queryIncomeDatasByOrderIds" resultType="com.jzg.commons.entity.finance.po.InsPlyIncome">