Просмотр исходного кода

Merge remote-tracking branch 'origin/master'

Qchen 2 недель назад
Родитель
Сommit
4c0416931d
40 измененных файлов с 1518 добавлено и 619 удалено
  1. 2 0
      commons/src/main/java/com/jzg/commons/entity/dto/CostExternalPageParam.java
  2. 4 0
      commons/src/main/java/com/jzg/commons/entity/dto/CostManagementVo.java
  3. 26 0
      commons/src/main/java/com/jzg/commons/entity/dto/PtlAgreementCostCopyExternalParam.java
  4. 3 0
      commons/src/main/java/com/jzg/commons/entity/dto/PtlAgreementCostParam.java
  5. 4 0
      commons/src/main/java/com/jzg/commons/entity/finance/dto/InsPlyIncomeDto.java
  6. 10 0
      commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncome.java
  7. 168 0
      commons/src/main/java/com/jzg/commons/entity/finance/vo/ExportPlatFormReceivableVO.java
  8. 4 0
      commons/src/main/java/com/jzg/commons/entity/finance/vo/importimcome/ImportContentExt.java
  9. 3 2
      commons/src/main/java/com/jzg/commons/entity/po/PtlAgreementUndwrtRulesAttrLink.java
  10. 7 0
      commons/src/main/java/com/jzg/commons/entity/quote/vo/aggregated/QuoteEchoVo.java
  11. 3 0
      commons/src/main/java/com/jzg/commons/entity/quote/vo/orders/InsOrdersVo.java
  12. 1 1
      commons/src/main/java/com/jzg/commons/entity/vo/RuleAttrMappingVo.java
  13. 0 92
      commons/src/main/java/com/jzg/commons/util/BuildDescUtils.java
  14. 1 1
      tenant/insurance/quotation-chengtai/src/main/java/com/jzg/quotation/chengtai/api/build/ChengTaiAPIQuoteResultsVoBuild.java
  15. 2 2
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/dto/GuoRenBiDTO.java
  16. 17 8
      tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/request/GuoRenCrawlerCalculationPremiumRequest.java
  17. 12 7
      tenant/insurance/quotation-huanong/src/main/java/com/jzg/quotation/huanong/crawler/constant/enums/JiaYiXianProduct.java
  18. 4 4
      tenant/insurance/quotation-huanong/src/main/java/com/jzg/quotation/huanong/crawler/entity/request/HuaNongCrawlerQuotedPriceRequestRequest.java
  19. 28 23
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/component/HtCrawlerRequestComponent.java
  20. 33 29
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/component/HuaTaiCrawlerCacheComponent.java
  21. 48 41
      tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/service/Impl/HuaTaiCrawlerRequestImpl.java
  22. 17 30
      tenant/insurance/quotation-renbaonew/src/main/java/com/jzg/quotation/renbaonew/crawler/service/impl/RenBaoNewCrawlerRequestBuilder.java
  23. 148 40
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/FactorMatch.java
  24. 1 0
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/controller/InsOrdersController.java
  25. 48 21
      tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/service/impl/InsOrdersServiceImpl.java
  26. 4 4
      tenant/insurance/quotation-summary/src/main/resources/mapper/InsOrdersMapper.xml
  27. 231 0
      tenant/organization/src/main/java/com/jzg/organization/component/BuildDescUtils.java
  28. 6 1
      tenant/organization/src/main/java/com/jzg/organization/controller/AreaController.java
  29. 11 0
      tenant/organization/src/main/java/com/jzg/organization/controller/PtlAgreementCostExternalController.java
  30. 1 0
      tenant/organization/src/main/java/com/jzg/organization/service/PtlAgreementCostExternalService.java
  31. 58 2
      tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementCostExternalServiceImpl.java
  32. 12 2
      tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementCostServiceImpl.java
  33. 79 37
      tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementServiceImpl.java
  34. 44 128
      tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementUndwrtRulesServiceImpl.java
  35. 140 144
      tenant/organization/src/main/java/com/jzg/organization/service/impl/ReceivableServiceImpl.java
  36. 3 0
      tenant/organization/src/main/java/com/jzg/organization/service/impl/importincome/ImportIncomeServiceImpl.java
  37. 315 0
      tenant/organization/src/main/java/com/jzg/organization/utils/IncomeInvoiceLinkUtil.java
  38. 3 0
      tenant/organization/src/main/resources/mapper/PtlAgreementCostExternalMapper.xml
  39. 15 0
      tenant/organization/src/main/resources/mapper/PtlAgreementCostMapper.xml
  40. 2 0
      tenant/organization/src/main/resources/mapper/SysUserMapper.xml

+ 2 - 0
commons/src/main/java/com/jzg/commons/entity/dto/CostExternalPageParam.java

@@ -31,6 +31,8 @@ public class CostExternalPageParam extends PageRequest {
 
     @Schema(description = "有效状态")
     private String validState;
+    @Schema(description = "协议id")
+    private String ptlAgreementId;
 
     @Schema(description = "生效时间")
     private List<String> effectiveTime;

+ 4 - 0
commons/src/main/java/com/jzg/commons/entity/dto/CostManagementVo.java

@@ -98,4 +98,8 @@ public class CostManagementVo {
     @Schema(description = "协议描述")
     private String agreementName;
 
+    @Schema(description = "出单账号简称")
+    private String userAbbr;
+
+    private String attributionId;
 }

+ 26 - 0
commons/src/main/java/com/jzg/commons/entity/dto/PtlAgreementCostCopyExternalParam.java

@@ -0,0 +1,26 @@
+package com.jzg.commons.entity.dto;
+
+
+import com.jzg.commons.core.base.PageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Schema(description = "协议费用复制参数")
+public class PtlAgreementCostCopyExternalParam extends PageRequest {
+
+    @Schema(description = "需要复制的费用id")
+    private List<String> costIds;
+
+    @Schema(description = "需要复制的协议id")
+    private String agreementId;
+
+    @Schema(description = "对接人姓名")
+    private String contactPerson;
+
+    @Schema(description = "对接人手机号")
+    private String contactMobile;
+}

+ 3 - 0
commons/src/main/java/com/jzg/commons/entity/dto/PtlAgreementCostParam.java

@@ -41,6 +41,9 @@ public class PtlAgreementCostParam extends PageRequest {
     @Schema(description = "费用规则")
     private List<String> costRulesList;
 
+    @Schema(description = "费用描述")
+    private String[] costDescribes;
+
     @Schema(description = "费用状态")
     private String agreementStatus;
 

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

@@ -316,6 +316,10 @@ public class InsPlyIncomeDto extends BaseModel {
     @Schema(description = "订单关联的发票信息")
     private String invoiceId;
 
+    @TableField(exist = false)
+    @Schema(description = "订单关联的发票信息")
+    private String invoiceIds;
+
     @TableField(exist = false)
     @Schema(description = "1-手续费, 2-跟单费, 3-驾意险手续费, 4-驾意险跟单费, 5-平台应收-车险, 6-平台应收-非车险")
     private String invoiceType;

+ 10 - 0
commons/src/main/java/com/jzg/commons/entity/finance/po/InsPlyIncome.java

@@ -66,6 +66,11 @@ public class InsPlyIncome extends BaseModel {
     @ExcelIgnore
     private String signYear;
 
+    @Schema(description = "签单月份")
+    @TableField(exist = false)
+    @ExcelIgnore
+    private String signMonth;
+
     @Schema(description = "签单日期 yyyy-mm-dd 格式")
     @TableField(exist = false)
     @ExcelProperty("签单日期")
@@ -397,6 +402,11 @@ public class InsPlyIncome extends BaseModel {
     @ExcelIgnore
     private String partnerCompanyId;
 
+    @TableField(exist = false)
+    @ExcelIgnore
+    @Schema(description = "1-手续费, 2-跟单费, 3-驾意险手续费, 4-驾意险跟单费, 5-平台应收-车险, 6-平台应收-非车险")
+    private String invoiceType;
+
     @TableField(exist = false)
     @Schema(description = "禁用标志:1-已对账(禁用),0-未对账(可用)")
     @ExcelIgnore

+ 168 - 0
commons/src/main/java/com/jzg/commons/entity/finance/vo/ExportPlatFormReceivableVO.java

@@ -0,0 +1,168 @@
+package com.jzg.commons.entity.finance.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+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.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Schema(description = "财务应收表")
+public class ExportPlatFormReceivableVO extends BaseModel {
+
+    @Serial
+    private static final long serialVersionUID = 6161862008963938376L;
+
+    @ExcelIgnore
+    private String id;
+
+    // ==================== 以下为按表头顺序导出的字段 ====================
+
+    @Schema(description = "子订单号")
+    @ExcelProperty(value = "子订单号", index = 0)
+    private String orderNo;
+
+    @Schema(description = "协议号")
+    @ExcelProperty(value = "协议号", index = 1)
+    private String agreementId;
+
+    @Schema(description = "签单日期 yyyy-mm-dd 格式")
+    @TableField(exist = false)
+    @ExcelProperty(value = "签单日期", index = 2)
+    private String signDate;
+
+    @Schema(description = "车牌号")
+    @ExcelProperty(value = "车牌号", index = 3)
+    private String licenseNo;
+
+    @Schema(description = "被保险人")
+    @ExcelProperty(value = "被保险人", index = 4)
+    private String insuredPerson;
+
+    @Schema(description = "保险公司名称")
+    @TableField(exist = false)
+    @ExcelProperty(value = "保险公司名称", index = 5)
+    private String companyName;
+
+    @Schema(description = "险种")
+    @ExcelProperty(value = "险种", index = 6)
+    private String productName;
+
+    @Schema(description = "保司机构")
+    @TableField(exist = false)
+    @ExcelProperty(value = "保司机构", index = 7)
+    private String partnerCompanySimplyName;
+
+    @Schema(description = "对接人")
+    @ExcelProperty(value = "对接人", index = 8)
+    private String contactPerson;
+
+    @Schema(description = "交强险保单号")
+    @ExcelProperty(value = "交强险保单号", index = 9)
+    private String jqPolicyNo;
+
+    @Schema(description = "商业保单号")
+    @ExcelProperty(value = "商业保单号", index = 10)
+    private String syPolicyNo;
+
+    @Schema(description = "非车保单号")
+    @ExcelProperty(value = "非车保单号", index = 11)
+    private String jyPolicyNo;
+
+    @Schema(description = "交强险保费")
+    @ExcelProperty(value = "交强险保费", index = 12)
+    private String jqPremium;
+
+    @Schema(description = "车船税")
+    @ExcelProperty(value = "车船税", index = 13)
+    private String taxPremium;
+
+    @Schema(description = "商业险保费")
+    @ExcelProperty(value = "商业险保费", index = 14)
+    private String syPremium;
+
+    @Schema(description = "非车保费")
+    @ExcelProperty(value = "非车保费", index = 15)
+    private String jyPremium;
+
+    @Schema(description = "交强险应收比例")
+    @TableField(exist = false)
+    @ExcelProperty(value = "交强险应收比例", index = 16)
+    private String jqTotalProportion;
+
+    @Schema(description = "交强险手续费比例")
+    @ExcelProperty(value = "交强险手续费比例", index = 17)
+    private String jqSuperviseCostsProportion;
+
+    @Schema(description = "交强险非跟单比例")
+    @ExcelProperty(value = "交强险非跟单比例", index = 18)
+    private String jqOtherCostsProportion;
+
+    @Schema(description = "商业应收比例")
+    @TableField(exist = false)
+    @ExcelProperty(value = "商业应收比例", index = 19)
+    private String syTotalProportion;
+
+    @Schema(description = "商业手续费比例")
+    @ExcelProperty(value = "商业手续费比例", index = 20)
+    private String sySuperviseCostsProportion;
+
+    @Schema(description = "商业非跟单比例")
+    @ExcelProperty(value = "商业非跟单比例", index = 21)
+    private String syOtherCostsProportion;
+
+    @Schema(description = "非车应收比例")
+    @TableField(exist = false)
+    @ExcelProperty(value = "非车应收比例", index = 22)
+    private String jyTotalProportion;
+
+    @Schema(description = "非车手续费比例")
+    @ExcelProperty(value = "非车手续费比例", index = 23)
+    private String jySuperviseCostsProportion;
+
+    @Schema(description = "非车非跟单比例")
+    @ExcelProperty(value = "非车非跟单比例", index = 24)
+    private String jyOtherCostsProportion;
+
+    @Schema(description = "手续费金额")
+    @TableField(exist = false)
+    @ExcelProperty(value = "手续费金额", index = 25)
+    private String superviseCostsPremiums;
+
+    @Schema(description = "非跟单金额")
+    @TableField(exist = false)
+    @ExcelProperty(value = "非跟单金额", index = 26)
+    private String otherCostsPremiums;
+
+    @Schema(description = "创建日期")
+    @TableField(exist = false)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ExcelProperty(value = "创建日期", index = 27)
+    private LocalDateTime createTime;
+
+    @Schema(description = "结算日期")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ExcelProperty(value = "结算日期", index = 28)
+    private LocalDateTime settlementTime;
+
+    @Schema(description = "结算人")
+    @TableField(exist = false)
+    @ExcelProperty(value = "结算人", index = 29)
+    private String settlementPerson;
+
+    @Schema(description = "结算状态")
+    @TableField(exist = false)
+    @ExcelProperty(value = "结算状态", index = 30)
+    private String settlementStatusName;
+
+
+}

+ 4 - 0
commons/src/main/java/com/jzg/commons/entity/finance/vo/importimcome/ImportContentExt.java

@@ -3,6 +3,7 @@ package com.jzg.commons.entity.finance.vo.importimcome;
 
 import cn.hutool.core.util.StrUtil;
 import com.jzg.commons.entity.finance.po.InsPlyIncome;
+import com.jzg.commons.entity.orders.po.InsOrdersPolicy;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
@@ -33,6 +34,9 @@ public class ImportContentExt extends ExcelContent4Platform {
     @Schema(description = "应收订单")
     private InsPlyIncome insPlyIncome;
 
+    @Schema(description = "车险订单信息")
+    private InsOrdersPolicy insOrdersPolicy;
+
     //  20 表示平台协议  11表示车险手续费 12 表示非车险手续费
     @Schema(description = "类型")
     private String type;

+ 3 - 2
commons/src/main/java/com/jzg/commons/entity/po/PtlAgreementUndwrtRulesAttrLink.java

@@ -1,7 +1,6 @@
 package com.jzg.commons.entity.po;
 
-import java.io.Serializable;
-import java.util.Date;
+import java.io.Serial;
 
 import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.annotation.TableField;
@@ -17,6 +16,8 @@ import lombok.Data;
 @Data
 public class PtlAgreementUndwrtRulesAttrLink extends BaseModel {
 
+    @Serial
+    private static final long serialVersionUID = -3935263637863405461L;
     /** 主键/自增 */
     @Schema(description = "主键/自增")
     private String id;

+ 7 - 0
commons/src/main/java/com/jzg/commons/entity/quote/vo/aggregated/QuoteEchoVo.java

@@ -78,6 +78,12 @@ public class QuoteEchoVo extends QuoteVo {
         private String agreementId;
         private String agreementName;
         private String agreementType;
+
+        /**
+         * 协议归属
+         */
+        private String agreementOwnership;
+
         private String createBy;
 
         /**
@@ -357,6 +363,7 @@ public class QuoteEchoVo extends QuoteVo {
         this.setOrdersNo(insOrdersVo.getId());
         this.getOrder().setAgreementId(insOrdersVo.getInsOrders().getAgreementId());
         this.getOrder().setAgreementName(insOrdersVo.getAgreementName());
+        this.getOrder().setAgreementOwnership(insOrdersVo.getAgreementOwnership());
         this.getOrder().setAgreementType(insOrdersVo.getAgreementType());
         this.getOrder().setOrdersNo(insOrdersVo.getId());
         this.getOrder().setQuoteNo(insOrdersVo.getQuoteNo());

+ 3 - 0
commons/src/main/java/com/jzg/commons/entity/quote/vo/orders/InsOrdersVo.java

@@ -52,6 +52,9 @@ public class InsOrdersVo extends QuoteVo {
     @Schema(description = "协议id")
     private String agreementId;
 
+    @Schema(description = "协议归属名称")
+    private String agreementOwnership;
+
     @Schema(description = "审核状态")
     private Integer auditStatus;
 

+ 1 - 1
commons/src/main/java/com/jzg/commons/entity/vo/RuleAttrMappingVo.java

@@ -327,7 +327,7 @@ public class RuleAttrMappingVo {
         // 车牌地区
         ruleAttrMappingVoList.add(new RuleAttrMappingVo("LicenseArea", insOrdersCarInfo.getLicenseNo().substring(0, 1)));
         // 系数
-        ruleAttrMappingVoList.add(new RuleAttrMappingVo("Coefficient", getCoefficient(insOrdersCarInfo, insOrdersCosts).toString()));
+        ruleAttrMappingVoList.add(new RuleAttrMappingVo("CoefficientFactor", getCoefficient(insOrdersCarInfo, insOrdersCosts).toString()));
         // 商业是否有费用
         ruleAttrMappingVoList.add(new RuleAttrMappingVo("CiMessage", (insOrdersOther.getCiMessage() == null || "".equals(insOrdersOther.getCiMessage())) ? "0" : "1"));
 

+ 0 - 92
commons/src/main/java/com/jzg/commons/util/BuildDescUtils.java

@@ -1,92 +0,0 @@
-package com.jzg.commons.util;
-
-import com.jzg.commons.entity.po.PtlAgreementUndwrtRulesAttr;
-import com.jzg.commons.entity.po.PtlAgreementUndwrtRulesDictLabal;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-public class BuildDescUtils {
-
-
-    public static String buildDesc(String companyId, List<PtlAgreementUndwrtRulesAttr> linkList, List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList) {
-        StringBuilder desc = new StringBuilder();
-        Map<String, PtlAgreementUndwrtRulesAttr> attrMap = undwrtRulesAttrList.stream()
-                .filter(attr -> "all".equals(attr.getCompanyIds()) || (attr.getCompanyIds() != null && attr.getCompanyIds().equals(companyId)))
-                .collect(Collectors.toMap(PtlAgreementUndwrtRulesAttr::getAttrCode, Function.identity()));
-
-        for (PtlAgreementUndwrtRulesAttr link : linkList) {
-            PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr = attrMap.get(link.getAttrCode());
-            if (ptlAgreementUndwrtRulesAttr == null) {
-                continue;
-            }
-
-            String attrType = ptlAgreementUndwrtRulesAttr.getAttrType();
-
-            if ("inputNumber".equals(attrType)) {
-                desc.append(inputNumberIdentifying(ptlAgreementUndwrtRulesAttr, link)).append(",");
-            } else if ("radio".equals(attrType)) {
-                desc.append(radioIdentifying(ptlAgreementUndwrtRulesAttr, link)).append(",");
-            } else if ("checkbox".equals(attrType) || "select".equals(attrType)) {
-                if(link.getMinArray() != null && link.getMinArray().length > 0){
-                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含");
-                    for (int k=0 ; k<link.getMinArray().length; k++){
-                        PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = null;
-                        for (PtlAgreementUndwrtRulesDictLabal x : ptlAgreementUndwrtRulesAttr.getDictLabal()) {
-                            if (Arrays.asList(link.getMinArray()).contains("select".equals(attrType) ? x.getName() : x.getCode())) {
-                                ptlAgreementUndwrtRulesDictLabal = x;
-                                link.getMinArray()[k] = null;
-                                break;
-                            }
-                        }
-                        desc.append(null != ptlAgreementUndwrtRulesDictLabal ? ptlAgreementUndwrtRulesDictLabal.getName() : "").append(",");
-                    }
-
-                }else {
-                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含").append(link.getMin()).append(",");
-                }
-            } else if ("inputTag".equals(attrType)) {
-                if (link.getOperator() != null && "1".equals(link.getOperator())) {
-                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含").append(link.getMin()).append(",");
-                } else {
-                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("不包含").append(link.getMin()).append(",");
-                }
-            }
-        }
-        return desc.toString();
-    }
-
-    private static String radioIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttr link) {
-        Optional<PtlAgreementUndwrtRulesDictLabal> dictLabel = Optional.ofNullable(
-                        ptlAgreementUndwrtRulesAttr.getDictLabal())
-                .flatMap(list -> list.stream().filter(x -> x.getCode().equals(link.getMin())).findFirst());
-
-        return dictLabel.map(dl -> ptlAgreementUndwrtRulesAttr.getAttrName() + "为" + dl.getName())
-                .orElse("");
-    }
-
-    private static String inputNumberIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttr link) {
-        String operator = link.getOperator();
-        switch (operator) {
-            case "1":
-                return ptlAgreementUndwrtRulesAttr.getAttrName() + "范围值" + link.getMin() + "到" + link.getMax();
-            case "2":
-                return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于" + link.getMin();
-            case "3":
-                return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于等于" + link.getMin();
-            case "4":
-                return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于" + link.getMax();
-            case "5":
-                return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于等于" + link.getMax();
-            default:
-                return ptlAgreementUndwrtRulesAttr.getAttrName() + "未知操作符";
-        }
-    }
-
-
-
-}

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

@@ -132,7 +132,7 @@ public final class ChengTaiAPIQuoteResultsVoBuild {
                     .orElse(null);
             scoreJson.add(new JSONObject()
                     .fluentPut("scoreName", "自主定价系数")
-                    .fluentPut("scoreKey","coefficient")
+                    .fluentPut("scoreKey","pricingCoefficient")
                     .fluentPut("score", coefficientVal));
             scoreJson.add(new JSONObject()
                     .fluentPut("scoreName", "一账通评分")

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

@@ -127,7 +127,7 @@ public class GuoRenBiDTO {
             prpTmain.setEndDate(TimeUtils.extractDate(biRiskInfoVo.getEndDate()));
             prpTmain.setEndHour(24);
             prpTmain.setEndMinute(0);
-            this.prptitemkindList = GuoRenCrawlerCalculationPremiumRequest.PrptitemkindListDTO.getBiPrptitemkindList(kindInfoVo, prpTmain);
+            this.prptitemkindList = GuoRenCrawlerCalculationPremiumRequest.PrptitemkindListDTO.getBiPrptitemkindList(kindInfoVo, prpTmain, prpTitemCar.getNewEnergyFlag());
 
         }
         this.prpTitemCar = prpTitemCar;
@@ -160,7 +160,7 @@ public class GuoRenBiDTO {
         this.inputRisk0530 = 0;
         this.shortRate = 100;
         this.calculateFlagBI = "1";
-        this.termsSystem = "04";
+        this.termsSystem = "1".equals(prpTitemCar.getNewEnergyFlag()) ? "05" : "04";
     }
 
     @NoArgsConstructor

+ 17 - 8
tenant/insurance/quotation-guoren/src/main/java/com/jzg/quotation/guoren/crawler/entity/request/GuoRenCrawlerCalculationPremiumRequest.java

@@ -2,7 +2,8 @@ package com.jzg.quotation.guoren.crawler.entity.request;
 
 import com.alibaba.nacos.common.utils.CollectionUtils;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.jzg.commons.entity.quote.vo.*;
+import com.jzg.commons.entity.quote.vo.KindInfoVo;
+import com.jzg.commons.entity.quote.vo.QuoteVo;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.commons.util.StringUtils;
 import com.jzg.quotation.commons.utils.TimeProcessing;
@@ -14,7 +15,7 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 import org.springframework.util.ObjectUtils;
 
-import java.util.*;
+import java.util.List;
 
 /**
  * @author wks
@@ -265,14 +266,22 @@ public class GuoRenCrawlerCalculationPremiumRequest {
         private Integer shareSumLimitSign;
 
 
-        public PrptitemkindListDTO(KindInfoVo kindInfoVo, GuoRenPrpTmainDTO prpTmain) {
-            InsuranceItem insuranceItem = InsuranceMap.INSURANCE_MAP.get(kindInfoVo.getKindCode());
+        public PrptitemkindListDTO(KindInfoVo kindInfoVo, GuoRenPrpTmainDTO prpTmain, String newEnergyFlag) {
+            String kindCode = kindInfoVo.getKindCode();
+            // 新能源汽车:普通条款险种(20X)切换为新能源条款险种(21X)
+            if ("1".equals(newEnergyFlag) && kindCode.startsWith("20")) {
+                String newEnergyCode = "21" + kindCode.substring(2);
+                if (InsuranceMap.INSURANCE_MAP.containsKey(newEnergyCode)) {
+                    kindCode = newEnergyCode;
+                }
+            }
+            InsuranceItem insuranceItem = InsuranceMap.INSURANCE_MAP.get(kindCode);
             if (ObjectUtils.isEmpty(insuranceItem)) {
                 throw new SystemException("未知险种");
             }
             this.checked = 1;
-            this.kindCode = kindInfoVo.getKindCode();
-            this.kindName = kindInfoVo.getKindName();
+            this.kindCode = kindCode;
+            this.kindName = kindCode.equals(kindInfoVo.getKindCode()) ? kindInfoVo.getKindName() : insuranceItem.getName();
             this.itemKindNo = Integer.parseInt(insuranceItem.getItemKindNo());
             this.shortRateFlag = "2";
             this.shortRate = "100.0000";
@@ -359,8 +368,8 @@ public class GuoRenCrawlerCalculationPremiumRequest {
             return List.of(prptitemkindListDTO);
         }
 
-        public static List<PrptitemkindListDTO> getBiPrptitemkindList(List<KindInfoVo> kindInfoVo, GuoRenPrpTmainDTO prpTmain) {
-            return kindInfoVo.stream().map(x -> new PrptitemkindListDTO(x, prpTmain)).toList();
+        public static List<PrptitemkindListDTO> getBiPrptitemkindList(List<KindInfoVo> kindInfoVo, GuoRenPrpTmainDTO prpTmain, String newEnergyFlag) {
+            return kindInfoVo.stream().map(x -> new PrptitemkindListDTO(x, prpTmain, newEnergyFlag)).toList();
         }
 
 

+ 12 - 7
tenant/insurance/quotation-huanong/src/main/java/com/jzg/quotation/huanong/crawler/constant/enums/JiaYiXianProduct.java

@@ -12,7 +12,7 @@ import java.util.List;
 @Getter
 public enum JiaYiXianProduct {
 
-    ANXIANG_100("112", "安享驾乘(100)", true, Arrays.asList(
+    ANXIANG_100("112","2781", "安享驾乘(100)", true, Arrays.asList(
             new CoverageConfig("Z27096-1", "交通出行团体意外伤害保险C款条款", "0955", "必选责任:意外伤害身故保险责任", 0.051, "80000", "8.0万元/座", " ", false),
             new CoverageConfig("Z27096-2", "交通出行团体意外伤害保险C款条款", "0956", "可选责任:意外伤害伤残保险责任", 0.034, "80000", "8.0万元/座", " ", false),
             new CoverageConfig("F27002", "附加团体意外伤害医疗保险条款", "0008", "意外伤害医疗保险责任", 0.28, "5000", "0.5万元/座", "每次事故免赔300后80%比例赔付", false),
@@ -21,7 +21,7 @@ public enum JiaYiXianProduct {
             new CoverageConfig("F27048-6", "附加救护车车费保险条款", "0095", "救护车费用", 0.2, "1000", "0.1万元/座", "每次事故最高赔付300元", false)
     )),
 
-    ANXIANG_120("113", "安享驾乘(120)", false, Arrays.asList(
+    ANXIANG_120("113","2781", "安享驾乘(120)", false, Arrays.asList(
             new CoverageConfig("Z27096-1", "交通出行团体意外伤害保险C款条款", "0955", "必选责任:意外伤害身故保险责任", 0.063, "120000", "12.0万元/座", " ", false),
             new CoverageConfig("Z27096-2", "交通出行团体意外伤害保险C款条款", "0956", "可选责任:意外伤害伤残保险责任", 0.042, "120000", "12.0万元/座", " ", false),
             new CoverageConfig("F27002", "附加团体意外伤害医疗保险条款", "0008", "意外伤害医疗保险责任", 0.26, "10000", "1.0万元/座", "每次事故免赔300后80%比例赔付", false),
@@ -30,13 +30,13 @@ public enum JiaYiXianProduct {
             new CoverageConfig("F27048-6", "附加救护车车费保险条款", "0095", "救护车费用", 0.2, "2000", "0.2万元/座", "每次事故最高赔付300元", false)
     )),
 
-    ANXIANG_36("111", "安享驾乘(36)", true, Arrays.asList(
+    ANXIANG_36("111","2781", "安享驾乘(36)", true, Arrays.asList(
             new CoverageConfig("Z27096-1", "交通出行团体意外伤害保险C款条款", "0955", "必选责任:意外伤害身故保险责任", 0.06, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("Z27096-2", "交通出行团体意外伤害保险C款条款", "0956", "可选责任:意外伤害伤残保险责任", 0.04, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("F27002", "附加团体意外伤害医疗保险条款", "0008", "意外伤害医疗保险责任", 0.24, "1000", "0.1万元/座", "每次事故免赔300后80%比例赔付", false),
             new CoverageConfig("F27001", "附加团体意外伤害住院津贴保险条款", "0032", "意外伤害住院津贴保险责任", 0.2, "10", "0.001万元/座", "10元/天/座", true)
     )),
-    ANXIANG_260("260", "安享驾乘(260)", true, Arrays.asList(
+    ANXIANG_260("260", "2781","安享驾乘(260)", true, Arrays.asList(
             new CoverageConfig("Z27096-1", "交通出行团体意外伤害保险C款条款", "0955", "必选责任:意外伤害身故保险责任", 0.06, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("Z27096-2", "交通出行团体意外伤害保险C款条款", "0956", "可选责任:意外伤害伤残保险责任", 0.04, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("F27002", "附加团体意外伤害医疗保险条款", "0008", "意外伤害医疗保险责任", 0.24, "1000", "0.1万元/座", "每次事故免赔300后80%比例赔付", false),
@@ -44,14 +44,14 @@ public enum JiaYiXianProduct {
             new CoverageConfig("F27047-5", "附加辅助金保险条款", "0094", "家庭辅助金", 0.02, "10", "20000", "2.0万元/座", true),
             new CoverageConfig("F27048-6", "附加救护车车费保险条款", "0095", "救护车费用", 0.1, "10", "2000", "0.2万元/座", true)
     )),
-    ANXIANG_50("091", "好运连连(50)", true, Arrays.asList(
+    ANXIANG_50("091","2782", "好运连连(50)", true, Arrays.asList(
             new CoverageConfig("Z27096-1", "交通出行团体意外伤害保险C款条款", "0955", "必选责任:意外伤害身故保险责任", 0.06, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("Z27096-2", "交通出行团体意外伤害保险C款条款", "0956", "可选责任:意外伤害伤残保险责任", 0.04, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("F27002", "附加团体意外伤害医疗保险条款", "0008", "意外伤害医疗保险责任", 0.24, "1000", "0.1万元/座", "每次事故免赔300后80%比例赔付", false),
             new CoverageConfig("F27001", "附加团体意外伤害住院津贴保险条款", "0032", "意外伤害住院津贴保险责任", 0.2, "10", "0.001万元/座", "10元/天/座", true)
 
     )),
-    ANXIANG_240("092", "好运连连(240)", true, Arrays.asList(
+    ANXIANG_240("092","2782", "好运连连(240)", true, Arrays.asList(
             new CoverageConfig("Z27096-1", "交通出行团体意外伤害保险C款条款", "0955", "必选责任:意外伤害身故保险责任", 0.06, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("Z27096-2", "交通出行团体意外伤害保险C款条款", "0956", "可选责任:意外伤害伤残保险责任", 0.04, "30000", "3.0万元/座", " ", false),
             new CoverageConfig("F27002", "附加团体意外伤害医疗保险条款", "0008", "意外伤害医疗保险责任", 0.24, "1000", "0.1万元/座", "每次事故免赔300后80%比例赔付", false),
@@ -66,6 +66,10 @@ public enum JiaYiXianProduct {
      * 产品代码
      */
     private final String productCode;
+    /**
+     * 产品代码
+     */
+    private final String nvehRiskCode;
 
     /**
      * 产品名称
@@ -82,8 +86,9 @@ public enum JiaYiXianProduct {
      */
     private final List<CoverageConfig> coverageConfigs;
 
-    JiaYiXianProduct(String productCode, String productName, boolean emptyEngage, List<CoverageConfig> coverageConfigs) {
+    JiaYiXianProduct(String productCode, String nvehRiskCode,String productName, boolean emptyEngage, List<CoverageConfig> coverageConfigs) {
         this.productCode = productCode;
+        this.nvehRiskCode = nvehRiskCode;
         this.productName = productName;
         this.emptyEngage = emptyEngage;
         this.coverageConfigs = coverageConfigs;

+ 4 - 4
tenant/insurance/quotation-huanong/src/main/java/com/jzg/quotation/huanong/crawler/entity/request/HuaNongCrawlerQuotedPriceRequestRequest.java

@@ -1798,18 +1798,18 @@ public class HuaNongCrawlerQuotedPriceRequestRequest implements HuaNongHeadReque
                 if (quoteVo.getAccidentalDrivings() != null) {
                     quoteVo.getAccidentalDrivings().forEach(accident -> {
                         String productCode = accident.getProductCode();
+                        JiaYiXianProduct product = JiaYiXianProduct.fromProductCode(productCode);
                         ContractAccidentDTO contractAccidentDTO = new ContractAccidentDTO();
                         contractAccidentDTO.setAccident(new ArrayList<>());
                         contractAccidentDTO.setAdjust(new ArrayList<>());
                         contractAccidentDTO.setAutoPriceApqMainList(new ArrayList<>());
                         contractAccidentDTO.setChoosed(true);
-                        contractAccidentDTO.setContractMain(buildContractMainDTO(accident));
+                        contractAccidentDTO.setContractMain(buildContractMainDTO(accident,product));
                         contractAccidentDTO.setLoocked(false);
 
                         List<CoverageDTO> coverageDTOS = buildCoverageDTOs(productCode);
                         contractAccidentDTO.setCoverage(coverageDTOS);
 
-                        JiaYiXianProduct product = JiaYiXianProduct.fromProductCode(productCode);
                         if (product.isEmptyEngage()) {
                             contractAccidentDTO.setEngage(new ArrayList<>());
                         } else {
@@ -1896,7 +1896,7 @@ public class HuaNongCrawlerQuotedPriceRequestRequest implements HuaNongHeadReque
                 return platFormMessage;
             }
 
-            private static ContractMainDTO buildContractMainDTO(AccidentalDrivingVo accidentVo) {
+            private static ContractMainDTO buildContractMainDTO(AccidentalDrivingVo accidentVo,JiaYiXianProduct product) {
                 ContractMainDTO contractMainDTO = new ContractMainDTO();
                 contractMainDTO.setAllQuantity(String.valueOf(accidentVo.getQuantity()));
                 contractMainDTO.setChoosed(true);
@@ -1905,7 +1905,7 @@ public class HuaNongCrawlerQuotedPriceRequestRequest implements HuaNongHeadReque
                 contractMainDTO.setInsuredObjectCode("7");
                 contractMainDTO.setInsuredObjectName("从车");
                 contractMainDTO.setIsRegistered("0");
-                contractMainDTO.setNvehRiskCode("2781");
+                contractMainDTO.setNvehRiskCode(product.getNvehRiskCode());
                 contractMainDTO.setNvehRiskName("安享驾");
                 contractMainDTO.setNvehRiskType("01");
                 contractMainDTO.setParagraph("1");

+ 28 - 23
tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/component/HtCrawlerRequestComponent.java

@@ -45,65 +45,71 @@ public class HtCrawlerRequestComponent {
      * @return
      */
     public Map<String, Object> authenticate(LoginDTO loginDTO, int loginsNumber) {
-        if(StringUtils.isNotBlank(huaTaiCrawlerCacheComponent.getCookie()) && StringUtils.isNotBlank(huaTaiCrawlerCacheComponent.getGsInfo())) {
+        String username = loginDTO.getUsername();
+        String comcode  = loginDTO.getComcode();
+
+        String cachedCookie = huaTaiCrawlerCacheComponent.getCookie(username, comcode);
+        String cachedGsInfo = huaTaiCrawlerCacheComponent.getGsInfo(username, comcode);
+        if (StringUtils.isNotBlank(cachedCookie) && StringUtils.isNotBlank(cachedGsInfo)) {
             Map<String, Object> res = new HashMap<>();
-            res.put("cookies", huaTaiCrawlerCacheComponent.getCookie());
-            res.put("gsInfo", huaTaiCrawlerCacheComponent.getGsInfo());
-            res.put("comCode", huaTaiCrawlerCacheComponent.getComCode());
+            res.put("cookies", cachedCookie);
+            res.put("gsInfo", cachedGsInfo);
+            res.put("comCode", huaTaiCrawlerCacheComponent.getComCode(username, comcode));
             return res;
         }
-        Map<String, Object> stringObjectMap = null;
+        Map<String, Object> stringObjectMap;
         try {
-            stringObjectMap = cookieUtils.mainFlow(loginDTO.getUsername(), loginDTO.getPassword(), loginDTO.getComcode(), loginDTO.getUseridname(), loginDTO.getDlrCode(), loginDTO.getDlxyCode(), 9999);
-            if(stringObjectMap == null) return null;
+            stringObjectMap = cookieUtils.mainFlow(username, loginDTO.getPassword(), comcode,
+                    loginDTO.getUseridname(), loginDTO.getDlrCode(), loginDTO.getDlxyCode(), 9999);
+            if (stringObjectMap == null) return null;
             log.info("登录stringObjectMap:{}", JSONObject.toJSONString(stringObjectMap));
-            huaTaiCrawlerCacheComponent.cacheCookie(stringObjectMap.get("cookies").toString());
-            huaTaiCrawlerCacheComponent.cacheGsInfo(stringObjectMap.get("gsInfo").toString());
-            huaTaiCrawlerCacheComponent.cacheComCode(stringObjectMap.get("comCode").toString());
+            huaTaiCrawlerCacheComponent.cacheCookie(username, comcode, stringObjectMap.get("cookies").toString());
+            huaTaiCrawlerCacheComponent.cacheGsInfo(username, comcode, stringObjectMap.get("gsInfo").toString());
+            huaTaiCrawlerCacheComponent.cacheComCode(username, comcode, stringObjectMap.get("comCode").toString());
         } catch (Exception e) {
             throw new RuntimeException("抱歉,无法获取登录信息,请稍后重试");
         }
         return stringObjectMap;
     }
 
-    public String getCookie() {
-        return huaTaiCrawlerCacheComponent.getCookie();
+    // 这三个方法改为带参(原来无参)
+    public String getCookie(String username, String comcode) {
+        return huaTaiCrawlerCacheComponent.getCookie(username, comcode);
     }
 
-    public String getGsInfo() {
-        return huaTaiCrawlerCacheComponent.getGsInfo();
+    public String getGsInfo(String username, String comcode) {
+        return huaTaiCrawlerCacheComponent.getGsInfo(username, comcode);
     }
 
-    public String getHaTaiCookie(String requestUrl) {
-        return huaTaiCrawlerCacheComponent.getCookie();
+    public String getHaTaiCookie(String username, String comcode, String requestUrl) {
+        return huaTaiCrawlerCacheComponent.getCookie(username, comcode);
     }
 
     /**
      * 获取请求头
      * @return
      */
-    public HttpHeaders getHeaders() {
+    public HttpHeaders getHeaders(String username, String comcode) {
         HttpHeaders httpHeaders = new HttpHeaders();
         httpHeaders.set("Accept", "application/json, text/javascript, */*; q=0.01");
         httpHeaders.set("Accept-Language", "zh-CN,zh;q=0.9");
         httpHeaders.set("Cache-Control", "no-cache");
         httpHeaders.set("Connection", "keep-alive");
-        httpHeaders.add(HtRequestConstant.COOKIE, huaTaiCrawlerCacheComponent.getCookie());
+        httpHeaders.add(HtRequestConstant.COOKIE, huaTaiCrawlerCacheComponent.getCookie(username, comcode));
         httpHeaders.setContentType(MediaType.valueOf("application/x-www-form-urlencoded; charset=UTF-8"));
         httpHeaders.set("Origin", "https://cspxp.pc.ehuatai.com");
         httpHeaders.set("Pragma", "no-cache");
         httpHeaders.set("Referer", "https://cspxp.pc.ehuatai.com/htcsp/");
         httpHeaders.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36");
-        httpHeaders.set("sec-ch-ua", "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"");
+        httpHeaders.set("sec-ch-ua", "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand)\";v=\"24\"");
         httpHeaders.set("sec-ch-ua-mobile", "?0");
         httpHeaders.set("sec-ch-ua-platform", "\"Windows\"");
-
         return httpHeaders;
     }
 
-    public HttpHeaders getHeaders1() {
+    public HttpHeaders getHeaders1(String username, String comcode) {
         HttpHeaders headers = new HttpHeaders();
-        headers.add(HtRequestConstant.COOKIE, huaTaiCrawlerCacheComponent.getCookie());
+        headers.add(HtRequestConstant.COOKIE, huaTaiCrawlerCacheComponent.getCookie(username, comcode));
         headers.set("Accept", "application/json, text/javascript, */*; q=0.01");
         headers.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
         headers.set("Cache-Control", "no-cache");
@@ -114,7 +120,6 @@ public class HtCrawlerRequestComponent {
         headers.set("Referer", "https://cspxp.pc.ehuatai.com/htcsp/VehicleInfoEntryAction.do?cmd=startProcess");
         headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
         headers.set("X-Requested-With", "XMLHttpRequest");
-
         return headers;
     }
 

+ 33 - 29
tenant/insurance/quotation-huatai/src/main/java/com/jzg/quotation/huatai/crawler/component/HuaTaiCrawlerCacheComponent.java

@@ -18,45 +18,44 @@ public class HuaTaiCrawlerCacheComponent {
     private final RedissonClient redissonClient;
 
     public static final String HUATAI_COOKIE = "huatai:cookie:";
-
     public static final String HUATAI_GSINFO = "huatai:gsinfo:";
-
     public static final String HUATAI_COMCODE = "huatai:comcode:";
-
     public static final String HUATAI_YINGXIANG_UPLOADED = "huatai:yx:";
 
     public static final String COOKIE = "Cookie:";
-
     public static final String GSINFO = "gsInfo:";
-
     public static final String COMCODE = "comCode:";
-
     public static final String YINGXIANG_UPLOADED = "yx:";
 
-    public void cacheCookie(String cookie) {
-        Map<String, String> authorizationMap = new HashMap<>();
+    /**
+     * 用账号维度生成缓存 key。账号唯一标识优先用 username,兜底用 comcode。
+     */
+    private String accountKey(String prefix, String username, String comcode) {
+        String account = org.springframework.util.StringUtils.hasText(username) ? username : comcode;
+        return prefix + account;
+    }
 
-        authorizationMap.put(COOKIE, cookie);
-        RMap<Object, Object> map = redissonClient.getMap(HUATAI_COOKIE);
-        map.putAll(authorizationMap);
+    public void cacheCookie(String username, String comcode, String cookie) {
+        Map<String, String> m = new HashMap<>();
+        m.put(COOKIE, cookie);
+        RMap<Object, Object> map = redissonClient.getMap(accountKey(HUATAI_COOKIE, username, comcode));
+        map.putAll(m);
         map.expire(Duration.ofDays(30L));
     }
 
-    public void cacheGsInfo(String gsinfo) {
-        Map<String, String> authorizationMap = new HashMap<>();
-
-        authorizationMap.put(GSINFO, gsinfo);
-        RMap<Object, Object> map = redissonClient.getMap(HUATAI_GSINFO);
-        map.putAll(authorizationMap);
+    public void cacheGsInfo(String username, String comcode, String gsinfo) {
+        Map<String, String> m = new HashMap<>();
+        m.put(GSINFO, gsinfo);
+        RMap<Object, Object> map = redissonClient.getMap(accountKey(HUATAI_GSINFO, username, comcode));
+        map.putAll(m);
         map.expire(Duration.ofDays(30L));
     }
 
-    public void cacheComCode(String comcode) {
-        Map<String, String> authorizationMap = new HashMap<>();
-
-        authorizationMap.put(COMCODE, comcode);
-        RMap<Object, Object> map = redissonClient.getMap(HUATAI_COMCODE);
-        map.putAll(authorizationMap);
+    public void cacheComCode(String username, String comcode, String comcodeVal) {
+        Map<String, String> m = new HashMap<>();
+        m.put(COMCODE, comcodeVal);
+        RMap<Object, Object> map = redissonClient.getMap(accountKey(HUATAI_COMCODE, username, comcode));
+        map.putAll(m);
         map.expire(Duration.ofDays(30L));
     }
 
@@ -69,20 +68,25 @@ public class HuaTaiCrawlerCacheComponent {
         map.expire(Duration.ofDays(30L));
     }
 
-    public String getCookie() {
-        return (String) redissonClient.getMap(HUATAI_COOKIE).get(COOKIE);
+    public String getCookie(String username, String comcode) {
+        return (String) redissonClient.getMap(accountKey(HUATAI_COOKIE, username, comcode)).get(COOKIE);
     }
 
-    public String getGsInfo() {
-        return (String) redissonClient.getMap(HUATAI_GSINFO).get(GSINFO);
+    public String getGsInfo(String username, String comcode) {
+        return (String) redissonClient.getMap(accountKey(HUATAI_GSINFO, username, comcode)).get(GSINFO);
     }
 
-    public String getComCode() {
-        return (String) redissonClient.getMap(HUATAI_COMCODE).get(COMCODE);
+    public String getComCode(String username, String comcode) {
+        return (String) redissonClient.getMap(accountKey(HUATAI_COMCODE, username, comcode)).get(COMCODE);
     }
 
     public String getYingXiangUploaded() {
         return (String) redissonClient.getMap(HUATAI_YINGXIANG_UPLOADED).get(YINGXIANG_UPLOADED);
     }
 
+    public void clearLoginCache(String username, String comcode) {
+        redissonClient.getMap(accountKey(HUATAI_COOKIE, username, comcode)).delete();
+        redissonClient.getMap(accountKey(HUATAI_GSINFO, username, comcode)).delete();
+        redissonClient.getMap(accountKey(HUATAI_COMCODE, username, comcode)).delete();
+    }
 }

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

@@ -138,9 +138,11 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
 
     @Override
     public boolean detectLogin(DetectLoginRequest r) {
-        String cookie = req.getCookie();
+        String username = r.getUsername();   // 若 DetectLoginRequest 无 username,用 r.getAttr().getUsername()
+        String comcode  = r.getPassword();
+        String cookie = req.getCookie(username, comcode);
         if (!StringUtils.hasText(cookie)) {
-            cookie = req.getHaTaiCookie(RequestUrl.NE_COOKIE.getRequestUrl(props.getBaseUrl()));
+            cookie = req.getHaTaiCookie(username, comcode, RequestUrl.NE_COOKIE.getRequestUrl(props.getBaseUrl()));
         }
         return StringUtils.hasText(cookie);
     }
@@ -219,6 +221,8 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         // 构造登录参数
         AttributionInformationVo attr = quoteVo.getOrder().getAttributionInformationVo();
         LoginDTO dto = buildLoginDTO(attr);
+        String username = dto.getUsername();
+        String comcode  = dto.getComcode();
         // 获取登录相关信息
         Map<String, Object> mmap = login(dto);
         // 归属信息
@@ -232,7 +236,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         // 带重试的获取报价编号、车型型号
         Map<String, Object> xbResult = retry(() -> {
             try {
-                String freshCookie = cache.getCookie();
+                String freshCookie = cache.getCookie(username, comcode);
                 String useCookie = StringUtils.hasText(freshCookie) ? freshCookie : cookie;
                 return xbQuery(dto.getUsername(), useCookie, car);
             }
@@ -247,7 +251,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         // 报价前预处理-保存车辆信息、过滤车型等处理
         PreResult pre = null;
         try {
-            pre = prepareQuote(attr, mmap, car, modelcodexh, cache.getCookie(), quotationNo, xbResult, "");
+            pre = prepareQuote(attr, mmap, car, modelcodexh, cache.getCookie(username, comcode), quotationNo, xbResult, "");
         } catch (Exception e) {
             log.error("报价失败:保存车辆信息、过滤车型异常,{}",e.getMessage());
             throw new SystemException(e.getMessage());
@@ -255,7 +259,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         // 报价
         CoreResult core = null;
         try {
-            core = executeQuote(quoteVo, quotationNo, dto, cache.getCookie(), pre, gsInfo, attr);
+            core = executeQuote(quoteVo, quotationNo, dto, cache.getCookie(username, comcode), pre, gsInfo, attr);
         } catch (Exception e) {
             log.error("报价失败,{}",e.getMessage());
             throw new SystemException(e.getMessage());
@@ -287,9 +291,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
     }
 
     private void refreshLogin(LoginDTO dto) {
-        cache.cacheCookie("");
-        cache.cacheGsInfo("");
-        cache.cacheComCode("");
+        cache.clearLoginCache(dto.getUsername(), dto.getComcode());
         log.info("refreshLogin gsInfo: {}", JSONObject.toJSONString(login(dto)));
     }
 
@@ -329,9 +331,9 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         HtCrawlerCarQueryResponse.DataDTO dataDTO = null;
         //车辆查询
         if (StringUtils.isBlank(carChange)) {
-            dataDTO = req.carQuery(new HtCrawlerCarQueryRequest(car, modelcodexh, xbResult), req.getHeaders1(), car);
+            dataDTO = req.carQuery(new HtCrawlerCarQueryRequest(car, modelcodexh, xbResult), req.getHeaders1(attr.getUsername(), attr.getConfigInfo().get("comCode")), car);
         } else {
-            HttpHeaders headers = buildReCarQueryHeaders(req.getCookie());
+            HttpHeaders headers = buildReCarQueryHeaders(req.getCookie(attr.getUsername(), attr.getConfigInfo().get("comCode")));
             Map<String, Object> dataDTOMap = new HashMap<>();
             dataDTOMap.put("vehiclemodel", car.getBrandName());
             fillVehicleTypeCodes(car, dataDTOMap, "motortypecode", "motorusagetypecode");
@@ -379,8 +381,8 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         log.info("车辆查询,carDTO,{}",carDTO);
         if (carDTO == null) {
             // 未匹配则第二次carquery,带modelCName-->vehiclemodel
-            //dataDTO = req.carQuery(new HtCrawlerCarQueryRequest2(car), req.getHeaders1(), car);
-            dataDTO = req.carQuery(new HtCrawlerCarQueryRequest(car, "", xbResult), req.getHeaders1(), car);
+            //dataDTO = req.carQuery(new HtCrawlerCarQueryRequest2(car), req.getHeaders1(attr.getUsername(), attr.getConfigInfo().get("comCode")), car);
+            dataDTO = req.carQuery(new HtCrawlerCarQueryRequest(car, "", xbResult), req.getHeaders1(attr.getUsername(), attr.getConfigInfo().get("comCode")), car);
             log.info("未匹配则第二次车辆查询,dataDTO,{}",JSONObject.toJSONString(dataDTO));
             //过滤车辆
             carDTO = CarModelsFilterUtils.to(dataDTO.getDatalist(), car);
@@ -850,7 +852,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
 
         HtCrawlerSubmitRequest submit = new HtCrawlerSubmitRequest(vo, params, order.getCiCategory(), order.getBiCategory());
         log.info("提交核保参数: {}", JSONObject.toJSONString(submit));
-        String res = req.submit2(submit, req.getHeaders());
+        String res = req.submit2(submit, req.getHeaders(attr.getUsername(), attr.getConfigInfo().get("comCode")));
         log.info("提交核保结果: {}", res);
         //cookie还在,但服务器已断开连接,需重新连接
         if(res.contains("<html>")) {
@@ -891,17 +893,18 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
             return r;
         }
         // 转人工处理
-        if (res.contains("infomsg\":\"") && res.contains("htmlMsgTeam\":\"")) return handleManualUnderwriting(order, params);
+        if (res.contains("infomsg\":\"") && res.contains("htmlMsgTeam\":\"")) return handleManualUnderwriting(order, params, attr);
         return r;
     }
 
-    private UnderwritingResultsVo handleManualUnderwriting(HtCrawlerOrder order, HtCrawlerConfigureParameters params) {
+    private UnderwritingResultsVo handleManualUnderwriting(HtCrawlerOrder order, HtCrawlerConfigureParameters params, AttributionInformationVo attr) {
         try {
             // 提交人工核保
             MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
             data.add("zrgms", ""); data.add("zrgpriority", "1");
             String url = ORIGIN + "/htcsp/checkInsAction.do";
-            Map<String, Object> body = exchangeForm(url, Map.of("cmd", "submitZRG"), data, buildManualUwHeaders());
+            String uwCookie = req.getCookie(attr.getUsername(), params.getComCode());
+            Map<String, Object> body = exchangeForm(url, Map.of("cmd", "submitZRG"), data, buildManualUwHeaders(uwCookie));
 
             UnderwritingResultsVo r = new UnderwritingResultsVo();
             r.setUnderwritingStatus(InsOrderStatusEnum.AUDIT_FAIL);
@@ -913,11 +916,11 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
             String url2 = ORIGIN + "/htcsp/checkInsAction.do?cmd=queryUnderwriterAdvice";
             MultiValueMap<String, String> d2 = new LinkedMultiValueMap<>();
             d2.add("businessNo", order.getQuotationRelaNo()); d2.add("excet", "");
-            String html = String.valueOf(exchangeForm(url2, null, d2, buildPaymentLinkHeaders()));
+            String html = String.valueOf(exchangeForm(url2, null, d2, buildPaymentLinkHeaders(uwCookie)));
 
             if (html.contains("审核通过")) {
                 HtCrawlerPaymentLinkResponse pl = req.getPaymentLink(
-                        new HtCrawlerPaymentLinkRequest(params, new PaymentLinkVo(), html), buildPaymentLinkHeaders());
+                        new HtCrawlerPaymentLinkRequest(params, new PaymentLinkVo(), html), buildPaymentLinkHeaders(uwCookie));
                 r.setPaymentLink(pl.getImg());
                 r.setUnderwritingStatus(InsOrderStatusEnum.PAY_PENDING);
                 return r;
@@ -980,14 +983,15 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         qp.add("Single_system", "10000");
         qp.add("checkStatus", "1");
         qp.add("quotationno", order.getQuotationRelaNo());
-        String body = get(ORIGIN + "/htcsp/PaymentAction.do", buildPaymentListHeaders(req.getCookie()), qp);
+        AttributionInformationVo plAttr = vo.getOrder().getAttributionInformationVo();
+        String body = get(ORIGIN + "/htcsp/PaymentAction.do", buildPaymentListHeaders(req.getCookie(plAttr.getUsername(), plAttr.getConfigInfo().get("comCode"))), qp);
         //返回的是html 页面,暂不打印
         // log.info("queryPaymentList body: {}", body);
         HtCrawlerPaymentLinkRequest htCrawlerPaymentLinkRequest = new HtCrawlerPaymentLinkRequest(params, vo, body);
         log.info("htCrawlerPaymentLinkRequest: {}", JSONObject.toJSONString(htCrawlerPaymentLinkRequest));
         HtCrawlerPaymentLinkResponse paymentLink = new HtCrawlerPaymentLinkResponse();
         try {
-            paymentLink = req.getPaymentLink(htCrawlerPaymentLinkRequest, buildPaymentListHeaders(req.getCookie()));
+            paymentLink = req.getPaymentLink(htCrawlerPaymentLinkRequest, buildPaymentListHeaders(req.getCookie(plAttr.getUsername(), plAttr.getConfigInfo().get("comCode"))));
         } catch (Exception e) {
             log.error(e.getMessage());
             return null;
@@ -997,11 +1001,13 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
 
     // ====================== Cookie 检查 ======================
     private void doLoginCookie(AttributionInformationVo attr) {
-        if (StringUtils.isBlank(req.getCookie())) {
+        String username = attr.getUsername();
+        String comcode  = attr.getConfigInfo().get("comCode");
+        if (StringUtils.isBlank(req.getCookie(username, comcode))) {                 // 改
             Map<String, Object> m = login(buildLoginDTO(attr));
             String cookie = (String) m.get("cookies");
-            req.getHeaders().remove(HtRequestConstant.COOKIE);
-            req.getHeaders().add(HtRequestConstant.COOKIE, cookie);
+            req.getHeaders(username, comcode).remove(HtRequestConstant.COOKIE);       // getHeaders 改带参
+            req.getHeaders(username, comcode).add(HtRequestConstant.COOKIE, cookie);
         }
     }
 
@@ -1027,24 +1033,24 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
             }
         }
 
-        HtCrawlerGerUploadUrlResponse up = req.getUploadUrl(new HtCrawlerGerUploadUrlRequest(quotationRelaNo), buildUploadHeaders(req.getCookie()));
+        HtCrawlerGerUploadUrlResponse up = req.getUploadUrl(new HtCrawlerGerUploadUrlRequest(quotationRelaNo), buildUploadHeaders(req.getCookie(attr.getUsername(), attr.getConfigInfo().get("comCode"))));
         String token = "";
         Map<String, Object> updata = new LinkedHashMap<>();
         if (HtRequestConstant.GET_UPLOAD_URL_SUCCESS.equals(up.getFlag())) {
             token = up.getUrl().substring(up.getUrl().lastIndexOf("=") + 1);
-            updata = fetchUploadInitParam(token);
+            updata = fetchUploadInitParam(token, req.getCookie(attr.getUsername(), attr.getConfigInfo().get("comCode")));
         }
 
         try {
-            new UploadUtil().upLoad(quotationRelaNo, b64List, parseToMap(req.getGsInfo()),
-                    attr.getUsername(), params.getComCode(), convertCookieToMap(req.getCookie()), token, updata, cookieUtils);
+            new UploadUtil().upLoad(quotationRelaNo, b64List, parseToMap(req.getGsInfo(attr.getUsername(), attr.getConfigInfo().get("comCode"))),
+                    attr.getUsername(), params.getComCode(), convertCookieToMap(req.getCookie(attr.getUsername(), attr.getConfigInfo().get("comCode"))), token, updata, cookieUtils);
             cache.cacheYingXiangUploaded(quotationRelaNo);
         } catch (Exception e) { throw new SystemException(e.getMessage()); }
     }
 
-    private Map<String, Object> fetchUploadInitParam(String token) {
+    private Map<String, Object> fetchUploadInitParam(String token, String cookie) {
         HttpHeaders h = new HttpHeaders();
-        h.set(HtRequestConstant.COOKIE, req.getCookie());
+        h.set(HtRequestConstant.COOKIE, cookie);
         h.set("Accept", "application/json, text/plain, */*");
         h.set("Referer", "https://img-web.pc.ehuatai.com/app/upload.img?token=" + token);
         h.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36");
@@ -1084,7 +1090,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         fd.add("ownPrintElectronicFlag", "0");
         fd.add("isbzdown", "1");
         String body = post(ORIGIN + "/htcsp/DocumentPrintAction.do?cmd=electronicPolicyEndorseDownload",
-                buildDocumentHeaders(req.getCookie()), fd);
+                buildDocumentHeaders(req.getCookie(vo.getOrder().getAttributionInformationVo().getUsername(), vo.getOrder().getAttributionInformationVo().getConfigInfo().get("comCode"))), fd);
         HtCrawlerDownloadPolicyResponse res = JSONObject.parseObject(body, HtCrawlerDownloadPolicyResponse.class);
         log.info("华泰电子保单---订单号{},保单号{},下载保单响应结果,{}", order.getOrdersNo(), no, JSONObject.toJSONString(res));
         DownloadPolicyResultVo r = new DownloadPolicyResultVo();
@@ -1102,7 +1108,8 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         // 下载驾意险保单
         if (StringUtils.isNotBlank(order.getJyPolicyno())) {
             // 1. 构造请求头
-            HttpHeaders headers = getHeaders();
+            AttributionInformationVo dlAttr = vo.getOrder().getAttributionInformationVo();
+            HttpHeaders headers = getHeaders(req.getCookie(dlAttr.getUsername(), dlAttr.getConfigInfo().get("comCode")));
 
             // 2. url + query params(cmd=perDocumentPrint)
             String baseUrl = "https://cspxp.pc.ehuatai.com";
@@ -1153,7 +1160,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
     }
 
     @NotNull
-    private HttpHeaders getHeaders() {
+    private HttpHeaders getHeaders(String cookie) {
         HttpHeaders headers = new HttpHeaders();
         headers.set("Accept", "application/json, text/javascript, */*; q=0.01");
         headers.set("Accept-Language", "zh-CN,zh;q=0.9");
@@ -1162,7 +1169,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         headers.set("Origin", "https://cspxp.pc.ehuatai.com");
         headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36");
         headers.set("X-Requested-With", "XMLHttpRequest");
-        headers.set("Cookie", req.getCookie());
+        headers.set("Cookie", cookie);
         return headers;
     }
 
@@ -1174,7 +1181,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         HtCrawlerConfigureParameters params = ConfigureParametersConversion.conversionEntity(
                 HtCrawlerConfigureParameters.class, attr.getConfigInfo());
         HtCrawlerOrder order = orderService.findByOrdersNo(vo.getOrder().getOrdersNo());
-        String html = req.queryOrderStatus(new HtCrawlerOrderStateRequest(params, order.getQuotationRelaNo()), req.getHeaders());
+        String html = req.queryOrderStatus(new HtCrawlerOrderStateRequest(params, order.getQuotationRelaNo()), req.getHeaders(attr.getUsername(), attr.getConfigInfo().get("comCode")));
         String regex = "<div style=\"display: inline-block;width:50px;text-align: center; \">(.*?)</div>";
         Pattern pattern = Pattern.compile(regex);
         Matcher matcher = pattern.matcher(html);
@@ -1189,7 +1196,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
             orderStatus = orderStatusEnum.getCode();
             List<Map<String, String>> fcxList = new ArrayList<>();
             // 1. 构建请求头 headers
-            HttpHeaders headers = getHttpHeaders();
+            HttpHeaders headers = getHttpHeaders(req.getCookie(attr.getUsername(), attr.getConfigInfo().get("comCode")));
 
             // 2. 拼接URL + GET参数 params
             String baseUrl = "https://cspxp.pc.ehuatai.com/htcsp/DocumentPrintAction.do";
@@ -1238,7 +1245,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
     }
 
     @NotNull
-    private HttpHeaders getHttpHeaders() {
+    private HttpHeaders getHttpHeaders(String cookie) {
         HttpHeaders headers = new HttpHeaders();
         headers.set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
         headers.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
@@ -1248,7 +1255,7 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         headers.set("Referer", "https://cspxp.pc.ehuatai.com/htcsp/QuotationQueryAction.do?cmd=quotationListAll");
         headers.set("Upgrade-Insecure-Requests", "1");
         headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
-        headers.set("Cookie", req.getCookie()); // 塞入cookie
+        headers.set("Cookie", cookie); // 塞入cookie
         return headers;
     }
 
@@ -1394,18 +1401,18 @@ public class HuaTaiCrawlerRequestImpl implements HuaTaiCrawlerRequest {
         });
     }
 
-    private HttpHeaders buildManualUwHeaders() {
+    private HttpHeaders buildManualUwHeaders(String cookie) {
         return header(h -> {
-            h.add(HtRequestConstant.COOKIE, req.getCookie());
+            h.add(HtRequestConstant.COOKIE, cookie);
             h.add("Accept", ACCEPT_JSON);
             h.add("Content-Type", CONTENT_TYPE_FORM);
             h.add("X-Requested-With", XHR);
         });
     }
 
-    private HttpHeaders buildPaymentLinkHeaders() {
+    private HttpHeaders buildPaymentLinkHeaders(String cookie) {
         return header(h -> {
-            h.add(HtRequestConstant.COOKIE, req.getCookie());
+            h.add(HtRequestConstant.COOKIE, cookie);
             h.add("Accept", "text/html, */*; q=0.01");
             h.add("Content-Type", CONTENT_TYPE_FORM);
             h.add("Referer", ORIGIN + "/htcsp/QuotationQueryAction.do?cmd=quotationListAll");

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

@@ -3510,37 +3510,24 @@ public class RenBaoNewCrawlerRequestBuilder {
      */
     private String calculateEnergyTypePlat(String fuelType) {
         if (fuelType == null || fuelType.isEmpty()) {
-            return "0";  // 默认为燃油
-        }
-        // 只取第一个字符进行匹配(如D1取D,B1取B)
-        String baseType = fuelType.substring(0, 1);
-
-        switch (baseType) {
-            case "A":  // 汽油
-            case "B":  // 柴油
-            case "C":  // 电/汽油(增程式)
-            case "D":  // 电/柴油
-            case "E":  // 天然气
-            case "F":  // 液化石油气
-            case "G":  // 电/天然气
-            case "H":  // 电/液化石油气
-            case "I":  // 其他
-            case "J":  // 无
-            case "K":  // 乙醇
-            case "L":  // 太阳能
-            case "M":  // 电/太阳能
-            case "N":  // 电/其他
-                return "0";  // 传统燃油或非新能源
-            case "O":  // 纯电动
-                return "1";
-            case "P":  // 燃料电池
-                return "2";
-            case "Q":  // 插电式混合动力
-                return "3";
-            case "R":  // 其他混合动力(非插电)
-                return "4";
+            return "9";  // 默认其他
+        }
+        // 根据完整 fuelType 编码匹配能源类型
+        switch (fuelType) {
+            case "D1":   // 燃油
+            case "D2":   // 燃油
+            case "D13":  // 燃油
+                return "0";  // 燃油
+            case "D6":   // 纯电动
+                return "1";  // 纯电动
+            case "D8":   // 燃料电池
+                return "2";  // 燃料电池
+            case "D5":   // 其他混合动力
+                return "4";  // 其他混合动力
+            case "D12":  // 插电式混合动力
+                return "3";  // 插电式混合动力
             default:
-                return "0";  // 未知类型默认燃油
+                return "9";  // 其他
         }
     }
 

+ 148 - 40
tenant/insurance/quotation-summary/src/main/java/com/jzg/quotation/summary/aop/FactorMatch.java

@@ -89,9 +89,13 @@ public class FactorMatch {
                     .findFirst()
                     .get();
 
+            // 构造 name->code 反向索引(attrMeta 为 null 时返回空 map,比较按纯文本走)
+            Map<String, String> nameToCodeMap = buildNameToCodeMap(ptlAgreementUndwrtRulesAttr, undwrtRulesDictList);
+
             boolean useFlag;
             if(!Objects.isNull(ruleAttrMappingVo.getValue())) {
-                useFlag = attrEqual(ptlAgreementProductCostsAttrLink, ptlAgreementUndwrtRulesAttr, ruleAttrMappingVo.getValue(), false);
+                // useFlag = attrEqual(ptlAgreementProductCostsAttrLink, ptlAgreementUndwrtRulesAttr, ruleAttrMappingVo.getValue(), false);
+                useFlag = attrEqual(ptlAgreementProductCostsAttrLink, ptlAgreementUndwrtRulesAttr, ruleAttrMappingVo.getValue(), false, nameToCodeMap);
             }else{
                 useFlag = false;
             }
@@ -102,26 +106,34 @@ public class FactorMatch {
                         .toList();
 
                 Map<String, String> codeToNameMap = new HashMap<>();
-                if (CollUtil.isNotEmpty(undwrtRulesDictList)){
-                    Map<Integer, List<PtlAgreementUndwrtRulesDictLabal>> dictMap = undwrtRulesDictList.stream()
-                            .collect(Collectors.groupingBy(PtlAgreementUndwrtRulesDictLabal::getDictId));
-                    List<PtlAgreementUndwrtRulesDictLabal> ptlAgreementUndwrtRulesDictLabals = dictMap.getOrDefault(ptlAgreementUndwrtRulesAttr.getDictId(), CollUtil.newArrayList());
-                    if (CollUtil.isNotEmpty(ptlAgreementUndwrtRulesDictLabals)){
-                        codeToNameMap = ptlAgreementUndwrtRulesDictLabals.stream()
-                                .collect(Collectors.toMap(
-                                        PtlAgreementUndwrtRulesDictLabal::getCode,
-                                        PtlAgreementUndwrtRulesDictLabal::getName,
-                                        (existing, replacement) -> existing
-                                ));
+                if (CollUtil.isNotEmpty(undwrtRulesDictList) && ptlAgreementProductCostsAttrLink != null) {
+                    String minStr = ptlAgreementProductCostsAttrLink.getMin();
+                    if (minStr != null && !minStr.isEmpty()) {
+                        // 拆出 rule 里所有 code
+                        Set<String> ruleCodes = Arrays.stream(minStr.split(","))
+                                .map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toSet());
+                        // 在全量 dict 里找 code 匹配的 label
+                        for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
+                            if (ruleCodes.contains(label.getCode())) {
+                                codeToNameMap.put(label.getCode(), label.getName());
+                            }
+                        }
+                        // 如果还是空的(rule 里 code 在 dict 都不存在),fallback 用 attrMeta 的 dictId 再查一遍
+                        if (codeToNameMap.isEmpty() && ptlAgreementUndwrtRulesAttr != null && ptlAgreementUndwrtRulesAttr.getDictId() != null) {
+                            Integer dictId = ptlAgreementUndwrtRulesAttr.getDictId();
+                            for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
+                                if (Objects.equals(label.getDictId(), dictId)) {
+                                    codeToNameMap.put(label.getCode(), label.getName());
+                                }
+                            }
+                        }
                     }
                 }
 
                 if (ptlAgreementProductCostsAttrLink.getMin() != null) {
-                    String min = codeToNameMap.getOrDefault(ptlAgreementProductCostsAttrLink.getMin(), StrUtil.EMPTY);
-                    logs.append("检验的值:" + (StrUtil.isNotBlank(min) ? min : ptlAgreementProductCostsAttrLink.getMin()) + "\n");
+                    logs.append("检验的值:").append(displayRuleValue(ptlAgreementProductCostsAttrLink.getMin(), codeToNameMap)).append("\n");
                 } else {
-                    String max = codeToNameMap.getOrDefault(ptlAgreementProductCostsAttrLink.getMax(), StrUtil.EMPTY);
-                    logs.append("检验的值:" + (StrUtil.isNotBlank(max) ? max : ptlAgreementProductCostsAttrLink.getMax()) + "\n");
+                    logs.append("检验的值:").append(displayRuleValue(ptlAgreementProductCostsAttrLink.getMax(), codeToNameMap)).append("\n");
                 }
 
                 String val = codeToNameMap.getOrDefault(ruleAttrMappingVo.getValue(), StrUtil.EMPTY);
@@ -137,6 +149,45 @@ public class FactorMatch {
         return true;
     }
 
+    // ============================================================
+    // 字典映射
+    // ============================================================
+
+    /**
+     * 把 attrMeta 的 dictLabal 构造成 name->code 反向索引,用于把传入的"家庭自用"还原成"04"。
+     */
+    private static Map<String, String> buildNameToCodeMap(PtlAgreementUndwrtRulesAttr attrMeta,
+                                                          List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
+        if (attrMeta == null || attrMeta.getDictId() == null || CollUtil.isEmpty(undwrtRulesDictList)) {
+            return Collections.emptyMap();
+        }
+        return undwrtRulesDictList.stream()
+                .filter(d -> Objects.equals(d.getDictId(), attrMeta.getDictId()))
+                .collect(Collectors.toMap(
+                        PtlAgreementUndwrtRulesDictLabal::getName,
+                        PtlAgreementUndwrtRulesDictLabal::getCode,
+                        (a, b) -> a));
+    }
+
+    /**
+     * 展示"检验的值":规则里存的是 code(如 04),翻成 name(如 家庭自用)方便对账。
+     * 支持多选逗号分隔,逐个翻译;翻译不到的保留原 code。
+     */
+    private static String displayRuleValue(String ruleVal, Map<String, String> codeToNameMap) {
+        if (ruleVal == null) return "";
+        if (CollUtil.isEmpty(codeToNameMap)) return ruleVal;
+        String[] codes = Arrays.stream(ruleVal.split(","))
+                .map(String::trim).filter(s -> !s.isEmpty()).toArray(String[]::new);
+        if (codes.length == 0) return ruleVal;
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < codes.length; i++) {
+            String code = codes[i];
+            String name = codeToNameMap.getOrDefault(code, code);
+            sb.append(name).append("(").append(code).append(")");
+            if (i < codes.length - 1) sb.append(",");
+        }
+        return sb.toString();
+    }
 
     public static boolean judgementsUnderwrite(List<PtlAgreementUndwrtRulesAttrLink> ptlAgreementCostRuleList, List<RuleAttrMappingVo> ruleAttrMappingVoList,
                                      List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList, StringBuilder logs, List<PtlAgreementUndwrtRulesDictLabal> undwrtRulesDictList) {
@@ -162,6 +213,13 @@ public class FactorMatch {
                 return false;
             }
 
+            PtlAgreementUndwrtRulesAttr attrMeta = undwrtRulesAttrList.stream()
+                    .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
+                    .findFirst().orElse(null);
+
+            Map<String, String> nameToCodeMap = buildNameToCodeMap(attrMeta, undwrtRulesDictList);
+
+
             //查找 费用因子规则
             PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr = undwrtRulesAttrList.stream()
                     .filter(x -> x.getAttrCode().equals(ptlAgreementProductCostsAttrLink.getAttrCode()))
@@ -174,7 +232,7 @@ public class FactorMatch {
                 ptlAgreementCostRule.setMax(ptlAgreementProductCostsAttrLink.getMax());
                 ptlAgreementCostRule.setOperator(ptlAgreementProductCostsAttrLink.getOperator());
                 ptlAgreementCostRule.setAttrCode(ptlAgreementProductCostsAttrLink.getAttrCode());
-                useFlag = attrEqual(ptlAgreementCostRule, ptlAgreementUndwrtRulesAttr, ruleAttrMappingVo.getValue(), true);
+                useFlag = attrEqual(ptlAgreementCostRule, ptlAgreementUndwrtRulesAttr, ruleAttrMappingVo.getValue(), true, nameToCodeMap);
                 log.info("承保规则校验-单属性匹配: attrCode={}, attrType={}, 规则值(min={}, max={}, operator={}), 传入值={}, useFlag={}",
                         ptlAgreementProductCostsAttrLink.getAttrCode(), ptlAgreementUndwrtRulesAttr.getAttrType(),
                         ptlAgreementProductCostsAttrLink.getMin(), ptlAgreementProductCostsAttrLink.getMax(),
@@ -199,22 +257,38 @@ public class FactorMatch {
                         .toList();
 
                 Map<String, String> codeToNameMap = new HashMap<>();
-                if (CollUtil.isNotEmpty(undwrtRulesDictList)){
-                    Map<Integer, List<PtlAgreementUndwrtRulesDictLabal>> dictMap = undwrtRulesDictList.stream()
-                            .collect(Collectors.groupingBy(PtlAgreementUndwrtRulesDictLabal::getDictId));
-                    List<PtlAgreementUndwrtRulesDictLabal> ptlAgreementUndwrtRulesDictLabals = dictMap.getOrDefault(ptlAgreementUndwrtRulesAttr.getDictId(), CollUtil.newArrayList());
-                    if (CollUtil.isNotEmpty(ptlAgreementUndwrtRulesDictLabals)){
-                        codeToNameMap = ptlAgreementUndwrtRulesDictLabals.stream()
-                                .collect(Collectors.toMap(
-                                        PtlAgreementUndwrtRulesDictLabal::getCode,
-                                        PtlAgreementUndwrtRulesDictLabal::getName,
-                                        (existing, replacement) -> existing
-                                ));
+                if (CollUtil.isNotEmpty(undwrtRulesDictList)) {
+                    String minStr = ptlAgreementProductCostsAttrLink.getMin();
+                    String maxStr = ptlAgreementProductCostsAttrLink.getMax();
+                    Set<String> ruleCodes = new HashSet<>();
+                    if (minStr != null) {
+                        for (String s : minStr.split(",")) {
+                            if (!s.trim().isEmpty()) ruleCodes.add(s.trim());
+                        }
+                    }
+                    if (maxStr != null) {
+                        for (String s : maxStr.split(",")) {
+                            if (!s.trim().isEmpty()) ruleCodes.add(s.trim());
+                        }
+                    }
+                    for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
+                        if (ruleCodes.contains(label.getCode())) {
+                            codeToNameMap.put(label.getCode(), label.getName());
+                        }
+                    }
+                    if (codeToNameMap.isEmpty() && ptlAgreementUndwrtRulesAttr != null && ptlAgreementUndwrtRulesAttr.getDictId() != null) {
+                        Integer dictId = ptlAgreementUndwrtRulesAttr.getDictId();
+                        for (PtlAgreementUndwrtRulesDictLabal label : undwrtRulesDictList) {
+                            if (Objects.equals(label.getDictId(), dictId)) {
+                                codeToNameMap.put(label.getCode(), label.getName());
+                            }
+                        }
                     }
                 }
                 if (ptlAgreementProductCostsAttrLink.getMin() != null) {
                     String min = codeToNameMap.getOrDefault(ptlAgreementProductCostsAttrLink.getMin(), StrUtil.EMPTY);
-                    logs.append("检验的值:" + (StrUtil.isNotBlank(min) ? min : ptlAgreementProductCostsAttrLink.getMin()) + "\n");
+                    logs.append("检验的值:").append(displayRuleValue(ptlAgreementProductCostsAttrLink.getMin(), codeToNameMap)).append("\n");
+
                 } else {
                     String max = codeToNameMap.getOrDefault(ptlAgreementProductCostsAttrLink.getMax(), StrUtil.EMPTY);
                     logs.append("检验的值:" + (StrUtil.isNotBlank(max) ? max : ptlAgreementProductCostsAttrLink.getMax()) + "\n");
@@ -232,37 +306,71 @@ public class FactorMatch {
         return groupUseFlag;
     }
 
+    /**
+     * 把传入的 value 标准化成"与规则同一形态":
+     *  - 如果 value 是字典名称,先翻成 code
+     *  - 如果 value 是 code,保持原样
+     *  - 如果是"是/否"类,做归一化
+     */
+    private static String normalizeValue(String value, PtlAgreementUndwrtRulesAttr attrMeta,
+                                         Map<String, String> nameToCodeMap) {
+        if (value == null) return null;
+        String v = value.trim();
+        if (CollUtil.isNotEmpty(nameToCodeMap) && nameToCodeMap.containsKey(v)) {
+            return nameToCodeMap.get(v);
+        }
+        String normalized = normalizeYesNo(v);
+        if (normalized != null && !normalized.equals(v)) {
+            return normalized;
+        }
+        return v;
+    }
 
     private static boolean attrEqual(PtlAgreementCostRule ptlAgreementCostRule,
-                              PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, String value,boolean isUnderwrite) {
+                              PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, String value,boolean isUnderwrite,
+                                     Map<String, String> nameToCodeMap) {
+
+        String normalizedValue = normalizeValue(value, ptlAgreementUndwrtRulesAttr, nameToCodeMap);
+
+        if (ptlAgreementUndwrtRulesAttr == null || ptlAgreementUndwrtRulesAttr.getAttrType() == null) {
+            // 没有元数据:无法得知 attrType,按"包含"语义做纯文本兜底比较
+            // (BrandandModel 等 inputTag 大多是 contains 语义,与原逻辑 inputTextEqual 默认分支一致)
+            log.warn("attrEqual 因子元数据缺失,按纯文本兜底比较, attrCode={}", ptlAgreementCostRule.getAttrCode());
+            if (ptlAgreementCostRule.getMin() == null || normalizedValue == null) return false;
+            String finalNormalizedValue = normalizedValue;
+            boolean contains = Arrays.stream(ptlAgreementCostRule.getMin().split(","))
+                    .anyMatch(o -> finalNormalizedValue.contains(o.trim()));
+            return isUnderwrite ? !contains : contains;
+        }
+
         boolean result = false;
         if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("checkbox")) {
-            result = checkBoxEqual(ptlAgreementCostRule, value);
+            result = checkBoxEqual(ptlAgreementCostRule, normalizedValue);
         } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("inputNumber")) {
-            result = inputNumberEqual(ptlAgreementCostRule, value);
+            result = inputNumberEqual(ptlAgreementCostRule, normalizedValue);
         } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("input") || ptlAgreementUndwrtRulesAttr.getAttrType().equals("inputTag")) {
             //截取身份证号码
             if (ptlAgreementCostRule.getAttrCode().equals("CarOwnersIDNumber") || ptlAgreementCostRule.getAttrCode().equals("ApplicantIDNumber") ||
                     ptlAgreementCostRule.getAttrCode().equals("InsuredIDNumber")) {
-                value = value.substring(0, ptlAgreementCostRule.getMin().length());
+                normalizedValue = normalizedValue.substring(0, ptlAgreementCostRule.getMin().length());
             }
-            result = inputTextEqual(ptlAgreementCostRule, value);
+            result = inputTextEqual(ptlAgreementCostRule, normalizedValue);
         } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("select")) {
             //截取车牌
             if (ptlAgreementUndwrtRulesAttr.getAttrCode().equals("LicenseNo")) {
-                value = value.substring(0, 2);
+                normalizedValue = normalizedValue.substring(0, 2);
             }
-            result = selectEqual(ptlAgreementCostRule, value);
+            result = selectEqual(ptlAgreementCostRule, normalizedValue);
             //车龄
             if (ptlAgreementUndwrtRulesAttr.getAttrCode().equals("VehicleAge")) {
-                result = inputNumberEqual(ptlAgreementCostRule, value);
+                result = inputNumberEqual(ptlAgreementCostRule, normalizedValue);
             }
         } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("radio")) {
-            result = radioEqual(ptlAgreementCostRule, value);
+            result = radioEqual(ptlAgreementCostRule, normalizedValue);
         } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("datetime")){ // 时间
-            result = dateTimeEqual(ptlAgreementCostRule, value);
+            result = dateTimeEqual(ptlAgreementCostRule, normalizedValue);
         } else if (ptlAgreementUndwrtRulesAttr.getAttrType().equals("date")){
-            result = dateEqual(ptlAgreementCostRule, value);
+            result = dateEqual(ptlAgreementCostRule, normalizedValue);
         }
         // 判断是否是承保规则
         if(isUnderwrite){

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

@@ -74,6 +74,7 @@ public class InsOrdersController extends BaseController {
     @Operation(summary = "录入订单新增")
     @PostMapping("saveOrders")
     public HttpResult saveOrUpdate(@RequestBody InsOrdersAdd ordersAdd) {
+        log.info("补录订单。 saveOrUpdate=[{}]", JSONUtil.toJsonStr(ordersAdd));
         ordersAdd.setSystemCode(getSystemCode());
         return insOrdersService.saveOrders(ordersAdd);
     }

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

@@ -503,7 +503,7 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
             ordersPolicy.setJyPolicyNo(ordersExternalPolicy.getNoPolicy());
             insOrdersPolicyService.updateByOrderNo(ordersPolicy);
 
-            insFeeAuditService.feeAuditAdd(insOrders, baseController.getSystemCode());
+
             // 发送订单状态变更消息
             this.orderStatusMessage(insOrders, insOrdersCarInfo, insOrdersCosts, baseController.getToken(), MessageCodeEnum.TEMPLATE_9);
             if (nullOrders) {
@@ -512,6 +512,10 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
                 insOrdersAdd.setInsOrders(orders);
                 saveOrders(insOrdersAdd);
             }
+            // modify by lipf , 2026年8月21日11:49:44 解决新车第一次报价的时候,费用审核有两条记录的bug
+            if (!nullOrders) {
+                insFeeAuditService.feeAuditAdd(insOrders, baseController.getSystemCode());
+            }
             // 返回成功信息,表示订单、车辆和用户信息保存成功
             return HttpResult.ok("保存成功");
 
@@ -753,6 +757,14 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
             if (ptlAgreement.getData() != null && ptlAgreement.getData().getAgreement() != null) {
                 insOrdersVo.setAgreementType(ptlAgreement.getData().getAgreement().getAgreementType());
                 insOrdersVo.setAgreementName(ptlAgreement.getData().getAgreement().getAgreementName());
+                // 根据ptlAgreement获取deptId,根据deptId查询sys_dept,然后根据sys_dept的name给agreementAttributionName赋值
+                String deptId = ptlAgreement.getData().getAgreement().getDeptId();
+                if (StringUtils.isNotEmpty(deptId)) {
+                    HttpResult<SysDeptVo> deptResult = orgClient.deptGet(deptId);
+                    if (deptResult != null && deptResult.getData() != null) {
+                        insOrdersVo.setAgreementOwnership(deptResult.getData().getName());
+                    }
+                }
             }
         }
 
@@ -912,6 +924,15 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
             if (ptlAgreement.getData() != null && ptlAgreement.getData().getAgreement() != null) {
                 insOrdersVo.setAgreementType(ptlAgreement.getData().getAgreement().getAgreementType());
                 insOrdersVo.setAgreementName(ptlAgreement.getData().getAgreement().getAgreementName());
+
+                // 根据ptlAgreement获取deptId,根据deptId查询sys_dept,然后根据sys_dept的name给agreementAttributionName赋值
+                String deptId = ptlAgreement.getData().getAgreement().getDeptId();
+                if (StringUtils.isNotEmpty(deptId)) {
+                    HttpResult<SysDeptVo> deptResult = orgClient.deptGet(deptId);
+                    if (deptResult != null && deptResult.getData() != null) {
+                        insOrdersVo.setAgreementOwnership(deptResult.getData().getName());
+                    }
+                }
             }
         }
         // 2. 处理车辆用户信息列表
@@ -1832,30 +1853,36 @@ public class InsOrdersServiceImpl extends ServiceImpl<InsOrdersMapper, InsOrders
         } else {
             insOrdersVo = selectById(Long.parseLong(ordersNo));
         }
+        HttpResult<List<SysArea>> sysAreasResult;
+        try{
+            sysAreasResult = orgClient.queryAllParent(insOrdersVo.getOwnersInfo().getCountyCode());
+            if(sysAreasResult.getCode() == 200) {
+                List<SysArea> sysAreas = sysAreasResult.getData();
+                insOrdersVo.getOwnersInfo().setProvince(sysAreas.get(3).getAreaCname());
+                insOrdersVo.getOwnersInfo().setCity(sysAreas.get(2).getAreaCname());
+                insOrdersVo.getOwnersInfo().setCounty(sysAreas.get(1).getAreaCname());
+            }
+            HttpResult<List<SysArea>> sysPoliCyAreasResult = orgClient.queryAllParent(insOrdersVo.getPolicyHolderInfo().getCountyCode());
+            if(sysPoliCyAreasResult.getCode() == 200) {
+                List<SysArea> sysAreas = sysAreasResult.getData();
+                insOrdersVo.getPolicyHolderInfo().setProvince(sysAreas.get(3).getAreaCname());
+                insOrdersVo.getPolicyHolderInfo().setCity(sysAreas.get(2).getAreaCname());
+                insOrdersVo.getPolicyHolderInfo().setCounty(sysAreas.get(1).getAreaCname());
+            }
 
-        HttpResult<List<SysArea>> sysAreasResult = orgClient.queryAllParent(insOrdersVo.getOwnersInfo().getCountyCode());
-        if(sysAreasResult.getCode() == 200) {
-            List<SysArea> sysAreas = sysAreasResult.getData();
-            insOrdersVo.getOwnersInfo().setProvince(sysAreas.get(3).getAreaCname());
-            insOrdersVo.getOwnersInfo().setCity(sysAreas.get(2).getAreaCname());
-            insOrdersVo.getOwnersInfo().setCounty(sysAreas.get(1).getAreaCname());
+            HttpResult<List<SysArea>> sysInsuredAreasResult = orgClient.queryAllParent(insOrdersVo.getInsuredPersonInfo().getCountyCode());
+            if(sysInsuredAreasResult.getCode() == 200) {
+                List<SysArea> sysAreas = sysAreasResult.getData();
+                insOrdersVo.getInsuredPersonInfo().setProvince(sysAreas.get(3).getAreaCname());
+                insOrdersVo.getInsuredPersonInfo().setCity(sysAreas.get(2).getAreaCname());
+                insOrdersVo.getInsuredPersonInfo().setCounty(sysAreas.get(1).getAreaCname());
+            }
+        }catch (Exception e){
+            log.info("处理地址异常");
         }
 
-        HttpResult<List<SysArea>> sysPoliCyAreasResult = orgClient.queryAllParent(insOrdersVo.getPolicyHolderInfo().getCountyCode());
-        if(sysPoliCyAreasResult.getCode() == 200) {
-            List<SysArea> sysAreas = sysAreasResult.getData();
-            insOrdersVo.getPolicyHolderInfo().setProvince(sysAreas.get(3).getAreaCname());
-            insOrdersVo.getPolicyHolderInfo().setCity(sysAreas.get(2).getAreaCname());
-            insOrdersVo.getPolicyHolderInfo().setCounty(sysAreas.get(1).getAreaCname());
-        }
 
-        HttpResult<List<SysArea>> sysInsuredAreasResult = orgClient.queryAllParent(insOrdersVo.getInsuredPersonInfo().getCountyCode());
-        if(sysInsuredAreasResult.getCode() == 200) {
-            List<SysArea> sysAreas = sysAreasResult.getData();
-            insOrdersVo.getInsuredPersonInfo().setProvince(sysAreas.get(3).getAreaCname());
-            insOrdersVo.getInsuredPersonInfo().setCity(sysAreas.get(2).getAreaCname());
-            insOrdersVo.getInsuredPersonInfo().setCounty(sysAreas.get(1).getAreaCname());
-        }
+
 
         log.info("根据orderNo=[{}]查询到的订单明细[{}]", ordersNo, JSONUtil.toJsonStr(insOrdersVo));
         AssertionUtils.isFail(Objects.isNull(insOrdersVo), "订单不存在");

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

@@ -934,7 +934,7 @@
         ) x WHERE x.rn = 1
         ) ioa1 ON io.id = ioa1.order_no
         <where>
-            and io.is_delete = 0 and io.parent_id != '0' and sujis.type_attr !=3
+            and io.is_delete = 0 and io.parent_id != '0' and sujis.type_attr NOT IN (1,2,3,4,5)
             <if test="insOrdersParam.jzgInfoId != null and insOrdersParam.jzgInfoId != ''">
                 and io.real_quote_user_id in (select bbb.mobile from sys_user bbb where bbb.id in ( select ss.user_id
                 from sys_user_jzg_info ss where 1 =1 and (ss.leader_id = #{insOrdersParam.jzgInfoId} or ss.id
@@ -1123,7 +1123,7 @@
         ) x WHERE x.rn = 1
         ) ioa1 ON io.id = ioa1.order_no
         <where>
-            and io.is_delete = 0 and sujis.type_attr !=3
+            and io.is_delete = 0 and sujis.type_attr NOT IN (1,2,3,4,5)
             <if test="insOrdersParam.orderNo != null and insOrdersParam.orderNo != ''">
                 AND io.id = #{insOrdersParam.orderNo}
             </if>
@@ -1331,7 +1331,7 @@
         ) x WHERE x.rn = 1
         ) ioa1 ON io.id = ioa1.order_no
         <where>
-            and io.is_delete = 0 and io.parent_id != '0' and sujis.type_attr !=3
+            and io.is_delete = 0 and io.parent_id != '0' and sujis.type_attr NOT IN (1,2,3,4,5)
             <if test="insOrdersParam.jzgInfoId != null and insOrdersParam.jzgInfoId != ''">
                 and io.real_quote_user_id in (select bbb.mobile from sys_user bbb where bbb.id in ( select ss.user_id
                 from sys_user_jzg_info ss where 1 =1 and (ss.leader_id = #{insOrdersParam.jzgInfoId} or ss.id
@@ -1544,7 +1544,7 @@
             ) x WHERE x.rn = 1
             ) ioa1 ON io.id = ioa1.order_no
             <where>
-                and io.is_delete = 0 and io.parent_id != '0' and sujis.type_attr !=3
+                and io.is_delete = 0 and io.parent_id != '0' and sujis.type_attr NOT IN (1,2,3,4,5)
                 <if test="insOrdersParam.jzgInfoId != null and insOrdersParam.jzgInfoId != ''">
                     and io.real_quote_user_id in (select bbb.mobile from sys_user bbb where bbb.id in ( select ss.user_id
                     from sys_user_jzg_info ss where 1 =1 and (ss.leader_id = #{insOrdersParam.jzgInfoId} or ss.id

+ 231 - 0
tenant/organization/src/main/java/com/jzg/organization/component/BuildDescUtils.java

@@ -0,0 +1,231 @@
+package com.jzg.organization.component;
+
+import com.jzg.commons.entity.po.PtlAgreementUndwrtRulesAttr;
+import com.jzg.commons.entity.po.PtlAgreementUndwrtRulesAttrLink;
+import com.jzg.commons.entity.po.PtlAgreementUndwrtRulesDictLabal;
+import com.jzg.commons.util.StringUtils;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class BuildDescUtils {
+
+    /**
+     * 遍历费用属性描述文字
+     *
+     * @param linkList
+     * @param undwrtRulesAttrList
+     * @return
+     */
+    public static String buildDesc(String companyId, List<PtlAgreementUndwrtRulesAttr> linkList, List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList) {
+        StringBuilder desc = new StringBuilder();
+        Map<String, PtlAgreementUndwrtRulesAttr> attrMap = undwrtRulesAttrList.stream()
+                .filter(attr -> "all".equals(attr.getCompanyIds()) || (attr.getCompanyIds() != null && attr.getCompanyIds().equals(companyId)))
+                .collect(Collectors.toMap(PtlAgreementUndwrtRulesAttr::getAttrCode, Function.identity()));
+        // 这里进行排序,保证生成的承保规则描述是有序的
+        linkList = linkList.stream().sorted(Comparator.comparing(PtlAgreementUndwrtRulesAttr::getAttrCode)).toList();
+        List<PtlAgreementUndwrtRulesAttr> sortedLinks = new ArrayList<>(linkList);
+
+        for (PtlAgreementUndwrtRulesAttr link : sortedLinks) {
+            PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr = attrMap.get(link.getAttrCode());
+            if (ptlAgreementUndwrtRulesAttr == null) {
+                continue;
+            }
+
+            String attrType = ptlAgreementUndwrtRulesAttr.getAttrType();
+
+            if ("inputNumber".equals(attrType)) {
+                desc.append(inputNumberIdentifying(ptlAgreementUndwrtRulesAttr, link)).append(";\t\t");
+            } else if ("radio".equals(attrType)) {
+                desc.append(radioIdentifying(ptlAgreementUndwrtRulesAttr, link)).append(";\t\t");
+            } else if ("checkbox".equals(attrType) || "select".equals(attrType)) {
+                if(link.getMinArray() != null && link.getMinArray().length > 0){
+                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含");
+                    for (int k=0 ; k<link.getMinArray().length; k++){
+                        PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = null;
+                        for (PtlAgreementUndwrtRulesDictLabal x : link.getDictLabal()) {
+                            if (Arrays.asList(link.getMinArray()).contains(x.getCode())) {
+                                ptlAgreementUndwrtRulesDictLabal = x;
+                                link.getMinArray()[k] = null;
+                                break;
+                            }
+                        }
+                        desc.append(null != ptlAgreementUndwrtRulesDictLabal ? ptlAgreementUndwrtRulesDictLabal.getName() : "").append(",");
+                    }
+                    // 条件描述后补充一个分号
+                    desc.append(",");
+                }else {
+                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含").append(link.getMin()).append(";\t\t");
+                }
+            } else if ("inputTag".equals(attrType)) {
+                if (link.getOperator() != null && "1".equals(link.getOperator())) {
+                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含").append(link.getMin()).append(";\t\t");
+                } else {
+                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("不包含").append(link.getMin()).append(";\t\t");
+                }
+            }
+        }
+        return desc.toString();
+    }
+
+    private static String radioIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttr link) {
+        Optional<PtlAgreementUndwrtRulesDictLabal> dictLabel = Optional.ofNullable(
+                        ptlAgreementUndwrtRulesAttr.getDictLabal())
+                .flatMap(list -> list.stream().filter(x -> x.getCode().equals(link.getMin())).findFirst());
+
+        return dictLabel.map(dl -> ptlAgreementUndwrtRulesAttr.getAttrName() + "为" + dl.getName())
+                .orElse("");
+    }
+
+    private static String inputNumberIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttr link) {
+        String operator = link.getOperator();
+        switch (operator) {
+            case "1":
+                return ptlAgreementUndwrtRulesAttr.getAttrName() + "范围值" + link.getMin() + "到" + link.getMax();
+            case "2":
+                return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于" + link.getMin();
+            case "3":
+                return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于等于" + link.getMin();
+            case "4":
+                return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于" + link.getMax();
+            case "5":
+                return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于等于" + link.getMax();
+            default:
+                return ptlAgreementUndwrtRulesAttr.getAttrName() + "未知操作符";
+        }
+    }
+
+
+    /**
+     * 遍历承保规则属性描述文字
+     *
+     * @param linkList
+     * @param undwrtRulesAttrList
+     * @return
+     */
+    public static String buildDesc(List<PtlAgreementUndwrtRulesAttrLink> linkList, List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList) {
+        StringBuilder desc = new StringBuilder();
+        // 这里进行排序,保证生成的承保规则描述是有序的
+        linkList = linkList.stream().sorted(Comparator.comparing(PtlAgreementUndwrtRulesAttrLink::getAttrCode)).toList();
+        List<PtlAgreementUndwrtRulesAttrLink> sortedLinks = new ArrayList<>(linkList);
+
+        for (int i = 0; i < sortedLinks.size(); i++) {
+            int finalI = i;
+            PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr = undwrtRulesAttrList.stream()
+                    .filter(x -> x.getAttrCode().equals(sortedLinks.get(finalI).getAttrCode()))
+                    .findAny()
+                    .get();
+
+            //数值范围判断
+
+            if ("inputNumber".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
+                desc.append(inputNumberIdentifying(ptlAgreementUndwrtRulesAttr, sortedLinks.get(i))).append(",");
+            }
+            if ("radio".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
+                desc.append(radioIdentifying(ptlAgreementUndwrtRulesAttr, sortedLinks.get(i))).append(",");
+            }
+            if ("select".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
+                desc.append(selectIdentifying(ptlAgreementUndwrtRulesAttr, sortedLinks.get(i)));
+            }
+            if ("checkbox".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
+                desc.append(checkBoxIdentifying(ptlAgreementUndwrtRulesAttr, sortedLinks.get(i)));
+            }
+            if ("inputTag".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
+                if (sortedLinks.get(i).getOperator() != null && sortedLinks.get(i).getOperator().equals("1")) {
+                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含").append(sortedLinks.get(i).getMin()).append(",");
+                } else {
+                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("不包含").append(sortedLinks.get(i).getMin()).append(",");
+                }
+            }
+        }
+        // 相邻的","或","转换为"或"字
+        return StringUtils.convertAdjacentCommaToOr(desc.toString());
+    }
+
+    public static String radioIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink ptlAgreementProductCostsAttrLink) {
+        PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = ptlAgreementUndwrtRulesAttr.getDictLabal().stream().filter(x -> x.getCode().equals(ptlAgreementProductCostsAttrLink.getMin())).findAny().get();
+        return ptlAgreementUndwrtRulesAttr.getAttrName() + "为" + ptlAgreementUndwrtRulesDictLabal.getName();
+    }
+
+    public static String selectIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink ptlAgreementProductCostsAttrLink) {
+        StringBuilder returnMsg = new StringBuilder();
+        returnMsg.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("为");
+        // 兼容 min 逗号分隔存储,保证 minArray 有值
+        if (ptlAgreementProductCostsAttrLink.getMinArray() == null) {
+            String minStr = ptlAgreementProductCostsAttrLink.getMin();
+            if (minStr != null && !minStr.isEmpty()) {
+                ptlAgreementProductCostsAttrLink.setMinArray(minStr.split(","));
+            }
+        }
+
+        // 优先取 link 上的字典标签列表(JSONArray -> List),为空时回退到属性自带字典
+        List<PtlAgreementUndwrtRulesDictLabal> dictLabalList = getDictLabalList(ptlAgreementUndwrtRulesAttr, ptlAgreementProductCostsAttrLink);
+
+        // 用字典标签的 code 匹配 minArray 中的每一项
+        if (ptlAgreementProductCostsAttrLink.getMinArray() != null) {
+            for (String item : ptlAgreementProductCostsAttrLink.getMinArray()) {
+                PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = dictLabalList.stream()
+                        .filter(x -> x.getCode() != null && x.getCode().equals(item)).findAny().orElse(null);
+                if (ptlAgreementUndwrtRulesDictLabal != null) {
+                    returnMsg.append(ptlAgreementUndwrtRulesDictLabal.getName()).append(",");
+                }
+            }
+        }
+        return returnMsg.toString();
+    }
+
+    public static String checkBoxIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink linkList) {
+        StringBuilder returnMsg = new StringBuilder();
+        returnMsg.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("为");
+        // 兼容 min 逗号分隔存储,保证 minArray 有值
+        if (linkList.getMinArray() == null) {
+            String minStr = linkList.getMin();
+            if (minStr != null && !minStr.isEmpty()) {
+                linkList.setMinArray(minStr.split(","));
+            }
+        }
+        if (linkList.getMinArray() != null) {
+            // 优先取 link 上的字典标签列表(JSONArray -> List),为空时回退到属性自带字典
+            List<PtlAgreementUndwrtRulesDictLabal> dictLabalList = getDictLabalList(ptlAgreementUndwrtRulesAttr, linkList);
+            // code -> 字典标签 映射,避免 O(n²) 循环匹配
+            Map<String, PtlAgreementUndwrtRulesDictLabal> dictLabalMap = dictLabalList.stream()
+                    .filter(x -> x.getCode() != null)
+                    .collect(Collectors.toMap(PtlAgreementUndwrtRulesDictLabal::getCode, Function.identity(), (oldItem, newItem) -> newItem));
+            for (String code : linkList.getMinArray()) {
+                PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = dictLabalMap.get(code);
+                returnMsg.append(ptlAgreementUndwrtRulesDictLabal != null ? ptlAgreementUndwrtRulesDictLabal.getName() : "").append(",");
+            }
+        }
+        return returnMsg.toString();
+    }
+
+    /**
+     * 获取字典标签列表:优先取 link 上的字典标签列表(JSONArray -> List),为空时回退到属性自带字典
+     */
+    private static List<PtlAgreementUndwrtRulesDictLabal> getDictLabalList(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink link) {
+        if (link.getDictLabal() != null && !link.getDictLabal().isEmpty()) {
+            return link.getDictLabal().toJavaList(PtlAgreementUndwrtRulesDictLabal.class);
+        }
+        if (ptlAgreementUndwrtRulesAttr.getDictLabal() != null) {
+            return ptlAgreementUndwrtRulesAttr.getDictLabal();
+        }
+        return Collections.emptyList();
+    }
+
+    private static String inputNumberIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink ptlAgreementProductCostsAttrLink) {
+        if ("1".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
+            return ptlAgreementUndwrtRulesAttr.getAttrName() + "范围值" + ptlAgreementProductCostsAttrLink.getMin() + "到" + ptlAgreementProductCostsAttrLink.getMax();
+        } else if ("2".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
+            return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于" + ptlAgreementProductCostsAttrLink.getMin();
+        } else if ("3".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
+            return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于等于" + ptlAgreementProductCostsAttrLink.getMin();
+        } else if ("4".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
+            return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于" + ptlAgreementProductCostsAttrLink.getMax();
+        } else if ("5".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
+            return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于等于" + ptlAgreementProductCostsAttrLink.getMax();
+        }
+        return "";
+    }
+
+}

+ 6 - 1
tenant/organization/src/main/java/com/jzg/organization/controller/AreaController.java

@@ -10,7 +10,9 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * 区划管理接口
@@ -43,6 +45,9 @@ public class AreaController {
         SysArea sysArea = new SysArea();
         sysArea.setLevel(3);
         sysArea.setUpperCode(countyCode);
-        return HttpResult.ok(sysAreaService.queryAllParent(sysArea));
+        if(Objects.nonNull(countyCode)) {
+            return HttpResult.ok(sysAreaService.queryAllParent(sysArea));
+        }
+        return HttpResult.ok(new ArrayList<>());
     }
 }

+ 11 - 0
tenant/organization/src/main/java/com/jzg/organization/controller/PtlAgreementCostExternalController.java

@@ -118,5 +118,16 @@ public class PtlAgreementCostExternalController {
         return ptlAgreementCostExternalService.auditFailed(costReviewParam);
     }
 
+    @Operation(summary = "批量复制外部费用")
+    @PostMapping("copyCostExternal")
+    public HttpResult copyCostExternal(@RequestBody PtlAgreementCostCopyExternalParam costCopyParam){
+        try{
+            return ptlAgreementCostExternalService.copyCostExternal(costCopyParam);
+
+        }catch (Exception e){
+            return HttpResult.error("批量复制外部费用异常:"+e.getMessage());
+        }
+    }
+
 
 }

+ 1 - 0
tenant/organization/src/main/java/com/jzg/organization/service/PtlAgreementCostExternalService.java

@@ -37,4 +37,5 @@ public interface PtlAgreementCostExternalService extends IService<PtlAgreementCo
 
     HttpResult auditFailed(CostReviewParam costReviewParam);
 
+    HttpResult copyCostExternal(PtlAgreementCostCopyExternalParam costCopyParam);
 }

+ 58 - 2
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementCostExternalServiceImpl.java

@@ -2,6 +2,8 @@ package com.jzg.organization.service.impl;
 
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.nacos.common.utils.CollectionUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -13,14 +15,15 @@ import com.jzg.commons.core.base.BaseController;
 import com.jzg.commons.core.base.StateResult;
 import com.jzg.commons.core.page.HttpResult;
 import com.jzg.commons.entity.dto.*;
-import com.jzg.commons.entity.po.PtlAgreementCostExternal;
-import com.jzg.commons.entity.po.PtlAgreementCostExternalType;
+import com.jzg.commons.entity.po.*;
 import com.jzg.commons.entity.vo.CostExternalListVo;
 import com.jzg.commons.entity.vo.CostExternalVo;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.commons.util.DateTimeUtils;
 import com.jzg.commons.util.StringUtils;
+import com.jzg.commons.util.idgen.IdGenerate;
 import com.jzg.organization.mapper.PtlAgreementCostExternalMapper;
+import com.jzg.organization.mapper.PtlAgreementCostExternalTypeMapper;
 import com.jzg.organization.service.PtlAgreementCostExternalService;
 import com.jzg.organization.service.PtlAgreementCostExternalTypeService;
 import com.jzg.organization.state.listener.CostStateListener;
@@ -39,6 +42,8 @@ public class PtlAgreementCostExternalServiceImpl extends ServiceImpl<PtlAgreemen
 
     @Resource
     private PtlAgreementCostExternalTypeService ptlAgreementCostExternalTypeService;
+    @Resource
+    private PtlAgreementCostExternalTypeMapper ptlAgreementCostExternalTypeMapper;
 
     @Resource
     private CostStateListener costStateListener;
@@ -260,6 +265,57 @@ public class PtlAgreementCostExternalServiceImpl extends ServiceImpl<PtlAgreemen
         return audit(costReviewParam, false);
     }
 
+    @Override
+    @Transactional
+    public HttpResult copyCostExternal(PtlAgreementCostCopyExternalParam costCopyParam) {
+        // 参数校验:所有参数都不能为空
+        if (costCopyParam == null) {
+            return HttpResult.error("复制参数不能为空");
+        }
+        if (CollUtil.isEmpty(costCopyParam.getCostIds())) {
+            return HttpResult.error("费用ID列表不能为空");
+        }
+        if (StrUtil.isBlank(costCopyParam.getAgreementId())) {
+            return HttpResult.error("协议ID不能为空");
+        }
+        if (StrUtil.isBlank(costCopyParam.getContactPerson())) {
+            return HttpResult.error("对接人姓名不能为空");
+        }
+        if (StrUtil.isBlank(costCopyParam.getContactMobile())) {
+            return HttpResult.error("对接人手机号不能为空");
+        }
+        List<String> costIds = costCopyParam.getCostIds();
+        StringBuilder errMsg = new StringBuilder();
+        costIds.forEach(oldCostId-> {
+            PtlAgreementCostExternal oldCostExternal = baseMapper.selectOne(
+                    new LambdaQueryWrapper<PtlAgreementCostExternal>()
+                            .eq(PtlAgreementCostExternal::getId, oldCostId));
+            if(oldCostExternal != null){
+                List<PtlAgreementCostExternalType> ptlAgreementCostTypes = ptlAgreementCostExternalTypeMapper.selectList(new LambdaQueryWrapper<PtlAgreementCostExternalType>()
+                        .eq(PtlAgreementCostExternalType::getPtlAgreementCostExternalId, oldCostExternal.getId()));
+                // 调整数据 进行copy
+                oldCostExternal.setId(null);
+                oldCostExternal.setPtlAgreementId(Long.valueOf(costCopyParam.getAgreementId()));
+                oldCostExternal.setCostDescribe("复制的费用:" + oldCostExternal.getCostDescribe());
+                oldCostExternal.setCreateTime(LocalDateTime.now());
+                oldCostExternal.setUpdateTime(LocalDateTime.now());
+                oldCostExternal.setContactPerson(costCopyParam.getContactPerson());
+                oldCostExternal.setContactMobile(costCopyParam.getContactMobile());
+                // 复制的费用为待审核
+                oldCostExternal.setAuditStatus(0);
+                baseMapper.insert(oldCostExternal);
+                ptlAgreementCostTypes.forEach(item ->{
+                    item.setId(null);
+                });
+                saveOrUpdate(ptlAgreementCostTypes,oldCostExternal.getId());
+
+            }else{
+                errMsg.append("costId为"+oldCostId+"的费用信息已被删除;");
+            }
+        });
+        return HttpResult.ok(errMsg.toString());
+    }
+
     private HttpResult audit(CostReviewParam costReviewParam, boolean isSuccess) {
         // 批量查询所有记录
         List<PtlAgreementCostExternal> records = baseMapper.selectBatchIds(costReviewParam.getIds());

+ 12 - 2
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementCostServiceImpl.java

@@ -20,11 +20,11 @@ import com.jzg.commons.entity.vo.CostsListVo;
 import com.jzg.commons.entity.vo.PtlAgreementCostRuleVo;
 import com.jzg.commons.entity.vo.PtlAgreementCostVo;
 import com.jzg.commons.exception.SystemException;
-import com.jzg.commons.util.BuildDescUtils;
 import com.jzg.commons.util.ListUtils;
 import com.jzg.commons.util.StringUtils;
 import com.jzg.commons.util.idgen.IdGenerate;
 import com.jzg.organization.client.EsmInsCompanyClient;
+import com.jzg.organization.component.BuildDescUtils;
 import com.jzg.organization.mapper.PtlAgreementCostMapper;
 import com.jzg.organization.mapper.PtlAgreementCostRuleMapper;
 import com.jzg.organization.mapper.PtlAgreementCostTypeMapper;
@@ -95,6 +95,8 @@ public class PtlAgreementCostServiceImpl extends ServiceImpl<PtlAgreementCostMap
     private PtlAgreementService ptlAgreementService;
     @Autowired
     private SysUserJzgInfoService sysUserJzgInfoService;
+    @Autowired
+    private PtlAreaLicenseService ptlAreaLicenseService;
 
     /**
      * 报价配置/费用管理/新增费用
@@ -212,7 +214,7 @@ public class PtlAgreementCostServiceImpl extends ServiceImpl<PtlAgreementCostMap
             return null;
         }
         String desc = BuildDescUtils.buildDesc(companyId, BeanUtil.copyToList(list, PtlAgreementUndwrtRulesAttr.class), agreementUndwrtRulesAttrService.getUndwrtRulesAttrList());
-        // 两个及以上相邻的逗号(中文","或英文",")合并为一个逗
+        // buildDesc返回的条件间以相邻双逗号分隔,此处合并为单个分
         return StringUtils.mergeAdjacentComma(desc);
     }
 
@@ -259,6 +261,14 @@ public class PtlAgreementCostServiceImpl extends ServiceImpl<PtlAgreementCostMap
         }
         RList<SysDict> rules = redissonClient.getList(DICT_REDIS_KEY + "rule_operator");
         for (PtlAgreementCostRuleVo ruleCondition : ptlAgreementCostVo.getRuleConditions()) {
+            // LicenseNo/LicenseArea 属性中的车牌号、地区名称统一转换为地区编码
+            if (("LicenseNo".equals(ruleCondition.getAttrCode()) || "LicenseArea".equals(ruleCondition.getAttrCode()))
+                    && StringUtils.isNotEmpty(ruleCondition.getMin())) {
+                String value = ruleCondition.getMin();
+                value = ptlAreaLicenseService.convertLicense2AreaNumber(value);
+                value = ptlAreaLicenseService.convertAreaName2AreaNumber(value);
+                ruleCondition.setMin(value);
+            }
             String attrType = ruleCondition.getAttrType();
             if (StringUtils.isNotEmpty(attrType) && java.util.Arrays.stream(COST_TYPE_DICT_KEY).anyMatch(attrType::equals)){
                 if (StringUtils.isNotEmpty(ruleCondition.getMin()) && ruleCondition.getMin().contains(",")){

+ 79 - 37
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementServiceImpl.java

@@ -399,25 +399,43 @@ public class PtlAgreementServiceImpl extends ServiceImpl<PtlAgreementMapper, Ptl
         if(CollUtil.isEmpty(costIds)){
             return;
         }
-        List<PtlAgreementCost> costList = agreementCostService.listByIds(costIds);
+        List<PtlAgreementCost> selectedCostList = agreementCostService.listByIds(costIds);
+        if (CollUtil.isEmpty(selectedCostList)) {
+            return;
+        }
+        // 根据被勾选费用关联的承保规则,查询其 parent_id 相同的所有承保规则(整组复制)
+        Set<String> coverRuleIds = selectedCostList.stream()
+                .map(PtlAgreementCost::getCoverRuleId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        List<PtlAgreementUndwrtRules> selectedRules = agreementRulesService.listByIds(coverRuleIds);
+        Set<String> parentIds = selectedRules.stream()
+                .map(PtlAgreementUndwrtRules::getParentId)
+                .filter(parentId -> parentId != null && !parentId.isEmpty())
+                .collect(Collectors.toSet());
+        List<PtlAgreementUndwrtRules> rulesList;
+        if (CollUtil.isNotEmpty(parentIds)) {
+            // 查询 parent_id 相同的所有承保规则
+            LambdaQueryWrapper<PtlAgreementUndwrtRules> rulesWrapper = new LambdaQueryWrapper<>();
+            rulesWrapper.in(PtlAgreementUndwrtRules::getParentId, parentIds);
+            rulesList = agreementRulesService.list(rulesWrapper);
+        } else {
+            rulesList = selectedRules;
+        }
+        Set<String> allRuleIds = rulesList.stream().map(PtlAgreementUndwrtRules::getId).collect(Collectors.toSet());
+        // 查询整组承保规则关联的所有费用
+        LambdaQueryWrapper<PtlAgreementCost> costWrapper = new LambdaQueryWrapper<>();
+        costWrapper.in(PtlAgreementCost::getCoverRuleId, allRuleIds);
+        List<PtlAgreementCost> costList = agreementCostService.list(costWrapper);
         if(CollUtil.isEmpty(costList)){
             return;
         }
+        List<String> allCostIds = costList.stream().map(PtlAgreementCost::getId).collect(Collectors.toList());
 
-        Map<String,List<PtlAgreementCost>> costGroup = new HashMap<>();
-        for (PtlAgreementCost ptlAgreementCost : costList) {
-            List<PtlAgreementCost> costGroupList = new ArrayList<>();
-            if(costGroup.containsKey(ptlAgreementCost.getCoverRuleId())){
-                costGroupList = costGroup.get(ptlAgreementCost.getCoverRuleId());
-                costGroupList.add(ptlAgreementCost);
-                costGroup.put(ptlAgreementCost.getCoverRuleId(), costGroupList);
-            }else{
-                costGroupList.add(ptlAgreementCost);
-                costGroup.put(ptlAgreementCost.getCoverRuleId(), costGroupList);
-            }
-        }
+        Map<String, List<PtlAgreementCost>> costGroup = costList.stream()
+                .collect(Collectors.groupingBy(PtlAgreementCost::getCoverRuleId));
         // 复制承保规则和费用
-        this.copyRuleAndCost(costGroup,costIds,costList,newAgreement);
+        this.copyRuleAndCost(costGroup, allCostIds, costList, newAgreement);
 
     }
 
@@ -428,35 +446,53 @@ public class PtlAgreementServiceImpl extends ServiceImpl<PtlAgreementMapper, Ptl
                                 List<PtlAgreementCost> costList,
                                 PtlAgreement newAgreement) {
 
-        // 获取查询所有的旧承保规则
+        // 获取所有的旧承保规则,并按 parent_id 扩展为整组(含无费用关联的规则)
         Set<String> rulesIds = costList.stream().map(PtlAgreementCost :: getCoverRuleId).collect(Collectors.toSet());
         List<PtlAgreementUndwrtRules> rulesList = agreementRulesService.listByIds(rulesIds);
-        Map<String, PtlAgreementUndwrtRules> rulesMap = rulesList.stream().collect(
-                Collectors.toMap(PtlAgreementUndwrtRules::getId,rules -> rules));
+        Set<String> parentIds = rulesList.stream()
+                .map(PtlAgreementUndwrtRules::getParentId)
+                .filter(parentId -> parentId != null && !parentId.isEmpty())
+                .collect(Collectors.toSet());
+        if (CollUtil.isNotEmpty(parentIds)) {
+            // 查询 parent_id 相同的所有承保规则(整组复制)
+            LambdaQueryWrapper<PtlAgreementUndwrtRules> rulesWrapper = new LambdaQueryWrapper<>();
+            rulesWrapper.in(PtlAgreementUndwrtRules::getParentId, parentIds);
+            rulesList = agreementRulesService.list(rulesWrapper);
+        }
+        Set<String> allRuleIds = rulesList.stream().map(PtlAgreementUndwrtRules::getId).collect(Collectors.toSet());
 
         // 查询所有承保规则明细
         LambdaQueryWrapper<PtlAgreementUndwrtRulesAttrLink> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.in(PtlAgreementUndwrtRulesAttrLink::getRulesId, rulesIds);
+        queryWrapper.in(PtlAgreementUndwrtRulesAttrLink::getRulesId, allRuleIds);
         List<PtlAgreementUndwrtRulesAttrLink> linkList = agreementUndwrtRulesAttrLinkService.list(queryWrapper);
         Map<String, List<PtlAgreementUndwrtRulesAttrLink>> linkMap = linkList.stream().collect(
                 Collectors.groupingBy(PtlAgreementUndwrtRulesAttrLink::getRulesId));
 
+        // 整组承保规则关联的所有费用
+        LambdaQueryWrapper<PtlAgreementCost> costWrapper = new LambdaQueryWrapper<>();
+        costWrapper.in(PtlAgreementCost::getCoverRuleId, allRuleIds);
+        List<PtlAgreementCost> allCostList = agreementCostService.list(costWrapper);
+        Set<String> allCostIds = allCostList.stream().map(PtlAgreementCost::getId).collect(Collectors.toSet());
+        Map<String, List<PtlAgreementCost>> allCostGroup = allCostList.stream().collect(
+                Collectors.groupingBy(PtlAgreementCost::getCoverRuleId));
+
         // 查询所有旧的费用规则
         LambdaQueryWrapper<PtlAgreementCostRule> costRuleLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        costRuleLambdaQueryWrapper.in(PtlAgreementCostRule::getPtlAgreementCostId, costIds);
+        costRuleLambdaQueryWrapper.in(PtlAgreementCostRule::getPtlAgreementCostId, allCostIds);
         List<PtlAgreementCostRule> costRuleList = agreementCostRuleService.list(costRuleLambdaQueryWrapper);
         Map<String, List<PtlAgreementCostRule>> costRulesMap = costRuleList.stream().collect(
                 Collectors.groupingBy(PtlAgreementCostRule::getPtlAgreementCostId));
         // 查询所有旧的费用明细
         LambdaQueryWrapper<PtlAgreementCostType> costTypeLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        costTypeLambdaQueryWrapper.in(PtlAgreementCostType::getPtlAgreementCostId, costIds);
+        costTypeLambdaQueryWrapper.in(PtlAgreementCostType::getPtlAgreementCostId, allCostIds);
         List<PtlAgreementCostType> costTypeList = agreementCostTypeService.list(costTypeLambdaQueryWrapper);
         Map<String, List<PtlAgreementCostType>> costTypeMap = costTypeList.stream().collect(
                 Collectors.groupingBy(PtlAgreementCostType::getPtlAgreementCostId));
 
-        costGroup.forEach((key, value) -> {
+        // 源 parentId -> 新 parentId,同组规则复制后共享同一个新 parentId,保持分组结构
+        Map<String, String> newParentIdMap = new HashMap<>();
+        for (PtlAgreementUndwrtRules agreementUndwrtRules : rulesList) {
             //1-承保规则复制
-            PtlAgreementUndwrtRules agreementUndwrtRules = rulesMap.get(key);
             PtlAgreementUndwrtRules newAgreementUndwrtRules = new PtlAgreementUndwrtRules();
             BeanUtils.copyProperties(agreementUndwrtRules,newAgreementUndwrtRules);
             newAgreementUndwrtRules.setId(null);
@@ -469,7 +505,10 @@ public class PtlAgreementServiceImpl extends ServiceImpl<PtlAgreementMapper, Ptl
             PtlAgreementUndwrtRules maxRules = agreementRulesService.getOne(undwrtRulesLambdaQueryWrapper);
             newAgreementUndwrtRules.setOrderNum(Objects.nonNull(maxRules) ? maxRules.getOrderNum() + 1 : 1);
             newAgreementUndwrtRules.setAgreementId(newAgreement.getId());
-            newAgreementUndwrtRules.setParentId(IdGenerate.nextId());
+            String sourceParentId = agreementUndwrtRules.getParentId();
+            newAgreementUndwrtRules.setParentId(sourceParentId != null && !sourceParentId.isEmpty()
+                    ? newParentIdMap.computeIfAbsent(sourceParentId, k -> IdGenerate.nextId())
+                    : IdGenerate.nextId());
             agreementRulesService.save(newAgreementUndwrtRules);
 
             // 复制承保规则因子
@@ -482,8 +521,8 @@ public class PtlAgreementServiceImpl extends ServiceImpl<PtlAgreementMapper, Ptl
                 agreementUndwrtRulesAttrLinkService.saveBatch(links);
             }
 
-            // 2-费用复制
-            List<PtlAgreementCost> agreementCostList = costGroup.get(key);
+            // 2-费用复制(该规则关联的所有费用,无费用关联的规则仅复制规则本身)
+            List<PtlAgreementCost> agreementCostList = allCostGroup.get(agreementUndwrtRules.getId());
             if(CollUtil.isNotEmpty(agreementCostList)) {
                 agreementCostList.forEach(agreementCost -> {
                     PtlAgreementCost newAgreementCost = new PtlAgreementCost();
@@ -495,21 +534,24 @@ public class PtlAgreementServiceImpl extends ServiceImpl<PtlAgreementMapper, Ptl
                     newAgreementCost.setPtlAgreementId(newAgreement.getId());
                     agreementCostService.save(newAgreementCost);
                     List<PtlAgreementCostRule> costRules = costRulesMap.get(agreementCost.getId());
-                    costRules.forEach(costRule -> {
-                        costRule.setId(null);
-                        costRule.setPtlAgreementCostId(newAgreementCost.getId());
-                    });
-                    agreementCostRuleService.saveBatch(costRules);
+                    if (CollUtil.isNotEmpty(costRules)) {
+                        costRules.forEach(costRule -> {
+                            costRule.setId(null);
+                            costRule.setPtlAgreementCostId(newAgreementCost.getId());
+                        });
+                        agreementCostRuleService.saveBatch(costRules);
+                    }
                     List<PtlAgreementCostType> costTypes = costTypeMap.get(agreementCost.getId());
-                    costTypes.forEach(costType -> {
-                        costType.setId(null);
-                        costType.setPtlAgreementCostId(newAgreementCost.getId());
-                    });
-                    agreementCostTypeService.saveBatch(costTypes);
-
+                    if (CollUtil.isNotEmpty(costTypes)) {
+                        costTypes.forEach(costType -> {
+                            costType.setId(null);
+                            costType.setPtlAgreementCostId(newAgreementCost.getId());
+                        });
+                        agreementCostTypeService.saveBatch(costTypes);
+                    }
                 });
             }
-        });
+        }
     }
 
     @Override

+ 44 - 128
tenant/organization/src/main/java/com/jzg/organization/service/impl/PtlAgreementUndwrtRulesServiceImpl.java

@@ -3,6 +3,7 @@ package com.jzg.organization.service.impl;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -16,6 +17,7 @@ import com.jzg.commons.entity.vo.CostListVo;
 import com.jzg.commons.exception.SystemException;
 import com.jzg.commons.util.StringUtils;
 import com.jzg.commons.util.idgen.IdGenerate;
+import com.jzg.organization.component.BuildDescUtils;
 import com.jzg.organization.mapper.PtlAgreementUndwrtRulesAttrLinkMapper;
 import com.jzg.organization.mapper.PtlAgreementUndwrtRulesMapper;
 import com.jzg.organization.service.*;
@@ -195,7 +197,7 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
 
                 // andAttrLinks 为空时不设置 ruleDescription,避免 updateById 覆盖已有数据
                 if (CollUtil.isNotEmpty(andAttrLinks)) {
-                    rules.setRuleDescription(buildDesc(andAttrLinks, undwrtRulesAttrList));
+                    rules.setRuleDescription(BuildDescUtils.buildDesc(andAttrLinks, undwrtRulesAttrList));
                 }
                 baseMapper.insert(rules);
             }
@@ -219,7 +221,7 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
             }
         });
         agreementUndwrtRulesAttrLinkMapper.insert(costsAttrLink);
-        rules.setRuleDescription(buildDesc(costsAttrLink, undwrtRulesAttrList));
+        rules.setRuleDescription(BuildDescUtils.buildDesc(costsAttrLink, undwrtRulesAttrList));
         baseMapper.insert(rules);
         baseMapper.updateById(rules);
         return true;
@@ -393,7 +395,7 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
                 int currentOrderNum = isExisting ? oldOrderNum : ++maxOrderNum;
                 // ruleDescription:有新的属性链接则重新生成,否则保留旧值
                 String ruleDescription = CollUtil.isNotEmpty(andAttrLinks)
-                        ? buildDesc(andAttrLinks, undwrtRulesAttrList)
+                        ? BuildDescUtils.buildDesc(andAttrLinks, undwrtRulesAttrList)
                         : oldRuleDescription;
                 // 相邻的","或","转换为"或"字
                 ruleDescription = StringUtils.convertAdjacentCommaToOr(ruleDescription);
@@ -475,7 +477,7 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
         mergeAttrLinks(oldLinks, costsAttrLink);
         rules.setStartTime(param.getStartTime());
         rules.setEndTime(param.getEndTime());
-        rules.setRuleDescription(buildDesc(costsAttrLink, attrList));
+        rules.setRuleDescription(BuildDescUtils.buildDesc(costsAttrLink, attrList));
         baseMapper.updateById(rules);
         return true;
     }
@@ -630,6 +632,15 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
         String newParentId = IdGenerate.nextId();
         List<RulesGroupParam> groups = new ArrayList<>();
 
+        // LicenseNo/LicenseArea 属性的字典选项来自 ptl_area_license 表,复制时用于回填 link 的 dictLabal
+        List<PtlAreaLicense> areaLicenses = ptlAreaLicenseService.queryAllAreaLicense();
+        JSONArray licenseDictLabal = CollUtil.isEmpty(areaLicenses) ? null : new JSONArray(areaLicenses.stream()
+                .map(area -> buildAreaDictLabal(area.getLicense(), area.getAreaNumber()))
+                .collect(Collectors.toList()));
+        JSONArray areaNameDictLabal = CollUtil.isEmpty(areaLicenses) ? null : new JSONArray(areaLicenses.stream()
+                .map(area -> buildAreaDictLabal(area.getAreaName(), area.getAreaNumber()))
+                .collect(Collectors.toList()));
+
         // 获取属性映射(用于组装返回结果)
         PtlAgreement agreement = ptlAgreementService.getById(rules.getAgreementId());
         Map<String, PtlAgreementUndwrtRulesAttr> attrMap = undwrtRulesAttrList.stream()
@@ -654,11 +665,30 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
 
             // 复制属性链接,关联到新规则ID
             List<PtlAgreementUndwrtRulesAttrLink> newAttrLinks = processAndAttrs(sourceLinkList, undwrtRulesAttrList, newRuleId);
+            // dictLabal 为非持久化字段,queryLinkList 查询不会加载;复制后回填,避免返回数据丢失字典标签
+            if (CollUtil.isNotEmpty(newAttrLinks)) {
+                // 普通属性:从属性元数据回填
+                Map<String, List<PtlAgreementUndwrtRulesDictLabal>> dictLabalMap = undwrtRulesAttrList.stream()
+                        .filter(attr -> CollUtil.isNotEmpty(attr.getDictLabal()))
+                        .collect(Collectors.toMap(PtlAgreementUndwrtRulesAttr::getAttrCode, PtlAgreementUndwrtRulesAttr::getDictLabal, (oldVal, newVal) -> oldVal));
+                newAttrLinks.forEach(link -> {
+                    if ("LicenseNo".equals(link.getAttrCode()) && licenseDictLabal != null) {
+                        link.setDictLabal(licenseDictLabal);
+                    } else if ("LicenseArea".equals(link.getAttrCode()) && areaNameDictLabal != null) {
+                        link.setDictLabal(areaNameDictLabal);
+                    } else {
+                        List<PtlAgreementUndwrtRulesDictLabal> dictLabal = dictLabalMap.get(link.getAttrCode());
+                        if (CollUtil.isNotEmpty(dictLabal)) {
+                            link.setDictLabal(new JSONArray(dictLabal));
+                        }
+                    }
+                });
+            }
             if (CollUtil.isNotEmpty(newAttrLinks)) {
                 agreementUndwrtRulesAttrLinkMapper.insert(newAttrLinks);
             }
 
-            newRules.setRuleDescription(buildDesc(newAttrLinks, undwrtRulesAttrList));
+            newRules.setRuleDescription(BuildDescUtils.buildDesc(newAttrLinks, undwrtRulesAttrList));
             baseMapper.insert(newRules);
 
             // 组装返回的 groups
@@ -683,8 +713,15 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
         return rulesVo;
     }
 
-
-
+    /**
+     * 构建地区字典标签:code 为地区编码,name 为展示名(车牌前缀或地区名称)
+     */
+    private PtlAgreementUndwrtRulesDictLabal buildAreaDictLabal(String name, String areaNumber) {
+        PtlAgreementUndwrtRulesDictLabal dictLabal = new PtlAgreementUndwrtRulesDictLabal();
+        dictLabal.setName(name);
+        dictLabal.setCode(areaNumber);
+        return dictLabal;
+    }
 
     /**
      * 将属性链接丰富为完整的属性对象
@@ -755,127 +792,6 @@ public class PtlAgreementUndwrtRulesServiceImpl extends ServiceImpl<PtlAgreement
         // 批量删除规则
         return baseMapper.deleteBatchIds(deleteRuleIds) > 0;
     }
-
-    /**
-     * 遍历费用属性描述文字
-     *
-     * @param linkList
-     * @param undwrtRulesAttrList
-     * @return
-     */
-    private String buildDesc(List<PtlAgreementUndwrtRulesAttrLink> linkList, List<PtlAgreementUndwrtRulesAttr> undwrtRulesAttrList) {
-        StringBuilder desc = new StringBuilder();
-        for (int i = 0; i < linkList.size(); i++) {
-            int finalI = i;
-            PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr = undwrtRulesAttrList.stream()
-                    .filter(x -> x.getAttrCode().equals(linkList.get(finalI).getAttrCode()))
-                    .findAny()
-                    .get();
-
-            //数值范围判断
-
-            if ("inputNumber".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
-                desc.append(inputNumberIdentifying(ptlAgreementUndwrtRulesAttr, linkList.get(i))).append(",");
-            }
-            if ("radio".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
-                desc.append(radioIdentifying(ptlAgreementUndwrtRulesAttr, linkList.get(i))).append(",");
-            }
-            if ("select".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
-                desc.append(selectIdentifying(ptlAgreementUndwrtRulesAttr, linkList.get(i)));
-            }
-            if ("checkbox".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
-                desc.append(checkBoxIdentifying(ptlAgreementUndwrtRulesAttr,linkList.get(i)));
-            }
-            if ("inputTag".equals(ptlAgreementUndwrtRulesAttr.getAttrType())) {
-                if (linkList.get(i).getOperator() != null && linkList.get(i).getOperator().equals("1")) {
-                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("包含").append(linkList.get(i).getMin()).append(",");
-                } else {
-                    desc.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("不包含").append(linkList.get(i).getMin()).append(",");
-                }
-            }
-        }
-        // 相邻的","或","转换为"或"字
-        return StringUtils.convertAdjacentCommaToOr(desc.toString());
-    }
-
-    private String radioIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink ptlAgreementProductCostsAttrLink) {
-        PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = ptlAgreementUndwrtRulesAttr.getDictLabal().stream().filter(x -> x.getCode().equals(ptlAgreementProductCostsAttrLink.getMin())).findAny().get();
-        return ptlAgreementUndwrtRulesAttr.getAttrName() + "为" + ptlAgreementUndwrtRulesDictLabal.getName();
-    }
-
-    private String selectIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink ptlAgreementProductCostsAttrLink) {
-        StringBuilder returnMsg = new StringBuilder();
-        returnMsg.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("为");
-        // 兼容 min 逗号分隔存储,保证 minArray 有值
-        if (ptlAgreementProductCostsAttrLink.getMinArray() == null) {
-            String minStr = ptlAgreementProductCostsAttrLink.getMin();
-            if (minStr != null && !minStr.isEmpty()) {
-                ptlAgreementProductCostsAttrLink.setMinArray(minStr.split(","));
-            }
-        }
-        // 优先取 link 上的字典标签列表(JSONArray -> List),为空时回退到属性自带字典
-        List<PtlAgreementUndwrtRulesDictLabal> dictLabalList = new ArrayList<>();
-        if (ptlAgreementProductCostsAttrLink.getDictLabal() != null && !ptlAgreementProductCostsAttrLink.getDictLabal().isEmpty()) {
-            dictLabalList = ptlAgreementProductCostsAttrLink.getDictLabal().toJavaList(PtlAgreementUndwrtRulesDictLabal.class);
-        } else if (ptlAgreementUndwrtRulesAttr.getDictLabal() != null) {
-            dictLabalList = ptlAgreementUndwrtRulesAttr.getDictLabal();
-        }
-        // 用字典标签的 code 匹配 minArray 中的每一项
-        if (ptlAgreementProductCostsAttrLink.getMinArray() != null) {
-            for (String item : ptlAgreementProductCostsAttrLink.getMinArray()) {
-                PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = dictLabalList.stream()
-                        .filter(x -> x.getCode() != null && x.getCode().equals(item)).findAny().orElse(null);
-                if (ptlAgreementUndwrtRulesDictLabal != null) {
-                    returnMsg.append(ptlAgreementUndwrtRulesDictLabal.getName()).append(",");
-                }
-            }
-        }
-        return returnMsg.toString();
-    }
-
-    private String checkBoxIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink linkList) {
-        StringBuilder returnMsg = new StringBuilder();
-        returnMsg.append(ptlAgreementUndwrtRulesAttr.getAttrName()).append("为");
-        if (linkList.getMinArray() == null) {
-            String minStr = linkList.getMin();
-            if (minStr != null && minStr.length() > 0) {
-                String[] minArray = minStr.split(",");
-                linkList.setMinArray(minArray);
-            }
-        }
-        if (linkList.getMinArray() != null) {
-            StringBuilder licenseInfo = new StringBuilder();
-            for(int i=0;i<linkList.getMinArray().length;i++) {
-                int finalI = i;
-                if(ptlAgreementUndwrtRulesAttr.getAttrCode().equals("LicenseNo") || ptlAgreementUndwrtRulesAttr.getAttrCode().equals("LicenseArea")) {
-                    licenseInfo.append(linkList.getMinArray()[i]).append(",");
-                }else {
-                    PtlAgreementUndwrtRulesDictLabal ptlAgreementUndwrtRulesDictLabal = ptlAgreementUndwrtRulesAttr.getDictLabal().stream().filter(x -> x.getCode().equals(linkList.getMinArray()[finalI])).findAny().get();
-                    returnMsg.append(ptlAgreementUndwrtRulesDictLabal.getName()).append(",");
-                }
-            }
-            if(StrUtil.isNotEmpty(licenseInfo.toString())){
-                String licenseName = ptlAreaLicenseService.convertAreaNumber2License(licenseInfo.toString());
-                returnMsg.append(licenseName).append(",");
-            }
-        }
-        return returnMsg.toString();
-    }
-
-    private String inputNumberIdentifying(PtlAgreementUndwrtRulesAttr ptlAgreementUndwrtRulesAttr, PtlAgreementUndwrtRulesAttrLink ptlAgreementProductCostsAttrLink) {
-        if ("1".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
-            return ptlAgreementUndwrtRulesAttr.getAttrName() + "范围值" + ptlAgreementProductCostsAttrLink.getMin() + "到" + ptlAgreementProductCostsAttrLink.getMax();
-        } else if ("2".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
-            return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于" + ptlAgreementProductCostsAttrLink.getMin();
-        } else if ("3".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
-            return ptlAgreementUndwrtRulesAttr.getAttrName() + "小于等于" + ptlAgreementProductCostsAttrLink.getMin();
-        } else if ("4".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
-            return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于" + ptlAgreementProductCostsAttrLink.getMax();
-        } else if ("5".equals(ptlAgreementProductCostsAttrLink.getOperator())) {
-            return ptlAgreementUndwrtRulesAttr.getAttrName() + "大于等于" + ptlAgreementProductCostsAttrLink.getMax();
-        }
-        return "";
-    }
 }
 
 

+ 140 - 144
tenant/organization/src/main/java/com/jzg/organization/service/impl/ReceivableServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.lang.tree.Tree;
 import cn.hutool.core.lang.tree.TreeNodeConfig;
 import cn.hutool.core.lang.tree.TreeUtil;
@@ -30,6 +31,7 @@ import com.jzg.commons.entity.finance.po.*;
 import com.jzg.commons.entity.finance.vo.*;
 import com.jzg.commons.entity.finance.vo.importimcome.*;
 import com.jzg.commons.entity.orders.po.InsOrderAdjust;
+import com.jzg.commons.entity.orders.po.InsOrdersPolicy;
 import com.jzg.commons.entity.po.EsmInsCompany;
 import com.jzg.commons.entity.po.InsFeeOrders;
 import com.jzg.commons.entity.po.PtlAgreement;
@@ -53,6 +55,7 @@ import com.jzg.organization.mapper.*;
 import com.jzg.organization.service.ReceivableService;
 import com.jzg.organization.service.SysUserJzgInfoSalesmanService;
 import com.jzg.organization.service.importincome.ImportIncomeService;
+import com.jzg.organization.utils.IncomeInvoiceLinkUtil;
 import org.apache.ibatis.annotations.Param;
 import org.jetbrains.annotations.NotNull;
 import org.redisson.api.RBucket;
@@ -868,7 +871,7 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                             || invoiceType.equals(income.getInvoiceType()))
                     .collect(Collectors.toCollection(ArrayList::new));
             List<InsPlyIncomeDto> copyList = CollUtil.newArrayList();
-            if (CollUtil.isNotEmpty(incomeDtoList) && InvoiceTypeEnum.FEE_2.getCode().equals(invoiceType)){
+            if (CollUtil.isNotEmpty(incomeDtoList) && (StrUtil.isBlank(invoiceType) || InvoiceTypeEnum.FEE_2.getCode().equals(invoiceType))){
                 // 如果有发票类型为4的,则复制一条4的数据,完全一致,但发票类型为2
                 incomeDtoList.stream().filter(item -> InvoiceTypeEnum.FEE_4.getCode().equals(item.getInvoiceType())).forEach(item -> {
                     InsPlyIncomeDto incomeDto = new InsPlyIncomeDto();
@@ -899,11 +902,14 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
             return resultList;
         }
 
-        // 如果status = 0或1,则直接返回空的filterIncomeList,如果status = 2,则查询所有ins_ply_follow_invoice_link表,过滤掉disableFlag为1 的,保留为0 的数据
-        List<InsPlyIncomeDto> filterIncomeList = insPlyIncomes.stream()
-                .filter(income -> "0".equals(income.getDisableFlag()))
-                .map(income -> BeanUtil.copyProperties(income, InsPlyIncomeDto.class))
-                .collect(Collectors.toList());
+        List<InsPlyIncomeDto> filterIncomeList = BeanUtil.copyToList(insPlyIncomes, InsPlyIncomeDto.class);
+        if ("2".equals(status)){
+            // 如果status = 0或1,则直接返回空的filterIncomeList,如果status = 2,则查询所有ins_ply_follow_invoice_link表,过滤掉disableFlag为1 的,保留为0 的数据
+            filterIncomeList = insPlyIncomes.stream()
+                    .filter(income -> "0".equals(income.getDisableFlag()))
+                    .map(income -> BeanUtil.copyProperties(income, InsPlyIncomeDto.class))
+                    .collect(Collectors.toList());
+        }
 
         // 5. 将其他费用收入合并进结果集合
         mergeOtherCostIncomeToResult(filterIncomeList, resultList, companyMap);
@@ -1138,93 +1144,6 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         return baseMapper.queryFollowIncome(incomeVo);
     }
 
-    /**
-     * 根据发票状态、开票时间、结算时间过滤收入数据
-     * 优化:嵌套anyMatch改为HashMapID映射匹配,O(n²) -> O(n)
-     */
-    private List<InsPlyIncomeDto>  filterIncomeByInvoiceAndSettleCondition(List<InsPlyIncomeDto> incomeList, SettlementReportQueryVo queryVo) {
-        log.info("对查询条件进行转义:queryVo=[{}]", JSONUtil.toJsonStr(queryVo));
-        String status = queryVo.getStatus();
-        String invoiceStartTime = queryVo.getInvoiceStartTime();
-        String invoiceEndTime = queryVo.getInvoiceEndTime();
-        String settleStartTime = queryVo.getSettlementStartTime();
-        String settleEndTime = queryVo.getSettlementEndTime();
-
-        // 无筛选条件,直接原数据返回
-        boolean noFilterCond = StrUtil.isBlank(status)
-                && (StrUtil.isBlank(invoiceStartTime) || StrUtil.isBlank(invoiceEndTime))
-                && (StrUtil.isBlank(settleStartTime) || StrUtil.isBlank(settleEndTime));
-        if (noFilterCond || CollUtil.isEmpty(incomeList)) {
-            return incomeList;
-        }
-
-        // 查询收入关联链路
-        List<String> incomeIds = incomeList.stream().map(InsPlyIncomeDto::getId).distinct().toList();
-        LambdaQueryWrapper<InsPlyIncomeInvoiceLink> linkWrapper = new LambdaQueryWrapper<>();
-        linkWrapper.in(InsPlyIncomeInvoiceLink::getIncomeId, incomeIds);
-        List<InsPlyIncomeInvoiceLink> linkList = insPlyIncomeInvoiceLinkMapper.selectList(linkWrapper);
-        if (CollUtil.isEmpty(linkList)) {
-            return new ArrayList<>(0);
-        }
-
-        // 链路ID映射
-        Map<String, InsPlyIncomeInvoiceLink> linkInvoiceIdMap = linkList.stream()
-                .collect(Collectors.toMap(InsPlyIncomeInvoiceLink::getInvoiceId, Function.identity(), (k1, k2) -> k1));
-        Set<String> linkInvoiceIds = linkInvoiceIdMap.keySet();
-
-        // 查询发票
-        LambdaQueryWrapper<InsPlyIncomeInvoice> invoiceWrapper = new LambdaQueryWrapper<>();
-        invoiceWrapper.in(InsPlyIncomeInvoice::getId, linkInvoiceIds);
-        if (StrUtil.isNotBlank(status)) {
-            invoiceWrapper.eq(InsPlyIncomeInvoice::getStatus, status);
-        }
-        boolean invoiceTimeCond = StrUtil.isNotBlank(invoiceStartTime) && StrUtil.isNotBlank(invoiceEndTime);
-        if (invoiceTimeCond) {
-            invoiceWrapper.between(InsPlyIncomeInvoice::getInvoiceTime, toStartDateTime(invoiceStartTime), toEndDateTime(invoiceEndTime));
-        }
-        invoiceWrapper.eq(InsPlyIncomeInvoice::getInvoiceType, queryVo.getInvoiceType());
-        List<InsPlyIncomeInvoice> invoiceList = insPlyIncomeInvoiceMapper.selectList(invoiceWrapper);
-        if ((StrUtil.isNotBlank(status) || invoiceTimeCond) && CollUtil.isEmpty(invoiceList)) {
-            return new ArrayList<>(0);
-        }
-
-        // 结算时间过滤
-        boolean settleTimeCond = StrUtil.isNotBlank(settleStartTime) && StrUtil.isNotBlank(settleEndTime);
-        if (settleTimeCond) {
-            Set<String> invoiceIdSet = invoiceList.stream().map(InsPlyIncomeInvoice::getId).collect(Collectors.toSet());
-            LambdaQueryWrapper<InsPlyIncomeInvoiceSettlement> settleWrapper = new LambdaQueryWrapper<>();
-            settleWrapper.in(InsPlyIncomeInvoiceSettlement::getInvoiceId, invoiceIdSet);
-            settleWrapper.between(InsPlyIncomeInvoiceSettlement::getCreateTime, toStartDateTime(settleStartTime), toEndDateTime(settleEndTime));
-            List<InsPlyIncomeInvoiceSettlement> settleList = insPlyIncomeInvoiceSettlementMapper.selectList(settleWrapper);
-            if (CollUtil.isEmpty(settleList)) {
-                return new ArrayList<>(0);
-            }
-            // 保留存在结算记录的发票
-            Set<String> validSettleInvoiceIds = settleList.stream()
-                    .map(InsPlyIncomeInvoiceSettlement::getInvoiceId)
-                    .collect(Collectors.toSet());
-            invoiceList = invoiceList.stream()
-                    .filter(invoice -> validSettleInvoiceIds.contains(invoice.getId()))
-                    .toList();
-        }
-
-        // 合法发票ID集合
-        Set<String> validInvoiceIds = invoiceList.stream()
-                .map(InsPlyIncomeInvoice::getId)
-                .collect(Collectors.toSet());
-        // 过滤合法链路
-        List<InsPlyIncomeInvoiceLink> validLinkList = linkList.stream()
-                .filter(link -> validInvoiceIds.contains(link.getInvoiceId()))
-                .toList();
-        Set<String> validIncomeIds = validLinkList.stream()
-                .map(InsPlyIncomeInvoiceLink::getIncomeId)
-                .collect(Collectors.toSet());
-
-        // 最终过滤收入数据
-        return incomeList.stream()
-                .filter(income -> validIncomeIds.contains(income.getId()))
-                .toList();
-    }
 
     /**
      * 将其他费用收入合并至结果集,不存在则新增VO
@@ -2637,14 +2556,73 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                 errrMsg.append("该批数据的保险机构为空. ");
             }
         }
+        errrMsg.append(this.validatePolicy(datas));
+        errrMsg.append(this.validateProportion(datas));
+        return errrMsg.toString();
+    }
 
-
+    /**
+     * 校验保单号是否一致
+     *
+     * @author lipf
+     * @date 2026/8/20 10:35
+     */
+    private String validatePolicy(List<ImportContentExt> datas) {
+        StringBuilder errrMsg = new StringBuilder();
+        for (ImportContentExt data : datas) {
+            boolean all = false;
+            boolean jq = false;
+            boolean sy = false;
+            boolean jy = false;
+            InsOrdersPolicy insOrdersPolicy = data.getInsOrdersPolicy();
+            if(insOrdersPolicy == null){
+                continue;
+            }
+            if(!Objects.equals(insOrdersPolicy.getJqPolicyNo(), data.getJqPolicyNo())){
+                all = true;
+                jq = true;
+            }
+            if(!Objects.equals(insOrdersPolicy.getSyPolicyNo(), data.getSyPolicyNo())){
+                all = true;
+                sy = true;
+            }
+            if(!Objects.equals(insOrdersPolicy.getJyPolicyNo(), data.getJyPolicyNo())){
+                all = true;
+                jy = true;
+            }
+            if(all){
+                errrMsg.append("车牌号为[").append(data.getLicenseNo()).append("]的保单号不一致: ");
+                if(jq){
+                    errrMsg.append("交强险保单号不一致. 应收数据的交强险保单号是[").append(insOrdersPolicy.getJqPolicyNo()).append("].excel维护的交强险保单号是[").append(data.getJqPolicyNo()).append("].");
+                }
+                if(sy){
+                    errrMsg.append("商业险保单号不一致. 应收数据的商业险保单号是[").append(insOrdersPolicy.getSyPolicyNo()).append("].excel维护的商业险保单号是[").append(data.getSyPolicyNo()).append("].");
+                }
+                if(jy){
+                    errrMsg.append("驾意险保单号不一致. 应收数据的驾意险保单号是[").append(insOrdersPolicy.getJyPolicyNo()).append("].excel维护的驾意险保单号是[").append(data.getJyPolicyNo()).append("].");
+                }
+            }
+        }
+        return errrMsg.toString();
+    }
+    /**
+     * 校验应收比例
+     *
+     *
+     * @author lipf
+     * @date 2026/8/20 10:27
+     */
+    private static String validateProportion(List<ImportContentExt> datas) {
+        StringBuilder errrMsg = new StringBuilder();
         for (ImportContentExt data : datas) {
             boolean all = false;
             boolean jq = false;
             boolean sy = false;
             boolean jy = false;
             InsPlyIncome insPlyIncome = data.getInsPlyIncome();
+            if(insPlyIncome == null){
+                continue;
+            }
             String jqadd = BigDecimalUtil.add(insPlyIncome.getJqSuperviseCostsProportion(), insPlyIncome.getJqOtherCostsProportion());
             BigDecimal jqsubtract = BigDecimalUtil.subtract(new BigDecimal(jqadd), new BigDecimal(data.getJqInletRatio()));
             if(BigDecimal.ZERO.compareTo(jqsubtract) != 0){
@@ -5414,11 +5392,42 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
 
         List<InsPlyIncome> receivableList = baseMapper.getReceivableList(receivableQueryVo);
         receivableList = this.fillOtherInfo(receivableList, null);
+        // 类型转换
+        List<ExportPlatFormReceivableVO> exportPlatFormReceivableVOS = BeanUtil.copyToList(receivableList, ExportPlatFormReceivableVO.class);
+        // 用ins_ply_income的id关联查到ins_ply_income_invoice_link再关联到ins_ply_income_invoice_settlement,取出结算日期、结算人
+        if (CollUtil.isNotEmpty(exportPlatFormReceivableVOS)) {
+            //1.拿到所有incomeId集合
+            List<String> incomeIdList = exportPlatFormReceivableVOS.stream()
+                    .map(ExportPlatFormReceivableVO::getId)
+                    .collect(Collectors.toList());
+
+            Map<String, InsPlyIncomeInvoiceSettlement> settlementMap = IncomeInvoiceLinkUtil.querySettlementByIncomeList(incomeIdList, insPlyIncomeInvoiceLinkMapper, insPlyIncomeInvoiceSettlementMapper);
+            Map<String, List<InsPlyIncomeInvoiceLink>> linkGroupByIncomeId = IncomeInvoiceLinkUtil.queryInvoiceLinkByIncomeId(incomeIdList, insPlyIncomeInvoiceLinkMapper);
+
+            //回填vo
+            for (ExportPlatFormReceivableVO vo : exportPlatFormReceivableVOS) {
+                List<InsPlyIncomeInvoiceLink> linkList = linkGroupByIncomeId.getOrDefault(vo.getId(), CollUtil.newArrayList());
+                if (CollUtil.isEmpty(linkList)) {
+                    continue;
+                }
+                // 取第一条有结算数据的link,可按业务修改:比如取最新一条
+                for (InsPlyIncomeInvoiceLink link : linkList) {
+                    InsPlyIncomeInvoiceSettlement settlement = settlementMap.get(link.getInvoiceId());
+                    if (settlement != null) {
+                        //结算日期
+                        vo.setSettlementTime(LocalDateTimeUtil.of(settlement.getReceivePaymentDate()));
+                        //结算人:创建人即为结算操作人
+                        vo.setSettlementPerson(settlement.getCreateBy());
+                        break;
+                    }
+                }
+            }
+        }
 
         // 调用动态导出
         return exportBaseService.upLoadFile(
-                InsPlyIncome.class,
-                receivableList,
+                ExportPlatFormReceivableVO.class,
+                exportPlatFormReceivableVOS,
                 "平台协议应收-批量导入-导出-" + Instant.now()
         );
     }
@@ -5887,8 +5896,37 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                 })
                 .toList();
         // 过滤数据
-        List<InsPlyIncomeDto> filterIncomeList = filterIncomeByInvoiceAndSettleCondition(dtoList, queryVo);
-        allCompaniesIncomeDatas = allCompaniesIncomeDatas.stream().filter(all -> filterIncomeList.stream().anyMatch(x -> x.getId().equals(all.getId()))).toList();
+        List<InsPlyIncomeDto> filterIncomeList = IncomeInvoiceLinkUtil.filterIncomeByInvoiceAndSettleCondition(
+                dtoList,
+                queryVo,
+                insPlyIncomeInvoiceLinkMapper,
+                insPlyIncomeInvoiceMapper,
+                insPlyIncomeInvoiceSettlementMapper
+        );
+        Map<String, List<String>> invoiceIdMap = IncomeInvoiceLinkUtil.queryIncomeToInvoiceIdMap(filterIncomeList.stream().map(InsPlyIncomeDto::getId).toList(), insPlyIncomeInvoiceLinkMapper);
+
+        // 给每个dto赋值逗号拼接的InvoiceIds
+        for (InsPlyIncomeDto incomeDto : filterIncomeList) {
+            String incomeId = incomeDto.getId();
+            List<String> invoiceIdList = invoiceIdMap.getOrDefault(incomeId, CollUtil.newArrayList());
+            if (CollUtil.isNotEmpty(invoiceIdList)) {
+                // 逗号拼接多个发票id
+                String invoiceIdsStr = String.join(",", invoiceIdList);
+                incomeDto.setInvoiceIds(invoiceIdsStr);
+            } else {
+                // 无关联发票,置空
+                incomeDto.setInvoiceIds(null);
+            }
+        }
+
+        // 根据结算状态过滤:0-已开票未结清 1-已结清 2-未开票
+        List<InsPlyIncomeDto> insPlyIncomeDtos = IncomeInvoiceLinkUtil.filterBySettlementStatus(filterIncomeList,
+                queryVo.getSettlementStatus(),
+                InsPlyIncomeDto::getInvoiceIds,
+                baseMapper);
+
+
+        allCompaniesIncomeDatas = allCompaniesIncomeDatas.stream().filter(all -> insPlyIncomeDtos.stream().anyMatch(x -> x.getId().equals(all.getId()))).toList();
 
         // 按公司ID分组聚合求和
         Map<String, List<CompanySuperviseSettlementReportDto>> groupByCompany = allCompaniesIncomeDatas.stream()
@@ -6385,31 +6423,10 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
                 }
             }
             // 根据结算状态过滤:0-已开票未结清 1-已结清 2-未开票
-            if(StrUtil.isNotEmpty(settlementReportQueryVo.getSettlementStatus())){
-                String settlementStatus = settlementReportQueryVo.getSettlementStatus();
-                if("2".equals(settlementStatus)){
-                    // 未开票:ins_ply_income_invoice没有数据
-                    records = records.stream().filter(action -> StrUtil.isEmpty(action.getInvoiceIds())).toList();
-                } else if("0".equals(settlementStatus)){
-                    // 已开票未结清:有发票数据,且存在status=0的发票
-                    records = records.stream().filter(action -> {
-                        if(StrUtil.isEmpty(action.getInvoiceIds())){
-                            return false;
-                        }
-                        List<String> invoiceIdList = Arrays.asList(action.getInvoiceIds().split(","));
-                        return baseMapper.countUnsettledInvoicesByInvoiceIds(invoiceIdList) > 0;
-                    }).toList();
-                } else if("1".equals(settlementStatus)){
-                    // 已结清:有发票数据,且所有发票status=1
-                    records = records.stream().filter(action -> {
-                        if(StrUtil.isEmpty(action.getInvoiceIds())){
-                            return false;
-                        }
-                        List<String> invoiceIdList = Arrays.asList(action.getInvoiceIds().split(","));
-                        return baseMapper.countUnsettledInvoicesByInvoiceIds(invoiceIdList) == 0;
-                    }).toList();
-                }
-            }
+            records = IncomeInvoiceLinkUtil.filterBySettlementStatus(records,
+                    settlementReportQueryVo.getSettlementStatus(),
+                    ReceiverSettlementReportDto::getInvoiceIds,
+                    baseMapper);
             receiverSettlementReport.setRecords(records);
         }
         return receiverSettlementReport;
@@ -6763,27 +6780,6 @@ public class ReceivableServiceImpl extends ServiceImpl<ReceivableMapper, InsPlyI
         }
     }
 
-    /**
-     * yyyy-MM-dd 转当天起始 LocalDateTime
-     */
-    public static LocalDateTime toStartDateTime(String dateStr) {
-        if (StrUtil.isBlank(dateStr)){
-            return null;
-        }
-        LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE);
-        return date.atStartOfDay();
-    }
-
-    /**
-     * yyyy-MM-dd 转当天结束 LocalDateTime
-     */
-    public static LocalDateTime toEndDateTime(String dateStr) {
-        if (StrUtil.isBlank(dateStr)){
-            return null;
-        }
-        LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE);
-        return date.atTime(23, 59, 59, 999999999);
-    }
 
     // 递归获取所有子孙ID
     private List<String> getAllChildIdsOptimized(String parentId, Map<String, List<EsmInsCompany>> parentGroupMap) {

+ 3 - 0
tenant/organization/src/main/java/com/jzg/organization/service/impl/importincome/ImportIncomeServiceImpl.java

@@ -123,6 +123,7 @@ public class ImportIncomeServiceImpl implements ImportIncomeService {
                 queryWrapper.eq(InsOrdersPolicy::getJqPolicyNo,excelContent.getJqPolicyNo());
                 List<InsOrdersPolicy> insOrdersPolicies = insOrdersPolicyMapper.selectList(queryWrapper);
                 if(CollUtil.isNotEmpty(insOrdersPolicies)){
+                    ext.setInsOrdersPolicy(insOrdersPolicies.get(0));
                     orderNO  = insOrdersPolicies.get(0).getOrderNo();
                 }
             }
@@ -131,6 +132,7 @@ public class ImportIncomeServiceImpl implements ImportIncomeService {
                 queryWrapper.eq(InsOrdersPolicy::getSyPolicyNo,excelContent.getSyPolicyNo());
                 List<InsOrdersPolicy> insOrdersPolicies = insOrdersPolicyMapper.selectList(queryWrapper);
                 if(CollUtil.isNotEmpty(insOrdersPolicies)){
+                    ext.setInsOrdersPolicy(insOrdersPolicies.get(0));
                     orderNO  = insOrdersPolicies.get(0).getOrderNo();
                 }
             }
@@ -139,6 +141,7 @@ public class ImportIncomeServiceImpl implements ImportIncomeService {
                 queryWrapper.eq(InsOrdersPolicy::getJyPolicyNo,excelContent.getJyPolicyNo());
                 List<InsOrdersPolicy> insOrdersPolicies = insOrdersPolicyMapper.selectList(queryWrapper);
                 if(CollUtil.isNotEmpty(insOrdersPolicies)){
+                    ext.setInsOrdersPolicy(insOrdersPolicies.get(0));
                     orderNO  = insOrdersPolicies.get(0).getOrderNo();
                 }
             }

+ 315 - 0
tenant/organization/src/main/java/com/jzg/organization/utils/IncomeInvoiceLinkUtil.java

@@ -0,0 +1,315 @@
+package com.jzg.organization.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.jzg.commons.entity.finance.dto.InsPlyIncomeDto;
+import com.jzg.commons.entity.finance.po.InsPlyIncomeInvoice;
+import com.jzg.commons.entity.finance.po.InsPlyIncomeInvoiceLink;
+import com.jzg.commons.entity.finance.po.InsPlyIncomeInvoiceSettlement;
+import com.jzg.commons.entity.finance.vo.SettlementReportQueryVo;
+import com.jzg.organization.mapper.InsPlyIncomeInvoiceLinkMapper;
+import com.jzg.organization.mapper.InsPlyIncomeInvoiceMapper;
+import com.jzg.organization.mapper.InsPlyIncomeInvoiceSettlementMapper;
+import com.jzg.organization.mapper.ReceivableMapper;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class IncomeInvoiceLinkUtil {
+
+    private IncomeInvoiceLinkUtil() {
+    }
+
+    /**
+     * 根据incomeId集合 查询应收开票结算的数据
+     * @param incomeIdList 应收主表id集合
+     * @param linkMapper link表mapper
+     * @param settlementMapper settlement表mapper
+     * @return key: invoiceId, value: settlement结算记录,旧值会被新值替换,按创建时间倒序
+     */
+    public static Map<String, InsPlyIncomeInvoiceSettlement> querySettlementByIncomeList(List<String> incomeIdList,
+                                                                          InsPlyIncomeInvoiceLinkMapper linkMapper,
+                                                                          InsPlyIncomeInvoiceSettlementMapper settlementMapper) {
+        if (CollUtil.isEmpty(incomeIdList)) {
+            return MapUtil.empty();
+        }
+
+        List<InsPlyIncomeInvoiceLink> invoiceLinkList = getInvoiceLinkList(incomeIdList, linkMapper);
+        if (CollUtil.isEmpty(invoiceLinkList)) {
+            return MapUtil.empty();
+        }
+
+        // 2、拿到所有invoiceId
+        List<String> invoiceIdList = invoiceLinkList.stream()
+                .map(InsPlyIncomeInvoiceLink::getInvoiceId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        // 3、key: invoiceId, value: settlement结算记录
+        Map<String, InsPlyIncomeInvoiceSettlement> settlementMap = new HashMap<>();
+        if (CollUtil.isNotEmpty(invoiceIdList)) {
+            LambdaQueryWrapper<InsPlyIncomeInvoiceSettlement> settlementWrapper = new LambdaQueryWrapper<>();
+            settlementWrapper.in(InsPlyIncomeInvoiceSettlement::getInvoiceId, invoiceIdList);
+            settlementWrapper.eq(InsPlyIncomeInvoiceSettlement::getIsDelete, 0);
+            List<InsPlyIncomeInvoiceSettlement> settlementList = settlementMapper.selectList(settlementWrapper);
+            // 按创建时间倒序
+            settlementList.sort((a,b)->b.getCreateTime().compareTo(a.getCreateTime()));
+            settlementMap = settlementList.stream()
+                    .collect(Collectors.toMap(InsPlyIncomeInvoiceSettlement::getInvoiceId, s -> s, (exist, newVal) -> newVal));
+        }
+
+        return settlementMap;
+    }
+
+
+    /**
+     * 根据应收id集合,获取 key:incomeId, value:对应多个invoiceId集合 的Map
+     * @param incomeIdList 应收主表id集合
+     * @param linkMapper link中间表mapper
+     * @return key:incomeId,value:该应收关联的发票id集合,无数据返回空Map,内部list不会为null
+     */
+    public static Map<String, List<String>> queryIncomeToInvoiceIdMap(List<String> incomeIdList,
+                                                                      InsPlyIncomeInvoiceLinkMapper linkMapper) {
+        if (CollUtil.isEmpty(incomeIdList)) {
+            return MapUtil.empty();
+        }
+        List<InsPlyIncomeInvoiceLink> invoiceLinkList = getInvoiceLinkList(incomeIdList, linkMapper);
+        if (CollUtil.isEmpty(invoiceLinkList)) {
+            return MapUtil.empty();
+        }
+
+        // 按incomeId分组,提取每个下的invoiceId,过滤null并去重
+        return invoiceLinkList.stream()
+                .collect(Collectors.groupingBy(
+                        InsPlyIncomeInvoiceLink::getIncomeId,
+                        Collectors.mapping(InsPlyIncomeInvoiceLink::getInvoiceId,
+                                Collectors.filtering(Objects::nonNull,
+                                        Collectors.toList()))
+                ));
+    }
+
+
+    /**
+     * 根据incomeId获取invoiceLink的Map分组
+     * @param incomeIdList incomeId的集合
+     * @param linkMapper ins_ply_income_invoice_link中间表的数据
+     * @return 按incomeId分组,一个income可能多条link
+     */
+    public static Map<String, List<InsPlyIncomeInvoiceLink>> queryInvoiceLinkByIncomeId(List<String> incomeIdList,
+                                                                                   InsPlyIncomeInvoiceLinkMapper linkMapper) {
+
+        List<InsPlyIncomeInvoiceLink> invoiceLinkList = getInvoiceLinkList(incomeIdList, linkMapper);
+        if (CollUtil.isEmpty(invoiceLinkList)) {
+            return MapUtil.empty();
+        }
+
+        // 按incomeId分组,一个income可能多条link
+        Map<String, List<InsPlyIncomeInvoiceLink>> invoiceLinkMap = invoiceLinkList.stream()
+                .collect(Collectors.groupingBy(InsPlyIncomeInvoiceLink::getIncomeId));
+        return invoiceLinkMap;
+    }
+
+
+    /**
+     * 单纯获取InsPlyIncomeInvoiceLink的列表
+     */
+    public static List<InsPlyIncomeInvoiceLink> getInvoiceLinkList(List<String> incomeIdList, InsPlyIncomeInvoiceLinkMapper linkMapper) {
+        // 1、查询link关联表
+        LambdaQueryWrapper<InsPlyIncomeInvoiceLink> linkWrapper = new LambdaQueryWrapper<>();
+        linkWrapper.in(InsPlyIncomeInvoiceLink::getIncomeId, incomeIdList);
+        linkWrapper.eq(InsPlyIncomeInvoiceLink::getIsDelete, 0);
+        return linkMapper.selectList(linkWrapper);
+    }
+
+    /**
+     * 根据发票状态、开票时间、结算时间过滤收入数据
+     * 优化:嵌套anyMatch改为HashMapID映射匹配,O(n²) -> O(n)
+     *
+     * @param incomeList               待过滤收入dto列表
+     * @param queryVo                  查询条件
+     * @param incomeInvoiceLinkMapper  收入发票链路mapper
+     * @param incomeInvoiceMapper      发票mapper
+     * @param invoiceSettlementMapper  发票结算mapper
+     * @return 过滤之后收入数据
+     */
+    public static List<InsPlyIncomeDto> filterIncomeByInvoiceAndSettleCondition(
+            List<InsPlyIncomeDto> incomeList,
+            SettlementReportQueryVo queryVo,
+            InsPlyIncomeInvoiceLinkMapper incomeInvoiceLinkMapper,
+            InsPlyIncomeInvoiceMapper incomeInvoiceMapper,
+            InsPlyIncomeInvoiceSettlementMapper invoiceSettlementMapper
+    ) {
+
+        String status = queryVo.getStatus();
+        String invoiceStartTime = queryVo.getInvoiceStartTime();
+        String invoiceEndTime = queryVo.getInvoiceEndTime();
+        String settleStartTime = queryVo.getSettlementStartTime();
+        String settleEndTime = queryVo.getSettlementEndTime();
+
+        // 无筛选条件,直接原数据返回
+        boolean noFilterCond = StrUtil.isBlank(status)
+                && (StrUtil.isBlank(invoiceStartTime) || StrUtil.isBlank(invoiceEndTime))
+                && (StrUtil.isBlank(settleStartTime) || StrUtil.isBlank(settleEndTime));
+        if (noFilterCond || CollUtil.isEmpty(incomeList)) {
+            return incomeList;
+        }
+
+        // 查询收入关联链路
+        List<String> incomeIds = incomeList.stream()
+                .map(InsPlyIncomeDto::getId)
+                .distinct()
+                .toList();
+
+        LambdaQueryWrapper<InsPlyIncomeInvoiceLink> linkWrapper = new LambdaQueryWrapper<>();
+        linkWrapper.in(InsPlyIncomeInvoiceLink::getIncomeId, incomeIds);
+        List<InsPlyIncomeInvoiceLink> linkList = incomeInvoiceLinkMapper.selectList(linkWrapper);
+        if (CollUtil.isEmpty(linkList)) {
+            return new ArrayList<>(0);
+        }
+
+        // 链路ID映射
+        Map<String, InsPlyIncomeInvoiceLink> linkInvoiceIdMap = linkList.stream()
+                .collect(Collectors.toMap(InsPlyIncomeInvoiceLink::getInvoiceId,
+                        Function.identity(),
+                        (k1, k2) -> k1));
+        Set<String> linkInvoiceIds = linkInvoiceIdMap.keySet();
+
+        // 查询发票
+        LambdaQueryWrapper<InsPlyIncomeInvoice> invoiceWrapper = new LambdaQueryWrapper<>();
+        invoiceWrapper.in(InsPlyIncomeInvoice::getId, linkInvoiceIds);
+        if (StrUtil.isNotBlank(status)) {
+            invoiceWrapper.eq(InsPlyIncomeInvoice::getStatus, status);
+        }
+        boolean invoiceTimeCond = StrUtil.isNotBlank(invoiceStartTime) && StrUtil.isNotBlank(invoiceEndTime);
+        if (invoiceTimeCond) {
+            invoiceWrapper.between(InsPlyIncomeInvoice::getInvoiceTime,
+                    toStartDateTime(invoiceStartTime),
+                    toEndDateTime(invoiceEndTime));
+        }
+        invoiceWrapper.eq(InsPlyIncomeInvoice::getInvoiceType, queryVo.getInvoiceType());
+        List<InsPlyIncomeInvoice> invoiceList = incomeInvoiceMapper.selectList(invoiceWrapper);
+
+        if ((StrUtil.isNotBlank(status) || invoiceTimeCond) && CollUtil.isEmpty(invoiceList)) {
+            return new ArrayList<>(0);
+        }
+
+        // 结算时间过滤
+        boolean settleTimeCond = StrUtil.isNotBlank(settleStartTime) && StrUtil.isNotBlank(settleEndTime);
+        if (settleTimeCond) {
+            Set<String> invoiceIdSet = invoiceList.stream()
+                    .map(InsPlyIncomeInvoice::getId)
+                    .collect(Collectors.toSet());
+
+            LambdaQueryWrapper<InsPlyIncomeInvoiceSettlement> settleWrapper = new LambdaQueryWrapper<>();
+            settleWrapper.in(InsPlyIncomeInvoiceSettlement::getInvoiceId, invoiceIdSet);
+            settleWrapper.between(InsPlyIncomeInvoiceSettlement::getCreateTime,
+                    toStartDateTime(settleStartTime),
+                    toEndDateTime(settleEndTime));
+
+            List<InsPlyIncomeInvoiceSettlement> settleList = invoiceSettlementMapper.selectList(settleWrapper);
+            if (CollUtil.isEmpty(settleList)) {
+                return new ArrayList<>(0);
+            }
+            // 保留存在结算记录的发票
+            Set<String> validSettleInvoiceIds = settleList.stream()
+                    .map(InsPlyIncomeInvoiceSettlement::getInvoiceId)
+                    .collect(Collectors.toSet());
+
+            invoiceList = invoiceList.stream()
+                    .filter(invoice -> validSettleInvoiceIds.contains(invoice.getId()))
+                    .toList();
+        }
+
+        // 合法发票ID集合
+        Set<String> validInvoiceIds = invoiceList.stream()
+                .map(InsPlyIncomeInvoice::getId)
+                .collect(Collectors.toSet());
+
+        // 过滤合法链路
+        List<InsPlyIncomeInvoiceLink> validLinkList = linkList.stream()
+                .filter(link -> validInvoiceIds.contains(link.getInvoiceId()))
+                .toList();
+
+        Set<String> validIncomeIds = validLinkList.stream()
+                .map(InsPlyIncomeInvoiceLink::getIncomeId)
+                .collect(Collectors.toSet());
+
+        // 最终过滤收入数据
+        return incomeList.stream()
+                .filter(income -> validIncomeIds.contains(income.getId()))
+                .toList();
+    }
+
+
+    /**
+     * 结算状态内存过滤(无接口,函数式getter)
+     * 0-已开票未结清 1-已结清 2-未开票
+     *
+     * @param records           待过滤dto列表
+     * @param settlementStatus  结算状态:0/1/2
+     * @param getInvoiceIdFunc  获取invoiceIds的getter函数
+     * @param baseMapper        mapper,用于统计未结清发票数量
+     * @param <T>               任意类型
+     * @return 过滤后集合
+     */
+    public static <T> List<T> filterBySettlementStatus(
+            List<T> records,
+            String settlementStatus,
+            Function<T, String> getInvoiceIdFunc,
+            ReceivableMapper baseMapper
+    ) {
+        if (CollUtil.isEmpty(records) || StrUtil.isBlank(settlementStatus)) {
+            return records;
+        }
+        return records.stream().filter(action -> {
+            String invoiceIds = getInvoiceIdFunc.apply(action);
+            if ("2".equals(settlementStatus)) {
+                return StrUtil.isEmpty(invoiceIds);
+            } else if ("0".equals(settlementStatus)) {
+                if (StrUtil.isEmpty(invoiceIds)) {
+                    return false;
+                }
+                List<String> invoiceIdList = Arrays.asList(invoiceIds.split(","));
+                return baseMapper.countUnsettledInvoicesByInvoiceIds(invoiceIdList) > 0;
+            } else if ("1".equals(settlementStatus)) {
+                if (StrUtil.isEmpty(invoiceIds)) {
+                    return false;
+                }
+                List<String> invoiceIdList = Arrays.asList(invoiceIds.split(","));
+                return baseMapper.countUnsettledInvoicesByInvoiceIds(invoiceIdList) == 0;
+            }
+            return true;
+        }).toList();
+    }
+
+
+
+
+    /**
+     * yyyy-MM-dd 转当天起始 LocalDateTime
+     */
+    public static LocalDateTime toStartDateTime(String dateStr) {
+        if (StrUtil.isBlank(dateStr)){
+            return null;
+        }
+        LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE);
+        return date.atStartOfDay();
+    }
+
+    /**
+     * yyyy-MM-dd 转当天结束 LocalDateTime
+     */
+    public static LocalDateTime toEndDateTime(String dateStr) {
+        if (StrUtil.isBlank(dateStr)){
+            return null;
+        }
+        LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE);
+        return date.atTime(23, 59, 59, 999999999);
+    }
+}

+ 3 - 0
tenant/organization/src/main/resources/mapper/PtlAgreementCostExternalMapper.xml

@@ -170,6 +170,9 @@
             <if test="costParam.productId != null and costParam.productId != ''">
                 AND ce.product_id = #{costParam.productId}
             </if>
+            <if test="costParam.ptlAgreementId != null and costParam.ptlAgreementId != ''">
+                AND ce.ptl_agreement_id = #{costParam.ptlAgreementId}
+            </if>
             <if test="costParam.rulesName != null and costParam.rulesName != ''">
                 AND ce.rule_description  like concat('%',#{costParam.rulesName},'%')
             </if>

+ 15 - 0
tenant/organization/src/main/resources/mapper/PtlAgreementCostMapper.xml

@@ -357,6 +357,8 @@
 
     <select id="pageList" resultMap="CostManagementVoMap">
         SELECT
+            pa.attribution_id,
+            (select paa.user_abbr  from ptl_agreement_attribution paa where paa.id  = pa.attribution_id  ) as user_abbr,
             pc.id as cost_id,
             pr_raw.id as ruld_id,
             pc.audit_time,
@@ -410,12 +412,25 @@
                     #{phone}
                 </foreach>
             </if>
+            <if test="costParam.costDescribes != null and costParam.costDescribes.length >0 ">
+                and (
+                    <foreach  collection="costParam.costDescribes" close=")" open="(" separator="and " item="costDescribe">
+                        pc.cost_describe like concat('%', #{costDescribe} ,'%')
+                    </foreach>
+                )
+            </if>
             <if test="costParam.auditTimeBegin != null and costParam.auditTimeBegin != ''">
                 and pc.audit_time &gt;= #{costParam.auditTimeBegin}
             </if>
             <if test="costParam.auditTimeEnd != null and costParam.auditTimeEnd != ''">
                 and pc.audit_time &lt; #{costParam.auditTimeEnd}
             </if>
+            <if test="costParam.agreement != null and costParam.agreement != ''">
+            AND (pa.agreement_title  like concat('%',#{costParam.agreement},'%') or
+            pa.agreement_code  like concat('%',#{costParam.agreement},'%') or
+            pa.agreement_abbreviation  like concat('%',#{costParam.agreement},'%') or
+            pa.id = #{costParam.agreement})
+            </if>
             <if test="costParam.companyIdList != null and costParam.companyIdList.size() >0">
                 and pa.partner_company_id in
                 <foreach collection="costParam.companyIdList" item="partnerCompnayId" open="(" close=")" separator=",">

+ 2 - 0
tenant/organization/src/main/resources/mapper/SysUserMapper.xml

@@ -64,6 +64,8 @@
                 AND su.mobile = #{mobile}
             </if>
         </where>
+        order by su.id desc
+        LIMIT 1
     </select>
 
     <select id="getUserRoleName" resultType="String">