Przeglądaj źródła

Merge branch 'master' into wrq_dev

wangrq 3 miesięcy temu
rodzic
commit
e92be2958b
81 zmienionych plików z 2293 dodań i 649 usunięć
  1. 4 25
      authentication/src/main/resources/application-dev.yml
  2. 2 23
      authentication/src/main/resources/application-prod.yml
  3. 25 2
      commons/pom.xml
  4. 6 1
      commons/src/main/java/com/jzg/commons/config/BaseFeignConfig.java
  5. 38 0
      commons/src/main/java/com/jzg/commons/config/SeataXidFilter.java
  6. 5 5
      commons/src/main/java/com/jzg/commons/entity/dto/AmountAuditParam.java
  7. 17 2
      commons/src/main/java/com/jzg/commons/entity/dto/FeeAuditParam.java
  8. 50 0
      commons/src/main/java/com/jzg/commons/entity/finance/dto/InsFeeFollowOrderDto.java
  9. 23 0
      commons/src/main/java/com/jzg/commons/entity/finance/dto/InsPlyFollowInvoiceLinkDto.java
  10. 319 0
      commons/src/main/java/com/jzg/commons/entity/finance/dto/InsPlyIncomeDto.java
  11. 51 0
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsFeeFollowOrder.java
  12. 27 0
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyFollowInvoiceLink.java
  13. 1 1
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoice.java
  14. 5 0
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoiceLink.java
  15. 4 4
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoiceSettlement.java
  16. 39 0
      commons/src/main/java/com/jzg/commons/entity/finance/vo/FollowInvoiceAndSettlementVo.java
  17. 42 0
      commons/src/main/java/com/jzg/commons/entity/finance/vo/FollowInvoicePageListVo.java
  18. 43 0
      commons/src/main/java/com/jzg/commons/entity/finance/vo/FollowInvoiceVo.java
  19. 55 0
      commons/src/main/java/com/jzg/commons/entity/finance/vo/InsFeeFollowOrderVo.java
  20. 27 0
      commons/src/main/java/com/jzg/commons/entity/finance/vo/InsPlyFollowInvoiceLinkVo.java
  21. 13 3
      commons/src/main/java/com/jzg/commons/entity/finance/vo/InvoiceRecordResult.java
  22. 2 2
      commons/src/main/java/com/jzg/commons/entity/finance/vo/SettlementVo.java
  23. 6 1
      commons/src/main/java/com/jzg/commons/exception/GlobalExceptionHandler.java
  24. 21 0
      commons/src/main/java/com/jzg/commons/util/BigDecimalUtil.java
  25. 1 2
      commons/src/main/java/com/jzg/commons/util/GsonUtils.java
  26. 5 2
      consoleStatistics/src/main/resources/application-dev.yml
  27. 4 0
      consoleStatistics/src/main/resources/application-prod.yml
  28. 7 2
      gateway/src/main/resources/application-dev.yml
  29. 8 1
      gateway/src/main/resources/application-prod.yml
  30. 7 4
      partner/src/main/resources/application-dev.yml
  31. 2 1
      partner/src/main/resources/application-prod.yml
  32. 2 22
      platform/src/main/resources/application-dev.yml
  33. 2 21
      platform/src/main/resources/application-prod.yml
  34. 1 0
      platform/src/main/resources/application.yml
  35. 1 1
      pom.xml
  36. 5 2
      tenant/costsorrule/src/main/resources/application-dev.yml
  37. 2 0
      tenant/costsorrule/src/main/resources/application-prod.yml
  38. 7 6
      tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/component/RequestComponent.java
  39. 2 2
      tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/filter/CarModelsFilterUtils.java
  40. 1 2
      tenant/insurance/quotation-dadi/src/main/java/com/jzg/quotation/dadi/utils/DaDiAESUtils.java
  41. 13 19
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/component/GuoRenCrawlerRequestComponent.java
  42. 7 1
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/dto/CiInsureDemandDTO.java
  43. 1 1
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/dto/GuoRenBiDTO.java
  44. 1 1
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/dto/GuoRenCiDTO.java
  45. 6 1
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/request/GuoRenCrawlerProposalNoSaveRequest.java
  46. 1 1
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/service/impl/GuoRenCrawlerRequestImpl.java
  47. 23 10
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/component/HengBangCrawlerRequestComponent.java
  48. 3 0
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/constant/enums/RequestUrl.java
  49. 50 19
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/entity/request/HbCrawlerCalculationRequest.java
  50. 4 15
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/entity/response/HbCrawlerRecordResponse.java
  51. 15 16
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/service/Impl/HengBangCrawlerRequestImpl.java
  52. 3 4
      tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/utils/HengBangAESUtil.java
  53. 8 4
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/service/Impl/HuaTaiCrawlerRequestImpl.java
  54. 60 45
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/util/UploadUtil.java
  55. 10 9
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestBuilder.java
  56. 76 115
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestImpl.java
  57. 9 3
      tenant/insurance/quotation-summary/pom.xml
  58. 16 0
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/client/SeataTestOrgClient.java
  59. 6 1
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/InsFeeAuditController.java
  60. 70 0
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/SeataTestController.java
  61. 8 4
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/UnifyOrderManager.java
  62. 3 2
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/InsOrdersService.java
  63. 182 146
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsFeeAuditServiceImpl.java
  64. 7 5
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsOrdersServiceImpl.java
  65. 14 10
      tenant/insurance/quotation-summary/src/main/resources/application-dev.yaml
  66. 8 6
      tenant/insurance/quotation-summary/src/main/resources/application-prod.yaml
  67. 9 22
      tenant/insurance/quotation-summary/src/main/resources/mapper/InsFeeAuditMapper.xml
  68. 2 2
      tenant/insurance/quotation-zhongan/pom.xml
  69. 1 1
      tenant/organization/pom.xml
  70. 71 0
      tenant/organization/src/main/java/com/jzg/organization/controller/ReceivableController.java
  71. 32 9
      tenant/organization/src/main/java/com/jzg/organization/controller/TestController.java
  72. 14 0
      tenant/organization/src/main/java/com/jzg/organization/mapper/InsFeeFollowOrderMapper.java
  73. 13 0
      tenant/organization/src/main/java/com/jzg/organization/mapper/InsPlyFollowInvoiceLinkMapper.java
  74. 0 8
      tenant/organization/src/main/java/com/jzg/organization/mapper/InsPlyIncomeInvoiceLinkMapper.java
  75. 19 0
      tenant/organization/src/main/java/com/jzg/organization/mapper/ReceivableMapper.java
  76. 36 0
      tenant/organization/src/main/java/com/jzg/organization/service/ReceivableService.java
  77. 508 24
      tenant/organization/src/main/java/com/jzg/organization/service/impl/ReceivableServiceImpl.java
  78. 3 2
      tenant/organization/src/main/resources/application-dev.yml
  79. 1 1
      tenant/organization/src/main/resources/application-prod.yml
  80. 97 4
      tenant/organization/src/main/resources/mapper/ReceivableMapper.xml
  81. 1 1
      tenant/organization/src/main/resources/mapper/SysUserJzgInfoBankCardMapper.xml

+ 4 - 25
authentication/src/main/resources/application-dev.yml

@@ -6,10 +6,10 @@ spring:
       password:
       config:
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
   config:
     import:
       - nacos:mysql
@@ -17,28 +17,7 @@ spring:
       - nacos:minio
 config:
   sso: 0
-seata:
-  enabled: true
-  application-id: ${spring.application.name}
-  # 事务组的名称,对应service.vgroupMapping.default_tx_group=xxx中配置的default_tx_group
-  tx-service-group: default_tx_group
-  # 配置事务组与集群的对应关系
-  service:
-    vgroup-mapping:
-      # default_tx_group为事务组的名称,default为集群名称(与registry.conf中的一致)
-      default_tx_group: default
-    disable-global-transaction: false
-  registry:
-    type: nacos
-    nacos:
-      application: seata-server
-      server-addr: 192.168.5.250:8848
-      group: SEATA_GROUP
-      namespace: ba488f58-c785-49b4-9c02-b5d9b8a965a5
-      username:
-      password:
-      # registry.conf中,配置cluster名称
-      cluster: default
+
 wechat:
   open:
     properties:
@@ -48,4 +27,4 @@ wechat:
 feign:
   client:
     jzg-partner:
-      url: http://localhost:8087/
+      url: http://localhost:8087/

+ 2 - 23
authentication/src/main/resources/application-prod.yml

@@ -17,31 +17,10 @@ spring:
       - nacos:mysql
       - nacos:redisson
       - nacos:minio
+      - nacos:seata-server
 config:
   sso: 0
 
-#seata:
-#  enabled: true
-#  application-id: ${spring.application.name}
-#  # 事务组的名称,对应service.vgroupMapping.default_tx_group=xxx中配置的default_tx_group
-#  tx-service-group: default_tx_group
-#  # 配置事务组与集群的对应关系
-#  service:
-#    vgroup-mapping:
-#      # default_tx_group为事务组的名称,default为集群名称(与registry.conf中的一致)
-#      default_tx_group: default
-#    disable-global-transaction: false
-#  registry:
-#    type: nacos
-#    nacos:
-#      application: seata-server
-#      server-addr: 172.24.144.227:8848
-#      group: SEATA_GROUP
-#      namespace: f9ca025f-b5df-47a9-b3f6-15bcbad3ebd3
-#      username: nacos
-#      password: AnrNjNn6zCS3QdIy
-#      # registry.conf中,配置cluster名称
-#      cluster: default
 wechat:
   open:
     properties:
@@ -53,4 +32,4 @@ wechat:
 feign:
   client:
     jzg-partner:
-      url: http://172.24.144.227:8087/
+      url: http://172.24.144.227:8087/

+ 25 - 2
commons/pom.xml

@@ -136,6 +136,29 @@
             <artifactId>opentelemetry-api</artifactId>
             <version>1.41.0</version>
         </dependency>
+        <dependency>
+            <groupId>io.seata</groupId>
+            <artifactId>seata-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!--Gson 转化-->
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.9.1</version>
+            <scope>compile</scope>
+            <exclusions>
+                <exclusion>
+                    <artifactId>spring</artifactId>
+                    <groupId>*</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>c3p0</artifactId>
+                    <groupId>*</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
     </dependencies>
 
     <build>
@@ -144,8 +167,8 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <configuration>
-                    <source>14</source>
-                    <target>14</target>
+                    <source>17</source>
+                    <target>17</target>
 <!--                    <annotationProcessorPaths>-->
 <!--                        <path>-->
 <!--                            <groupId>org.projectlombok</groupId>-->

+ 6 - 1
commons/src/main/java/com/jzg/commons/config/BaseFeignConfig.java

@@ -1,9 +1,11 @@
 package com.jzg.commons.config;
 
 import feign.RequestInterceptor;
+import io.seata.core.context.RootContext;
 import jakarta.servlet.http.HttpServletRequest;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -13,7 +15,6 @@ public class BaseFeignConfig {
 
     @Bean
     public RequestInterceptor requestInterceptor() {
-        //拦截器 设置Token
         return requestTemplate -> {
             RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
             if (requestAttributes instanceof ServletRequestAttributes) {
@@ -27,6 +28,10 @@ public class BaseFeignConfig {
                      requestTemplate.header("system_code", systemCode);
                 }
             }
+            String xid = RootContext.getXID();
+            if (StringUtils.hasText(xid)) {
+                requestTemplate.header(RootContext.KEY_XID, xid);
+            }
         };
     }
 

+ 38 - 0
commons/src/main/java/com/jzg/commons/config/SeataXidFilter.java

@@ -0,0 +1,38 @@
+package com.jzg.commons.config;
+
+import io.seata.core.context.RootContext;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class SeataXidFilter implements Filter {
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        String xid = httpRequest.getHeader(RootContext.KEY_XID);
+
+        if (StringUtils.hasText(xid) && !StringUtils.hasText(RootContext.getXID())) {
+            RootContext.bind(xid);
+            try {
+                chain.doFilter(request, response);
+            } finally {
+                RootContext.unbind();
+            }
+        } else {
+            chain.doFilter(request, response);
+        }
+    }
+}

+ 5 - 5
commons/src/main/java/com/jzg/commons/entity/dto/AmountAuditParam.java

@@ -5,7 +5,7 @@ import com.jzg.commons.core.base.PageRequest;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
-import java.time.LocalDateTime;
+import java.time.LocalDate;
 
 @Schema(description = "提现申请分页查询dto")
 @Data
@@ -22,19 +22,19 @@ public class AmountAuditParam extends PageRequest {
 
     @Schema(description = "提现时间开始")
     @JsonFormat(pattern = "yyyy-MM-dd")
-    private LocalDateTime createTimeStart;
+    private LocalDate createTimeStart;
 
     @Schema(description = "提现时间结束")
     @JsonFormat(pattern = "yyyy-MM-dd")
-    private LocalDateTime createTimeEnd;
+    private LocalDate createTimeEnd;
 
     @Schema(description = "审核时间开始")
     @JsonFormat(pattern = "yyyy-MM-dd")
-    private LocalDateTime auditTimeStart;
+    private LocalDate auditTimeStart;
 
     @Schema(description = "审核时间结束")
     @JsonFormat(pattern = "yyyy-MM-dd")
-    private LocalDateTime auditTimeEnd;
+    private LocalDate auditTimeEnd;
 
     @Schema(description = "审核人")
     private String auditingUser;

+ 17 - 2
commons/src/main/java/com/jzg/commons/entity/dto/FeeAuditParam.java

@@ -42,11 +42,11 @@ public class FeeAuditParam extends PageRequest {
 
     @Schema(description = "报价时间开始")
     @JsonFormat(pattern = "yyyy-MM-dd")
-    private LocalDateTime quotationTimeStart;
+    private LocalDate quotationTimeStart;
 
     @Schema(description = "报价时间结束")
     @JsonFormat(pattern = "yyyy-MM-dd")
-    private LocalDateTime quotationTimeEnd;
+    private LocalDate quotationTimeEnd;
 
     @Schema(description = "签单时间开始")
     @JsonFormat(pattern = "yyyy-MM-dd")
@@ -96,6 +96,21 @@ public class FeeAuditParam extends PageRequest {
     @Schema(description = "领取人")
     private String receiveUser;
 
+    /**
+     * 在订单审核-任务中心&我的审核,两个页面,调用的都是同一个接口 feeAudit/auditList
+     * 但是实际业务邀请中,
+     *      任务中心要求查看的是该租户下的所有的数据。
+     *      我的审核要求查看的是,指定用户领取后的数据。
+     *      ---
+     *      在任务中心,待领取tab页,需要查询所有未被领取的数据, 在我的审核tab页中,需要查询所有本人已经领取,但是未处理的数据
+     *      现在两个待领取的tab页,一个需要查询出所有未领取的数据,一个需要查询本人领取过的数据。 同时还不能对其他tab页的数据造成影响。
+     *      ---
+     *      allToReceive 表示 查询全部待领取,默认是false,查询的时候,不对receiveUser进行判断,
+     *      只有在任务中心-待审核 tab页面,会给他赋值 true, 此时,会要求专门查询 recieve_user 和 recieve_time 为空的
+     */
+    @Schema(description = "是否要查询全部待领取")
+    private boolean allTodoReceive;
+
     @Schema(description = "审核方式 1 人工审核 2 自动审核")
     private Integer auditType;
 

+ 50 - 0
commons/src/main/java/com/jzg/commons/entity/finance/dto/InsFeeFollowOrderDto.java

@@ -0,0 +1,50 @@
+package com.jzg.commons.entity.finance.dto;
+
+import com.jzg.commons.core.base.PageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 保费跟踪订单 DTO
+ * 用于:前端接口入参、微服务feign远程调用
+ */
+@Data
+@Schema(description = "保费跟踪订单传输DTO")
+public class InsFeeFollowOrderDto extends PageRequest {
+
+    @Schema(description = "主键ID(修改时必传)")
+    private Long id;
+
+    @Schema(description = "年份")
+    private Integer year;
+
+    @Schema(description ="月份")
+    private Integer month;
+
+    @Schema(description = "保险公司ID")
+    private String companyId;
+
+    @Schema(description = "分支保险公司ID")
+    private String partnerCompanyId;
+
+    @Schema(description = "应收金额")
+    private BigDecimal receivablePremium;
+
+    @Schema(description = "签单时间")
+    private LocalDateTime signingTime;
+
+    @Schema(description = "已开票")
+    private BigDecimal invoiced;
+
+    @Schema(description = "未开票")
+    private BigDecimal uninvoiced;
+
+    @Schema(description = "本次开票金额")
+    private BigDecimal invoicedAmountTime;
+
+    @Schema(description = "系统编码")
+    private String systemCode;
+}

+ 23 - 0
commons/src/main/java/com/jzg/commons/entity/finance/dto/InsPlyFollowInvoiceLinkDto.java

@@ -0,0 +1,23 @@
+package com.jzg.commons.entity.finance.dto;
+
+import com.jzg.commons.core.base.PageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 保单跟踪发票关联 DTO
+ * 用于:前端接口入参、feign远程调用
+ */
+@Data
+@Schema(description = "保单发票关联传输DTO")
+public class InsPlyFollowInvoiceLinkDto extends PageRequest {
+
+    @Schema(description = "主键ID(修改时必传)")
+    private Long id;
+
+    @Schema(description = "发票ID")
+    private Long invoiceId;
+
+    @Schema(description = "跟踪单ID")
+    private Long followId;
+}

+ 319 - 0
commons/src/main/java/com/jzg/commons/entity/finance/dto/InsPlyIncomeDto.java

@@ -0,0 +1,319 @@
+package com.jzg.commons.entity.finance.dto;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.jzg.commons.core.base.BaseModel;
+import com.jzg.commons.entity.po.InsFeeOrders;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.time.LocalDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class InsPlyIncomeDto extends BaseModel {
+    @Serial
+    private static final long serialVersionUID = 6161862008963938376L;
+
+    @ExcelIgnore
+    private String id;
+
+    @Schema(description = "订单号")
+    @ExcelProperty("订单号")
+    private String orderNo;
+
+    @Schema(description = "协议类型")
+    @ExcelProperty("协议类型")
+    private String agreementType;
+
+    @Schema(description = "机构")
+    @ExcelProperty("机构id")
+    private String deptId;
+
+    @Schema(description = "机构名称")
+    @ExcelProperty("机构名称")
+    private String deptName;
+
+    @Schema(description = "产品id")
+    @ExcelProperty("产品id")
+    private String productId;
+
+    @Schema(description = "产品名称")
+    @ExcelProperty("产品名称")
+    private String productName;
+
+    @Schema(description = "险种")
+    @ExcelProperty("险种")
+    private String riskCode;
+
+    @Schema(description = "签单时间")
+    //@TableField(exist = false)
+    @ExcelProperty("签单时间")
+    private LocalDateTime signingTime;
+
+    @Schema(description = "签单年份")
+    @TableField(exist = false)
+    private String signYear;
+
+    @Schema(description = "签单日期 yyyy-mm-dd 格式")
+    @TableField(exist = false)
+    private String signDate;
+
+    @Schema(description = "保司ID")
+    @ExcelProperty("保司ID")
+    private String companyId;
+
+    @Schema(description = "保司ID")
+    @ExcelProperty("保司ID")
+    private String companyName;
+
+    @Schema(description = "三级保司ID")
+    @ExcelProperty("三级保司ID")
+    private String partnerCompanyId;
+
+    @Schema(description = "三级保司名称")
+    @ExcelProperty("三级保司名称")
+    private String partnerCompanyName;
+
+    @Schema(description = "协议Id")
+    @ExcelProperty("协议Id")
+    private String agreementId;
+
+    @Schema(description = "协议名称")
+    @ExcelProperty("协议名称")
+    private String agreementName;
+
+    @Schema(description = "被保人")
+    @ExcelProperty("被保人")
+    private String insuredPerson;
+
+    @Schema(description = "车牌号")
+    @ExcelProperty("车牌号")
+    private String licenseNo;
+
+    @Schema(description = "车船税")
+    @ExcelProperty("车船税")
+    private String taxPremium;
+
+    @Schema(description = "交强保单号")
+    @ExcelProperty("交强保单号")
+    private String jqPolicyNo;
+
+    @Schema(description = "商业保单号")
+    @ExcelProperty("商业保单号")
+    private String syPolicyNo;
+
+    @Schema(description = "驾意保单号")
+    @ExcelProperty("驾意保单号")
+    private String jyPolicyNo;
+
+    @Schema(description = "交强保费")
+    @ExcelProperty("交强保费")
+    private String jqPremium;
+
+    @Schema(description = "商业保费")
+    @ExcelProperty("商业保费")
+    private String syPremium;
+
+    @Schema(description = "驾意保费")
+    @ExcelProperty("驾意保费")
+    private String jyPremium;
+
+    @Schema(description = "交强手续费")
+    @ExcelProperty("交强手续费")
+    private String jqSuperviseCostsPremiums;
+
+    @Schema(description = "交强手续费比例")
+    @ExcelProperty("交强手续费比例")
+    private String jqSuperviseCostsProportion;
+
+    @Schema(description = "商业手续费")
+    @ExcelProperty("商业手续费")
+    private String sySuperviseCostsPremiums;
+
+    @Schema(description = "商业手续费比例")
+    @ExcelProperty("商业手续费比例")
+    private String sySuperviseCostsProportion;
+
+    @Schema(description = "驾意手续费")
+    @ExcelProperty("驾意手续费")
+    private String jySuperviseCostsPremiums;
+
+    @Schema(description = "驾意手续费比例")
+    @ExcelProperty("驾意手续费比例")
+    private String jySuperviseCostsProportion;
+
+    @TableField(exist = false)
+    @Schema(description = "应收手续费")
+    @ExcelProperty("应收手续费")
+    private String superviseCostsPremiums;
+
+    @TableField(exist = false)
+    @Schema(description = "应收跟单费")
+    @ExcelProperty("应收跟单费")
+    private String otherCostsPremiums;
+
+    @Schema(description = "交强跟单费比例")
+    @ExcelProperty("交强跟单费比例")
+    private String jqOtherCostsProportion;
+
+    @Schema(description = "交强跟单费")
+    @ExcelProperty("交强跟单费")
+    private String jqOtherCostsPremiums;
+
+    @Schema(description = "商业跟单费比例")
+    @ExcelProperty("商业跟单费比例")
+    private String syOtherCostsProportion;
+
+    @Schema(description = "商业跟单费")
+    @ExcelProperty("商业跟单费")
+    private String syOtherCostsPremiums;
+
+    @Schema(description = "驾意跟单费比例")
+    @ExcelProperty("驾意跟单费比例")
+    private String jyOtherCostsProportion;
+
+    @Schema(description = "驾意跟单费")
+    @ExcelProperty("驾意跟单费")
+    private String jyOtherCostsPremiums;
+
+    @Schema(description = "交强应收费用")
+    @ExcelProperty("交强应收费用")
+    private String jqReceivablePremium;
+
+    @Schema(description = "商业应收费用")
+    @ExcelProperty("商业应收费用")
+    private String syReceivablePremium;
+
+    @Schema(description = "驾意应收费用")
+    @ExcelProperty("驾意应收费用")
+    private String jyReceivablePremium;
+
+    @TableField(exist = false)
+    @Schema(description = "总应收金额")
+    @ExcelProperty("总应收金额")
+    private String receivableAmount;
+
+    @Schema(description = "手续费是否已经结算")
+    @ExcelProperty("手续费是否已经结算")
+    private String superviseSettlement;
+
+    @Schema(description = "跟单费是否已经结算")
+    @ExcelProperty("跟单费是否已经结算")
+    private String otherSettlement;
+
+    @Schema(description = "驾意险手续费是否已经结算")
+    @ExcelProperty("驾意险手续费是否已经结算")
+    private String jySuperviseSettlement;
+
+    @Schema(description = "驾意跟单位是否已经结算")
+    @ExcelProperty("驾意跟单位是否已经结算")
+    private String jyOtherSettlement;
+
+    @Schema(description = "平台协议车险应收是否已经结算  0 未结算  1 结算")
+    @ExcelProperty("平台协议车险应收是否已经结算")
+    private String platformSuperviseSettlement;
+
+    @Schema(description = "平台协议非车险应收是否已经结算  0 未结算  1 结算")
+    @ExcelProperty("平台协议非车险应收是否已经结算")
+    private String platformOtherSettlement;
+
+    @Schema(description = "维护人工号")
+    @ExcelProperty("维护人工号")
+    private String leaderId;
+
+    @Schema(description = "维护人名称")
+    @ExcelProperty("维护人名称")
+    private String leaderName;
+
+    @Schema(description = "对接人")
+    @ExcelProperty("对接人")
+    private String contactPerson;
+
+    @Schema(description = "对接人手机号")
+    private String contactPersonPhone;
+
+    public String getDockingPerson(){
+        return this.contactPerson + "-" + this.contactPersonPhone;
+    }
+
+    @Schema(description = "系统code")
+    @ExcelProperty("系统code")
+    private String systemCode;
+
+    @Schema(description = "业务来源")
+    @ExcelProperty("业务来源")
+    private String businessSource;
+
+    @Schema(description = "结算时间")
+    private LocalDateTime settlementTime;
+
+    // 设置初始值为0, 未结算
+    @Schema(description = "结算状态(编码)")
+    @ExcelProperty("结算状态(编码)")
+    private String settlementStatus="0";
+
+    @TableField(exist = false)
+    @Schema(description = "结算状态(转义后)")
+    @ExcelProperty("结算状态(转义后)")
+    private String settlementStatusName;
+
+    @Schema(description = "增加类型")
+    @ExcelProperty("增加类型")
+    private String addType;
+
+    @TableField(exist = false)
+    @Schema(description = "交强起保日期")
+    @ExcelProperty("交强起保日期")
+    private String jqStartDate;
+
+    @TableField(exist = false)
+    @Schema(description = "商业起保日期")
+    @ExcelProperty("商业起保日期")
+    private String syStartDate;
+
+    @TableField(exist = false)
+    @Schema(description = "订单类型")
+    @ExcelProperty("订单类型")
+    private String orderType;
+
+    @TableField(exist = false)
+    @Schema(description = "订单类型(code)")
+    private String entryStatus;
+
+    @TableField(exist = false)
+    @Schema(description = "业务员类型 团队/渠道/电销/个代 5:团队 6:个代 7:渠道 8 :电销")
+    private String typeAttr;
+
+    @TableField(exist = false)
+    @Schema(description = "业务员类型 团队/渠道/电销/个代 5:团队 6:个代 7:渠道 8 :电销")
+    private String typeAttrName;
+
+    @TableField(exist = false)
+    @Schema(description = "实际签单用户手机号2")
+    private String realQuoteUserId;
+
+    @TableField(exist = false)
+    @Schema(description = "实际签单用户手机号")
+    private String realQuoteUserName;
+
+    @TableField(exist = false)
+    @Schema(description = "订单费用表")
+    private InsFeeOrders insFeeOrders;
+
+    @TableField(exist = false)
+    @Schema(description = "是否已经开票")
+    private String invoiceStatusName;
+
+    @TableField(exist = false)
+    @Schema(description = "订单关联的发票信息")
+    private String invoiceId;
+
+    @TableField(exist = false)
+    @Schema(description = "地区归属")
+    private String companyNameSimple;
+
+}

+ 51 - 0
commons/src/main/java/com/jzg/commons/entity/finance/po/InsFeeFollowOrder.java

@@ -0,0 +1,51 @@
+package com.jzg.commons.entity.finance.po;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.jzg.commons.core.base.BaseModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 保费跟踪订单表 PO
+ */
+@Data
+@Schema(description = "保费跟踪订单表")
+@TableName("ins_fee_follow_order")
+public class InsFeeFollowOrder extends BaseModel {
+
+    @Schema(description = "主键ID")
+    private String id;
+
+    @Schema(description = "年份")
+    private Integer year;
+
+    @Schema(description = "月份")
+    private Integer month;
+
+    @Schema(description = "保险公司ID")
+    private String companyId;
+
+    @Schema(description = "分支保险公司ID")
+    private String partnerCompanyId;
+
+    @Schema(description = "应收金额")
+    private BigDecimal receivablePremium;
+
+    @Schema(description = "签单时间")
+    private LocalDateTime signingTime;
+
+    @Schema(description = "已开票金额")
+    private BigDecimal invoiced;
+
+    @Schema(description = "未开票金额")
+    private BigDecimal uninvoiced;
+
+    @Schema(description = "本次开票金额")
+    private BigDecimal invoicedAmountTime;
+
+    @Schema(description = "系统编码")
+    private String systemCode;
+
+}

+ 27 - 0
commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyFollowInvoiceLink.java

@@ -0,0 +1,27 @@
+package com.jzg.commons.entity.finance.po;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.jzg.commons.core.base.BaseModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 保单跟踪发票关联表 PO
+ */
+@Data
+@Schema(description = "保单跟踪发票关联表")
+@TableName("ins_ply_follow_invoice_link")
+public class InsPlyFollowInvoiceLink extends BaseModel {
+
+    @Schema(description = "主键ID")
+    private String id;
+
+    @Schema(description = "发票ID")
+    private String invoiceId;
+
+    @Schema(description = "跟踪单ID")
+    private String followId;
+
+}

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

@@ -37,7 +37,7 @@ public class InsPlyIncomeInvoice extends BaseModel {
         return Objects.requireNonNullElse(this.overinflatedAmount, BigDecimal.ZERO);
     }
 
-    @Schema(description = "应收费用/开票金额")
+    @Schema(description = "开票金额")
     private BigDecimal receivableSupervisePremium;
 
     public BigDecimal getReceivableSupervisePremium(){

+ 5 - 0
commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoiceLink.java

@@ -1,5 +1,6 @@
 package com.jzg.commons.entity.finance.po;
 
+import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.jzg.commons.core.base.BaseModel;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
@@ -21,4 +22,8 @@ public class InsPlyIncomeInvoiceLink{
     @Schema(description = "开票类型")
     private String invoiceType;
 
+    @Schema(description = "是否删除")
+    @TableLogic
+    private Integer isDelete;
+
 }

+ 4 - 4
commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncomeInvoiceSettlement.java

@@ -30,10 +30,10 @@ public class InsPlyIncomeInvoiceSettlement extends BaseModel {
     private Date receivePaymentDate;
 
     @Schema(description = "回款差额")
-    private BigDecimal paymentDifference;
+    private BigDecimal settlementPaymentDifference;
 
     @Schema(description = "回款原因")
-    private String paymentReason;
+    private String settlementPaymentReason;
 
 
     @TableField(exist = false)
@@ -49,8 +49,8 @@ public class InsPlyIncomeInvoiceSettlement extends BaseModel {
         this.invoiceId = invoiceId;
         this.actualReceivedAmount = actualReceivedAmount;
         this.receivePaymentDate = receivePaymentDate;
-        this.paymentDifference = paymentDifference;
-        this.paymentReason = paymentReason;
+        this.settlementPaymentDifference = paymentDifference;
+        this.settlementPaymentReason = paymentReason;
     }
 
 }

+ 39 - 0
commons/src/main/java/com/jzg/commons/entity/finance/vo/FollowInvoiceAndSettlementVo.java

@@ -0,0 +1,39 @@
+package com.jzg.commons.entity.finance.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+
+/**
+ * 结算Vo
+ */
+@Data
+@Schema(description = "开票并结算Vo")
+public class FollowInvoiceAndSettlementVo {
+
+    /**
+     * 开票的相关信息
+     */
+    private FollowInvoiceVo invoiceVO;
+
+    @Schema(description = "开票id")
+    private String invoicingId;
+
+    @Schema(description = "实收金额")
+    private BigDecimal actualReceivedAmount;
+
+    @Schema(description = "收款日期")
+    private String receivePaymentDate;
+
+    @Schema(description = "回款差额")
+    private BigDecimal paymentDifference;
+
+    @Schema(description = "差额原因")
+    private String paymentReason;
+
+    @Schema(description = "附加id")
+    private List<String> attachmentIds;
+}

+ 42 - 0
commons/src/main/java/com/jzg/commons/entity/finance/vo/FollowInvoicePageListVo.java

@@ -0,0 +1,42 @@
+package com.jzg.commons.entity.finance.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 保费跟踪订单表 VO
+ */
+@Data
+@Schema(description = "保费跟踪订单返回视图")
+public class FollowInvoicePageListVo implements Serializable {
+
+    // 逻辑判断符: "and" 或 "or"
+    private String logicJudge;
+
+    // 搜索条件列表
+    private List<SearchVO> searchList;
+
+    @Data
+    public static class SearchVO {
+        // 定义常量
+        public static final String companyId = "companyId";
+        public static final String signDate = "signDate";
+        public static final String ptlAgreement = "ptlAgreement";
+        public static final String orderType = "orderType";
+        public static final String signYear = "signYear";
+        public static final String licenseNo = "licenseNo";
+        public static final String jqPolicyNo = "jqPolicyNo";
+        public static final String syPolicyNo = "syPolicyNo";
+        public static final String contactPerson = "contactPersonPhone";
+
+        private String name;   // 条件名
+        private String type;   // 操作符: 等于, 包含, 大于等于 等
+        private String search; // 条件值
+    }
+
+}

+ 43 - 0
commons/src/main/java/com/jzg/commons/entity/finance/vo/FollowInvoiceVo.java

@@ -0,0 +1,43 @@
+package com.jzg.commons.entity.finance.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class FollowInvoiceVo implements Serializable {
+
+    private List<InsFeeFollowOrderVo> details;
+
+    private String companyId;
+
+    @Schema(description = "开票号/发票号")
+    private String invoiceNo;
+
+    @Schema(description = "开票类型")
+    private String invoiceType;
+
+    @Schema(description = "开票方")
+    @ExcelProperty(value = "开票方")
+    private String invoiceParty;
+
+    @Schema(description = "虚增金额")
+    private BigDecimal overinflatedAmount;
+
+    @Schema(description = "差额原因")
+    private String paymentReason;
+
+    @Schema(description = "应收费用/开票金额")
+    private BigDecimal receivableSupervisePremium;
+
+    @Schema(description = "税点")
+    private String taxPoint;
+
+    @Schema(description = "选择单条还是多条:0-单条,1-多条")
+    private String selectFlag;
+
+}

+ 55 - 0
commons/src/main/java/com/jzg/commons/entity/finance/vo/InsFeeFollowOrderVo.java

@@ -0,0 +1,55 @@
+package com.jzg.commons.entity.finance.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 保费跟踪订单表 VO
+ */
+@Data
+@Schema(description = "保费跟踪订单返回视图")
+public class InsFeeFollowOrderVo implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "年份")
+    private Integer year;
+
+    @Schema(description = "月份")
+    private Integer month;
+
+    @Schema(description = "保险公司ID")
+    private String companyId;
+
+    @Schema(description = "保险公司名称")
+    private String companyName;
+
+    @Schema(description = "三级保险公司ID")
+    private String partnerCompanyId;
+
+    @Schema(description = "分支保险公司名称")
+    private String partnerCompanyName;
+
+    @Schema(description = "应收金额")
+    private BigDecimal receivablePremium;
+
+    @Schema(description = "签单时间")
+    private LocalDateTime signingTime;
+
+    @Schema(description = "已开票金额")
+    private BigDecimal invoiced;
+
+    @Schema(description = "未开票金额")
+    private BigDecimal uninvoiced;
+
+    @Schema(description = "本次开票金额")
+    private BigDecimal invoicedAmountTime;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+}

+ 27 - 0
commons/src/main/java/com/jzg/commons/entity/finance/vo/InsPlyFollowInvoiceLinkVo.java

@@ -0,0 +1,27 @@
+package com.jzg.commons.entity.finance.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 保单跟踪发票关联表 VO
+ */
+@Data
+@Schema(description = "保单跟踪发票关联返回视图")
+public class InsPlyFollowInvoiceLinkVo implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "发票ID")
+    private Long invoiceId;
+
+    @Schema(description = "跟踪单ID")
+    private Long followId;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+}

+ 13 - 3
commons/src/main/java/com/jzg/commons/entity/finance/vo/InvoiceRecordResult.java

@@ -38,7 +38,7 @@ public class InvoiceRecordResult extends PageRequest {
     /**
      * 保险公司名称及父级的名称,例如
      */
-    private String companyALlName;
+    private String companyAllName;
 
     /**
      * 开票方
@@ -55,6 +55,11 @@ public class InvoiceRecordResult extends PageRequest {
      */
     private String paymentReason;
 
+    /**
+     * 结算时的差额原因
+     */
+    private String settlementPaymentReason;
+
     /**
      * 应收项(转义)
      * 保留这个是为了兼容之前的功能
@@ -71,15 +76,20 @@ public class InvoiceRecordResult extends PageRequest {
      */
     private String invoiceRiskTypeName;
     /**
-     * 应收费用
+     * 应收费用,现在当做开票金额使用
      */
     private BigDecimal receivableSupervisePremium;
 
     /**
-     * 开票金额
+     * 实收金额
      */
     private String actualReceivedAmount;
 
+    /**
+     * 开票金额
+     */
+    private String receivableAmount;
+
     /**
      * 税点
      */

+ 2 - 2
commons/src/main/java/com/jzg/commons/entity/finance/vo/SettlementVo.java

@@ -24,10 +24,10 @@ public class SettlementVo {
     private String receivePaymentDate;
 
     @Schema(description = "回款差额")
-    private BigDecimal paymentDifference;
+    private BigDecimal settlementPaymentDifference;
 
     @Schema(description = "回款原因")
-    private String paymentReason;
+    private String settlementPaymentReason;
 
     @Schema(description = "附加id")
     private List<String> attachmentIds;

+ 6 - 1
commons/src/main/java/com/jzg/commons/exception/GlobalExceptionHandler.java

@@ -2,16 +2,19 @@ package com.jzg.commons.exception;
 
 import com.jzg.commons.core.page.HttpResult;
 import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.ConstraintViolationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.dao.DuplicateKeyException;
+import org.springframework.http.HttpStatus;
 import org.springframework.validation.BindingResult;
 import org.springframework.validation.ObjectError;
 import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
 
 import java.sql.SQLIntegrityConstraintViolationException;
 import java.util.List;
@@ -84,9 +87,11 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(Exception.class)
     @ResponseBody
-    public HttpResult handleException(HttpServletRequest request, Exception e) {
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public HttpResult handleException(HttpServletRequest request, HttpServletResponse response, Exception e) {
         String uri = request.getRequestURI();
         String method = request.getMethod();
+        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
         LOGGER.error("【系统异常】接口 [{}],[{}] 出错", method, uri, e);
         return HttpResult.error(500, "程序异常,请联系管理员");
     }

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

@@ -1,7 +1,10 @@
 package com.jzg.commons.util;
 
+import cn.hutool.core.collection.CollUtil;
+
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.List;
 
 
 /**
@@ -29,6 +32,24 @@ public class BigDecimalUtil {
         return v1.add(v2);
     }
 
+    /**
+     * 将多个值进行相加
+     *
+     * @param items 要相加的值
+     * @author lipf
+     * @date 2026/6/6 10:22
+     */
+    public static BigDecimal add(List<BigDecimal> items){
+        if(CollUtil.isEmpty(items)) {
+            return BigDecimal.ZERO;
+        }
+        BigDecimal sum = BigDecimal.ZERO;
+        for (BigDecimal item : items) {
+            sum = sum.add(item);
+        }
+        return sum;
+    }
+
     /**
      * 提供精确的减法运算
      * @param v1 被减数

+ 1 - 2
tenant/insurance/quotation-dadi/src/main/java/com/jzg/quotation/dadi/utils/GsonUtils.java → commons/src/main/java/com/jzg/commons/util/GsonUtils.java

@@ -1,7 +1,6 @@
-package com.jzg.quotation.dadi.utils;
+package com.jzg.commons.util;
 
 import com.alibaba.excel.support.cglib.beans.BeanMap;
-import com.alibaba.excel.util.StringUtils;
 import com.alibaba.fastjson.JSONArray;
 import com.google.common.collect.Maps;
 import com.google.gson.*;

+ 5 - 2
consoleStatistics/src/main/resources/application-dev.yml

@@ -6,10 +6,10 @@ spring:
       password: nacos
       config:
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 353f8f05-4d2c-4a17-b64d-b678408f5759
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 353f8f05-4d2c-4a17-b64d-b678408f5759
   config:
     import:
       - nacos:mysql
@@ -20,6 +20,9 @@ spring:
 config:
   sso: 1
 
+seata:
+  enabled: false
+
 #银联API参数配置
 unionPay:
   api:

+ 4 - 0
consoleStatistics/src/main/resources/application-prod.yml

@@ -32,3 +32,7 @@ unionPay:
     publicKey: 0414bb7e9c3914bb65b85079b1dc6e1ca2a0ee04dd9bb55b2f8d31704a6dec0cca695739e128f9c931330e1c0f493be4d56244236434c9d344c4716ba64a4470ba
     privateKey: 24ebfde60aecb81214605050763926ec342c487859a8da57d5badcd3c1559336
 
+
+
+seata:
+  enabled: false

+ 7 - 2
gateway/src/main/resources/application-dev.yml

@@ -18,12 +18,14 @@ spring:
         import-check:
           enabled: false
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
 
     gateway:
+      httpclient:
+        response-timeout: 120000
       routes:
         - id: auth_route
           uri: http://localhost:8081/
@@ -158,3 +160,6 @@ spring:
                   - /partner/rule/**
                   - /partner/**
                   - /purchaseRecord/**
+
+seata:
+  enabled: false

+ 8 - 1
gateway/src/main/resources/application-prod.yml

@@ -95,6 +95,7 @@ spring:
                   - /newCarJudgeRules/**
                   - /sysDistributionSetting/**
                   - /organization/wechat/**
+                  - /signIn/**
 
         - id: summary_route
           uri: http://172.24.144.227:9898/
@@ -106,11 +107,14 @@ spring:
                   - /supplementOrder/**
                   - /order/carModel/**
                   - /unify/**
+                  - /homepage/**
                   - /order/taskImages/**
                   - /order/qrCode/**
                   - /orderTrack/**
                   - /feeAudit/**
                   - /adjust/**
+                  - /rightsGrant/**
+                  - /insOrderRights/**
 
         - id: costsorrule_route
           uri: http://172.24.144.227:8085/
@@ -152,4 +156,7 @@ spring:
     redisson:
       address: redis://172.24.144.227:6379
       password: 123456
-      database: 0
+      database: 0
+
+seata:
+  enabled: false

+ 7 - 4
partner/src/main/resources/application-dev.yml

@@ -2,19 +2,22 @@ spring:
   cloud:
     nacos:
       server-addr: 192.168.5.250:8848
-      username:
-      password:
+      username: nacos
+      password: nacos
       config:
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
   config:
     import:
       - nacos:mysql
       - nacos:redisson
 
+seata:
+  enabled: false
+
 
 
 feign:

+ 2 - 1
partner/src/main/resources/application-prod.yml

@@ -15,7 +15,8 @@ spring:
       - nacos:mysql
       - nacos:redisson
 
-
+seata:
+  enabled: false
 
 
 feign:

+ 2 - 22
platform/src/main/resources/application-dev.yml

@@ -8,10 +8,10 @@ spring:
       password: nacos
       config:
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 353f8f05-4d2c-4a17-b64d-b678408f5759
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 353f8f05-4d2c-4a17-b64d-b678408f5759
   config:
     import:
       - nacos:mysql
@@ -23,23 +23,3 @@ config:
   sso: 1
 
 
-seata:
-  tx:
-    service:
-      grouplist: 192.168.5.250:8091
-  application-id: platform-service
-  tx-service-group: my_global_tx_group  # 事务服务组
-  enable-auto-data-source-proxy: true  # 启用数据源代理
-  service:
-    vgroup-mapping:
-      my_global_tx_group: default  # 事务组与 Seata Server 绑定
-  store:
-    mode: db  # 存储模式,可以是 db 或 file 等
-    db:
-      datasource:
-        driver-class-name: com.mysql.cj.jdbc.Driver
-        url: jdbc:mysql://39.98.239.48:3306/seata
-        username: root
-        password: '@RqGHG#KZ*C9ytkQ'
-
-

+ 2 - 21
platform/src/main/resources/application-prod.yml

@@ -23,24 +23,5 @@ spring:
 config:
   sso: 1
 
-
-seata:
-  tx:
-    service:
-      grouplist: 172.24.144.227:8091
-  application-id: platform-service
-  tx-service-group: my_global_tx_group  # 事务服务组
-  enable-auto-data-source-proxy: true  # 启用数据源代理
-  service:
-    vgroup-mapping:
-      my_global_tx_group: default  # 事务组与 Seata Server 绑定
-  store:
-    mode: db  # 存储模式,可以是 db 或 file 等
-    db:
-      datasource:
-        driver-class-name: com.mysql.cj.jdbc.Driver
-        url: jdbc:mysql://39.98.239.48:3306/seata
-        username: root
-        password: '@RqGHG#KZ*C9ytkQ'
-
-
+graylog:
+  host: 172.24.144.227

+ 1 - 0
platform/src/main/resources/application.yml

@@ -69,6 +69,7 @@ tenant:
   ignoreFunction:
     - /userInfo/getUser
     - /menu/getSystemMenu
+    - /esmInsCompany/getChildCompanyId
 
 data-scope:
   tables:

+ 1 - 1
pom.xml

@@ -284,7 +284,7 @@
       <!--seata-->
       <dependency>
         <groupId>io.seata</groupId>
-        <artifactId>seata-all</artifactId>
+        <artifactId>seata-spring-boot-starter</artifactId>
         <version>${seata.version}</version>
       </dependency>
 

+ 5 - 2
tenant/costsorrule/src/main/resources/application-dev.yml

@@ -6,10 +6,10 @@ spring:
       password:
       config:
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
   config:
     import:
       - nacos:mysql
@@ -17,3 +17,6 @@ spring:
       - nacos:minio
       - nacos:rocketmq
 
+seata:
+  enabled: false
+

+ 2 - 0
tenant/costsorrule/src/main/resources/application-prod.yml

@@ -17,3 +17,5 @@ spring:
       - nacos:minio
       - nacos:rocketmq
 
+seata:
+  enabled: false

+ 7 - 6
tenant/insurance/quotation-commons/src/main/java/com/jzg/quotation/commons/component/RequestComponent.java

@@ -6,7 +6,6 @@ import com.jzg.commons.constants.quote.enums.base.InsuranceEnum;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.quotation.commons.config.WebClientConfig;
 import com.jzg.quotation.commons.entity.CookiesRequest;
-import com.jzg.quotation.commons.utils.HtEncryptDecryptUtils;
 import com.jzg.quotation.commons.utils.InsuranceLog;
 import com.jzg.quotation.commons.utils.RequestObjectConversion;
 import lombok.RequiredArgsConstructor;
@@ -14,10 +13,11 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.core.ParameterizedTypeReference;
 import org.springframework.core.io.buffer.DataBuffer;
 import org.springframework.core.io.buffer.DataBufferUtils;
-import org.springframework.http.*;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
-import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.web.reactive.function.BodyInserters;
 import org.springframework.web.reactive.function.client.WebClient;
@@ -31,7 +31,8 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
-import java.util.*;
+import java.util.Map;
+import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
 
@@ -689,11 +690,11 @@ public class RequestComponent {
             // 判断返回的字符串是否为有效的JSON格式
             if (isValidJson(responseBody)) {
                 result = JSON.parseObject(responseBody, responseClass);
-                InsuranceLog.infoLog(InsuranceEnum.XDCX.getPinyin(), "\t---------> URl: {} \t---------> 请求参数:{}", url, BodyInserters.fromValue(body));
-                InsuranceLog.infoLog(InsuranceEnum.XDCX.getPinyin(), "\t---------> 响应参数:{}", result);
             }else{
                 result = (Response)responseBody;
             }
+            InsuranceLog.infoLog(InsuranceEnum.XDCX.getPinyin(), "\t---------> URl: {} \t---------> 请求参数:{}", url, BodyInserters.fromValue(body));
+            InsuranceLog.infoLog(InsuranceEnum.XDCX.getPinyin(), "\t---------> 响应参数:{}", result);
             // 构建返回的ResponseEntity
             HttpHeaders responseHeaders = new HttpHeaders();
             responseHeaders.setContentType(MediaType.APPLICATION_JSON);

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

@@ -16,7 +16,7 @@ import java.util.stream.Collectors;
 public final class CarModelsFilterUtils {
 
     public static <T extends CarModelsFilter> T to(List<T> carModelsFilterList, CarInfoVo carInfo) {
-        if (carModelsFilterList == null) {
+        if (carModelsFilterList == null || carModelsFilterList.isEmpty()) {
             throw new SystemException("核心查询车型失败 请联系业务人员处理");
         }
         List<T> stadarCarDTOS = new ArrayList<>();
@@ -35,7 +35,7 @@ public final class CarModelsFilterUtils {
 
         carModelsFilterList = carModelsFilterList.stream().filter(car -> carInfo.getPurchasePrice().equals(String.valueOf(car.getPurchasePrice()))).collect(Collectors.toList());
 
-        if (carModelsFilterList.isEmpty()) {
+        if (!stadarCarDTOS.isEmpty()) {
             return stadarCarDTOS.get(0);
         }
 

+ 1 - 2
tenant/insurance/quotation-dadi/src/main/java/com/jzg/quotation/dadi/utils/DaDiAESUtils.java

@@ -1,10 +1,9 @@
 package com.jzg.quotation.dadi.utils;
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
 import com.google.gson.JsonParser;
 import com.google.gson.JsonSyntaxException;
 import com.jzg.commons.text.CharsetKit;
+import com.jzg.commons.util.GsonUtils;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.SecretKeySpec;

+ 13 - 19
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/component/GuoRenCrawlerRequestComponent.java

@@ -178,9 +178,7 @@ public class GuoRenCrawlerRequestComponent {
     public GuoRenCrawlerGetLoginComInfoResponse getLoginComInfo(String username, String comCode, HttpHeaders httpHeaders) {
         String url = guoRenCrawlerProperties.getBaseUrl() + RequestUrl.GET_LOGIN_COM_INFO.getUri();
         GuoRenCrawlerGetLoginComInfoRequest guoRenCrawlerGetLoginComInfoRequest = new GuoRenCrawlerGetLoginComInfoRequest(username);
-        HttpRequest request = HttpRequest.post(url)
-                .header(httpHeaders)
-                .body(JSON.toJSONString(guoRenCrawlerGetLoginComInfoRequest));
+        HttpRequest request = HttpRequest.post(url).header(httpHeaders).body(JSON.toJSONString(guoRenCrawlerGetLoginComInfoRequest));
         HttpResponse httpResponse = request.execute();
         String body = httpResponse.body();
 
@@ -189,10 +187,11 @@ public class GuoRenCrawlerRequestComponent {
         if (body.trim().startsWith("[")) {
             getLoginComInfoResponseList = JSON.parseArray(body, GuoRenCrawlerGetLoginComInfoResponse.class);
         } else {
-            GuoRenCrawlerBaseResponse<List<GuoRenCrawlerGetLoginComInfoResponse>> response = JSON.parseObject(body, new TypeReference<>() {});
+            GuoRenCrawlerBaseResponse<List<GuoRenCrawlerGetLoginComInfoResponse>> response = JSON.parseObject(body, new TypeReference<>() {
+            });
             // 检查登录失效,返回特殊code码
             if (response != null && response.getResultCode() == 40301) {
-                throw new  TokenExpireException();
+                throw new TokenExpireException();
             }
             getLoginComInfoResponseList = response.getData();
         }
@@ -295,6 +294,9 @@ public class GuoRenCrawlerRequestComponent {
         GuoRenCrawlerGetCarPriceRequest getCarPriceRequest = new GuoRenCrawlerGetCarPriceRequest(queryCarModelData, prpTmainDTO, quoteVo, ownersAddr, relationShip);
         GuoRenCrawlerBaseResponse<List<GuoRenCrawlerGetCarPriceData>> request = request(RequestUrl.GET_CAR_PRICE, getCarPriceRequest, new ParameterizedTypeReference<>() {
         }, httpHeaders);
+        if (request.getResultCode() == 1) {
+            throw new SystemException(request.getResultMsg());
+        }
         return new GuoRenPrpTitemCarDTO(getCarPriceRequest, request.getData().get(0));
     }
 
@@ -306,7 +308,7 @@ public class GuoRenCrawlerRequestComponent {
      * @return 保费计算响应
      */
     public GuoRenCrawlerBaseResponse<GuoRenCrawlerCalculationPremiumData> calculationPremium(GuoRenCrawlerCalculationPremiumRequest crawlerCalculationPremiumRequest, HttpHeaders httpHeaders) {
-        GuoRenCrawlerBaseResponse<GuoRenCrawlerCalculationPremiumData>  request = request(RequestUrl.CALCULATION_PREMIUM, crawlerCalculationPremiumRequest, new ParameterizedTypeReference<>() {
+        GuoRenCrawlerBaseResponse<GuoRenCrawlerCalculationPremiumData> request = request(RequestUrl.CALCULATION_PREMIUM, crawlerCalculationPremiumRequest, new ParameterizedTypeReference<>() {
         }, httpHeaders);
         if (request.getResultCode() == 1 && !request.getResultMsg().contains("重复投保")) {
             throw new SystemException(request.getResultMsg());
@@ -323,8 +325,8 @@ public class GuoRenCrawlerRequestComponent {
      * @param httpHeaders               请求头
      * @return 投保单号
      */
-    public GuoRenCrawlerProposalNoSaveData proposalNoSave(GuoRenCrawlerCalculationPremiumRequest calculationPremiumRequest, GuoRenCrawlerCalculationPremiumData calculationPremium, HttpHeaders httpHeaders) {
-        GuoRenCrawlerProposalNoSaveRequest guoRenCrawlerProposalNoSaveRequest = new GuoRenCrawlerProposalNoSaveRequest(calculationPremium, calculationPremiumRequest);
+    public GuoRenCrawlerProposalNoSaveData proposalNoSave(QuoteVo quoteVo, GuoRenCrawlerCalculationPremiumRequest calculationPremiumRequest, GuoRenCrawlerCalculationPremiumData calculationPremium, HttpHeaders httpHeaders) {
+        GuoRenCrawlerProposalNoSaveRequest guoRenCrawlerProposalNoSaveRequest = new GuoRenCrawlerProposalNoSaveRequest(quoteVo, calculationPremium, calculationPremiumRequest);
         GuoRenCrawlerBaseResponse<GuoRenCrawlerProposalNoSaveData> request = request(RequestUrl.PROPOSAL_NO_SAVE, guoRenCrawlerProposalNoSaveRequest, new ParameterizedTypeReference<>() {
         }, httpHeaders);
         if (request.getResultCode() != 0) {
@@ -393,14 +395,7 @@ public class GuoRenCrawlerRequestComponent {
                     // 根据影像类型确定fileGroup
                     String fileGroup = getFileGroup(insuranceImageVo);
 
-                    Part[] parts = {
-                            filePart,
-                            new StringPart("businessType", "T"),
-                            new StringPart("businessNo", businessNo),
-                            new StringPart("operatorCode", operatorCode),
-                            new StringPart("comCode", comCode),
-                            new StringPart("fileGroup", fileGroup)
-                    };
+                    Part[] parts = {filePart, new StringPart("businessType", "T"), new StringPart("businessNo", businessNo), new StringPart("operatorCode", operatorCode), new StringPart("comCode", comCode), new StringPart("fileGroup", fileGroup)};
 
                     // 发送请求
                     String result = HttpUtil.sendPostFromFile(parts, url);
@@ -435,9 +430,8 @@ public class GuoRenCrawlerRequestComponent {
      */
     public GuoRenCrawlerSubmitUnderwriteData submitUnderwrite(String proposalNoCI, String proposalNoBI, String userCode, HttpHeaders httpHeaders) {
         GuoRenCrawlerSubmitUnderwriteRequest guoRenCrawlerSubmitUnderwriteRequest = new GuoRenCrawlerSubmitUnderwriteRequest(proposalNoCI, proposalNoBI, userCode);
-        GuoRenCrawlerBaseResponse<List<GuoRenCrawlerSubmitUnderwriteData>> response = request(RequestUrl.SUBMIT_UNDERWRITE, guoRenCrawlerSubmitUnderwriteRequest,
-                new ParameterizedTypeReference<>() {
-                }, httpHeaders);
+        GuoRenCrawlerBaseResponse<List<GuoRenCrawlerSubmitUnderwriteData>> response = request(RequestUrl.SUBMIT_UNDERWRITE, guoRenCrawlerSubmitUnderwriteRequest, new ParameterizedTypeReference<>() {
+        }, httpHeaders);
         return response.getData().get(0);
     }
 

+ 7 - 1
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/dto/CiInsureDemandDTO.java

@@ -1,6 +1,8 @@
 package com.jzg.quotation.guoren.crawler.entity.dto;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.jzg.commons.entity.quote.vo.CarInfoVo;
+import com.jzg.commons.entity.quote.vo.QuoteVo;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
@@ -417,9 +419,13 @@ public class CiInsureDemandDTO  implements Serializable {
     @JsonProperty("carOwner")
     private String carOwner;
 
-    public CiInsureDemandDTO(String demandNo, String checkCode) {
+    public CiInsureDemandDTO(String demandNo, String checkCode, QuoteVo quoteVo) {
         this.demandNo = demandNo;
         this.checkCode = checkCode;
+
+        CarInfoVo carInfoVo = quoteVo.getCarInfoVo();
+        this.seatCount = carInfoVo.getSeatCount();
+        this.tonCount = carInfoVo.getLimitLoad();
     }
 
 }

+ 1 - 1
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/dto/GuoRenBiDTO.java

@@ -126,7 +126,7 @@ public class GuoRenBiDTO {
             this.prpTmainRate = new GuoRenPrpTmainRateDTO();
         }
         this.prpTitemCar = prpTitemCar;
-        this.ciInsureDemand = new CiInsureDemandDTO("", "");
+        this.ciInsureDemand = new CiInsureDemandDTO("", "",quoteVo);
         this.ciInsureDemandPay = new ArrayList<>();
         this.ciInsureDemandWarningClaim = new ArrayList<>();
         this.prpTenGearAllocationFeeList = new ArrayList<>();

+ 1 - 1
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/dto/GuoRenCiDTO.java

@@ -136,7 +136,7 @@ public class GuoRenCiDTO {
         this.prptengageList = new ArrayList<>();
         this.prpDclause = new ArrayList<>();
         this.prpTitemCarExt = new PrpTitemCarExtDTO();
-        this.ciInsureDemand = new CiInsureDemandDTO("", "");
+        this.ciInsureDemand = new CiInsureDemandDTO("", "",quoteVo);
         this.ciInsureDemandPay = new ArrayList<>();
         this.ciInsureDemandLoss = new ArrayList<>();
         this.ciInsureDemandWarningClaim = new ArrayList<>();

+ 6 - 1
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/request/GuoRenCrawlerProposalNoSaveRequest.java

@@ -1,6 +1,7 @@
 package com.jzg.quotation.guoren.crawler.entity.request;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.jzg.commons.entity.quote.vo.QuoteVo;
 import com.jzg.quotation.guoren.crawler.entity.dto.GuoRenBcDTO;
 import com.jzg.quotation.guoren.crawler.entity.response.GuoRenCrawlerCalculationPremiumData;
 import lombok.Data;
@@ -28,12 +29,16 @@ public class GuoRenCrawlerProposalNoSaveRequest implements Serializable {
     @JsonProperty("ci")
     private GuoRenCrawlerCalculationPremiumData.CiDTO ci;
 
-    public GuoRenCrawlerProposalNoSaveRequest(GuoRenCrawlerCalculationPremiumData guoRenCrawlerCalculationPremiumData, GuoRenCrawlerCalculationPremiumRequest calculationPremiumRequest) {
+    public GuoRenCrawlerProposalNoSaveRequest(QuoteVo quoteVo, GuoRenCrawlerCalculationPremiumData guoRenCrawlerCalculationPremiumData, GuoRenCrawlerCalculationPremiumRequest calculationPremiumRequest) {
         this.bc = calculationPremiumRequest.getBc();
         this.bc.setPrpTmain(guoRenCrawlerCalculationPremiumData.getBc().getPrpTmain());
         this.bc.getPrpTmain().setCaptcha("");
         this.bc.getPrpTmain().setEProposalFlag("1");
         this.basePartVo = calculationPremiumRequest.getBasePartVo();
+        guoRenCrawlerCalculationPremiumData.getCi().getCiInsureDemand().setTonCount(quoteVo.getCarInfoVo().getTonCount());
+        guoRenCrawlerCalculationPremiumData.getCi().getCiInsureDemand().setSeatCount(quoteVo.getCarInfoVo().getSeatCount());
+        guoRenCrawlerCalculationPremiumData.getBi().getCiInsureDemand().setTonCount(quoteVo.getCarInfoVo().getTonCount());
+        guoRenCrawlerCalculationPremiumData.getBi().getCiInsureDemand().setSeatCount(quoteVo.getCarInfoVo().getSeatCount());
         this.bi = guoRenCrawlerCalculationPremiumData.getBi();
         this.ci = guoRenCrawlerCalculationPremiumData.getCi();
     }

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

@@ -132,7 +132,7 @@ public class GuoRenCrawlerRequestImpl implements GuoRenCrawlerRequest {
         // 投保验证码处理
         GuoRenCrawlerCalculationPremiumData calculationPremiumData = guoRenCrawlerQuoteProcessService.insuranceVerificationCodeProcessing(crawlerCalculationPremiumRequest, calculationPremium.getData(), headers);
         // 保存投保单号
-        GuoRenCrawlerProposalNoSaveData proposalNoSave = guoRenCrawlerRequestComponent.proposalNoSave(crawlerCalculationPremiumRequest, calculationPremiumData, headers);
+        GuoRenCrawlerProposalNoSaveData proposalNoSave = guoRenCrawlerRequestComponent.proposalNoSave(quoteVo, crawlerCalculationPremiumRequest, calculationPremiumData, headers);
         // 保存数据
         guoRenCrawlerOrderService.saveOrderInfo(quoteVo, proposalNoSave);
         return GuoRenCrawlerQuoteResultsVoBuild.buildQuoteResultsVo(quoteVo, calculationPremiumData, proposalNoSave);

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

@@ -5,12 +5,10 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.jzg.commons.constants.quote.enums.base.InsuranceEnum;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.quotation.commons.component.RequestComponent;
 import com.jzg.quotation.commons.constant.CacheConstant;
 import com.jzg.quotation.commons.service.ExteriorUtilsRequest;
-import com.jzg.quotation.commons.utils.InsuranceLog;
 import com.jzg.quotation.commons.utils.RequestObjectConversion;
 import com.jzg.quotation.hengbang.crawler.constant.RequestConstant;
 import com.jzg.quotation.hengbang.crawler.constant.enums.RequestUrl;
@@ -25,13 +23,13 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.redisson.api.RMap;
 import org.redisson.api.RedissonClient;
 import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.*;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.StringUtils;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.reactive.function.BodyInserters;
 import org.springframework.web.reactive.function.client.WebClientResponseException;
 
 import javax.crypto.Cipher;
@@ -173,10 +171,15 @@ public class HengBangCrawlerRequestComponent {
         } catch (Exception e) {
             throw new SystemException("解密失败");
         }
-        HbCrawlerQueryCarRequest.CbitDataDTO cbitDataDTO = JSONObject.parseObject(plaintext).getJSONObject("carMappings")
-                .getJSONArray("cbitData")
-                .getJSONObject(0).toJavaObject(HbCrawlerQueryCarRequest.CbitDataDTO.class);
-        return cbitDataDTO;
+        log.info("解密结果:{}", plaintext);
+        if (JSONObject.parseObject(plaintext).getJSONObject("carMappings") != null) {
+            HbCrawlerQueryCarRequest.CbitDataDTO cbitDataDTO = JSONObject.parseObject(plaintext).getJSONObject("carMappings")
+                    .getJSONArray("cbitData")
+                    .getJSONObject(0).toJavaObject(HbCrawlerQueryCarRequest.CbitDataDTO.class);
+            return cbitDataDTO;
+        } else {
+            throw new SystemException("未查询到车辆信息");
+        }
     }
 
     /**
@@ -296,6 +299,7 @@ public class HengBangCrawlerRequestComponent {
             ObjectMapper mapper = new ObjectMapper();
             mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
             String json = mapper.writeValueAsString(calculationRequest);
+            log.info("提交报价数据请求参数: {}", json);
             req.setHbEncD(HengBangAESUtil.encryptStr(json));
             HbCrawlerSaleInfoResponse response = requestComponent.toStringPost(RequestUrl.CALCULATION.getRequestUrl(hbCrawlerProperties.getBaseUrl()), req, RequestUrl.CALCULATION.getRequestDescription(), HbCrawlerSaleInfoResponse.class, headers);
             System.out.println(HengBangAESUtil.decryptStr(response.getHbEncD()));
@@ -323,6 +327,15 @@ public class HengBangCrawlerRequestComponent {
      * @param headers
      * @return
      */
+    public HbCrawlerProposalResponse carInsureItem(String plyappno, HttpHeaders headers) {
+        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
+        params.add("plyappno", plyappno);
+        requestComponent.get(RequestUrl.CARINSUREITEM.getRequestUrl(hbCrawlerProperties.getBaseUrl()), params, RequestUrl.CARINSUREITEM.getRequestDescription(), null, headers);
+        requestComponent.get(RequestUrl.CHARGINGPOSTS.getRequestUrl(hbCrawlerProperties.getBaseUrl()), params, RequestUrl.CHARGINGPOSTS.getRequestDescription(), null, headers);
+        requestComponent.get(RequestUrl.CARINSUREDEVICES.getRequestUrl(hbCrawlerProperties.getBaseUrl()), params, RequestUrl.CARINSUREDEVICES.getRequestDescription(), null, headers);
+        return proposal(plyappno, headers);
+    }
+
     public HbCrawlerProposalResponse proposal(String plyappno, HttpHeaders headers) {
         MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
         String encryptPlyappno;
@@ -395,7 +408,7 @@ public class HengBangCrawlerRequestComponent {
             log.error("恒邦calculation接口调用失败[{}]: {}", e.getStatusCode(), e.getResponseBodyAsString(StandardCharsets.UTF_8));
             if (e.getStatusCode().equals(HttpStatusCode.valueOf(500))) {
                 return JSONObject.parseObject(e.getResponseBodyAsString(StandardCharsets.UTF_8)).getString("detail");
-            } else  if (e.getStatusCode().equals(HttpStatusCode.valueOf(400))) {
+            } else if (e.getStatusCode().equals(HttpStatusCode.valueOf(400))) {
                 return JSONObject.parseObject(e.getResponseBodyAsString(StandardCharsets.UTF_8)).getString("title");
             } else {
                 throw new SystemException(e.getMessage());

+ 3 - 0
tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/constant/enums/RequestUrl.java

@@ -22,6 +22,9 @@ public enum RequestUrl implements CrawlerRequestUrlStandard {
     FIND_SALE_INFO("/backstage/api/findSaleInformationNew", "查询归属信息"),
     DEPRECIATIONRATE("/backstage/api/coredblink/depreciationrate", "获取折旧价"),
     QUOTEIMMEDIATELY("/new-insure/api/carinsurenew/quoteimmediately", "生成投保单号"),
+    CARINSUREITEM("/new-insure/api/carinsurenew/carInsureItem", "校验投保单号"),
+    CHARGINGPOSTS("/new-insure/api/carinsurenew/chargingPosts", "校验投保单号"),
+    CARINSUREDEVICES("/new-insure/api/carinsurenew/carInsureDevices", "汽车保险设备"),
     CALCULATION("/new-insure/api/carinsurenew/calculation", "提交报价数据"),
     PROPOSAL("/new-insure/api/carinsurenew/proposal", "获取报价数据"),
 

+ 50 - 19
tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/entity/request/HbCrawlerCalculationRequest.java

@@ -1,9 +1,11 @@
 package com.jzg.quotation.hengbang.crawler.entity.request;
 
 import cn.hutool.core.collection.CollUtil;
+import com.alibaba.excel.util.StringUtils;
 import com.jzg.commons.entity.quote.vo.AccidentalDrivingVo;
 import com.jzg.commons.entity.quote.vo.KindInfoVo;
 import com.jzg.commons.entity.quote.vo.QuoteVo;
+import com.jzg.commons.entity.quote.vo.RiskInfoVo;
 import com.jzg.quotation.commons.utils.DateUtil;
 import com.jzg.quotation.hengbang.crawler.entity.response.HbCrawlerRecordResponse;
 import lombok.Data;
@@ -80,6 +82,11 @@ public class HbCrawlerCalculationRequest extends HbBaseRequest {
         List<KindInfoVo> kindInfoVoList = quoteVo.getKindInfoVo();
         this.risktype = "0";
         this.begintimeBi = DateUtil.toUtcDateTime(quoteVo.getCiRiskInfoVo().getStartDate());
+        if (Objects.nonNull(quoteVo.getCiRiskInfoVo())) {
+            this.begintimeCi = DateUtil.toUtcDateTime(quoteVo.getCiRiskInfoVo().getStartDate());
+            this.begintimeAccident = DateUtil.toUtcDateTime(quoteVo.getCiRiskInfoVo().getStartDate());
+            this.risktype = "1";
+        }
         if (CollUtil.isNotEmpty(kindInfoVoList)) {
             this.epersonliabilityList = getEpersonliabilityList(kindInfoVoList);
             this.begintimeBi = DateUtil.toUtcDateTime(quoteVo.getBiRiskInfoVo().getStartDate());
@@ -89,11 +96,6 @@ public class HbCrawlerCalculationRequest extends HbBaseRequest {
 //            this.begintimeBi = DateUtil.toUtcDateTime(quoteVo.getCiRiskInfoVo().getStartDate());
             this.epersonliabilityList = new ArrayList<>();
         }
-        if (Objects.nonNull(quoteVo.getCiRiskInfoVo())) {
-            this.begintimeCi = DateUtil.toUtcDateTime(quoteVo.getCiRiskInfoVo().getStartDate());
-            this.begintimeAccident = DateUtil.toUtcDateTime(quoteVo.getCiRiskInfoVo().getStartDate());
-            this.risktype = "1";
-        }
         this.plyappno = plyappno;
         this.ecarshiptaxPart = "投保人";
         this.taxonomyCode = "02";
@@ -101,11 +103,11 @@ public class HbCrawlerCalculationRequest extends HbBaseRequest {
         this.endtimeBi = "";
         this.endtimeCi = "";
         this.expdisc = "";
-        this.accidentEpolicyFlag = "";
+        this.accidentEpolicyFlag = "Y";
         this.effectfLag = null;
         this.ecarChargingPosts = new ArrayList<>();
         this.carInsureDevices = new ArrayList<>();
-        this.accidentInsuranceList = getAccidentInsuranceList(quoteVo.getAccidentalDrivings());
+        this.accidentInsuranceList = getAccidentInsuranceList(quoteVo.getBiRiskInfoVo(), quoteVo.getAccidentalDrivings());
     }
 
     public List<EpersonliabilityDTO> getEpersonliabilityList(List<KindInfoVo> kindInfoVoList) {
@@ -122,31 +124,38 @@ public class HbCrawlerCalculationRequest extends HbBaseRequest {
         return epersonliabilityDTOList;
     }
 
-    public List<AccidentInsuranceDTO> getAccidentInsuranceList(List<AccidentalDrivingVo> accidentalDrivings) {
+    public List<AccidentInsuranceDTO> getAccidentInsuranceList(RiskInfoVo biRiskInfoVo, List<AccidentalDrivingVo> accidentalDrivings) {
         if (accidentalDrivings == null || accidentalDrivings.isEmpty()) {
             return new ArrayList<>();
         }
         List<AccidentInsuranceDTO> accidentInsuranceList = new ArrayList<>();
         for (AccidentalDrivingVo vo : accidentalDrivings) {
-            accidentInsuranceList.add(convertToAccidentInsuranceDTO(vo));
+            accidentInsuranceList.add(convertToAccidentInsuranceDTO(biRiskInfoVo, vo));
         }
         return accidentInsuranceList;
     }
 
-    public static AccidentInsuranceDTO convertToAccidentInsuranceDTO(AccidentalDrivingVo vo) {
+    public static AccidentInsuranceDTO convertToAccidentInsuranceDTO(RiskInfoVo biRiskInfoVo, AccidentalDrivingVo vo) {
         AccidentInsuranceDTO dto = new AccidentInsuranceDTO();
         dto.setNonriskcode(vo.getProductCode().split("_")[1]);
         dto.setProgramcode(vo.getProductCode());
         dto.setProgramname(vo.getProductName());
+        if (biRiskInfoVo != null && StringUtils.isNotBlank(biRiskInfoVo.getStartDate()) && StringUtils.isNotBlank(biRiskInfoVo.getEndDate())) {
+            dto.setBegintime(DateUtil.toUtcDateTime(DateUtil.getCurrentDate(1) + " 00:00:00"));
+            dto.setEndtime(DateUtil.toUtcDateTime(DateUtil.getNextYearDateStrTranfer(DateUtil.getCurrentDate(1) + " 00:00:00")));
+        }
         dto.setCompanycode("0107");
-        dto.setSuminsured(vo.getSumAmount());
-        dto.setSumgrosspremium(vo.getSumPremium());
+        dto.setRelationcarowner("a");
+        dto.setSuminsured("525000");
+        dto.setSumgrosspremium(vo.getPremium());
         dto.setIfdefault(true);
         dto.setBuynum(vo.getQuantity());
         dto.setGgriskprogramclauselist(getGgriskprogramclauseList(vo.getProductCode().split("_")[1], vo.getProductCode()));
         dto.setGgriskprogramdetailslist(getGgriskprogramdetailsList(vo.getProductCode().split("_")[1], vo.getProductCode()));
         dto.setGgriskprogramdeductiblelist(new ArrayList<>());
-        dto.setGgriskprogramparamlist(new ArrayList<>());
+        List<GgriskprogramparamlDTO> ggriskprogramparamlist = new ArrayList<>();
+        ggriskprogramparamlist.add(new GgriskprogramparamlDTO("2021-05-19T07:00:00.000+0000", "subsidy", "99.9", "0107_1119_003", "2021-05-19T07:00:00.000+0000", "1"));
+        dto.setGgriskprogramparamlist(ggriskprogramparamlist);
         return dto;
     }
 
@@ -219,10 +228,10 @@ public class HbCrawlerCalculationRequest extends HbBaseRequest {
         detail1.setSerialno(1);
         detail1.setKindcode("1119066");
         detail1.setKindname("机动车辆驾乘意外伤害保险意外身故保险责任(2024款)");
-        detail1.setKindrate("0.225");
-        detail1.setKindunitinsured("30000");
-        detail1.setKindsuminsured("240000");
-        detail1.setKindunitpremium("6.75");
+        detail1.setKindrate("0.098");
+        detail1.setKindunitinsured("50000");
+        detail1.setKindsuminsured("250000");
+        detail1.setKindunitpremium("4.9");
         detail1.setKindgrosspremium("54");
         list.add(detail1);
 
@@ -322,13 +331,13 @@ public class HbCrawlerCalculationRequest extends HbBaseRequest {
         private List<GgriskprogramclauseDTO> ggriskprogramclauselist;
         private List<Object> ggriskprogramdeductiblelist;
         private List<GgriskprogramdetailsDTO> ggriskprogramdetailslist;
-        private List<Object> ggriskprogramparamlist;
+        private List<GgriskprogramparamlDTO> ggriskprogramparamlist;
         private String familyinsureitem;
         private String gasinsureitem;
         private String risk1205field;
         private String risk1283items;
         private String drivingtype;
-        private String relationcarowner;
+        private String relationcarowner = "a";
     }
 
     @Data
@@ -347,6 +356,28 @@ public class HbCrawlerCalculationRequest extends HbBaseRequest {
         private String serialno;
     }
 
+    @Data
+    public static class GgriskprogramparamlDTO implements Serializable {
+        @Serial
+        private static final long serialVersionUID = -9183755801236888529L;
+
+        private String createtime;
+        private String paramname;
+        private String paramvalue;
+        private String programcode;
+        private String updatetime;
+        private String validind;
+
+        public GgriskprogramparamlDTO(String createtime, String paramname, String paramvalue, String programcode, String updatetime, String validind) {
+            this.createtime = createtime;
+            this.updatetime = updatetime;
+            this.paramname = paramname;
+            this.paramvalue = paramvalue;
+            this.programcode = programcode;
+            this.validind = validind;
+        }
+    }
+
     @Data
     public static class GgriskprogramdetailsDTO implements Serializable {
         @Serial

+ 4 - 15
tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/entity/response/HbCrawlerRecordResponse.java

@@ -21,16 +21,6 @@ public class HbCrawlerRecordResponse extends HbBaseResponse {
     @Serial
     private static final long serialVersionUID = 7654321987612345678L;
 
-    /**
-     * 投保单号
-     */
-    private String plyappno;
-
-    /**
-     * 交强险验证码图片(base64)
-     */
-    private String checkcodeJqImg;
-
     /**
      * 商业险投保单号
      */
@@ -51,14 +41,13 @@ public class HbCrawlerRecordResponse extends HbBaseResponse {
      */
     private String checktypeSy;
 
-    /**
-     * 商业险验证码图片(base64)
-     */
-    private String checkcodeSyImg;
-
     /**
      * 交强险验证码
      */
     private String checkcodeJq;
 
+    /**
+     * 商业险验证码
+     */
+    private String checkcodeSy;
 }

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

@@ -84,7 +84,7 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
     }
 
     @Override
-  public boolean detectLogin(DetectLoginRequest detectLoginRequest) {
+    public boolean detectLogin(DetectLoginRequest detectLoginRequest) {
         String username = detectLoginRequest.getUsername();
         String password = detectLoginRequest.getPassword();
         String authorization = hbCrawlerRequestComponent.getAuthorization(username);
@@ -157,18 +157,6 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
             status = JSONObject.parseObject(resJson).getString("status");
         }
 
-        if (errorMsg.contains("该车是转保车,请输入对应的转保验证码")) {
-            JSONObject resJsonObj = JSONObject.parseObject(resJson);
-            String responseBody = resJsonObj.getString("responseBody");
-            HbCrawlerRecordResponse recordResponse = JSONObject.parseObject(responseBody, HbCrawlerRecordResponse.class);
-            calculationRequest.setOutofwarrantytype("N");
-            calculationRequest.setTransferVehicle(recordResponse);
-
-            resJson = hbCrawlerRequestComponent.getCalculationResult(calculationRequest, headers);
-            errorMsg = JSONObject.parseObject(resJson).getString("message");
-            status = JSONObject.parseObject(resJson).getString("status");
-        }
-
         if (errorMsg.contains("重复投保")) {
             String time = extractDuplicateEndTime(errorMsg);
             if (time != null) {
@@ -181,10 +169,21 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
             status = JSONObject.parseObject(resJson).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");
+        }
+
         if (errorMsg.equals("成功")) {
             errorMsg = "成功";
             // 5.2 报价第三步,获取报价数据
-            proposalResponse = hbCrawlerRequestComponent.proposal(plyappno, headers);
+            proposalResponse = hbCrawlerRequestComponent.carInsureItem(plyappno, headers);
             HbCrawlerOrder hbCrawlerOrder = new HbCrawlerOrder();
             hbCrawlerOrder.setOrdersNo(quoteVo.getOrder().getOrdersNo());
             hbCrawlerOrder.setPlyappno(plyappno);
@@ -217,7 +216,7 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
         List<HbCrawlerCalculationRequest.AccidentInsuranceDTO> accidentList = new ArrayList<>();
         quoteVo.getAccidentalDrivings().stream()
                 .filter(e -> StrUtil.isNotEmpty(e.getProductCode()))
-                .forEach(e -> accidentList.add(HbCrawlerCalculationRequest.convertToAccidentInsuranceDTO(e)));
+                .forEach(e -> accidentList.add(HbCrawlerCalculationRequest.convertToAccidentInsuranceDTO(quoteVo.getBiRiskInfoVo(), e)));
         calculationRequest.setAccidentInsuranceList(accidentList);
     }
 
@@ -373,7 +372,7 @@ public class HengBangCrawlerRequestImpl implements HengBangCrawlerRequest {
                     String base64Str = imageDTO.getImageBase64Str();
                     if (base64Str.startsWith("http")) {
                         SysUploadFiles uploadFile = platformClient.getUploadFile(imageDTO.getImageId());
-                        base64Str =  minioUtils.getBase64Str(uploadFile.getBucketName(), uploadFile.getFileName());
+                        base64Str = minioUtils.getBase64Str(uploadFile.getBucketName(), uploadFile.getFileName());
                     }
                     fileBuff.add("data:image/jpeg;base64," + base64Str);
                     if (InsuranceImageEnum.C02.getCode().equals(insuranceImageVo.getImageCode())) {

Plik diff jest za duży
+ 3 - 4
tenant/insurance/quotation-hengbang/src/main/java/com/jzg/quotation/hengbang/crawler/utils/HengBangAESUtil.java


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

@@ -400,7 +400,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
 
         HtCrawlerQuoteResponse.DataDTO quoteResponseData = null;
 
-        maxRetries = 5;
+        maxRetries = 3;
         retryCount = 0;
         success = false;
 
@@ -480,6 +480,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                 if(decryptData.contains("errrorFlag") && decryptData.contains("carChange")) {//请修改新车购置价
                     //重新获取车型
                     //throw new RuntimeException("请修改新车购置价/重新获取车型");
+                    retryCount = maxRetries + 1;
                     HtCrawlerQuoteResponse.DataErrDTO dataErr = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataErrDTO.class);
                     throw new RuntimeException("报价失败:"+dataErr.getErroeInfo().substring(dataErr.getErroeInfo().indexOf("中国")));
                 }
@@ -487,6 +488,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                 if(decryptData.contains("erroeInfo") && decryptData.contains("投保查询校验信息不存在或已经校验成功")) {
                     //投保查询校验信息不存在或已经校验成功
                     //throw new RuntimeException("投保查询校验信息不存在或已经校验成功");
+                    retryCount = maxRetries + 1;
                     HtCrawlerQuoteResponse.DataErrDTO dataErr = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataErrDTO.class);
                     throw new RuntimeException("报价失败:"+dataErr.getErroeInfo().substring(dataErr.getErroeInfo().indexOf("中国")));
                 }
@@ -494,12 +496,14 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                 if(decryptData.contains("erroeInfo") && decryptData.contains("起保日期必须在当前日期的30天范围内")) {
                     //投保查询校验信息不存在或已经校验成功
                     //throw new RuntimeException("起保日期必须在当前日期的30天范围内");
+                    retryCount = maxRetries + 1;
                     HtCrawlerQuoteResponse.DataErrDTO dataErr = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataErrDTO.class);
                     throw new RuntimeException("报价失败:"+dataErr.getErroeInfo().substring(dataErr.getErroeInfo().indexOf("中国")));
                 }
                 //晋AB8H22存在有效交强险保单,请核实保期进入续保期后再进行投保。
                 //{"erroeInfo":"投保业务:投保业务:中国银保信交强险平台友情提示:号牌号码“晋AB8H22”的保单发生重复投保,与其重复投保的本公司的保单信息如下:车架号 LGWEF4A54HF017616;发动机号 1725032719;地区 山西。","erroeType":"01","errrorFlag":"true"}
                 if(decryptData.contains("erroeInfo") && decryptData.contains("发生重复投保") && !decryptData.contains("终保日期")) {
+                    retryCount = maxRetries + 1;
                     HtCrawlerQuoteResponse.DataErrDTO dataErr = JSONObject.parseObject(decryptData, HtCrawlerQuoteResponse.DataErrDTO.class);
                     throw new RuntimeException("报价失败:"+dataErr.getErroeInfo().substring(dataErr.getErroeInfo().indexOf("中国")));
                 }
@@ -639,7 +643,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                     System.out.println("quoteResponseData.getApplicationdto() 为 null,第 " + retryCount + " 次重试...");
 
                     if (retryCount >= maxRetries) {
-                        throw new RuntimeException("报价重试次数已达上限(" + maxRetries + "次),仍然无法获取 applicationdto");
+                        throw new RuntimeException("报价重试次数已达上限,仍然无法获取数据");
                     }
                 }
             } catch (RuntimeException e) {
@@ -650,7 +654,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                 System.out.println("报价异常,第 " + retryCount + " 次重试... 异常信息: " + e.getMessage());
 
                 if (retryCount >= maxRetries) {
-                    throw new RuntimeException("报价重试次数已达上限(" + maxRetries + "次): " + e.getMessage());
+                    throw new RuntimeException("报价重试次数已达上限: " + e.getMessage());
                 }
 
                 try {
@@ -664,7 +668,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
                 System.out.println("报价异常,第 " + retryCount + " 次重试... 异常信息: " + e.getMessage());
 
                 if (retryCount >= maxRetries) {
-                    throw new RuntimeException("报价重试次数已达上限(" + maxRetries + "次): " + e.getMessage());
+                    throw new RuntimeException("报价重试次数已达上限: " + e.getMessage());
                 }
 
                 try {

+ 60 - 45
tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/util/UploadUtil.java

@@ -3,6 +3,7 @@ package com.jzg.quotation.huatai.crawler.util;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.ByteArrayResource;
 import org.springframework.core.io.FileSystemResource;
 import org.springframework.http.*;
 import org.springframework.stereotype.Service;
@@ -70,74 +71,75 @@ public class UploadUtil {
 
     /**
      * 压缩图片并返回 Base64(对应 Python 中的 ys_photo)
+     * 【已改造】无文件创建、纯内存流、保留压缩、返回新base64
      */
     public String ysPhoto(String base64Image) throws Exception {
+        // 1. Base64 解码成字节数组
         byte[] imageBytes = Base64.getDecoder().decode(base64Image);
-        File tempDir = new File(imagesPath + "huatai/");
-        if (!tempDir.exists()) {
-            tempDir.mkdirs();
-        }
-        File zzFile = File.createTempFile(imagesPath + "huatai/" + "upload_temp_zz", ".jpg", tempDir);
-        try (FileOutputStream fos = new FileOutputStream(zzFile)) {
-            fos.write(imageBytes);
-        }
 
-        BufferedImage img = ImageIO.read(zzFile);
+        // 2. 从内存流直接读取图片(不创建文件)
+        ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
+        BufferedImage img = ImageIO.read(bais);
+        bais.close();
+
         if (img == null) {
-            throw new IOException("无法读取图片: " + zzFile.getAbsolutePath());
+            throw new IOException("无法读取图片:字节流解析失败");
         }
+
+        // 3. 转 RGB 格式(去掉透明通道,原逻辑保留)
         BufferedImage rgbImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
         rgbImg.createGraphics().drawImage(img, 0, 0, null);
 
-        File sbFile = File.createTempFile(imagesPath + "huatai/" + "upload_temp_sb", ".jpg", tempDir);
+        // 4. 【重点】内存流压缩,不写文件!
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
         ImageWriteParam param = writer.getDefaultWriteParam();
+
+        // 保留压缩
         param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
         param.setCompressionQuality(0.4f);
-        try (FileImageOutputStream output = new FileImageOutputStream(sbFile)) {
-            writer.setOutput(output);
-            writer.write(null, new IIOImage(rgbImg, null, null), param);
-        } finally {
-            writer.dispose();
-        }
 
-        byte[] resultBytes;
-        try (FileInputStream fis = new FileInputStream(sbFile)) {
-            resultBytes = new byte[(int) sbFile.length()];
-            fis.read(resultBytes);
-        }
-        sbFile.delete();
-        tempDir.delete();
+        // 输出到内存流,不写文件
+        writer.setOutput(ImageIO.createImageOutputStream(baos));
+        writer.write(null, new IIOImage(rgbImg, null, null), param);
+
+        // 5. 清理
+        writer.dispose();
+        img.flush();
+        rgbImg.flush();
+
+        // 6. 压缩后的字节 -> 转 Base64 返回
+        byte[] resultBytes = baos.toByteArray();
+        baos.close();
+
         return Base64.getEncoder().encodeToString(resultBytes);
     }
 
     /**
      * 保存图片并返回尺寸和大小(对应 Python 中的 ys_photo_new)
+     * 【已改造】:不创建任何文件,纯流处理,解决服务器文件权限问题
      */
     public double[] ysPhotoNew(byte[] imageBytes, String imgName) throws Exception {
-        File tempDir = new File(imagesPath + "huatai/");
-        if (!tempDir.exists()) {
-            tempDir.mkdirs();
-        }
-        File originalFile = File.createTempFile(imagesPath + "huatai/" + "upload_temp_" + imgName, ".jpg", tempDir.getAbsoluteFile());
-        try (FileOutputStream fos = new FileOutputStream(originalFile)) {
-            fos.write(imageBytes);
-        }
+        // ===================== 1. 直接从字节数组读取图片(零文件) =====================
+        ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
+        BufferedImage img = ImageIO.read(bais);
+        bais.close();
 
-        BufferedImage img = ImageIO.read(originalFile);
         if (img == null) {
-            throw new IOException("无法读取图片: " + originalFile.getAbsolutePath());
+            throw new IOException("无法读取图片:字节流解析失败");
         }
 
-        String width = "";
-        String height = "";
-
+        // ===================== 2. 处理透明通道(不变) =====================
         if (img.getColorModel().hasAlpha() || img.getType() == BufferedImage.TYPE_BYTE_INDEXED) {
             BufferedImage rgbImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
             rgbImg.createGraphics().drawImage(img, 0, 0, null);
             img = rgbImg;
         }
 
+        // ===================== 3. 缩放逻辑(你的原有逻辑,完全不变) =====================
+        String width = "";
+        String height = "";
+
         if (width != null && !width.isEmpty() || height != null && !height.isEmpty()) {
             int originalWidth = img.getWidth();
             int originalHeight = img.getHeight();
@@ -165,17 +167,21 @@ public class UploadUtil {
             img = resizedImg;
         }
 
-        File newFile = new File(imagesPath + "huatai/" + imgName + "new.jpg");
-        ImageIO.write(img, "jpg", newFile);
+        // ===================== 4. 【关键】纯流获取文件大小,不写磁盘 =====================
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ImageIO.write(img, "jpg", baos);
+        byte[] outputBytes = baos.toByteArray();
+        baos.close();
 
+        // 宽、高、大小(KB)—— 完全不需要文件
         int newWidth = img.getWidth();
         int newHeight = img.getHeight();
-        long fileSize = newFile.length();
-        double sizeKb = fileSize / 1024.0;
+        double sizeKb = outputBytes.length / 1024.0;
 
-        newFile.delete();
-        tempDir.delete();
+        // ===================== 5. 释放资源 =====================
+        img.flush();
 
+        // 返回和原来完全一样:[宽度,高度,大小KB]
         return new double[]{newWidth, newHeight, sizeKb};
     }
 
@@ -281,7 +287,7 @@ public class UploadUtil {
         double[] whSize = ysPhotoNew(originalBytes, filename);
         double fileSizeKb = whSize[2];
 
-        File uploadFile = new File(imagesPath + "huatai/" + filename + "new.jpg");
+        //File uploadFile = new File(imagesPath + "huatai/" + filename + "new.jpg");
 
         // 2. 上传文件
         HttpHeaders uploadHeaders = new HttpHeaders();
@@ -307,7 +313,16 @@ public class UploadUtil {
         String uploadUrl = "https://img-web.pc.ehuatai.com/app/ImgFileUpload/upload.do";
 
         MultiValueMap<String, Object> uploadBody = new LinkedMultiValueMap<>();
-        uploadBody.add("file", new FileSystemResource(uploadFile));
+        //uploadBody.add("file", new FileSystemResource(uploadFile));
+        // ByteArrayResource 代替 FileSystemResource,不需要创建文件
+        ByteArrayResource resource = new ByteArrayResource(originalBytes) {
+            @Override
+            public String getFilename() {
+                // 必须重写,否则上传文件名会为null
+                return filename + ".jpg";
+            }
+        };
+        uploadBody.add("file", resource);
         uploadBody.add("token", this.token);
         uploadBody.add("bucket", getUpdataValue("typeTreeNodeVo.bucket"));
         uploadBody.add("storageType", getUpdataValue("storageType"));

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

@@ -972,7 +972,7 @@ public class RenBaoNewCrawlerRequestBuilder {
     }
 
     /**
-     * 将险种信息列表转换为Map列表(使用反射,不依赖 KindInfoVo 的具体实现)
+     * 将险种信息列表转换为Map列表
      *
      * @param kindList 险种信息列表
      * @return Map列表,每个元素包含 kindCode, kindName, amount, serviceTimes, deductibleRate 等字段
@@ -1178,6 +1178,7 @@ public class RenBaoNewCrawlerRequestBuilder {
         Map<String, Object> prpCmain = new HashMap<>();
 
         // ========== 基础字段 ==========
+        prpCmain.put("newCarFlag", "0");
         prpCmain.put("activeNames", Arrays.asList("0"));
         prpCmain.put("showCarCheckTimeFlag", false);
         prpCmain.put("userName", "");
@@ -1385,9 +1386,9 @@ public class RenBaoNewCrawlerRequestBuilder {
         prpCmain.put("inputFlag", "");
         prpCmain.put("sumInsuredType", "");
         prpCmain.put("agriFlag", "");
-        prpCmain.put("preDiscount", null);
-        prpCmain.put("adjustSelfPricingDiscount", null);
-        prpCmain.put("cashComMarketRateDownRatio", null);
+        prpCmain.put("preDiscount", "");
+        prpCmain.put("adjustSelfPricingDiscount", "");
+        prpCmain.put("cashComMarketRateDownRatio", "");
         prpCmain.put("preDiscountOld", "");
         prpCmain.put("wufubaoType", "");
         prpCmain.put("isDisabled", true);
@@ -1532,7 +1533,7 @@ public class RenBaoNewCrawlerRequestBuilder {
         prpCmain.put("isShowTruckNetModel", true);
 
         // ========== 文件相关 ==========
-        prpCmain.put("fileName", null);
+        prpCmain.put("fileName",new ArrayList<>() );
         prpCmain.put("handler1CodeDesToSearch", null);
 
         // ========== 支付账户 ==========
@@ -3018,10 +3019,10 @@ public class RenBaoNewCrawlerRequestBuilder {
         Object chooseFlag = quoteVo.getCiRiskInfoVo() != null ? "on" : false;
         itemKindCI.put("chooseFlag", chooseFlag);
         itemKindCI.put("amount", "200000.00");
-        itemKindCI.put("kindPremium", "");
+        itemKindCI.put("kindPremium", 570);
         itemKindCI.put("kindCode", "051074");
         itemKindCI.put("kindName", "机动车交通事故责任强制保险");
-        itemKindCI.put("quantity", "");
+        //itemKindCI.put("quantity", "");
         itemKindCI.put("allNetPremium", "");
         itemKindCI.put("allTaxFee", "");
         itemKindCI.put("changeFlag", false);
@@ -3034,8 +3035,8 @@ public class RenBaoNewCrawlerRequestBuilder {
         itemKindCI.put("riskCode", "DAA");
         itemKindCI.put("showDWXKindFlag", false);
         itemKindCI.put("newInsertFlag", "");
-        itemKindCI.put("unitAmount", "");
-        itemKindCI.put("modeCode", "");
+        // itemKindCI.put("unitAmount", "");
+        //itemKindCI.put("modeCode", "");
         itemKindCI.put("rationType", "");
         itemKindCI.put("rationComCode", "");
 

+ 76 - 115
tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestImpl.java

@@ -339,7 +339,7 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
                 request.getPrpCmainCI().put("startMinute", startMinute);
 
                 // 终保日期:起保日期 + 364天
-                java.time.LocalDateTime endDateTime = ciDateTime.plusDays(364);
+                java.time.LocalDateTime endDateTime = ciDateTime.plusDays(365);
 
                 // 特殊处理:如果终保时间是 00:00,调整为前一天的 24:00
                 String endDateStr;
@@ -348,8 +348,8 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
                 if (endDateTime.getHour() == 0 && endDateTime.getMinute() == 0) {
                     java.time.LocalDateTime adjustedEndDateTime = endDateTime.minusDays(1);
                     endDateStr = adjustedEndDateTime.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
-                    endHour = "24";
-                    endMinute = "00";
+                    endHour = "23";
+                    endMinute = "59";
                 } else {
                     endDateStr = endDateTime.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                     endHour = String.format("%02d", endDateTime.getHour());
@@ -631,50 +631,59 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
                 headers
         );
 
+        // ① 校验查询订单接口返回 —  :先检查message是否为Success
+        if (orderInfo == null || !"Success".equals(orderInfo.get("message"))) {
+            String statusText = orderInfo != null ? (String) orderInfo.get("statusText") : "查询订单响应为空";
+            throw new SystemException("查询订单异常: " + statusText);
+        }
+
+        // ② 获取result列表并校验长度 —  :只能为1或2条
+        Map<String, Object> data = (Map<String, Object>) orderInfo.get("data");
+        if (data == null || !data.containsKey("result")) {
+            throw new SystemException("查询订单异常: 未查询到订单");
+        }
+        List<Map<String, Object>> itemList = (List<Map<String, Object>>) data.get("result");
+        if (itemList == null || itemList.isEmpty()) {
+            throw new SystemException("查询订单异常: 未查询到订单");
+        }
+        if (itemList.size() > 2) {
+            throw new SystemException("查询订单异常");
+        }
+
+        // ③ 提取交强/商业单号,以及驾意险信息 —  
         String syProposalNo = "";
         String jqProposalNo = "";
-        List<String> documentnolist = new ArrayList<>();
         List<Map<String, String>> unitNoCarDataList = new ArrayList<>();
-
-        if (orderInfo != null && orderInfo.containsKey("data")) {
-            Map<String, Object> data = (Map<String, Object>) orderInfo.get("data");
-            if (data != null && data.containsKey("result")) {
-                List<Map<String, Object>> itemList = (List<Map<String, Object>>) data.get("result");
-                if (itemList != null && !itemList.isEmpty()) {
-                    for (Map<String, Object> item : itemList) {
-                         businessNo = (String) item.get("businessNo");
-                        if (businessNo != null) {
-                            if (businessNo.contains("DAA")) {
-                                syProposalNo = businessNo;
-                                documentnolist.add(businessNo);
-                            } else if (businessNo.contains("DZA")) {
-                                jqProposalNo = businessNo;
-                                documentnolist.add(businessNo);
-                            }
-                        }
-                    }
-
-                    Map<String, Object> firstItem = itemList.get(0);
-                    if (firstItem.containsKey("relationUPolicyInfolist")) {
-                        List<Map<String, Object>> relationList = (List<Map<String, Object>>) firstItem.get("relationUPolicyInfolist");
-                        if (relationList != null) {
-                            for (Map<String, Object> item : relationList) {
-                                String connectProposalNo = (String) item.get("connectProposalNo");
-                                if (connectProposalNo != null && !connectProposalNo.isEmpty()) {
-                                    documentnolist.add(connectProposalNo);
-                                    if (connectProposalNo.contains("EAD")) {
-                                        unitNoCarDataList.add(Map.of("uniteProposalNo", connectProposalNo, "riskCode", "EAD"));
-                                    } else if (connectProposalNo.contains("EBS")) {
-                                        unitNoCarDataList.add(Map.of("uniteProposalNo", connectProposalNo, "riskCode", "EBS"));
-                                    }
-                                }
-                            }
+        for (Map<String, Object> item : itemList) {
+            String bNo = (String) item.get("businessNo");
+            if (bNo == null) {
+                continue;
+            }
+            if (bNo.contains("DAA")) {
+                syProposalNo = bNo;
+            } else if (bNo.contains("DZA")) {
+                jqProposalNo = bNo;
+            }
+        }
+        // 提取驾意险关联单号(从relationUPolicyInfolist)
+        Map<String, Object> firstItem = itemList.get(0);
+        if (firstItem.containsKey("relationUPolicyInfolist")) {
+            List<Map<String, Object>> relationList = (List<Map<String, Object>>) firstItem.get("relationUPolicyInfolist");
+            if (relationList != null) {
+                for (Map<String, Object> relItem : relationList) {
+                    String connectProposalNo = (String) relItem.get("connectProposalNo");
+                    if (connectProposalNo != null && !connectProposalNo.isEmpty()) {
+                        if (connectProposalNo.contains("EAD")) {
+                            unitNoCarDataList.add(Map.of("uniteProposalNo", connectProposalNo, "riskCode", "EAD"));
+                        } else if (connectProposalNo.contains("EBS")) {
+                            unitNoCarDataList.add(Map.of("uniteProposalNo", connectProposalNo, "riskCode", "EBS"));
                         }
                     }
                 }
             }
         }
 
+        // ④ 提交核保
         RenBaoNewCrawlerUnderwritingRequest uwRequest = new RenBaoNewCrawlerUnderwritingRequest();
         uwRequest.setPrpbi(syProposalNo);
         uwRequest.setPrpci(jqProposalNo);
@@ -687,17 +696,22 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
         RenBaoNewCrawlerUnderwritingResponse uwResponse = requestComponent.submitUnderwriting(uwRequest, headers);
         log.info("Underwriting response: {}", JSONUtil.toJsonStr(uwResponse));
 
+        // ⑤ 校验核保返回 —  :检查message
         if (uwResponse == null || !"Success".equals(uwResponse.getMessage())) {
             String errorMsg = uwResponse != null ? uwResponse.getStatusText() : "核保响应为空";
             throw new SystemException("提交核保异常: " + errorMsg);
         }
 
+        // ⑥ 解析核保结果 —  :从undwrtForPassList和unitNoCarUnderWriteStateList中收集状态和错误信息
+        //    同时在这里构建documentnolist(支付接口用,必须在核保成功后才收集)
         String syState = "";
         String jqState = "";
         String eadState = "";
         String ebsState = "";
         String errorMessage = "";
         boolean errorFlag = false;
+        List<String> documentnolist = new ArrayList<>();
+        List<String> jyxBusinessNoList = new ArrayList<>();
 
         if (uwResponse.getData() != null && uwResponse.getData().getUndwrtForPassList() != null) {
             for (RenBaoNewCrawlerUnderwritingResponse.UndwrtForPassItem item : uwResponse.getData().getUndwrtForPassList()) {
@@ -707,9 +721,15 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
                     continue;
                 }
                 if (item.getProposalNo().contains("DAA")) {
+                    // 商业险
+                    syProposalNo = item.getProposalNo();
+                    documentnolist.add(syProposalNo);
                     syState = item.getUndwrtFlag() != null ? item.getUndwrtFlag() : item.getState();
                     errorMessage += "商业提核结果:" + item.getHandleText() + "; ";
                 } else if (item.getProposalNo().contains("DZA")) {
+                    // 交强险
+                    jqProposalNo = item.getProposalNo();
+                    documentnolist.add(jqProposalNo);
                     jqState = item.getUndwrtFlag() != null ? item.getUndwrtFlag() : item.getState();
                     errorMessage += "交强提核结果:" + item.getHandleText() + "; ";
                 }
@@ -724,43 +744,34 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
                     continue;
                 }
                 if (item.getBusinessNo().contains("EAD")) {
+                    // 驾意险EAD
+                    documentnolist.add(item.getBusinessNo());
+                    jyxBusinessNoList.add(item.getBusinessNo());
                     eadState = item.getState();
                     errorMessage += "驾意险EAD提核结果:" + item.getContent() + "; ";
                 } else if (item.getBusinessNo().contains("EBS")) {
+                    // 驾意险EBS
+                    documentnolist.add(item.getBusinessNo());
+                    jyxBusinessNoList.add(item.getBusinessNo());
                     ebsState = item.getState();
                     errorMessage += "驾意险EBS提核结果:" + item.getContent() + "; ";
                 }
             }
         }
 
+        // ⑦ errorFlag为true时直接抛异常 —  
         if (errorFlag) {
             throw new SystemException(errorMessage);
         }
 
-        if ("2".equals(jqState) || "2".equals(syState)) {
-            StringBuilder detailErrorMessage = new StringBuilder();
-            if (!jqProposalNo.isEmpty() && "2".equals(jqState)) {
-                Map<String, Object> jqUndwrtDetail = requestComponent.viewUndwrt(jqProposalNo, loginInfo.getUserCode(), headers);
-                String jqHandleText = parseViewUndwrtHandleText(jqUndwrtDetail);
-                detailErrorMessage.append("交强返回:").append(jqHandleText != null ? jqHandleText : errorMessage).append("; ");
-            }
-            if (!syProposalNo.isEmpty() && "2".equals(syState)) {
-                Map<String, Object> syUndwrtDetail = requestComponent.viewUndwrt(syProposalNo, loginInfo.getUserCode(), headers);
-                String syHandleText = parseViewUndwrtHandleText(syUndwrtDetail);
-                detailErrorMessage.append("商业返回:").append(syHandleText != null ? syHandleText : errorMessage);
-            }
-            if (detailErrorMessage.length() > 0) {
-                throw new SystemException(detailErrorMessage.toString());
-            }
-            throw new SystemException(errorMessage);
-        }
-
+        // ⑧ 交强险/商业险状态校验 —  :只允许 3/4/5/9
         if (!jqState.isEmpty() && !"3".equals(jqState) && !"4".equals(jqState) && !"5".equals(jqState) && !"9".equals(jqState)) {
             throw new SystemException(errorMessage);
         }
         if (!syState.isEmpty() && !"3".equals(syState) && !"4".equals(syState) && !"5".equals(syState) && !"9".equals(syState)) {
             throw new SystemException(errorMessage);
         }
+        // 驾意险状态校验 —  :只允许 3/4/5(驾意险没有9状态)
         if (!eadState.isEmpty() && !"3".equals(eadState) && !"4".equals(eadState) && !"5".equals(eadState)) {
             throw new SystemException(errorMessage);
         }
@@ -768,80 +779,30 @@ public class RenBaoNewCrawlerRequestImpl implements RenBaoNewCrawlerRequest {
             throw new SystemException(errorMessage);
         }
 
+        // 状态为9(待人工核保)—  :直接返回
         if ("9".equals(jqState) || "9".equals(syState)) {
-            String processInfo = "";
-            if (!jqProposalNo.isEmpty() && "9".equals(jqState)) {
-                Map<String, Object> jqProcessInfo = requestComponent.getUndwrtProcessInfo(jqProposalNo, headers);
-                String jqMsg = parseUndwrtProcessInfoMsg(jqProcessInfo);
-                if (jqMsg != null && !jqMsg.isEmpty()) {
-                    processInfo = jqMsg;
-                }
-            }
-            if (!syProposalNo.isEmpty() && "9".equals(syState)) {
-                Map<String, Object> syProcessInfo = requestComponent.getUndwrtProcessInfo(syProposalNo, headers);
-                String syMsg = parseUndwrtProcessInfoMsg(syProcessInfo);
-                if (syMsg != null && !syMsg.isEmpty()) {
-                    processInfo = processInfo.isEmpty() ? syMsg : (processInfo + "; " + syMsg);
-                }
-            }
-            String finalErrorMessage = processInfo.isEmpty() ? errorMessage : processInfo;
             UnderwritingResultsVo resultsVo = new UnderwritingResultsVo();
             resultsVo.setUnderwritingStatus(InsOrderStatusEnum.STAFF_PROCESS);
-            resultsVo.setUnderwritingResult(finalErrorMessage);
+            resultsVo.setUnderwritingResult(errorMessage);
+            resultsVo.setJqApplyNo(jqProposalNo);
+            resultsVo.setSyApplyNo(syProposalNo);
+            resultsVo.setJyApplyNo(jyxBusinessNoList.isEmpty() ? "" : String.join(",", jyxBusinessNoList));
             return resultsVo;
         }
 
+        // ⑩ 正常核保通过 — 获取支付链接(带重试,与Python的get_pay_url循环一致)
         String payUrl = getPaymentUrlWithRetry(documentnolist, loginInfo, headers);
 
         UnderwritingResultsVo resultsVo = new UnderwritingResultsVo();
         resultsVo.setPaymentLink(payUrl);
         resultsVo.setUnderwritingStatus(InsOrderStatusEnum.PAY_PENDING);
         resultsVo.setUnderwritingResult("操作成功");
+        resultsVo.setJqPolicyno(jqProposalNo);
+        resultsVo.setSyPolicyno(syProposalNo);
+        resultsVo.setJyPolicyno(jyxBusinessNoList.isEmpty() ? "" : String.join(",", jyxBusinessNoList));
         return resultsVo;
     }
 
-    private String parseViewUndwrtHandleText(Map<String, Object> response) {
-        if (response == null) {
-            return null;
-        }
-        Object dataObj = response.get("data");
-        if (dataObj == null) {
-            return null;
-        }
-        if (dataObj instanceof List) {
-            @SuppressWarnings("unchecked")
-            List<Map<String, Object>> dataList = (List<Map<String, Object>>) dataObj;
-            if (!dataList.isEmpty()) {
-                Object handleText = dataList.get(0).get("handleText");
-                return handleText != null ? handleText.toString() : null;
-            }
-        }
-        return null;
-    }
-
-    private String parseUndwrtProcessInfoMsg(Map<String, Object> response) {
-        if (response == null) {
-            return null;
-        }
-        Object dataObj = response.get("data");
-        if (dataObj == null || !(dataObj instanceof Map)) {
-            return null;
-        }
-        @SuppressWarnings("unchecked")
-        Map<String, Object> data = (Map<String, Object>) dataObj;
-        Object underwritingInfosObj = data.get("underwritingInfos");
-        if (underwritingInfosObj == null || !(underwritingInfosObj instanceof List)) {
-            return null;
-        }
-        @SuppressWarnings("unchecked")
-        List<Map<String, Object>> underwritingInfos = (List<Map<String, Object>>) underwritingInfosObj;
-        if (!underwritingInfos.isEmpty()) {
-            Object msg = underwritingInfos.get(0).get("msg");
-            return msg != null ? msg.toString() : null;
-        }
-        return null;
-    }
-
     private String getPaymentUrlWithRetry(List<String> documentnolist, LoginInfoBO loginInfo, HttpHeaders headers) {
         RenBaoNewCrawlerPayRequest payRequest = new RenBaoNewCrawlerPayRequest();
         payRequest.setDocumentnolist(documentnolist);

+ 9 - 3
tenant/insurance/quotation-summary/pom.xml

@@ -106,12 +106,12 @@
             <version>1.0-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
-        <dependency>
+        <!--<dependency>
             <groupId>com.jzg</groupId>
             <artifactId>quotation-zijin</artifactId>
             <version>1.0-SNAPSHOT</version>
             <scope>compile</scope>
-        </dependency>
+        </dependency>-->
         <dependency>
             <groupId>com.jzg</groupId>
             <artifactId>quotation-hengbang</artifactId>
@@ -164,6 +164,12 @@
             <version>1.0-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
+        <!--<dependency>
+            <groupId>com.jzg</groupId>
+            <artifactId>quotation-yongan</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>-->
 
         <!-- thymeleaf 模版类 -->
         <dependency>
@@ -179,7 +185,7 @@
 
         <dependency>
             <groupId>io.seata</groupId>
-            <artifactId>seata-all</artifactId>
+            <artifactId>seata-spring-boot-starter</artifactId>
         </dependency>
     </dependencies>
     <build>

+ 16 - 0
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/client/SeataTestOrgClient.java

@@ -0,0 +1,16 @@
+package com.jzg.quotation.summary.client;
+
+import com.jzg.commons.core.page.HttpResult;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@FeignClient(name = "jzg-organization", url = "http://localhost:8084/")
+public interface SeataTestOrgClient {
+
+    @GetMapping("/tenant/seataTestWrite")
+    HttpResult<String> seataTestWrite(@RequestParam("name") String name);
+
+    @GetMapping("/tenant/seataTestWriteFail")
+    HttpResult<String> seataTestWriteFail(@RequestParam("name") String name);
+}

+ 6 - 1
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/InsFeeAuditController.java

@@ -38,11 +38,14 @@ public class InsFeeAuditController {
 
     /**
      * 订单列表分页
+     *
+     * 支持查询租户下的所有数据,或者租户下,指定业务员领取后的数据。
      * @return HttpResult<Page<FeeAuditVo>>
      */
     @PostMapping("/auditList")
     @Operation(summary = "订单审核列表分页")
     public HttpResult<Page<FeeAuditVo>> auditList(@RequestBody FeeAuditParam param){
+        log.info("订单审核列表分页=[{}]", JSONUtil.toJsonStr(param));
         param.setSystemCode(baseController.getUserSystemCode());
         Page<FeeAuditVo> result = insFeeAuditService.queryPage(param);
         return HttpResult.ok(result);
@@ -77,7 +80,9 @@ public class InsFeeAuditController {
     @GetMapping("/isLock")
     @Operation(summary = "判断领取按钮是否锁定")
     public HttpResult<Boolean> isLock(String orderNo){
-        return HttpResult.ok(insFeeAuditService.isLock(orderNo));
+        Boolean lock = insFeeAuditService.isLock(orderNo);
+        String msg = lock ? "订单已被领取":"";
+        return HttpResult.ok(msg, lock);
     }
 
     /**

+ 70 - 0
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/SeataTestController.java

@@ -0,0 +1,70 @@
+package com.jzg.quotation.summary.controller;
+
+import com.jzg.commons.core.page.HttpResult;
+import com.jzg.quotation.summary.client.SeataTestOrgClient;
+import io.seata.core.context.RootContext;
+import io.seata.spring.annotation.GlobalTransactional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/seataTest")
+public class SeataTestController {
+
+    private static final Logger log = LoggerFactory.getLogger(SeataTestController.class);
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Autowired
+    private SeataTestOrgClient seataTestOrgClient;
+
+    @GetMapping("/success")
+    @GlobalTransactional(rollbackFor = Exception.class)
+    public HttpResult<String> testSuccess(@RequestParam(defaultValue = "test") String name) {
+        String xid = RootContext.getXID();
+        log.info("【quotation-summary 发起方】开启全局事务,XID=[{}]", xid);
+
+        jdbcTemplate.update("INSERT INTO seata_test (name, source) VALUES (?, ?)", name, "quotation-summary");
+        log.info("【quotation-summary 发起方】本地写入成功");
+
+        HttpResult<String> result = seataTestOrgClient.seataTestWrite(name);
+        log.info("【quotation-summary 发起方】远程调用 organization 结果:{}", result);
+
+        return HttpResult.ok("分布式事务测试-成功路径,XID=" + xid + ",org结果=" + result.getMsg());
+    }
+
+    @GetMapping("/rollback")
+    @GlobalTransactional(rollbackFor = Exception.class)
+    public HttpResult<String> testRollback(@RequestParam(defaultValue = "test") String name) {
+        String xid = RootContext.getXID();
+        log.info("【quotation-summary 发起方】开启全局事务(回滚测试),XID=[{}]", xid);
+
+        jdbcTemplate.update("INSERT INTO seata_test (name, source) VALUES (?, ?)", name, "quotation-summary-rollback");
+        log.info("【quotation-summary 发起方】本地写入成功,即将调用 organization 故意失败接口");
+
+        HttpResult<String> remoteResult = null;
+        try {
+            remoteResult = seataTestOrgClient.seataTestWriteFail(name);
+        } catch (Exception e) {
+            log.info("【quotation-summary 发起方】Feign 调用异常:{},分布式事务将回滚", e.getMessage());
+            throw e;
+        }
+
+        // 双重保障:即使 Feign 没抛异常,检查返回码,非 200 也触发回滚
+        if (remoteResult == null || remoteResult.getCode() != 200) {
+            String errMsg = remoteResult != null ? remoteResult.getMsg() : "远程调用返回为空";
+            log.info("【quotation-summary 发起方】远程调用返回错误码 [{}]:{},手动触发回滚",
+                    remoteResult != null ? remoteResult.getCode() : "null", errMsg);
+            throw new RuntimeException("远程调用失败: " + errMsg);
+        }
+
+        return HttpResult.ok("不会走到这里");
+    }
+}

+ 8 - 4
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/UnifyOrderManager.java

@@ -236,11 +236,11 @@ public class UnifyOrderManager {
     @PostMapping("/quote")
     @QuoteVerification
     public HttpResult<QuoteResultsVo> quote(@RequestBody QuoteVo quoteVo) throws Exception {
-        log.info("进行车险报价:quoteVo=[{}]", JSONUtil.toJsonStr(quoteVo));
+        String systemCode = getSystemCode();
+        log.info("进行车险报价:systemCode=[{}],quoteVo=[{}]",systemCode, JSONUtil.toJsonStr(quoteVo));
         HttpResult<EsmInsCompany> esmInsCompanyHttpResult = platformClient.findById(quoteVo.getOrder().getInsuranceCompanyCode());
         log.info("车险报价:esmInsCompanyHttpResult=[{}]", JSONUtil.toJsonStr(esmInsCompanyHttpResult));
         AssertionUtils.isFail(esmInsCompanyHttpResult.getCode() != 200, esmInsCompanyHttpResult.getMsg());
-        String systemCode = getSystemCode();
         String companyName = CompanyConstants.getCompanyNameByCode(esmInsCompanyHttpResult.getData().getCompanyCode());
 
         log.info("查询保司配置的协议:systemCode=[{}],companyId=[{}]", systemCode, esmInsCompanyHttpResult.getData().getId());
@@ -267,7 +267,7 @@ public class UnifyOrderManager {
                 .getApiType(), sysQuoteConfigService, sysQuoteRedisService, platformClient, baseController);
 
         // 生成主订单及子订单  返回子订单id
-        String ordersNo = insOrdersService.generateOrders(quoteVo, companyName);
+        String ordersNo = insOrdersService.generateOrders(quoteVo, companyName,systemCode);
 
         // 添加车辆没有的参数: 车辆明细、
         QuoteInfoParameterFilling.setParameterSettings(quoteVo, esmInsCompanyHttpResult.getData().getCompanyCode());
@@ -390,7 +390,11 @@ public class UnifyOrderManager {
             log.error("车险报价异常", e);
             Throwable root = e.getCause() != null ? e.getCause() : e;
             String errMsg = root.getMessage() != null ? root.getMessage() : "系统异常,报价失败";
-            insOrdersService.orderAfterUpdateFailedQuote(ordersNo, errMsg);
+            try {
+                insOrdersService.orderAfterUpdateFailedQuote(ordersNo, errMsg);
+            } catch (Exception ex) {
+                log.error("更新报价失败状态异常, ordersNo: {}", ordersNo, ex);
+            }
             throw new SystemException(errMsg);
         }
         return quoteResultsVo.getQuoteStatusEnum()

+ 3 - 2
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/InsOrdersService.java

@@ -57,11 +57,12 @@ public interface InsOrdersService extends IService<InsOrders> {
     /**
      * 根据报价参数生成订单  返回订单号
      *
-     * @param quoteVo 报价信息
+     * @param quoteVo     报价信息
      * @param companyName 保险公司名称
+     * @param systemCode 租户代码
      * @return String
      */
-    String generateOrders(QuoteVo quoteVo, String companyName);
+    String generateOrders(QuoteVo quoteVo, String companyName, String systemCode);
 
 
     /**

+ 182 - 146
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsFeeAuditServiceImpl.java

@@ -387,7 +387,8 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
 
     @Override
     public void verifyOrder(VerifyOrderParam param) {
-        log.info("审核订单. param=[{}]", JSONUtil.toJsonStr(param));
+        String systemCode = baseController.getSystemCode();
+        log.info("审核订单.systemCode=[{}], param=[{}]",systemCode, JSONUtil.toJsonStr(param));
         InsFeeAudit audit = this.getById(param.getOrderNo());
         if(audit == null){
             throw new ServiceException("要审核的订单不存在");
@@ -413,7 +414,7 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
             AssertionUtils.isFail(true, "订单已被"+audit.getReceiveUser()+"领取返回待审核订单列表!");
         }
         InsFeeOrders feeOrders = null;
-        SysUserAccount userAccount = null,distributor1 = null,distributor2 = null,distributor3 = null;
+        SysUserAccount userAccount = null;
         if(param.getAuditStatus().equals(1)){
             List<SysUserAccountIncomeLog> incomeLogs = new ArrayList<>();
             StateResult stateFailResult = auditStateListener.statusChange(AuditState.AuditStateEnum.SUCCESS_AUDIT,audit.getAuditStatus()+"");
@@ -428,108 +429,11 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
 
             //计算出口费用减去实际花费的权益金额
             try{
-
-                // 获取权益列表
-                List<InsOrderRights> rightsList = insOrderRightsMapper.queryByOrderNo(param.getOrderNo());
-                List<InsOrderRightsPackage> rightsPackageList = insOrderRightsPackageMapper.queryByOrderNo(param.getOrderNo());
-
-                if(CollUtil.isNotEmpty(rightsList) || CollUtil.isNotEmpty(rightsPackageList)){
-                    // 计算权益金额 = (出口费用总和) - (出口留点费用总和)
-                    BigDecimal jqExportTotal = Optional.ofNullable(feeOrders.getJqExportPremium()).orElse(BigDecimal.ZERO);
-                    BigDecimal syExportTotal = Optional.ofNullable(feeOrders.getSyExportPremium()).orElse(BigDecimal.ZERO);
-                    BigDecimal jyExportTotal = Optional.ofNullable(feeOrders.getJyExportPremium()).orElse(BigDecimal.ZERO);
-                    BigDecimal jqExportKeepPoint = Optional.ofNullable(feeOrders.getJqExportKeepPointPremium()).orElse(BigDecimal.ZERO);
-                    BigDecimal syExportKeepPoint = Optional.ofNullable(feeOrders.getSyExportKeepPointPremium()).orElse(BigDecimal.ZERO);
-                    BigDecimal jyExportKeepPoint = Optional.ofNullable(feeOrders.getJyExportKeepPointPremium()).orElse(BigDecimal.ZERO);
-                    BigDecimal equityAmount = jqExportTotal.add(syExportTotal).add(jyExportTotal)
-                            .subtract(jqExportKeepPoint.add(syExportKeepPoint).add(jyExportKeepPoint));
-
-                    // 权益金额大于 0,计算实际花费的权益金额并发放权益券
-                    if (equityAmount.compareTo(BigDecimal.ZERO) > 0) {
-                        // 初始化变量
-                        BigDecimal rightsTotalAmount = BigDecimal.ZERO;
-                        BigDecimal rightsPackageTotalAmount = BigDecimal.ZERO;
-                        ChannelRightsApiGrantReceiverDTO receiverDTO = new ChannelRightsApiGrantReceiverDTO();
-                        List<ChannelRightsGrantRightsDTO> rightsRequest = new ArrayList<>();
-
-                        // 遍历权益单品,计算总金额并构建请求参数
-                        if (CollUtil.isNotEmpty(rightsList)) {
-                            for (InsOrderRights rights : rightsList) {
-                                // 提取接收人信息(取第一条有手机号的记录)
-                                if (StringUtils.isBlank(receiverDTO.getPhone()) && StringUtils.isNotBlank(rights.getReceiverMobile())) {
-                                    receiverDTO.setPhone(rights.getReceiverMobile());
-                                    receiverDTO.setReceiverName( rights.getReceiverName());
-                                    receiverDTO.setRemark("车险订单权益赠送");
-                                    receiverDTO.setReceiverAmountLimit(equityAmount);
-                                }
-                                // 累加金额
-                                BigDecimal amount = Optional.ofNullable(rights.getSettlePrice()).orElse(BigDecimal.ZERO);
-                                Integer count = Optional.ofNullable(rights.getRightsCount()).orElse(0);
-                                rightsTotalAmount = rightsTotalAmount.add(amount.multiply(new BigDecimal(count)));
-
-                                // 构建权益单品请求参数
-                                ChannelRightsGrantRightsDTO rightsItem = new ChannelRightsGrantRightsDTO();
-                                rightsItem.setRightsContentId( rights.getRightsId());
-                                rightsItem.setRightsCount( rights.getRightsCount());
-                                rightsItem.setRightsType( 1);
-                                rightsRequest.add(rightsItem);
-                            }
-                        }
-
-                        // 遍历权益套餐,计算总金额并构建请求参数
-                        if (CollUtil.isNotEmpty(rightsPackageList)) {
-                            for (InsOrderRightsPackage pkg : rightsPackageList) {
-                                // 提取接收人信息(取第一条有手机号的记录)
-                                if (StringUtils.isBlank(receiverDTO.getPhone()) && StringUtils.isNotBlank(pkg.getReceiverMobile())) {
-                                    receiverDTO.setPhone(pkg.getReceiverMobile());
-                                    receiverDTO.setReceiverName( pkg.getReceiverName());
-                                    receiverDTO.setRemark("车险订单权益赠送");
-                                    receiverDTO.setReceiverAmountLimit(equityAmount);
-                                }
-
-                                // 累加金额
-                                BigDecimal packagePrice = Optional.ofNullable(pkg.getPackagePrice()).orElse(BigDecimal.ZERO);
-                                Integer count = Optional.ofNullable(pkg.getRightsCount()).orElse(0);
-                                rightsPackageTotalAmount = rightsPackageTotalAmount.add(packagePrice.multiply(new BigDecimal(count)));
-
-                                // 构建权益套餐请求参数
-                                ChannelRightsGrantRightsDTO rightsItem = new ChannelRightsGrantRightsDTO();
-                                rightsItem.setRightsContentId( pkg.getRightsId());
-                                rightsItem.setRightsCount( pkg.getRightsCount());
-                                rightsItem.setRightsType( 2);
-                                rightsRequest.add(rightsItem);
-                            }
-                        }
-                        // 计算权益总花费并校验
-                        BigDecimal totalAmount = rightsTotalAmount.add(rightsPackageTotalAmount);
-                        if (totalAmount.compareTo(equityAmount) > 0) {
-                            AssertionUtils.isFail(true, "权益金额不足!");
-                        }
-
-                        // 调用权益发放接口
-                        ChannelRightsApiGrantRecordDTO grantRequest = new ChannelRightsApiGrantRecordDTO();
-                        grantRequest.setBusinessId(param.getOrderNo());
-                        grantRequest.setReceiverDTO(receiverDTO);
-                        grantRequest.setRightsList(rightsRequest);
-                        grantRequest.setRemark("订单号:" + param.getOrderNo() + "权益赠送");
-                        Result<String> grantResult = localLivingClient.distribution(grantRequest);
-                        // 处理响应结果
-                        if (grantResult != null  && grantResult.getCode() == 200) {
-                            // 发放成功后扣除订单总应收保费
-                            feeOrders.setTotalReceivablePremium(
-                                    feeOrders.getTotalReceivablePremium().subtract(totalAmount)
-                            );
-                        } else {
-                            AssertionUtils.isFail(true, "权益券下发失败:" + grantResult.getMessage());
-                        }
-                    }
-                }
-
-            }
-            catch (Exception e) {
+                this.dealRightsRelatedOrder(feeOrders);
+            } catch (Exception e) {
+                log.error("权益下发失败:orderNo=[{}]", JSONUtil.toJsonStr(param.getOrderNo()));
                 AssertionUtils.isFail(true, "权益券下发失败");
             }
-
             if(Objects.isNull(userAccount)){
                 userAccount = new SysUserAccount();
                 // 新建账户
@@ -537,12 +441,12 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
                 userAccount.setUserId(sysUserResult.getData().getId());
                 userAccount.setSumAmount(feeOrders.getTotalReceivablePremium());
                 userAccount.setSumAmountUnpaid(feeOrders.getTotalReceivablePremium());
+                userAccount.setSystemCode(systemCode);
             }else{
                 userAccount.setSumAmount(feeOrders.getTotalReceivablePremium().add(userAccount.getSumAmount()));
                 userAccount.setSumAmountUnpaid(feeOrders.getTotalReceivablePremium().add(userAccount.getSumAmount()));
             }
             orgClient.saveOrUpdate(userAccount);
-
             // 进入应收表
             List<String> orderNos = new ArrayList<>();
             orderNos.add(audit.getOrderNo());
@@ -550,42 +454,7 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
             requestData.put("orderNo",orderNos);
             HttpResult httpResult = orgClient.addReceivable(requestData);
             AssertionUtils.isFail(httpResult.getCode() == 500,"应收出错");
-
-            // 一级分销费用户进账
-            if (feeOrders.getFirstDistributorId() != null && feeOrders.getFirstDistributionFee().compareTo(BigDecimal.ZERO) > 0) {
-                HttpResult<SysUserJzgInfo> jzgInfoResult = orgClient.getUserInfoById(feeOrders.getFirstDistributorId());
-                AssertionUtils.isFail(jzgInfoResult.getCode() != 200,"获取用户失败!");
-                SysUserJzgInfo jzgInfo = jzgInfoResult.getData();
-                HttpResult<SysUserAccount> jzgAccountResult = orgClient.accountGet(jzgInfo.getUserId());
-                AssertionUtils.isFail(jzgAccountResult.getCode() != 200,"获取用户账户失败!");
-                distributor1 = jzgAccountResult.getData();
-                verifySysAccount(distributor1,jzgInfo,feeOrders);
-            }
-            // 二级分销费用户进账
-            if (feeOrders.getSecondDistributorId() != null && feeOrders.getSecondDistributionFee().compareTo(BigDecimal.ZERO) > 0) {
-                HttpResult<SysUserJzgInfo> jzgInfoResult = orgClient.getUserInfoById(feeOrders.getSecondDistributorId());
-                AssertionUtils.isFail(jzgInfoResult.getCode() != 200,"获取用户失败!");
-                SysUserJzgInfo jzgInfo = jzgInfoResult.getData();
-                HttpResult<SysUserAccount> jzgAccountResult = orgClient.accountGet(jzgInfo.getUserId());
-                AssertionUtils.isFail(jzgAccountResult.getCode() != 200,"获取用户账户失败!");
-                distributor2 = jzgAccountResult.getData();
-                verifySysAccount(distributor2,jzgInfo,feeOrders);
-            }
-            // 三级分销费用户进账
-            if (feeOrders.getThirdDistributorId() != null && feeOrders.getThirdDistributionFee().compareTo(BigDecimal.ZERO) > 0) {
-                HttpResult<SysUserJzgInfo> jzgInfoResult = orgClient.getUserInfoById(feeOrders.getThirdDistributorId());
-                AssertionUtils.isFail(jzgInfoResult.getCode() != 200,"获取用户失败!");
-                SysUserJzgInfo jzgInfo = jzgInfoResult.getData();
-                HttpResult<SysUserAccount> jzgAccountResult = orgClient.accountGet(jzgInfo.getUserId());
-                AssertionUtils.isFail(jzgAccountResult.getCode() != 200,"获取用户账户失败!");
-                distributor3 = jzgAccountResult.getData();
-                verifySysAccount(distributor3,jzgInfo,feeOrders);
-            }
-            // 增加账户的金额记录
-            // modify by lipf, 2026年5月20日11:31:59 审核通过后,才需要 生成收入明细日志
-            List<SysUserAccountIncomeLog>builderIncomeLog = this.builderIncomeLog(param.getOrderNo(),feeOrders,userAccount,distributor1,distributor2,distributor3);
-            HttpResult result2 = orgClient.addUserAccountIncomeLog(builderIncomeLog);
-            AssertionUtils.isFail(result2.getCode() != 200,"账户记录添加失败,审核失败");
+            this.dealDistribution(feeOrders,userAccount);
         }else{
             log.info("订单审核驳回:param=[{}]", JSONUtil.toJsonStr(param));
             StateResult stateFailResult = auditStateListener.statusChange(AuditState.AuditStateEnum.FAIL_AUDIT,audit.getAuditStatus()+"");
@@ -607,6 +476,163 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
         }
     }
 
+    /**
+     * 分销逻辑处理 (抽取原来的方法)
+     *      根据分销逻辑更新业务员账户的金额信息
+     *
+     * @param feeOrders 订单费用
+     * @param userAccount 业务员账户信息
+     * @author lipf
+     * @date 2026/6/6 10:49
+     */
+    private void dealDistribution(InsFeeOrders feeOrders, SysUserAccount userAccount){
+        log.info("处理分销逻辑。 feeOrders=[{}],userAccount=[{}]", JSONUtil.toJsonStr(feeOrders), JSONUtil.toJsonStr(userAccount));
+        SysUserAccount distributor1 = null,distributor2 = null,distributor3 = null;
+        // 一级分销费用户进账
+        if (feeOrders.getFirstDistributorId() != null && feeOrders.getFirstDistributionFee().compareTo(BigDecimal.ZERO) > 0) {
+            HttpResult<SysUserJzgInfo> jzgInfoResult = orgClient.getUserInfoById(feeOrders.getFirstDistributorId());
+            AssertionUtils.isFail(jzgInfoResult.getCode() != 200,"获取用户失败!");
+            SysUserJzgInfo jzgInfo = jzgInfoResult.getData();
+            HttpResult<SysUserAccount> jzgAccountResult = orgClient.accountGet(jzgInfo.getUserId());
+            AssertionUtils.isFail(jzgAccountResult.getCode() != 200,"获取用户账户失败!");
+            distributor1 = jzgAccountResult.getData();
+            updateSysAccount(distributor1,jzgInfo,feeOrders);
+        }
+        // 二级分销费用户进账
+        if (feeOrders.getSecondDistributorId() != null && feeOrders.getSecondDistributionFee().compareTo(BigDecimal.ZERO) > 0) {
+            HttpResult<SysUserJzgInfo> jzgInfoResult = orgClient.getUserInfoById(feeOrders.getSecondDistributorId());
+            AssertionUtils.isFail(jzgInfoResult.getCode() != 200,"获取用户失败!");
+            SysUserJzgInfo jzgInfo = jzgInfoResult.getData();
+            HttpResult<SysUserAccount> jzgAccountResult = orgClient.accountGet(jzgInfo.getUserId());
+            AssertionUtils.isFail(jzgAccountResult.getCode() != 200,"获取用户账户失败!");
+            distributor2 = jzgAccountResult.getData();
+            updateSysAccount(distributor2,jzgInfo,feeOrders);
+        }
+        // 三级分销费用户进账
+        if (feeOrders.getThirdDistributorId() != null && feeOrders.getThirdDistributionFee().compareTo(BigDecimal.ZERO) > 0) {
+            HttpResult<SysUserJzgInfo> jzgInfoResult = orgClient.getUserInfoById(feeOrders.getThirdDistributorId());
+            AssertionUtils.isFail(jzgInfoResult.getCode() != 200,"获取用户失败!");
+            SysUserJzgInfo jzgInfo = jzgInfoResult.getData();
+            HttpResult<SysUserAccount> jzgAccountResult = orgClient.accountGet(jzgInfo.getUserId());
+            AssertionUtils.isFail(jzgAccountResult.getCode() != 200,"获取用户账户失败!");
+            distributor3 = jzgAccountResult.getData();
+            updateSysAccount(distributor3,jzgInfo,feeOrders);
+        }
+        // 增加账户的金额记录
+        // modify by lipf, 2026年5月20日11:31:59 审核通过后,才需要 生成收入明细日志
+        List<SysUserAccountIncomeLog>builderIncomeLog = this.builderIncomeLog(feeOrders.getOrderNo(),feeOrders,userAccount,distributor1,distributor2,distributor3);
+        HttpResult result2 = orgClient.addUserAccountIncomeLog(builderIncomeLog);
+        AssertionUtils.isFail(result2.getCode() != 200,"账户记录添加失败,审核失败");
+    }
+
+
+    /**
+     * 处理订单关联的权益信息(将权益处理的逻辑抽取到一个单独的方法里面)
+     *
+     * @param feeOrders 订单的费用信息
+     * @author lipf
+     * @date 2026/6/6 10:18
+     */
+    private void dealRightsRelatedOrder(InsFeeOrders feeOrders) {
+        // 获取权益列表
+        List<InsOrderRights> rightsList = insOrderRightsMapper.queryByOrderNo(feeOrders.getOrderNo());
+        List<InsOrderRightsPackage> rightsPackageList = insOrderRightsPackageMapper.queryByOrderNo(feeOrders.getOrderNo());
+        if(CollUtil.isEmpty(rightsList) && CollUtil.isEmpty(rightsPackageList)){
+            log.info("订单[{}]关联的权益信息为空.不进行权益相关的处理",feeOrders.getOrderNo());
+            return;
+        }
+        log.info("订单[{}]关联的权益信息不为空.进行权益相关的处理",feeOrders.getOrderNo());
+        // 计算权益金额 = (出口费用总和) - (出口留点费用总和)
+        BigDecimal jqExportTotal = Optional.ofNullable(feeOrders.getJqExportPremium()).orElse(BigDecimal.ZERO);
+        BigDecimal syExportTotal = Optional.ofNullable(feeOrders.getSyExportPremium()).orElse(BigDecimal.ZERO);
+        BigDecimal jyExportTotal = Optional.ofNullable(feeOrders.getJyExportPremium()).orElse(BigDecimal.ZERO);
+        BigDecimal jqExportKeepPoint = Optional.ofNullable(feeOrders.getJqExportKeepPointPremium()).orElse(BigDecimal.ZERO);
+        BigDecimal syExportKeepPoint = Optional.ofNullable(feeOrders.getSyExportKeepPointPremium()).orElse(BigDecimal.ZERO);
+        BigDecimal jyExportKeepPoint = Optional.ofNullable(feeOrders.getJyExportKeepPointPremium()).orElse(BigDecimal.ZERO);
+        BigDecimal equityAmount = jqExportTotal.add(syExportTotal).add(jyExportTotal)
+                .subtract(jqExportKeepPoint.add(syExportKeepPoint).add(jyExportKeepPoint));
+
+        // 权益金额大于 0,计算实际花费的权益金额并发放权益券
+        if (equityAmount.compareTo(BigDecimal.ZERO) > 0) {
+            // 初始化变量
+            BigDecimal rightsTotalAmount = BigDecimal.ZERO;
+            BigDecimal rightsPackageTotalAmount = BigDecimal.ZERO;
+            ChannelRightsApiGrantReceiverDTO receiverDTO = new ChannelRightsApiGrantReceiverDTO();
+            List<ChannelRightsGrantRightsDTO> rightsRequest = new ArrayList<>();
+
+            // 遍历权益单品,计算总金额并构建请求参数
+            if (CollUtil.isNotEmpty(rightsList)) {
+                for (InsOrderRights rights : rightsList) {
+                    // 提取接收人信息(取第一条有手机号的记录)
+                    if (StringUtils.isBlank(receiverDTO.getPhone()) && StringUtils.isNotBlank(rights.getReceiverMobile())) {
+                        receiverDTO.setPhone(rights.getReceiverMobile());
+                        receiverDTO.setReceiverName( rights.getReceiverName());
+                        receiverDTO.setRemark("车险订单权益赠送");
+                        receiverDTO.setReceiverAmountLimit(equityAmount);
+                    }
+                    // 累加金额
+                    BigDecimal amount = Optional.ofNullable(rights.getSettlePrice()).orElse(BigDecimal.ZERO);
+                    Integer count = Optional.ofNullable(rights.getRightsCount()).orElse(0);
+                    rightsTotalAmount = rightsTotalAmount.add(amount.multiply(new BigDecimal(count)));
+
+                    // 构建权益单品请求参数
+                    ChannelRightsGrantRightsDTO rightsItem = new ChannelRightsGrantRightsDTO();
+                    rightsItem.setRightsContentId( rights.getRightsId());
+                    rightsItem.setRightsCount( rights.getRightsCount());
+                    rightsItem.setRightsType( 1);
+                    rightsRequest.add(rightsItem);
+                }
+            }
+
+            // 遍历权益套餐,计算总金额并构建请求参数
+            if (CollUtil.isNotEmpty(rightsPackageList)) {
+                for (InsOrderRightsPackage pkg : rightsPackageList) {
+                    // 提取接收人信息(取第一条有手机号的记录)
+                    if (StringUtils.isBlank(receiverDTO.getPhone()) && StringUtils.isNotBlank(pkg.getReceiverMobile())) {
+                        receiverDTO.setPhone(pkg.getReceiverMobile());
+                        receiverDTO.setReceiverName( pkg.getReceiverName());
+                        receiverDTO.setRemark("车险订单权益赠送");
+                        receiverDTO.setReceiverAmountLimit(equityAmount);
+                    }
+
+                    // 累加金额
+                    BigDecimal packagePrice = Optional.ofNullable(pkg.getPackagePrice()).orElse(BigDecimal.ZERO);
+                    Integer count = Optional.ofNullable(pkg.getRightsCount()).orElse(0);
+                    rightsPackageTotalAmount = rightsPackageTotalAmount.add(packagePrice.multiply(new BigDecimal(count)));
+
+                    // 构建权益套餐请求参数
+                    ChannelRightsGrantRightsDTO rightsItem = new ChannelRightsGrantRightsDTO();
+                    rightsItem.setRightsContentId( pkg.getRightsId());
+                    rightsItem.setRightsCount( pkg.getRightsCount());
+                    rightsItem.setRightsType( 2);
+                    rightsRequest.add(rightsItem);
+                }
+            }
+            // 计算权益总花费并校验
+            BigDecimal totalAmount = rightsTotalAmount.add(rightsPackageTotalAmount);
+            if (totalAmount.compareTo(equityAmount) > 0) {
+                AssertionUtils.isFail(true, "权益金额不足!");
+            }
+
+            // 调用权益发放接口
+            ChannelRightsApiGrantRecordDTO grantRequest = new ChannelRightsApiGrantRecordDTO();
+            grantRequest.setBusinessId(feeOrders.getOrderNo());
+            grantRequest.setReceiverDTO(receiverDTO);
+            grantRequest.setRightsList(rightsRequest);
+            grantRequest.setRemark("订单号:" + feeOrders.getOrderNo() + "权益赠送");
+            Result<String> grantResult = localLivingClient.distribution(grantRequest);
+            // 处理响应结果
+            if (grantResult != null  && grantResult.getCode() == 200) {
+                // 发放成功后扣除订单总应收保费
+                feeOrders.setTotalReceivablePremium(
+                        feeOrders.getTotalReceivablePremium().subtract(totalAmount)
+                );
+            } else {
+                AssertionUtils.isFail(true, "权益券下发失败:" + grantResult.getMessage());
+            }
+        }
+    }
+
     /**
      * 生成收入明细日志
      * @param orderNo       订单号
@@ -617,10 +643,15 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
      * @param distributor3  3级分销人员
      * @return
      */
-    private List<SysUserAccountIncomeLog> builderIncomeLog(String orderNo,InsFeeOrders feeOrders,SysUserAccount userAccount,SysUserAccount distributor1,
-                                                           SysUserAccount distributor2, SysUserAccount distributor3){
+    private List<SysUserAccountIncomeLog> builderIncomeLog(String orderNo,
+                                                           InsFeeOrders feeOrders,
+                                                           SysUserAccount userAccount,
+                                                           SysUserAccount distributor1,
+                                                           SysUserAccount distributor2,
+                                                           SysUserAccount distributor3){
+        log.info("生成收入明细日志:feeOrders=[{}],userAccount=[{}],distributor1=[{}],distributor2=[{}],distributor3=[{}]",
+                JSONUtil.toJsonStr(feeOrders), JSONUtil.toJsonStr(userAccount), JSONUtil.toJsonStr(distributor1), JSONUtil.toJsonStr(distributor2), JSONUtil.toJsonStr(distributor3));
         List<SysUserAccountIncomeLog> incomeLogs = new ArrayList<>();
-
         // 出口费用
         SysUserAccountIncomeLog exportIncomeLog = new SysUserAccountIncomeLog();
         exportIncomeLog.setOrderId(orderNo);
@@ -666,11 +697,16 @@ public class InsFeeAuditServiceImpl extends ServiceImpl<InsFeeAuditMapper, InsFe
         return incomeLogs;
     }
 
-
-
-    // 验证用户账户
-    public void verifySysAccount(SysUserAccount userAccount, SysUserJzgInfo jzgInfo, InsFeeOrders feeOrders) {
+    /**
+     * 更新分销账户的金额
+     *
+     * @author lipf
+     * @date 2026/6/6 11:20
+     */
+    void updateSysAccount(SysUserAccount userAccount, SysUserJzgInfo jzgInfo, InsFeeOrders feeOrders) {
         if (Objects.isNull(userAccount)) {
+            userAccount = new SysUserAccount();
+            userAccount.setSystemCode(jzgInfo.getSystemCode());
             userAccount.setUserId(jzgInfo.getUserId());
             userAccount.setSumAmount(feeOrders.getFirstDistributionFee());
             userAccount.setSumAmountUnpaid(feeOrders.getFirstDistributionFee());

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

@@ -978,11 +978,11 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
      */
     @Override
     @Transactional
-    public String generateOrders(QuoteVo quoteVo, String companyName) {
+    public String generateOrders(QuoteVo quoteVo, String companyName, String systemCode) {
         // 生成订单的条件
         // 1个保险公司只有一个主订单   不区分险种
         // 唯一性的判定标准:保险公司编号+ 车牌号 + 车架号
-        log.info("生成一个主订单和多个子订单:quoteVo=[{}],companyName=[{}]", JSONUtil.toJsonStr(quoteVo), companyName);
+        log.info("生成一个主订单和多个子订单:systemCode=[{}], quoteVo=[{}],companyName=[{}]",systemCode, JSONUtil.toJsonStr(quoteVo), companyName);
         InsOrders mainOrders = this.getMainOrders(
                 quoteVo.getOrder().getInsuranceCompanyCode(),
                 quoteVo.getCarInfoVo().getLicenseNo(),
@@ -995,13 +995,13 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
         // 如果订单号为0 则生成主订单
         if (!isExistsMainOrder) {
             // 生成主订单
-            mainOrderNo = this.saveBaseOrderorSubOrder(quoteVo, companyName, "0");
+            mainOrderNo = this.saveBaseOrderorSubOrder(quoteVo, companyName, "0",systemCode);
         } else {
             // 更新主订单
             mainOrderNo = this.updateMainOrders(quoteVo, mainOrders.getId(), companyName);
         }
         // 生成子订单
-        return this.saveBaseOrderorSubOrder(quoteVo, companyName, mainOrderNo);
+        return this.saveBaseOrderorSubOrder(quoteVo, companyName, mainOrderNo,systemCode );
     }
 
 
@@ -1011,11 +1011,13 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
      * @param quoteVo
      * @param companyName
      * @param mainOrderNo 主订单号
+     * @param systemCode
      * @return
      */
-    private String saveBaseOrderorSubOrder(QuoteVo quoteVo, String companyName, String mainOrderNo) {
+    private String saveBaseOrderorSubOrder(QuoteVo quoteVo, String companyName, String mainOrderNo, String systemCode) {
         // 保存主订单信息
         InsOrders insOrders = new InsOrders();
+        insOrders.setSystemCode(systemCode);
         // 生成主订单编号
         if ("0".equals(mainOrderNo)) {
             insOrders.setParentId("0");

+ 14 - 10
tenant/insurance/quotation-summary/src/main/resources/application-dev.yaml

@@ -6,10 +6,10 @@ spring:
       password: nacos
       config:
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
   config:
     import:
       - nacos:mysql
@@ -18,12 +18,16 @@ spring:
       - nacos:mongodb
       - nacos:identify
       - nacos:crawler-config
+      - nacos:rocketmq
+      - nacos:seata-server
   data:
     redis:
       host: 192.168.5.250
       port: 6379
       password: 123456  # 如果没有密码,可删除此行
       database: 0
+
+
 #百度图片识别API
 baidu:
   clientId: xG9M8tSjNvYPwSTNavM0ELlD
@@ -32,9 +36,9 @@ baidu:
   vehicle: https://aip.baidubce.com/rest/2.0/ocr/v1/vehicle_license
   idcard: https://aip.baidubce.com/rest/2.0/ocr/v1/idcard
 
-data-scope:
-  tables:
-    - sys_organization_user_link
+#data-scope:
+#  tables:
+#    - sys_organization_user_link
 
 
 image:
@@ -43,7 +47,7 @@ image:
 
 # 支付码相关配置
 qrcode:
-  genAddress: https://tennat.baoxianzhanggui.com/api/order/qrCode/index
+  genAddress: http://127.0.0.1:8001/order/qrCode/index
   imageSrc: http://127.0.0.1:8001/template/
 
 
@@ -135,12 +139,12 @@ huatai:
     sourceCode: '016'
     #    baseUrl: https://gears.pc.ehuatai.com:9003/interFaces/common/AutoInsuranceQuotation.do
     baseUrl: https://hound_t.pc.ehuatai.com/third/interFaces/common/AutoInsuranceQuotation.do
-    selfPublicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBOtXf1T/no13LOuy5ti+/d+SADHns8hAv3+LjhDFsQRbHk3tHhnUNX7I8b25576AVmgraefS7jnpyB299f58PK8WkWZN+UB6pcyEpktrtqOnKcaQSTbkQzaseCW4sCRdoea0alzSXUuXC2FN0pHAE3JkT07e5doX3IHas2GJAkwIDAQAB
-    selfPrivateKey: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAME61d/VP+ejXcs67Lm2L7935IAMeezyEC/f4uOEMWxBFseTe0eGdQ1fsjxvbnnvoBWaCtp59LuOenIHb31/nw8rxaRZk35QHqlzISmS2u2o6cpxpBJNuRDNqx4JbiwJF2h5rRqXNJdS5cLYU3SkcATcmRPTt7l2hfcgdqzYYkCTAgMBAAECgYAwAAWRDNBLInHyQjj8CR3jWk0Q4giHq7oJqnwaOIRud9zIxwIgym3ZXKRSDvxKnhQOSa3Yh+MblFEmcmJnsoQ4OedLq7FVOw1eur/nw6jMcROT6ZiP512ZGgKZI1OdkQDpjCqM9Vg1brrFuGdV2RwI9ak2bjKbi7vs6slvd4r3AQJBAOqU+5xkyuaupg9cO/7TIxMgnTQH8W7IC1uDcx3f28QdkP+udJYfU/n1dYyGuhkW66KVANSqz4dsrEiAmfhvydECQQDS304+LEvPi4j8YmNlOXrs1o8H0tPB69svC9ER2YbrTjZf49XvY9fuor11XAprXZhBp8yvJu07FuRWilrQ8FkjAkEAop1qmqzIdweE+ErxECJfQygtsd77v9cPAH5sM752Q0pXtNbD3TmUZkeBGExg/3mDGW5/Z+2M9Y0hFRWJJs6fsQJAO11mV5Z82Tb9H6BLPyoJczeMGLW/q65MjFgVSaMlmoTwRdqMVdKT7bifAbUhscwtmv40X3LkAmiVa8+TdZLUZQJACu2NgPZkw6gjKCZf5xOfvKaC3PqKlhPghq+ypyutzTih9y81+EBJ25emLWNcd5BtIZ/AXmr3UUiGg0zX4s5njA==
+    selfPublicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYUuKVmeF5N66v6MQmOR8pt4o33yrio/ZrDf2ehk/kHJ2BYdI+xE9dLrZW5dm0y8yPpIHN39JaLuYOlWz5DVsWJTSPj9eaq0hOiY9BGAHJ3yMHdX3wygpcD11BVc5v4wbX/kyAhmatK69sb4zzAtEve1NS/YwZVuGch0CrkGw3YQIDAQAB
+    selfPrivateKey: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJhS4pWZ4Xk3rq/oxCY5Hym3ijffKuKj9msN/Z6GT+QcnYFh0j7ET10utlbl2bTLzI+kgc3f0lou5g6VbPkNWxYlNI+P15qrSE6Jj0EYAcnfIwd1ffDKClwPXUFVzm/jBtf+TICGZq0rr2xvjPMC0S97U1L9jBlW4ZyHQKuQbDdhAgMBAAECgYAktOyXYoK8RFfbWjtSfrdsxScQub3K6foq6C8y8Wj1LpQiJqGUsJfze5tDK5agA/CiuODy7LnzM5QK9aVSCw7k0r8EeGpHcYRdLYZM0vXVFK2gIQctFemK4c6sg5p0Bn1LNRbJG7rgMMEA/OxcF6vyQahhFtPVtAQwc+dT0PV51QJBANd66rfTuxSRvIC/RE4W6iqzed2YZtx+0kd7KiN78Y4X6spposFCH4ALf4jUeRRXZAm+Qu71cq21m2GsQZ8bZdMCQQC096ilZXEjmEuvopQR3JvPbgAF+MtItusyDDsMR1CWwXq9XGMmxi6GhFY7r7C8b40j1IchVAEY5BcwxJwfjql7AkAp5QYZs5reBu/FgGqBXEcvAjIheW4WVLoBl9q7h2+RlujZbexs8rQ5KwMi0VyxLcSPeGR/HU2spHCPbvRbQPJtAkBANK4tIHSnwFz6D6ChRbgyPEDd3YuiXOPNefFpRdQd6L93d9Pl6eKP71v5CTiXYqyczcaz2G4bOrHPp7IMsD0JAkBO/q3ZMT+Nj2jdMn2AR1h+ngn38JnK46PznU0WoRPx72YoWbLxvaGWpu8scZAhpYeq/n+52NHFfu8ayDRNWjP2
     huaTaiPublicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYyF2SBNzLOP5N5fFW2T2eQ5+iaI5wVpsedu+UY290TVuG152tkdtSuTXsx+8BKnd79/PF966/2aSP4EUm7vD9kvQUnX2EDUiMooorGvDTRxoHNXFGqOtxdMiUOuqx19HwNb4gOXZ1WdjWJwSmqC6ZP6OgxKIotCHJwe826Q/w8wIDAQAB
-    huaTaiKey: e1YF83DYB8sj7gEr8M
+    huaTaiKey: AdOdn409KE6Z9XvFsJ
     key: kFl5aiHALbZ6TMTqQDxghMMC
-    tokenUrl: https://vehpxy.pc.ehuatai.com/testsign
+    tokenUrl: https://vehpxy.pc.ehuatai.com/sign
     username: "UE27017723VPN01"
     password: "Dhbx135....."
     comcode: "9270901-营业部代理业务一部"

+ 8 - 6
tenant/insurance/quotation-summary/src/main/resources/application-prod.yaml

@@ -18,6 +18,8 @@ spring:
       - nacos:mongodb
       - nacos:identify
       - nacos:crawler-config
+      - nacos:rocketmq
+      - nacos:seata-server
 
 #百度图片识别API
 baidu:
@@ -130,12 +132,12 @@ huatai:
     sourceCode: '016'
     #    baseUrl: https://gears.pc.ehuatai.com:9003/interFaces/common/AutoInsuranceQuotation.do
     baseUrl: https://hound_t.pc.ehuatai.com/third/interFaces/common/AutoInsuranceQuotation.do
-    selfPublicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBOtXf1T/no13LOuy5ti+/d+SADHns8hAv3+LjhDFsQRbHk3tHhnUNX7I8b25576AVmgraefS7jnpyB299f58PK8WkWZN+UB6pcyEpktrtqOnKcaQSTbkQzaseCW4sCRdoea0alzSXUuXC2FN0pHAE3JkT07e5doX3IHas2GJAkwIDAQAB
-    selfPrivateKey: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAME61d/VP+ejXcs67Lm2L7935IAMeezyEC/f4uOEMWxBFseTe0eGdQ1fsjxvbnnvoBWaCtp59LuOenIHb31/nw8rxaRZk35QHqlzISmS2u2o6cpxpBJNuRDNqx4JbiwJF2h5rRqXNJdS5cLYU3SkcATcmRPTt7l2hfcgdqzYYkCTAgMBAAECgYAwAAWRDNBLInHyQjj8CR3jWk0Q4giHq7oJqnwaOIRud9zIxwIgym3ZXKRSDvxKnhQOSa3Yh+MblFEmcmJnsoQ4OedLq7FVOw1eur/nw6jMcROT6ZiP512ZGgKZI1OdkQDpjCqM9Vg1brrFuGdV2RwI9ak2bjKbi7vs6slvd4r3AQJBAOqU+5xkyuaupg9cO/7TIxMgnTQH8W7IC1uDcx3f28QdkP+udJYfU/n1dYyGuhkW66KVANSqz4dsrEiAmfhvydECQQDS304+LEvPi4j8YmNlOXrs1o8H0tPB69svC9ER2YbrTjZf49XvY9fuor11XAprXZhBp8yvJu07FuRWilrQ8FkjAkEAop1qmqzIdweE+ErxECJfQygtsd77v9cPAH5sM752Q0pXtNbD3TmUZkeBGExg/3mDGW5/Z+2M9Y0hFRWJJs6fsQJAO11mV5Z82Tb9H6BLPyoJczeMGLW/q65MjFgVSaMlmoTwRdqMVdKT7bifAbUhscwtmv40X3LkAmiVa8+TdZLUZQJACu2NgPZkw6gjKCZf5xOfvKaC3PqKlhPghq+ypyutzTih9y81+EBJ25emLWNcd5BtIZ/AXmr3UUiGg0zX4s5njA==
+    selfPublicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYUuKVmeF5N66v6MQmOR8pt4o33yrio/ZrDf2ehk/kHJ2BYdI+xE9dLrZW5dm0y8yPpIHN39JaLuYOlWz5DVsWJTSPj9eaq0hOiY9BGAHJ3yMHdX3wygpcD11BVc5v4wbX/kyAhmatK69sb4zzAtEve1NS/YwZVuGch0CrkGw3YQIDAQAB
+    selfPrivateKey: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJhS4pWZ4Xk3rq/oxCY5Hym3ijffKuKj9msN/Z6GT+QcnYFh0j7ET10utlbl2bTLzI+kgc3f0lou5g6VbPkNWxYlNI+P15qrSE6Jj0EYAcnfIwd1ffDKClwPXUFVzm/jBtf+TICGZq0rr2xvjPMC0S97U1L9jBlW4ZyHQKuQbDdhAgMBAAECgYAktOyXYoK8RFfbWjtSfrdsxScQub3K6foq6C8y8Wj1LpQiJqGUsJfze5tDK5agA/CiuODy7LnzM5QK9aVSCw7k0r8EeGpHcYRdLYZM0vXVFK2gIQctFemK4c6sg5p0Bn1LNRbJG7rgMMEA/OxcF6vyQahhFtPVtAQwc+dT0PV51QJBANd66rfTuxSRvIC/RE4W6iqzed2YZtx+0kd7KiN78Y4X6spposFCH4ALf4jUeRRXZAm+Qu71cq21m2GsQZ8bZdMCQQC096ilZXEjmEuvopQR3JvPbgAF+MtItusyDDsMR1CWwXq9XGMmxi6GhFY7r7C8b40j1IchVAEY5BcwxJwfjql7AkAp5QYZs5reBu/FgGqBXEcvAjIheW4WVLoBl9q7h2+RlujZbexs8rQ5KwMi0VyxLcSPeGR/HU2spHCPbvRbQPJtAkBANK4tIHSnwFz6D6ChRbgyPEDd3YuiXOPNefFpRdQd6L93d9Pl6eKP71v5CTiXYqyczcaz2G4bOrHPp7IMsD0JAkBO/q3ZMT+Nj2jdMn2AR1h+ngn38JnK46PznU0WoRPx72YoWbLxvaGWpu8scZAhpYeq/n+52NHFfu8ayDRNWjP2
     huaTaiPublicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYyF2SBNzLOP5N5fFW2T2eQ5+iaI5wVpsedu+UY290TVuG152tkdtSuTXsx+8BKnd79/PF966/2aSP4EUm7vD9kvQUnX2EDUiMooorGvDTRxoHNXFGqOtxdMiUOuqx19HwNb4gOXZ1WdjWJwSmqC6ZP6OgxKIotCHJwe826Q/w8wIDAQAB
-    huaTaiKey: e1YF83DYB8sj7gEr8M
+    huaTaiKey: AdOdn409KE6Z9XvFsJ
     key: kFl5aiHALbZ6TMTqQDxghMMC
-    tokenUrl: https://vehpxy.pc.ehuatai.com/testsign
+    tokenUrl: https://vehpxy.pc.ehuatai.com/sign
     username: "UE27017723VPN01"
     password: "Dhbx135....."
     comcode: "9270901-营业部代理业务一部"
@@ -248,7 +250,7 @@ feign:
     jzg-partner:
       url: http://172.24.144.227:8087/
     jeecg-boot:
-      url: http://172.24.144.227:8082/jeecg-boot
+      url: https://life.baoxianzhanggui.com/locallive-pro-java
 # 和生活服务平台约定的appkey、appSeCret,用于获取token
 live:
   service:
@@ -258,4 +260,4 @@ live:
 localLive:
   base-url: http://192.168.1.108:8082/jeecg-boot
   app-key: _N7dxVy3UJQUNwvacEHUFPzZ63kGwSx1OxXablRXJ9g
-  app-secret: UByV_498NU1unnr3b0hvQtIKbRyIj0HGCkZXxxgzwAU
+  app-secret: UByV_498NU1unnr3b0hvQtIKbRyIj0HGCkZXxxgzwAU

+ 9 - 22
tenant/insurance/quotation-summary/src/main/resources/mapper/InsFeeAuditMapper.xml

@@ -80,17 +80,12 @@
         LEFT JOIN ins_orders_external_policy ioep on ioep.order_no = io.id AND ioep.is_delete=0
         <where>
             and ifa.is_delete=0 and ifa.system_code = #{param.systemCode}
-            <choose>
-                <when test="param.receiveUser != null and param.receiveUser != ''">
-                    and ifa.receive_user = #{param.receiveUser}
-                </when>
-                <when test="param.isProblem != null and param.isProblem == 1">
-                    and ifa.receive_user is NOT NULL
-                </when>
-                <otherwise>
-                    and ifa.receive_user is NULL
-                </otherwise>
-            </choose>
+            <if test="param.receiveUser != null and param.receiveUser != ''">
+                and ifa.receive_user = #{param.receiveUser}
+            </if>
+            <if test="param.allTodoReceive != null and param.allTodoReceive == true">
+                and ifa.receive_user is null
+            </if>
             <if test="param.auditType != null">
                 and ifa.audit_type = #{param.auditType}
             </if>
@@ -215,17 +210,9 @@
         LEFT JOIN ins_orders_external_policy ioep on ioep.order_no = io.id AND ioep.is_delete=0
         <where>
             and ifa.is_delete=0
-            <choose>
-                <when test="param.receiveUser != null and param.receiveUser != ''">
-                    and ifa.receive_user = #{param.receiveUser}
-                </when>
-                <when test="param.isProblem != null and param.isProblem == 1">
-                    and ifa.receive_user is NOT NULL
-                </when>
-                <otherwise>
-                    and ifa.receive_user is NULL
-                </otherwise>
-            </choose>
+            <if test="param.receiveUser != null and param.receiveUser != ''">
+                and ifa.receive_user = #{param.receiveUser}
+            </if>
             <if test="param.auditType != null">
                 and ifa.audit_type = #{param.auditType}
             </if>

+ 2 - 2
tenant/insurance/quotation-zhongan/pom.xml

@@ -53,8 +53,8 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <configuration>
-                    <source>14</source>
-                    <target>14</target>
+                    <source>15</source>
+                    <target>15</target>
                 </configuration>
             </plugin>
         </plugins>

+ 1 - 1
tenant/organization/pom.xml

@@ -55,7 +55,7 @@
 
         <dependency>
             <groupId>io.seata</groupId>
-            <artifactId>seata-all</artifactId>
+            <artifactId>seata-spring-boot-starter</artifactId>
         </dependency>
 
     </dependencies>

+ 71 - 0
tenant/organization/src/main/java/com/jzg/organization/controller/ReceivableController.java

@@ -224,6 +224,70 @@ public class ReceivableController {
         return HttpResult.error("开票失败");
     }
 
+    @GetMapping("/calculateTotalSum")
+    @Operation(summary = "计算相加的总数")
+    public HttpResult<String> calculateTotal(String sumList){
+        if (StrUtil.isBlank(sumList)){
+            return HttpResult.ok("参数不能为空", "0");
+        }
+        BigDecimal reduce = Arrays.asList(sumList.split(",")).stream().map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);
+        return HttpResult.ok("计算成功", reduce.toString());
+    }
+
+    /**
+     * 跟单费开票的列表
+     * @param followInvoicePageListVo
+     * @return
+     */
+    @PostMapping("/followInvoicePageList")
+    @Operation(summary = "跟单费开票的列表")
+    public HttpResult<List<InsFeeFollowOrderVo>> followInvoicePageList(@RequestBody FollowInvoicePageListVo followInvoicePageListVo){
+        // 开票类型  1、手续费  2、跟单费  3、驾意险手续费 4、驾意险跟单费 5、平台应收-车险 6、平台应收-非车险
+        List<InsFeeFollowOrderVo> page = receivableService.followInvoicePageList(followInvoicePageListVo);
+        return HttpResult.ok(page);
+    }
+
+    /**
+     * 跟单费开票
+     * @param followInvoiceVo
+     * @return
+     */
+    @PostMapping("/followInvoicing")
+    @Operation(summary = "跟单费开票")
+    public HttpResult<InvoiceingResult> followInvoicing(@RequestBody FollowInvoiceVo followInvoiceVo){
+        // 开票类型  1、手续费  2、跟单费  3、驾意险手续费 4、驾意险跟单费 5、平台应收-车险 6、平台应收-非车险
+        InvoiceingResult invoiceingResult = receivableService.followInvoicing(followInvoiceVo);
+        if (Objects.nonNull(invoiceingResult)){
+            return HttpResult.ok("开票成功,发票编号是" + invoiceingResult.getInvoiceId() ,invoiceingResult);
+        }
+        return HttpResult.error("开票失败");
+    }
+
+
+    @PostMapping("/followSettlement")
+    @Operation(summary = "跟单费结算")
+    public HttpResult<String> followSettlement(@RequestBody SettlementVo settlementVo){
+        try{
+            boolean settlement = receivableService.followSettlement(settlementVo);
+            return settlement ? HttpResult.ok("结算成功") : HttpResult.error("结算失败");
+        }catch (Exception e){
+            log.error("结算失败",e);
+            return HttpResult.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 跟单费开票并结算
+     *
+     * @author jk
+     * @date 2026/5/26 11:57
+     */
+    @PostMapping("/followInvoiceAndSettlement")
+    @Operation(summary = "跟单费开票并结算")
+    public HttpResult<String> followInvoiceAndSettlement(@RequestBody FollowInvoiceAndSettlementVo settlementVo){
+        return receivableService.followInvoiceAndSettlement(settlementVo);
+    }
+
     @PostMapping("otherInvoicing")
     @Operation(summary = "跟单费开票")
     public HttpResult<InvoiceingResult> otherInvoicing(@RequestBody InsPlyIncomeInvoiceVO invoicingVo){
@@ -487,6 +551,13 @@ public class ReceivableController {
         return HttpResult.ok(receivableService.platformSettlementExcel(page,settlementQueryVo));
     }
 
+    @PostMapping("/followSettlementExcel")
+    @Operation(summary = "跟单费用结算记录")
+    public HttpResult<Page<ExportSuperviseSettlementExcelDto>> followSettlementExcel(@RequestBody SettlementQueryVo settlementQueryVo) {
+        Page page = settlementQueryVo.getPage();
+        return HttpResult.ok(receivableService.followSettlementExcel(page,settlementQueryVo));
+    }
+
     @DeleteMapping("/deleteSettlementById/{id}")
     @Operation(summary = "删除结算记录")
     public HttpResult<Boolean> deleteSettlementById(@PathVariable String id) {

+ 32 - 9
tenant/organization/src/main/java/com/jzg/organization/controller/TestController.java

@@ -1,22 +1,45 @@
 package com.jzg.organization.controller;
 
-import com.alibaba.fastjson.JSONObject;
 import com.jzg.commons.core.page.HttpResult;
+import io.seata.core.context.RootContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping("/tenant")
 public class TestController {
 
-//    @Autowired
-//    TestClient testClient;
-//
-//    @GetMapping(value = "aa")
-//    public String aa(){
-//        HttpResult userPer = testClient.getUserPer();
-//        return JSONObject.toJSONString(userPer);
-//    }
+    private static final Logger log = LoggerFactory.getLogger(TestController.class);
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @GetMapping("/seataTestWrite")
+    @Transactional(rollbackFor = Exception.class)
+    public HttpResult<String> seataTestWrite(@RequestParam(defaultValue = "test") String name) {
+        String xid = RootContext.getXID();
+        log.info("【organization 参与方】收到请求,XID=[{}], name=[{}]", xid, name);
+
+        jdbcTemplate.update("INSERT INTO seata_test (name, source) VALUES (?, ?)", name, "organization");
+
+        return HttpResult.ok("organization 写入成功, XID=" + xid);
+    }
+
+    @GetMapping("/seataTestWriteFail")
+    @Transactional(rollbackFor = Exception.class)
+    public HttpResult<String> seataTestWriteFail(@RequestParam(defaultValue = "test") String name) {
+        String xid = RootContext.getXID();
+        log.info("【organization 参与方-故意失败】收到请求,XID=[{}], name=[{}]", xid, name);
+
+        jdbcTemplate.update("INSERT INTO seata_test (name, source) VALUES (?, ?)", name, "organization-fail");
+
+        throw new RuntimeException("organization 故意抛出异常,测试分布式事务回滚");
+    }
 }

+ 14 - 0
tenant/organization/src/main/java/com/jzg/organization/mapper/InsFeeFollowOrderMapper.java

@@ -0,0 +1,14 @@
+package com.jzg.organization.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jzg.commons.entity.finance.po.InsFeeFollowOrder;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 保费跟踪订单表 Mapper
+ */
+@Mapper
+public interface InsFeeFollowOrderMapper extends BaseMapper<InsFeeFollowOrder> {
+}

+ 13 - 0
tenant/organization/src/main/java/com/jzg/organization/mapper/InsPlyFollowInvoiceLinkMapper.java

@@ -0,0 +1,13 @@
+package com.jzg.organization.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jzg.commons.entity.finance.po.InsPlyFollowInvoiceLink;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 保单跟踪发票关联表 Mapper
+ */
+@Mapper
+public interface InsPlyFollowInvoiceLinkMapper extends BaseMapper<InsPlyFollowInvoiceLink> {
+
+}

+ 0 - 8
tenant/organization/src/main/java/com/jzg/organization/mapper/InsPlyIncomeInvoiceLinkMapper.java

@@ -9,12 +9,4 @@ import org.apache.ibatis.annotations.Param;
 @Mapper
 public interface InsPlyIncomeInvoiceLinkMapper extends BaseMapper<InsPlyIncomeInvoiceLink> {
 
-    /**
-     * 删除发票和订单的关联关系
-     *
-     * @author lipf
-     * @date 2026/5/29 14:29
-     */
-    @Delete("delete from ins_ply_income_invoice_link ipiil where ipiil.invoice_id = #{invoiceId}")
-    void deleteByInvoiceId(@Param("invoiceId") String invoiceId);
 }

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

@@ -6,6 +6,7 @@ import com.jzg.commons.entity.finance.dto.*;
 import com.jzg.commons.entity.finance.po.InsPlyIncome;
 import com.jzg.commons.entity.finance.po.InsPlyIncomeInvoiceLink;
 import com.jzg.commons.entity.finance.po.InsPlyIncomeInvoiceSettlement;
+import com.jzg.commons.entity.finance.po.InsPlyIncomeVO;
 import com.jzg.commons.entity.finance.vo.InvoiceRecordQueryVo;
 import com.jzg.commons.entity.finance.vo.InvoiceRecordResult;
 import com.jzg.commons.entity.finance.vo.InvoicingVo;
@@ -97,6 +98,13 @@ public interface ReceivableMapper extends BaseMapper<InsPlyIncome> {
      */
     Page<ExportSuperviseSettlementExcelDto> platformSettlementExcelDtos(Page page,@Param("settlementQueryVo") SettlementQueryVo settlementQueryVo);
 
+    /**
+     * 获取平台结算记录列表
+     * @param settlementQueryVo
+     * @return
+     */
+    Page<ExportSuperviseSettlementExcelDto> followSettlementExcelDtos(Page page,@Param("settlementQueryVo") SettlementQueryVo settlementQueryVo);
+
     /**
      * 获取应收结算记录明细列表
      * @param settlementQueryVo
@@ -229,4 +237,15 @@ public interface ReceivableMapper extends BaseMapper<InsPlyIncome> {
     List<CalRes> querySettledAmount(@Param("systemCode") String systemCode,
                                                @Param("settledStartDate") String settledStartDate,
                                                @Param("settledEndDate") String settledEndDate);
+
+    /**
+     * 查询
+     * @param startYear
+     * @param endYear
+     * @param systemCode
+     * @return
+     */
+    List<InsPlyIncomeDto> queryFollowIncome(@Param("startYear") Integer startYear,
+                                            @Param("endYear") Integer endYear,
+                                            @Param("systemCode")  String systemCode);
 }

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

@@ -414,4 +414,40 @@ public interface ReceivableService extends IService<InsPlyIncome> {
      * @date 2026/6/4 10:15
      */
     BigDecimal queryAllTotalReceivablePremium(List<String> orderNos);
+
+    /**
+     * 跟单费开票的列表
+     * @param followInvoicePageListVo
+     * @return
+     */
+    List<InsFeeFollowOrderVo> followInvoicePageList(FollowInvoicePageListVo followInvoicePageListVo);
+
+    /**
+     * 跟单费开票
+     * @param followInvoiceVo
+     * @return
+     */
+    InvoiceingResult followInvoicing(FollowInvoiceVo followInvoiceVo);
+
+    /**
+     * 跟单费结算
+     * @param settlementVo
+     * @return
+     */
+    boolean followSettlement(SettlementVo settlementVo);
+
+    /**
+     * 跟单费开票并结算
+     * @param settlementVo
+     * @return
+     */
+    HttpResult<String> followInvoiceAndSettlement(FollowInvoiceAndSettlementVo settlementVo);
+
+    /**
+     * 跟单费用结算记录
+     * @param page
+     * @param settlementQueryVo
+     * @return
+     */
+    Page<ExportSuperviseSettlementExcelDto> followSettlementExcel(Page page, SettlementQueryVo settlementQueryVo);
 }

+ 508 - 24
tenant/organization/src/main/java/com/jzg/organization/service/impl/ReceivableServiceImpl.java

@@ -12,6 +12,7 @@ import cn.hutool.json.JSONUtil;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.nacos.common.utils.CollectionUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -92,6 +93,12 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
     @Autowired
     private InsPlyIncomeInvoiceMapper insPlyIncomeInvoiceMapper;
 
+    @Autowired
+    private InsFeeFollowOrderMapper insFeeFollowOrderMapper;
+
+    @Autowired
+    private InsPlyFollowInvoiceLinkMapper insPlyFollowInvoiceLinkMapper;
+
     @Autowired
     private InsPlyIncomeInvoiceLinkMapper insPlyIncomeInvoiceLinkMapper;
 
@@ -531,9 +538,10 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean deleteInvoiceById(String id) {
         log.info("私有协议-开票记录-删除: id=[{}]",id);
-        insPlyIncomeInvoiceLinkMapper.deleteByInvoiceId(id);
+        insPlyIncomeInvoiceLinkMapper.deleteById(id);
         return insPlyIncomeInvoiceMapper.deleteById(id) > 0;
     }
 
@@ -649,7 +657,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         // 2. 数据库校验:是否已存在开票记录
         Integer count = baseMapper.invoicingCheck(invoicingVo);
         if (count > 0) {
-            log.info("数据库已存在开票记录,直接禁止");
+            log.info("数据库已存在开票记录,直接禁止开票");
             return false;
         }
 
@@ -822,7 +830,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
             log.info("开票异常.invoicingVo=[{}]",JSONUtil.toJsonStr(invoicingVo), e);
             // 回滚事务
             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-            return null;
+            throw new ServiceException(Objects.toString(e.getMessage(),"开票异常"));
         }
     }
 
@@ -836,9 +844,9 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         if(invoicingVo == null){
             throw new ServiceException("无法开票,开票参数为空");
         }
-        if(StrUtil.isEmpty(invoicingVo.getInvoiceNo())){
-            throw new ServiceException("发票号不能为空");
-        }
+//        if(StrUtil.isEmpty(invoicingVo.getInvoiceNo())){
+//            throw new ServiceException("发票号不能为空");
+//        }
     }
 
     @Override
@@ -854,38 +862,38 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
     }
 
     @Override
-    public boolean updateIncomeSettlementStatus(String invoiceType, List<String> invoiceIds, LocalDateTime settlementTime) {
-        log.info("更新结算状态: invoiceType=[{}],invoiceIds=[{}],settlementTime=[{}]", invoiceType, JSONUtil.toJsonStr(invoiceIds), settlementTime);
-        if (CollectionUtils.isEmpty(invoiceIds)) {
+    public boolean updateIncomeSettlementStatus(String invoiceType, List<String> incomeIds, LocalDateTime settlementTime) {
+        log.info("更新结算状态: invoiceType=[{}],incomeIds=[{}],settlementTime=[{}]", invoiceType, JSONUtil.toJsonStr(incomeIds), settlementTime);
+        if (CollectionUtils.isEmpty(incomeIds)) {
             return false;
         }
         long result;
         final String number = "1";
         result = switch (invoiceType) {
             case number -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
-                    .in(InsPlyIncome::getId, invoiceIds)
+                    .in(InsPlyIncome::getId, incomeIds)
                     .set(InsPlyIncome::getSuperviseSettlement, number)
                     .set(InsPlyIncome::getSettlementTime,settlementTime)
             );
             case "2" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
-                    .in(InsPlyIncome::getId, invoiceIds)
+                    .in(InsPlyIncome::getId, incomeIds)
                     .set(InsPlyIncome::getOtherSettlement, number)
                     .set(InsPlyIncome::getSettlementTime,settlementTime));
             case "3" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
-                    .in(InsPlyIncome::getId, invoiceIds)
+                    .in(InsPlyIncome::getId, incomeIds)
                     .set(InsPlyIncome::getJySuperviseSettlement, number)
                     .set(InsPlyIncome::getSettlementTime,settlementTime));
             case "4" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
-                    .in(InsPlyIncome::getId, invoiceIds)
+                    .in(InsPlyIncome::getId, incomeIds)
                     .set(InsPlyIncome::getJyOtherSettlement, number)
                     .set(InsPlyIncome::getSettlementTime,settlementTime));
             case "5" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
-                    .in(InsPlyIncome::getId, invoiceIds)
+                    .in(InsPlyIncome::getId, incomeIds)
                     .set(InsPlyIncome::getPlatformSuperviseSettlement, number)
                     .set(InsPlyIncome::getSettlementTime,settlementTime)
                     .set(InsPlyIncome::getSettlementStatus, number));
             case "6" -> baseMapper.update(new LambdaUpdateWrapper<InsPlyIncome>()
-                    .in(InsPlyIncome::getId, invoiceIds)
+                    .in(InsPlyIncome::getId, incomeIds)
                     .set(InsPlyIncome::getPlatformOtherSettlement, number)
                     .set(InsPlyIncome::getSettlementTime,settlementTime)
                     .set(InsPlyIncome::getSettlementStatus, number));
@@ -894,6 +902,83 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         return result > 0;
     }
 
+    /**
+     * 更新结算状态
+     *
+     * @param invoiceType 发票类型
+     * @param incomeIds 收入ID列表(当invoiceType为2或4时,此参数可为空,改用年月等条件更新)
+     * @param settlementTime 结算时间
+     * @param systemCode 系统编码(invoiceType为2或4时必传)
+     * @param companyId 公司ID(invoiceType为2或4时必传)
+     * @return 是否更新成功
+     */
+    public boolean updateIncomeSettlementStatus(String invoiceType, List<String> incomeIds,
+                                                LocalDateTime settlementTime,
+                                                String systemCode, String companyId) {
+        log.info("更新结算状态: invoiceType=[{}], incomeIds=[{}], settlementTime=[{}], systemCode=[{}], companyId=[{}]",
+                invoiceType, JSONUtil.toJsonStr(incomeIds), settlementTime, systemCode, companyId);
+        final String number = "1";
+        long result = 0;
+        // 针对 invoiceType 为 2 或 4 的特殊逻辑:按签约年月、系统编码、公司ID进行批量更新
+        if ("2".equals(invoiceType) || "4".equals(invoiceType)) {
+            // 参数校验
+            if (settlementTime == null || StringUtils.isBlank(systemCode) || StringUtils.isBlank(companyId)) {
+                log.warn("invoiceType为2或4时,settlementTime、systemCode、companyId不能为空");
+                return false;
+            }
+
+            // 提取 settlementTime 的年份和月份
+            int year = settlementTime.getYear();
+            int month = settlementTime.getMonthValue();
+
+            // 构建 LambdaUpdateWrapper
+            LambdaUpdateWrapper<InsPlyIncome> wrapper = new LambdaUpdateWrapper<>();
+            wrapper.eq(InsPlyIncome::getSystemCode, systemCode)
+                    .eq(InsPlyIncome::getCompanyId, companyId)
+                    // 核心逻辑:匹配 signingTime 的年份和月份
+                    .apply("YEAR(signing_time) = {0} AND MONTH(signing_time) = {1}", year, month)
+                    .set(InsPlyIncome::getSettlementTime, settlementTime);
+
+            // 根据具体的 invoiceType 设置对应的结算字段
+            if ("2".equals(invoiceType)) {
+                wrapper.set(InsPlyIncome::getOtherSettlement, number);
+            } else {
+                wrapper.set(InsPlyIncome::getJyOtherSettlement, number);
+            }
+
+            result = baseMapper.update(null, wrapper);
+        } else {
+            // 其他类型保持原有的根据 ID 列表更新的逻辑
+            if (CollectionUtils.isEmpty(incomeIds)) {
+                return false;
+            }
+
+            result = switch (invoiceType) {
+                case "1" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
+                        .in(InsPlyIncome::getId, incomeIds)
+                        .set(InsPlyIncome::getSuperviseSettlement, number)
+                        .set(InsPlyIncome::getSettlementTime, settlementTime));
+                case "3" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
+                        .in(InsPlyIncome::getId, incomeIds)
+                        .set(InsPlyIncome::getJySuperviseSettlement, number)
+                        .set(InsPlyIncome::getSettlementTime, settlementTime));
+                case "5" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
+                        .in(InsPlyIncome::getId, incomeIds)
+                        .set(InsPlyIncome::getPlatformSuperviseSettlement, number)
+                        .set(InsPlyIncome::getSettlementTime, settlementTime)
+                        .set(InsPlyIncome::getSettlementStatus, number));
+                case "6" -> baseMapper.update(null, new LambdaUpdateWrapper<InsPlyIncome>()
+                        .in(InsPlyIncome::getId, incomeIds)
+                        .set(InsPlyIncome::getPlatformOtherSettlement, number)
+                        .set(InsPlyIncome::getSettlementTime, settlementTime)
+                        .set(InsPlyIncome::getSettlementStatus, number));
+                default -> 0L;
+            };
+        }
+
+        return result > 0;
+    }
+
     /**
      * 开票并结算
      *
@@ -979,7 +1064,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
             // 处理结算数据
             // 未完全结算完成
             InsPlyIncomeInvoiceSettlement settlement = new InsPlyIncomeInvoiceSettlement(invoice.getId(), settlementVo.getActualReceivedAmount(),
-                    DateTime.now(), settlementVo.getPaymentDifference(), settlementVo.getPaymentReason());
+                    DateTime.now(), settlementVo.getSettlementPaymentDifference(), settlementVo.getSettlementPaymentReason());
             insPlyIncomeInvoiceSettlementMapper.insert(settlement);
 
             if (CollectionUtils.isNotEmpty(settlementVo.getAttachmentIds())) {
@@ -996,7 +1081,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
             }
 
             // 判断是否已经结算完成, 考虑回款差额
-            if (surplusPremium.compareTo(settlementVo.getActualReceivedAmount().add(platformSettlementVo.getSettlementVo().getPaymentDifference())) == 0) {
+            if (surplusPremium.compareTo(settlementVo.getActualReceivedAmount().add(platformSettlementVo.getSettlementVo().getSettlementPaymentDifference())) == 0) {
                 log.info("序号为[{}]的发票已经结算完成",invoice.getId());
                 // 更新发票状态
                 invoice.setStatus("1");
@@ -1402,8 +1487,8 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
             // 实际已经收了的金额
             BigDecimal actualReceivedAmount = this.getInvoicingAmount(settlementVo.getInvoicingId());
 
-            // 所有需要结算的金额
-            BigDecimal allToSettlement = insPlyIncomeInvoice.getReceivableSupervisePremium().add(insPlyIncomeInvoice.getOverinflatedAmount());
+            // 所有需要结算的金额,开票金额
+            BigDecimal allToSettlement = insPlyIncomeInvoice.getReceivableSupervisePremium();
             // 剩余的金额
             BigDecimal surplusPremium = allToSettlement.subtract(actualReceivedAmount);
             log.info("invoiceId=[{}],一共需要结算的金额[{}],实际已经结算的金额[{}],本次计划结算的金额[{}]",
@@ -1414,7 +1499,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
             // 处理结算数据
             // 未完全结算完成
             InsPlyIncomeInvoiceSettlement settlement = new InsPlyIncomeInvoiceSettlement(settlementVo.getInvoicingId(), settlementVo.getActualReceivedAmount(),
-                    DateTime.now(), settlementVo.getPaymentDifference(), settlementVo.getPaymentReason());
+                    DateTime.now(), settlementVo.getSettlementPaymentDifference(), settlementVo.getSettlementPaymentReason());
             insPlyIncomeInvoiceSettlementMapper.insert(settlement);
 
             if (CollectionUtils.isNotEmpty(settlementVo.getAttachmentIds())) {
@@ -1452,7 +1537,6 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
             log.error("结算异常. settlementVo=[{}]",JSONUtil.toJsonStr(settlementVo), e);
             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
             throw new RuntimeException(e.getMessage());
-            // return false;
         }
     }
 
@@ -2072,6 +2156,386 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         return true;
     }
 
+    /**
+     * 处理跟单费开票业务
+     * 包含:创建发票主表记录、批量创建跟单订单明细、建立发票与明细的关联关系
+     *
+     * @param vo 跟单开票请求参数
+     * @return 是否成功
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public InvoiceingResult followInvoicing(FollowInvoiceVo vo) {
+        // 校验明细列表,防止空列表导致无意义的数据库操作
+        InvoiceingResult result = new InvoiceingResult();
+        List<InsFeeFollowOrderVo> details = vo.getDetails();
+        if (CollUtil.isEmpty(details)) {
+            return result;
+        }
+
+        // 构建并保存发票主表信息
+        InsPlyIncomeInvoice invoice = buildInvoice(vo);
+
+        // 批量构建跟单订单明细和关联关系
+        List<InsFeeFollowOrder> orderList = new ArrayList<>(details.size());
+        List<InsPlyFollowInvoiceLink> links = new ArrayList<>(details.size());
+
+        for (InsFeeFollowOrderVo item : details) {
+            // 构建跟单订单明细
+            InsFeeFollowOrder order = new InsFeeFollowOrder();
+            BeanUtils.copyProperties(item, order);
+            order.setId(IdGenerate.nextId());
+            order.setSystemCode(baseController.getSystemCode());
+            // 如果选择了单条,则设置应收金额为开票金额
+            if ("0".equals(vo.getSelectFlag())){
+                order.setUninvoiced(order.getReceivablePremium().subtract(vo.getReceivableSupervisePremium()));
+                order.setInvoicedAmountTime(vo.getReceivableSupervisePremium());
+                order.setInvoiced(vo.getReceivableSupervisePremium());
+            }
+            orderList.add(order);
+
+            // 构建发票与订单的关联关系
+            InsPlyFollowInvoiceLink link = new InsPlyFollowInvoiceLink();
+            link.setId(IdGenerate.nextId());
+            link.setInvoiceId(invoice.getId());
+            link.setFollowId(order.getId());
+            links.add(link);
+        }
+
+        // 插入invoice表
+        insPlyIncomeInvoiceMapper.insert(invoice);
+        // 批量插入订单明细和关联关系,避免循环内单条插入导致的性能问题
+        // 注意:需确保 Mapper 中已配置对应的批量插入方法
+        insFeeFollowOrderMapper.insert(orderList);
+        insPlyFollowInvoiceLinkMapper.insert(links);
+
+        // 返回结果
+        result.setInvoiceId(invoice.getId());
+        result.setSumPrice(null != invoice.getReceivableSupervisePremium() ? invoice.getReceivableSupervisePremium().toString() : null);
+        return result;
+    }
+
+    /**
+     * 构建发票主表实体对象
+     */
+    private InsPlyIncomeInvoice buildInvoice(FollowInvoiceVo vo) {
+        InsPlyIncomeInvoice invoice = new InsPlyIncomeInvoice();
+        invoice.setId(IdGenerate.nextId());
+        invoice.setCompanyId(vo.getCompanyId());
+        // 发票类型:2-跟单费
+        invoice.setInvoiceType("2");
+        invoice.setInvoiceNo(vo.getInvoiceNo());
+        //  开票类型  1、手续费  2、跟单费  3、驾意险手续费 4、驾意险跟单费 5、平台应收-车险 6、平台应收-非车险
+        if("1".equals(vo.getInvoiceType()) || "2".equals(vo.getInvoiceType())){
+            invoice.setInvoiceRiskType("10");
+        }else if("5".equals(vo.getInvoiceType()) || "6".equals(vo.getInvoiceType())){
+            invoice.setInvoiceRiskType("30");
+        }else{
+            invoice.setInvoiceRiskType("20");
+        }
+        invoice.setInvoiceParty(vo.getInvoiceParty());
+        invoice.setOverinflatedAmount(vo.getOverinflatedAmount());
+        invoice.setPaymentReason(vo.getPaymentReason());
+        invoice.setReceivableSupervisePremium(vo.getReceivableSupervisePremium());
+        invoice.setTaxPoint(vo.getTaxPoint());
+        // 初始状态:0-待处理
+        invoice.setStatus("0");
+        return invoice;
+    }
+
+    /**
+     * 获取发票跟进分页列表数据
+     *
+     * @param followInvoicePageListVo
+     * @return 发票跟进VO列表
+     */
+    @Override
+    public List<InsFeeFollowOrderVo> followInvoicePageList(FollowInvoicePageListVo followInvoicePageListVo) {
+
+        if (null == followInvoicePageListVo || CollUtil.isEmpty(followInvoicePageListVo.getSearchList())){
+            return CollUtil.newArrayList();
+        }
+        FollowInvoicePageListVo.SearchVO searchYear = followInvoicePageListVo.getSearchList().stream().filter(search -> "signYear".equals(search.getName())).findFirst().orElse(new FollowInvoicePageListVo.SearchVO());
+        Integer startYear = Integer.parseInt(searchYear.getSearch().split(",")[0]);
+        Integer endYear = Integer.parseInt(searchYear.getSearch().split(",")[1]);
+
+        // 获取所有年份
+        List<Integer> yearList = new ArrayList<>();
+        if (startYear <= endYear) {
+            for (int year = startYear; year <= endYear; year++) {
+                yearList.add(year);
+            }
+        }
+
+        // 获取当前用户的系统编码
+        String systemCode = baseController.getUserSystemCode();
+
+        // 查询该年份下的所有跟进收入数据,并按合作方公司ID进行分组
+        List<InsPlyIncomeDto> incomes = baseMapper.queryFollowIncome(startYear, endYear, systemCode);
+
+        // ====================== 增加动态条件过滤 ======================
+        List<InsPlyIncomeDto> filterIncomes = new ArrayList<>();
+        if (CollUtil.isEmpty(followInvoicePageListVo.getSearchList())) {
+            filterIncomes = incomes;
+        } else {
+            List<FollowInvoicePageListVo.SearchVO> searchParams = followInvoicePageListVo.getSearchList();
+            if (CollUtil.isNotEmpty(searchParams)) {
+                Map<String, FollowInvoicePageListVo.SearchVO> searchVOMap = searchParams.stream()
+                        .filter(action -> StrUtil.isNotEmpty(action.getName()))
+                        .collect(Collectors.toMap(FollowInvoicePageListVo.SearchVO::getName, Function.identity(), (oldItem, newItem) -> oldItem));
+                Set<String> names = searchVOMap.keySet();
+
+                // 保险公司
+                Predicate<InsPlyIncomeDto> companyIdPredicate = insPlyIncome -> {
+                    if (!names.contains(FollowInvoicePageListVo.SearchVO.companyId)) {
+                        return true;
+                    }
+                    FollowInvoicePageListVo.SearchVO searchVO = searchVOMap.get(FollowInvoicePageListVo.SearchVO.companyId);
+                    String type = searchVO.getType();
+                    String search = searchVO.getSearch();
+                    if ("为空".equals(type) || "不为空".equals(type)) {
+                        return true;
+                    }
+                    if (StrUtil.isEmpty(search)) {
+                        return true;
+                    }
+                    if ("等于".equalsIgnoreCase(type) || "包含".equalsIgnoreCase(type)) {
+                        List<String> companyIds = Arrays.asList(search.split(","));
+                        return companyIds.contains(insPlyIncome.getCompanyId());
+                    }
+                    if ("不等于".equalsIgnoreCase(type) || "不包含".equalsIgnoreCase(type)) {
+                        List<String> companyIds = Arrays.asList(search.split(","));
+                        return !companyIds.contains(insPlyIncome.getCompanyId());
+                    }
+                    return true;
+                };
+                // 签单年份
+                Predicate<InsPlyIncomeDto> signYearPredicate = insPlyIncome -> {
+                    if (!names.contains(FollowInvoicePageListVo.SearchVO.signYear)) {
+                        return true;
+                    }
+                    FollowInvoicePageListVo.SearchVO searchVO = searchVOMap.get(FollowInvoicePageListVo.SearchVO.signYear);
+                    String type = searchVO.getType();
+                    String search = searchVO.getSearch();
+                    if ("为空".equals(type) || "不为空".equals(type)) {
+                        return true;
+                    }
+                    if (StrUtil.isEmpty(search)) {
+                        return true;
+                    }
+                    if ("等于".equalsIgnoreCase(type) || "包含".equalsIgnoreCase(type)) {
+                        List<String> signYears = Arrays.asList(search.split(","));
+                        return signYears.contains(insPlyIncome.getSignYear());
+                    }
+                    if ("不等于".equalsIgnoreCase(type) || "不包含".equalsIgnoreCase(type)) {
+                        List<String> signYears = Arrays.asList(search.split(","));
+                        return !signYears.contains(insPlyIncome.getSignYear());
+                    }
+                    return true;
+                };
+
+                // AND / OR 组合逻辑(和代码A完全一致)
+                if ("and".equalsIgnoreCase(followInvoicePageListVo.getLogicJudge())) {
+                    Predicate<InsPlyIncomeDto> and = companyIdPredicate.and(signYearPredicate);
+                    filterIncomes = incomes.stream().filter(and).toList();
+                } else if ("or".equalsIgnoreCase(followInvoicePageListVo.getLogicJudge())) {
+                    Predicate<InsPlyIncomeDto> or = companyIdPredicate.or(signYearPredicate);
+                    filterIncomes = incomes.stream().filter(or).toList();
+                }
+            }
+        }
+        // ======================条件过滤结束======================
+
+        // 按合作方分组(使用过滤后的数据)
+        Map<String, List<InsPlyIncomeDto>> groupMap = filterIncomes.stream()
+                .collect(Collectors.groupingBy(InsPlyIncomeDto::getPartnerCompanyId));
+
+        // 查询该年份下已开票的数据,并聚合为 [合作方ID_月份 -> 已开票金额] 的映射
+        Map<String, BigDecimal> invoicedMap = buildInvoicedMap(startYear, endYear);
+
+        // 遍历分组数据,组装最终的返回结果
+        List<InsFeeFollowOrderVo> result = new ArrayList<>();
+        for (Map.Entry<String, List<InsPlyIncomeDto>> entry : groupMap.entrySet()) {
+            String partnerCompanyId = entry.getKey();
+            List<InsPlyIncomeDto> list = entry.getValue();
+
+            // 过滤掉空列表,防止后续空指针异常
+            if (CollUtil.isEmpty(list)) {
+                continue;
+            }
+
+            // 计算该合作方每月的应收保费
+            Map<Integer, BigDecimal> monthReceivableMap = calculateMonthlyReceivable(list);
+
+            // 遍历12个月份,组装VO对象
+            InsPlyIncomeDto firstItem = list.get(0);
+            for (Integer year : yearList) {
+                for (int month = 1; month <= 12; month++) {
+                    InsFeeFollowOrderVo vo = buildFollowOrderVo(
+                            year, month, firstItem, partnerCompanyId,
+                            monthReceivableMap, invoicedMap
+                    );
+                    result.add(vo);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 构建已开票金额映射表
+     * 将已开票数据按 "合作方ID_月份" 进行聚合求和
+     */
+    private Map<String, BigDecimal> buildInvoicedMap(Integer startYear, Integer endYear) {
+        QueryWrapper<InsFeeFollowOrder> queryWrapper = new QueryWrapper<>();
+        queryWrapper.between("year", startYear, endYear);
+        List<InsFeeFollowOrder> invoicedList = insFeeFollowOrderMapper.selectList(queryWrapper);
+
+        return invoicedList.stream().collect(Collectors.groupingBy(
+                item -> item.getPartnerCompanyId() + "_" + item.getMonth(),
+                Collectors.reducing(
+                        BigDecimal.ZERO,
+                        InsFeeFollowOrder::getInvoiced,
+                        BigDecimal::add
+                )
+        ));
+    }
+
+    /**
+     * 计算单个月份的应收保费
+     * 避免在循环中重复创建BigDecimal对象,使用字符串构造或valueOf
+     */
+    private Map<Integer, BigDecimal> calculateMonthlyReceivable(List<InsPlyIncomeDto> list) {
+        return list.stream()
+                .filter(item -> item.getSigningTime() != null) // 防御性编程:过滤掉签约时间为空的数据
+                .collect(Collectors.groupingBy(
+                        item -> item.getSigningTime().getMonthValue(),
+                        Collectors.reducing(
+                                BigDecimal.ZERO,
+                                item -> {
+                                    // 优化点:将字符串转换为BigDecimal,避免在循环中频繁new对象
+                                    BigDecimal jqPremium = new BigDecimal(item.getJqReceivablePremium());
+                                    BigDecimal syPremium = new BigDecimal(item.getSyReceivablePremium());
+                                    return jqPremium.add(syPremium);
+                                },
+                                BigDecimal::add
+                        )
+                ));
+    }
+
+    /**
+     * 组装单个月的发票跟进VO对象
+     */
+    private InsFeeFollowOrderVo buildFollowOrderVo(Integer year, int month, InsPlyIncomeDto firstItem,
+                                                   String partnerCompanyId,
+                                                   Map<Integer, BigDecimal> monthReceivableMap,
+                                                   Map<String, BigDecimal> invoicedMap) {
+        // 获取当月应收保费,默认为0
+        BigDecimal receivable = monthReceivableMap.getOrDefault(month, BigDecimal.ZERO);
+        // 获取当月已开票金额,默认为0
+        BigDecimal invoiced = invoicedMap.getOrDefault(partnerCompanyId + "_" + month, BigDecimal.ZERO);
+
+        InsFeeFollowOrderVo vo = new InsFeeFollowOrderVo();
+        vo.setYear(year);
+        vo.setMonth(month);
+        vo.setCompanyName(firstItem.getCompanyName());
+        vo.setPartnerCompanyName(firstItem.getPartnerCompanyName());
+        vo.setCompanyId(firstItem.getCompanyId());
+        vo.setPartnerCompanyId(partnerCompanyId);
+        vo.setReceivablePremium(receivable);
+        vo.setInvoiced(invoiced);
+        // 未开票金额 = 应收保费 - 已开票金额
+        vo.setUninvoiced(receivable.subtract(invoiced));
+        return vo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean followSettlement(SettlementVo settlementVo) {
+        log.info("结算:settlementVo=[{}].DateTimeFormatter", JSONUtil.toJsonStr(settlementVo));
+        AssertionUtils.isFail(StrUtil.isEmpty(settlementVo.getReceivePaymentDate()), "结算日期不能为空");
+        try {
+            // 获取应收的金额
+            InsPlyIncomeInvoice insPlyIncomeInvoice = insPlyIncomeInvoiceMapper.selectById(settlementVo.getInvoicingId());
+            AssertionUtils.isFail(Objects.isNull(insPlyIncomeInvoice), "发票数据不存在");
+            // 实际已经收了的金额
+            BigDecimal actualReceivedAmount = this.getInvoicingAmount(settlementVo.getInvoicingId());
+
+            // 所有需要结算的金额,开票金额
+            BigDecimal allToSettlement = insPlyIncomeInvoice.getReceivableSupervisePremium();
+            // 剩余的金额
+            BigDecimal surplusPremium = allToSettlement.subtract(actualReceivedAmount);
+            log.info("invoiceId=[{}],一共需要结算的金额[{}],实际已经结算的金额[{}],本次计划结算的金额[{}]",
+                    settlementVo.getInvoicingId(), allToSettlement,actualReceivedAmount,settlementVo.getActualReceivedAmount());
+            // 校验是否超出了剩余的金额,判断 surplusPremium 是否 小于 实际到账金额
+            AssertionUtils.isFail(surplusPremium.compareTo(settlementVo.getActualReceivedAmount()) < 0, "已超出应收金额,请核对!");
+
+            // 处理结算数据
+            // 未完全结算完成
+            InsPlyIncomeInvoiceSettlement settlement = new InsPlyIncomeInvoiceSettlement(settlementVo.getInvoicingId(), settlementVo.getActualReceivedAmount(),
+                    DateTime.now(), settlementVo.getSettlementPaymentDifference(), settlementVo.getSettlementPaymentReason());
+            insPlyIncomeInvoiceSettlementMapper.insert(settlement);
+
+            if (CollectionUtils.isNotEmpty(settlementVo.getAttachmentIds())) {
+                log.info("结算的凭证是:settlementVo.getAttachmentIds()=[{}]", JSONUtil.toJsonStr(settlementVo.getAttachmentIds()));
+                // 添加发票附件
+                List<InsPlyIncomeInvoiceSettlementFile> files = new ArrayList<>();
+                for (String fileId : settlementVo.getAttachmentIds()) {
+                    InsPlyIncomeInvoiceSettlementFile file = new InsPlyIncomeInvoiceSettlementFile();
+                    file.setId(IdGenerate.nextId());
+                    file.setSettlementId(settlement.getId());
+                    file.setFileId(fileId);
+                    files.add(file);
+                }
+                insPlyIncomeInvoiceSettlementFileMapper.insert(files);
+            }
+
+            // 判断是否已经结算完成
+            if (surplusPremium.compareTo(settlementVo.getActualReceivedAmount()) == 0) {
+                log.info("序号为[{}]的发票已经结算完成",insPlyIncomeInvoice.getId());
+                // 更新发票状态
+                insPlyIncomeInvoice.setStatus("1");
+                insPlyIncomeInvoiceMapper.updateById(insPlyIncomeInvoice);
+                // 判断是需要更新哪个字段
+                this.updateIncomeSettlementStatus(insPlyIncomeInvoice.getInvoiceType(), null, LocalDateTime.now(),
+                        baseController.getSystemCode(), insPlyIncomeInvoice.getCompanyId());
+            }else{
+                log.info("invoiceId=[{}], 还剩[{}]钱没有结算",settlementVo.getInvoicingId(), surplusPremium.subtract(settlementVo.getActualReceivedAmount()));
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("结算异常. settlementVo=[{}]",JSONUtil.toJsonStr(settlementVo), e);
+            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+
+    /**
+     * 跟单费开票并结算
+     *
+     * @param followInvoiceAndSettlementVo 跟单费开票信息和结算信息
+     * @author jk
+     * @date 2026/5/26 11:44
+     */
+    @Override
+    public HttpResult<String> followInvoiceAndSettlement(FollowInvoiceAndSettlementVo followInvoiceAndSettlementVo) {
+        log.info("跟单费开票并结算: settlementVo=[{}]", JSONUtil.toJsonStr(followInvoiceAndSettlementVo));
+        InvoiceingResult result = this.followInvoicing(followInvoiceAndSettlementVo.getInvoiceVO());
+        if (Objects.nonNull(result)){
+            SettlementVo settlementVo = new SettlementVo();
+            BeanUtils.copyProperties(followInvoiceAndSettlementVo, settlementVo);
+            settlementVo.setInvoicingId(result.getInvoiceId());
+            boolean settlement = this.followSettlement(settlementVo);
+            if(settlement){
+                return HttpResult.ok("跟单费开票并结算成功");
+            }else{
+                return HttpResult.error("跟单费开票成功但结算失败");
+            }
+        }
+        return HttpResult.error("跟单费开票失败");
+    }
 
 
     @Override
@@ -2117,14 +2581,17 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                         .filter(Objects::nonNull)
                         .reduce(BigDecimal.ZERO, BigDecimal::add);
                 BigDecimal receivableSupervisePremium = action.getReceivableSupervisePremium()  != null ? action.getReceivableSupervisePremium(): BigDecimal.ZERO;
-                BigDecimal overinflatedAmount = action.getOverinflatedAmount() != null? action.getOverinflatedAmount(): BigDecimal.ZERO;
-                BigDecimal remainSettlementAmount = receivableSupervisePremium.add(overinflatedAmount).subtract(settlementAmount);
+//                BigDecimal overinflatedAmount = action.getOverinflatedAmount() != null? action.getOverinflatedAmount(): BigDecimal.ZERO;
+                // 待结算金额 = 开票金额 - 已收金额
+                BigDecimal remainSettlementAmount = receivableSupervisePremium.subtract(settlementAmount);
                 // 设置已结算的金额
                 action.setSettlementAmount(settlementAmount);
                 // 设置待结算金额
                 action.setRemainSettlementAmount(remainSettlementAmount);
-                action.setCompanyALlName(allCompanyHierarchyPaths.get(Objects.toString(action.getCompanyId(),"")));
+                action.setCompanyAllName(allCompanyHierarchyPaths.get(Objects.toString(action.getCompanyId(),"")));
                 action.setSettlementStatus(settlementStatusName);
+                // 开票金额
+                action.setReceivableAmount(action.getReceivableSupervisePremium().subtract(action.getOverinflatedAmount()).toString());
             }).toList();
             invoiceRecordList.setRecords(records);
         }
@@ -2236,6 +2703,23 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         return exportSuperviseSettlementExcelDtos;
     }
 
+    /**
+     * 跟单协议结算记录
+     *
+     * @param settlementQueryVo 查询条件
+     * @return Page<ExportSuperviseSettlementExcelDto>
+     */
+    @Override
+    public Page<ExportSuperviseSettlementExcelDto> followSettlementExcel(Page page, SettlementQueryVo settlementQueryVo) {
+        log.info("查询平台结算记录:settlementQueryVo=[{}]", JSONUtil.toJsonStr(settlementQueryVo));
+        // 设置空条件
+        setNoneCondition(settlementQueryVo);
+        Page<ExportSuperviseSettlementExcelDto> exportSuperviseSettlementExcelDtos = baseMapper.followSettlementExcelDtos(page, settlementQueryVo);
+        // 补充、转换列表的数据
+        supplementaryListData(exportSuperviseSettlementExcelDtos);
+        return exportSuperviseSettlementExcelDtos;
+    }
+
     /**
      * 设置空条件
      * @param settlementQueryVo

+ 3 - 2
tenant/organization/src/main/resources/application-dev.yml

@@ -6,16 +6,17 @@ spring:
       password:
       config:
         file-extension: yaml
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
         group: DEFAULT_GROUP
       discovery:
-        namespace: 4efb5e2a-71db-4a19-9550-bb69d437698d
+        namespace: 9fceaca4-c1d2-4bb8-aac9-f64d4b7de73c
   config:
     import:
       - nacos:mysql
       - nacos:redisson
       - nacos:minio
       - nacos:rocketmq
+      - nacos:seata-server
 
 config:
   sso: 1

+ 1 - 1
tenant/organization/src/main/resources/application-prod.yml

@@ -16,6 +16,7 @@ spring:
       - nacos:redisson
       - nacos:minio
       - nacos:rocketmq
+      - nacos:seata-server
 
 config:
   sso: 1
@@ -31,4 +32,3 @@ unionPay:
     bankCardVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/bankcard/verify
     publicKey: 0414bb7e9c3914bb65b85079b1dc6e1ca2a0ee04dd9bb55b2f8d31704a6dec0cca695739e128f9c931330e1c0f493be4d56244236434c9d344c4716ba64a4470ba
     privateKey: 24ebfde60aecb81214605050763926ec342c487859a8da57d5badcd3c1559336
-

+ 97 - 4
tenant/organization/src/main/resources/mapper/ReceivableMapper.xml

@@ -219,15 +219,21 @@
         ELSE ''
         END AS order_type,
         ipi.license_no,
-        (ipi.jq_receivable_premium + ipi.sy_receivable_premium + ipi.jy_receivable_premium) as receivableAmount,  -- 总应收金额
-        (ipi.jq_supervise_costs_premiums + ipi.sy_supervise_costs_premiums + ipi.jy_supervise_costs_premiums) as superviseCostsPremiums, -- 总应收手续费
-        (ipi.jq_other_costs_premiums + ipi.sy_other_costs_premiums + ipi.jy_other_costs_premiums) as otherCostsPremiums,  -- 总应收跟单费
+        -- 1. 总应收金额 (修复了多余的右括号)
+        (COALESCE(ipi.jq_receivable_premium, 0) + COALESCE(ipi.sy_receivable_premium, 0) + COALESCE(ipi.jy_receivable_premium, 0)) AS receivableAmount,
+        -- 2. 总应收手续费 (为最后一个字段补充了默认值 0)
+        (COALESCE(ipi.jq_supervise_costs_premiums, 0) + COALESCE(ipi.sy_supervise_costs_premiums, 0) + COALESCE(ipi.jy_supervise_costs_premiums, 0)) AS superviseCostsPremiums,
+        -- 3. 总应收跟单费 (修复了括号位置错误,并为最后一个字段补充了默认值 0)
+        (COALESCE(ipi.jq_other_costs_premiums, 0) + COALESCE(ipi.sy_other_costs_premiums, 0) + COALESCE(ipi.jy_other_costs_premiums, 0)) AS otherCostsPremiums,
         io.product_name,
         iocui.name AS insuredPerson,
         ipi.jq_premium,
         ipi.sy_premium,
         ipi.jy_premium,
         ipi.tax_premium,
+        ipi.jq_receivable_premium,
+        ipi.sy_receivable_premium,
+        ipi.jy_receivable_premium,
         ipi.jq_supervise_costs_proportion,
         ipi.sy_supervise_costs_proportion,
         ipi.jy_supervise_costs_proportion,
@@ -608,7 +614,7 @@
         AND ipii.is_delete = 0
         AND ipi.is_delete = 0
         AND io.is_delete = 0
-        AND agree.is_delete = 0
+<!--        AND agree.is_delete = 0 modify by lipf 2026年6月6日14:53:23   解决协议被删除后,相关的开票信息,结算信息查询不出来的问题 -->
         <if test="settlementQueryVo.settlementIds != null and settlementQueryVo.settlementIds != ''">
             AND ipiis.id IN
             <foreach collection="settlementQueryVo.settlementIds" item="settlementId" open="(" separator="," close=")">
@@ -658,6 +664,73 @@
         order by ipiis.create_time desc
     </select>
 
+    <select id="followSettlementExcelDtos" resultType="com.jzg.commons.entity.finance.dto.ExportSuperviseSettlementExcelDto">
+        SELECT
+        ipiis.id AS settlementId,
+        -- 拼接需要合并的字段,DISTINCT 去重,SEPARATOR '、' 用顿号分隔
+        GROUP_CONCAT(DISTINCT ipiis.invoice_id SEPARATOR ',') AS invoiceId,
+        GROUP_CONCAT(DISTINCT ipii.company_id SEPARATOR ',') AS company_id,
+        GROUP_CONCAT(DISTINCT eic.name SEPARATOR ',') AS company_name,
+        GROUP_CONCAT(DISTINCT ipii.invoice_type SEPARATOR ',') AS invoice_type,
+        GROUP_CONCAT(DISTINCT ipii.invoice_party SEPARATOR ',') AS invoice_party,
+        GROUP_CONCAT(DISTINCT ipii.tax_point SEPARATOR ',') AS tax_point,
+        -- 金额/日期这类同一settlementId相同的字段,直接取任意一个即可
+        MAX(ipiis.actual_received_amount) AS actual_received_amount,
+        MAX(ipiis.receive_payment_date) AS receive_payment_date,
+        MAX(ipiis.create_by) AS create_by,
+        MAX(ipiis.create_time) AS create_time
+        FROM ins_ply_income_invoice_settlement ipiis
+        LEFT JOIN ins_ply_income_invoice ipii ON ipii.id = ipiis.invoice_id AND ipii.is_delete = 0
+        LEFT JOIN ins_ply_follow_invoice_link link ON link.invoice_id = ipiis.invoice_id AND link.is_delete = 0
+        LEFT JOIN ins_fee_follow_order fo ON fo.id = link.follow_id AND fo.is_delete = 0
+        LEFT JOIN esm_ins_company eic ON ipii.company_id = eic.id  AND eic.is_delete = 0
+        WHERE 1 = 1
+        <if test="settlementQueryVo.settlementIds != null and settlementQueryVo.settlementIds != ''">
+            AND ipiis.id IN
+            <foreach collection="settlementQueryVo.settlementIds" item="settlementId" open="(" separator="," close=")">
+                #{settlementId}
+            </foreach>
+        </if>
+        <if test="settlementQueryVo.invoiceTypes != null and settlementQueryVo.invoiceTypes != ''">
+            AND ipii.invoice_type IN
+            <foreach collection="settlementQueryVo.invoiceTypes" item="invoiceType" open="(" separator="," close=")">
+                #{invoiceType}
+            </foreach>
+        </if>
+        <if test="settlementQueryVo.companyId != null and settlementQueryVo.companyId != ''">
+            and io.company_id = #{settlementQueryVo.companyId}
+        </if>
+        <if test="settlementQueryVo.invoiceParty != null and settlementQueryVo.invoiceParty != ''">
+            and ipii.invoice_party = #{settlementQueryVo.invoiceParty}
+        </if>
+        <if test="settlementQueryVo.invoiceRiskType != null and settlementQueryVo.invoiceRiskType != ''">
+            and ipii.invoice_risk_type = #{settlementQueryVo.invoiceRiskType}
+        </if>
+        <if test="settlementQueryVo.status != null and settlementQueryVo.status != ''">
+            and ipii.status = #{settlementQueryVo.status}
+        </if>
+        <if test="settlementQueryVo.settlementPerson != null and settlementQueryVo.settlementPerson != ''">
+            and ipiis.create_by = #{settlementQueryVo.settlementPerson}
+        </if>
+        <if test="settlementQueryVo.invoiceId != null and settlementQueryVo.invoiceId != ''">
+            AND ipiis.invoice_id = #{settlementQueryVo.invoiceId}
+        </if>
+        <if test="settlementQueryVo.settlementId != null and settlementQueryVo.settlementId != ''">
+            AND ipiis.id = #{settlementQueryVo.settlementId}
+        </if>
+        <if test="settlementQueryVo.settlementDateStart != null and settlementQueryVo.settlementDateStart != ''">
+            and ipiis.create_time >= date(#{settlementQueryVo.settlementDateStart})
+        </if>
+        <if test="settlementQueryVo.settlementDateEndStr != null and settlementQueryVo.settlementDateEndStr != ''">
+            and ipiis.create_time &lt;= date(#{settlementQueryVo.settlementDateEndStr})
+        </if>
+        <if test="settlementQueryVo.settlementId != null and settlementQueryVo.settlementId != ''">
+            AND ipiis.id = #{settlementQueryVo.settlementId}
+        </if>
+        GROUP BY ipiis.id
+        order by ipiis.create_time desc
+    </select>
+
 <!--    <select id="getSuperviseSettlementExcelDtos" resultType="com.jzg.commons.entity.finance.dto.ExportSuperviseSettlementExcelDto">-->
 <!--        select-->
 <!--            ipiis.id as settlementId,-->
@@ -1131,6 +1204,7 @@
         SELECT count(id) AS count
         FROM ins_ply_income_invoice_link
         <where>
+            is_delete = 0
             <if test="invoicingVo.incomeIds != null and invoicingVo.incomeIds.size > 0">
                 AND income_id IN
                 <foreach collection="invoicingVo.incomeIds"
@@ -1629,6 +1703,25 @@
         </where> ) a
         group by  a.company_id
     </select>
+    <select id="queryFollowIncome" resultType="com.jzg.commons.entity.finance.dto.InsPlyIncomeDto">
+        SELECT
+        a.*,
+        date(a.signing_time ) as sign_date,
+        year(a.signing_time) as sign_year,
+        b.partner_company_id,
+        (SELECT eic.name FROM esm_ins_company eic WHERE b.partner_company_id = eic.id) AS partnerCompanyName,
+        (SELECT eic.name FROM esm_ins_company eic WHERE a.company_id = eic.id) AS companyName
+        FROM ins_ply_income a
+        LEFT JOIN ptl_agreement b ON a.agreement_id = b.id
+        WHERE a.is_delete=0
+        AND a.agreement_type = 2
+        <if test="startYear != null and endYear != null">
+            AND YEAR(a.signing_time) BETWEEN #{startYear} AND #{endYear}
+        </if>
+        <if test="systemCode != null">
+            AND a.system_code = #{systemCode}
+        </if>
+    </select>
 
     <!--    <select id="getReceivableList" resultType="com.jzg.commons.entity.finance.po.InsPlyIncome">-->
 <!--        SELECT-->

+ 1 - 1
tenant/organization/src/main/resources/mapper/SysUserJzgInfoBankCardMapper.xml

@@ -63,7 +63,7 @@
         <if test="sysUserJzgInfoBankCardVo.applyTimeStart != null and sysUserJzgInfoBankCardVo.applyTimeEnd != null">
             and DATE_FORMAT(sujibc.apply_time, '%Y-%m-%d') between #{sysUserJzgInfoBankCardVo.applyTimeStart} AND #{sysUserJzgInfoBankCardVo.applyTimeEnd}
         </if>
-        <if test="sysUserJzgInfoBankCardVo.auditTimeStart != null and sysUserJzgInfoBankCardVo.auditTimeEnd == null">
+        <if test="sysUserJzgInfoBankCardVo.auditTimeStart != null and sysUserJzgInfoBankCardVo.auditTimeEnd != null">
             and DATE_FORMAT(sujibc.audit_time, '%Y-%m-%d') between #{sysUserJzgInfoBankCardVo.auditTimeStart} AND #{sysUserJzgInfoBankCardVo.auditTimeEnd}
         </if>
         <if test="sysUserJzgInfoBankCardVo.auditBy != null and sysUserJzgInfoBankCardVo.auditBy != ''">

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików