ソースを参照

Merge remote-tracking branch 'origin/master'

hurx 1 週間 前
コミット
24e132388a
56 ファイル変更2234 行追加469 行削除
  1. 5 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteCustomerContactService.java
  2. 5 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteCustomerService.java
  3. 45 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/AddrInfo.java
  4. 64 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/BusConnDetail.java
  5. 42 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/PayMoeInf.java
  6. 112 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/SupInfo.java
  7. 56 0
      ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/erp/domain/ErpAuth.java
  8. 0 50
      ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductDetailDto.java
  9. 1 0
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/RemoteCategoryService.java
  10. 14 8
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/RemoteProductService.java
  11. 41 0
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/domain/ProductClassificationDiyDto.java
  12. 144 0
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/erp/domain/push/ComProdInfo.java
  13. 63 0
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/RemoteThirdpartyProductService.java
  14. 1 2
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductBo.java
  15. 1 1
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductBrandBo.java
  16. 1 2
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductDetailBo.java
  17. 1 1
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductStockBo.java
  18. 1 1
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductUnitBo.java
  19. 1 1
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductBrandDto.java
  20. 3 3
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductCateDto.java
  21. 221 0
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductDetailDto.java
  22. 1 2
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductDto.java
  23. 1 1
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductPriceDto.java
  24. 1 1
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductStatusDto.java
  25. 2 2
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductStockDto.java
  26. 1 1
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductUnitDto.java
  27. 35 0
      ruoyi-auth/src/main/java/org/dromara/auth/form/SerialNumberLoginBody.java
  28. 58 36
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SerialNumberStrategy.java
  29. 19 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteCustomerContactServiceImpl.java
  30. 16 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteCustomerServiceImpl.java
  31. 20 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SupplierInfoMapper.java
  32. 280 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SupplierInfoServiceImpl.java
  33. 51 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/SupplierInfoMapper.xml
  34. 102 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/erp/ErpPushController.java
  35. 6 2
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/thirdparty/ThirdpartyCommonController.java
  36. 31 15
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/thirdparty/ThirdpartyProductController.java
  37. 3 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/tongji/TongJiPushController.java
  38. 12 6
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/zhongche/ZhongChePushController.java
  39. 36 4
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/dubbo/RemoteErpPushServiceImpl.java
  40. 32 1
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/ZhongChePushStrategy.java
  41. 8 8
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/impl/InvoiceApplyMessageHandler.java
  42. 3 3
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/service/impl/ExternalProductServiceImpl.java
  43. 2 2
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/pc/PcOrderController.java
  44. 1 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductBrandController.java
  45. 11 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductCategoryController.java
  46. 85 3
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/IndexProductController.java
  47. 5 2
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/PcProductBo.java
  48. 6 43
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductBaseImportVo.java
  49. 61 7
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/dubbo/RemoteProductServiceImpl.java
  50. 173 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/dubbo/RemoteThirdpartyProductServiceImpl.java
  51. 37 32
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/listener/ProductBaseImportListener.java
  52. 7 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IProductBaseService.java
  53. 239 200
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBaseServiceImpl.java
  54. 55 23
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBrandServiceImpl.java
  55. 11 5
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductPoolAuditServiceImpl.java
  56. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/pc/IndexSystemController.java

+ 5 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteCustomerContactService.java

@@ -13,4 +13,9 @@ public interface RemoteCustomerContactService {
      * 根据联系人ID批量查询联系人信息(含部门名称、联系人姓名)
      */
     Map<Long, RemoteCustomerContactVo> selectCustomerContactByIds(Set<Long> contactIds);
+
+    /**
+    * 根据客户id和登录名称查询联系人userId
+    * */
+    Long selectContactUserIdByCustomerIdAndLoginName(Long customerId, String loginName);
 }

+ 5 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteCustomerService.java

@@ -54,4 +54,9 @@ public interface RemoteCustomerService {
      * 客户详细信息 拼接json格式
      */
     String customerInfoJson(Long customerId);
+
+    /**
+    * 通过客户编号查询客户Id
+    * */
+    Long selectCustomerIdByCustomerNo(String customerNo);
 }

+ 45 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/AddrInfo.java

@@ -0,0 +1,45 @@
+package org.dromara.customer.api.erp.domain.push;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 供应商地址信息 - ERP推送
+ *
+ * @author RuoYi-Cloud-Plus
+ */
+@Data
+public class AddrInfo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /** 编号 */
+    private String SupInfoId;
+
+    /** 地址编号 */
+    private String AddrNo;
+
+    /** 地址 */
+    private String Addr;
+
+    /** 序号 */
+    private Integer RowNo;
+
+    /** 邮政编码 */
+    private String Ptc;
+
+    /** 联系人 */
+    private String ConnPer;
+
+    /** 联系人职称 */
+    private String ConnPos;
+
+    /** 联系电话 */
+    private String ConnTel;
+
+    /** 主要送货地址 */
+    private Boolean IsMainAddi;
+}

+ 64 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/BusConnDetail.java

@@ -0,0 +1,64 @@
+package org.dromara.customer.api.erp.domain.push;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 业务联系人详细信息 - ERP推送
+ *
+ * @author LionLi
+ * @date 2026-06-10
+ */
+@Data
+public class BusConnDetail implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /** 编号 */
+    private String SupInfoId;
+
+    /** 联系人 */
+    private String ConnPer;
+
+    /** 序号 */
+    private Integer RowNo;
+
+    /** 标识号 */
+    private Integer RowCd;
+
+    /** 主要联系人 */
+    private Boolean IsMainPer;
+
+    /** 性别 */
+    private Integer Sex;
+
+    /** 部门 */
+    private String Dept;
+
+    /** 职务 */
+    private String JobFun;
+
+    /** 办公电话 */
+    private String OffTel;
+
+    /** 分机号 */
+    private String ExtTel;
+
+    /** 传真 */
+    private String FaxMain;
+
+    /** E-mail */
+    private String EMail;
+
+    /** 联系电话 */
+    private String ConnTelph;
+
+    /** 手机 */
+    private String MobiPhn;
+
+    /** 联系地址 */
+    private String ConnAddr;
+}

+ 42 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/PayMoeInf.java

@@ -0,0 +1,42 @@
+package org.dromara.customer.api.erp.domain.push;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 供应商付款信息 - ERP推送
+ *
+ * @author RuoYi-Cloud-Plus
+ */
+@Data
+public class PayMoeInf implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /** 编号 */
+    private String SupInfoId;
+
+    /** 银行账号 */
+    private String BnAccNo;
+
+    /** 主账户 */
+    private Boolean MainAcc;
+
+    /** 开户银行 */
+    private String AccBn;
+
+    /** 账户名称 */
+    private String AccNm;
+
+    /** 联系电话 */
+    private String TelNo;
+
+    /** 有效期从 */
+    private Integer ValidityFromDate;
+
+    /** 有效期至 */
+    private Integer ValidityToDate;
+}

+ 112 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/erp/domain/push/SupInfo.java

@@ -0,0 +1,112 @@
+package org.dromara.customer.api.erp.domain.push;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 供应商基础信息 - ERP推送
+ *
+ * @author RuoYi-Cloud-Plus
+ */
+@Data
+public class SupInfo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /** 编号 */
+    private String SupInfoId;
+
+    /** 名称 */
+    private String SupNm;
+
+    /** 采购人员 */
+    private String BPPurPer;
+
+    /** 简称 */
+    private String SrtNm;
+
+    /** 有效期从 */
+    private Integer ValidityFromDate;
+
+    /** 有效期至 */
+    private Integer ValidityToDate;
+
+    /** 负责人 */
+    private String BPPerNm;
+
+    /** 统一社会信用代码 */
+    private String TaxChkNo;
+
+    /** 资本额 */
+    private BigDecimal CapSum;
+
+    /** 行业别 */
+    private String tradTy;
+
+    /** 电子邮件 */
+    private String Email;
+
+    /** 网址 */
+    private String URL;
+
+    /** 传真号码 */
+    private String faxNum;
+
+    /** 办公电话 */
+    private String Busphone;
+
+    /** 联系地址 */
+    private String Addr;
+
+    /** QQ/MSN */
+    private String QQMSN;
+
+    /** 类别 */
+    private String Type;
+
+    /** 国家/地区 */
+    private String CountryId;
+
+    /** 省份 */
+    private String PrvnId;
+
+    /** 城市 */
+    private String CityId;
+
+    /** 单价含税 */
+    private Boolean IsPrWTax;
+
+    /** 付款条件 */
+    private Integer PayTm;
+
+    /** 付款天数 */
+    private Integer PayDays;
+
+    /** 结账日 */
+    private Integer PayOtMo;
+
+    /** 结账日(半月结) */
+    private Integer PayDtHalf;
+
+    /** 结账日(周结) */
+    private Integer PayDtWeel;
+
+    /** 发票地址 */
+    private String InvAddr;
+
+    /** 供应商等级 */
+    private String GadId;
+
+    /** 公司 */
+    private String CU_comCo;
+
+    /** 采购开票类型 */
+    private String CU_MakeIr;
+
+    /** 供应品牌 */
+    private String CU_supplie;
+}

+ 56 - 0
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/erp/domain/ErpAuth.java

@@ -0,0 +1,56 @@
+package org.dromara.external.api.erp.domain;
+
+import lombok.Data;
+
+/**
+ * ERP API响应基础信息
+ */
+@Data
+public class ErpAuth implements java.io.Serializable{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 状态码
+     */
+    private Integer status;
+
+    /**
+     * 访问令牌
+     */
+    private String token;
+
+    /**
+     * 超时时间(秒)
+     */
+    private Integer timeout;
+
+    /**
+     * 请求时间
+     */
+    private Long time;
+
+    /**
+     * 签名
+     */
+    private String sign;
+
+    /**
+     * 交易代码
+     */
+    private Integer trcode;
+
+    /**
+     * 客户端交易代码
+     */
+    private Integer cltrcode;
+
+    /**
+     * 结束时间
+     */
+    private Long etime;
+
+    /**
+     * 执行时长(毫秒)
+     */
+    private Integer eduration;
+}

+ 0 - 50
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductDetailDto.java

@@ -1,50 +0,0 @@
-package org.dromara.external.api.thirdparty.domain.dto;
-
-import lombok.Data;
-
-import java.io.Serial;
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * 产品详情响应数据
- *
- * @author
- * @date 2026/4/9
- */
-@Data
-public class ThirdpartyProductDetailDto implements Serializable {
-    @Serial
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 商品id
-     */
-    private String productId;
-
-    /**
-     * 商品编码
-     */
-    private String productNo;
-
-    /**
-     * 商品名称
-     */
-    private String itemName;
-
-    /**
-     * 产品图片URL
-     */
-    private String productImage;
-
-    /**
-     * 商品多图,分隔
-     * */
-    private String imageUrl;
-
-    /**
-     * 商品详情
-     */
-    private String pcDetail;
-
-}

+ 1 - 0
ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/RemoteCategoryService.java

@@ -1,6 +1,7 @@
 package org.dromara.product.api;
 
 import org.dromara.product.api.domain.CategoryDto;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductCateDto;
 
 import java.util.List;
 import java.util.Map;

+ 14 - 8
ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/RemoteProductService.java

@@ -1,17 +1,10 @@
 package org.dromara.product.api;
 
 import org.dromara.common.core.domain.zhongche.domain.Prices;
-import org.dromara.product.api.domain.ProductCategoryRemoteVo;
-import org.dromara.product.api.domain.ProductChangeLogApiVo;
-import org.dromara.product.api.domain.ProductPriceInventoryRemoteVo;
-import org.dromara.product.api.domain.ProductVo;
-import org.dromara.product.api.domain.SiteProductRemoteBo;
-import org.dromara.product.api.domain.SiteProductRemoteResult;
-import org.dromara.product.api.domain.SiteProductRemoteVo;
+import org.dromara.product.api.domain.*;
 import org.dromara.product.api.domain.zhongche.dto.OrderProductDto;
 import org.dromara.product.api.domain.zhongche.dto.ProductAggregateDto;
 import org.dromara.product.api.domain.zhongche.dto.StocksResultDto;
-import org.dromara.product.api.domain.RemoteProductBrand;
 
 import java.util.List;
 import java.util.Map;
@@ -40,10 +33,21 @@ public interface RemoteProductService {
     * */
     List<ProductVo> getProductDetails(List<Long> productIds);
 
+    /**
+     * 获取多个商品详情 v2
+     * @param productIds
+     * @return
+     */
+    List<ProductVo> getProductDetailsV2(List<Long> productIds);
+
     /**
     * 获取多个商品详情
     * */
     List<ProductVo> getProductDetailsByNo(String productNos);
+    /**
+    * 获取商品自定义属性
+    * */
+    List<ProductClassificationDiyDto> getProductCustomAttribute(List<Long> productIds);
 
 
     /**
@@ -131,4 +135,6 @@ public interface RemoteProductService {
      */
     Long getTotalProductCount();
 
+
+
 }

+ 41 - 0
ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/domain/ProductClassificationDiyDto.java

@@ -0,0 +1,41 @@
+package org.dromara.product.api.domain;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+public class ProductClassificationDiyDto implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    private Long id;
+
+    /**
+     * 商品id
+     */
+    private Long productId;
+
+    /**
+     * 属性key
+     */
+    private String attributeKey;
+
+    /**
+     * 属性value
+     */
+    private String attributeValue;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 144 - 0
ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/erp/domain/push/ComProdInfo.java

@@ -0,0 +1,144 @@
+package org.dromara.product.api.erp.domain.push;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 产品基础信息推送对象 ComProdInfo
+ *
+ * @author RuoYi-Cloud-Plus
+ */
+@Data
+public class ComProdInfo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 产品编号
+     */
+    private String prodId;
+
+    /**
+     * 产品名称
+     */
+    private String prodNm;
+
+    /**
+     * 基本单位
+     */
+    private String uid;
+
+    /**
+     * 产品类别
+     */
+    private String prodCatgId;
+
+    /**
+     * 产品类型
+     */
+    private String typeId;
+
+    /**
+     * 规格型号
+     */
+    private String prodSpec;
+
+    /**
+     * UPC条码
+     */
+    private String barCd;
+
+    /**
+     * 使用交易多单位
+     */
+    private Boolean useMultUi;
+
+    /**
+     * 产品状态
+     */
+    private String prodSts;
+
+    /**
+     * 发票名称
+     */
+    private String invNm;
+
+    /**
+     * 发票规格
+     */
+    private String invSpec;
+
+    /**
+     * 单价含税(进价)
+     */
+    private Boolean purIsPrWtTax;
+
+    /**
+     * 标准进价
+     */
+    private BigDecimal purStdPr;
+
+    /**
+     * 最高进价
+     */
+    private BigDecimal highPurPr;
+
+    /**
+     * 主供应商
+     */
+    private String supId;
+
+    /**
+     * 税码(进价)
+     */
+    private String purTaxId;
+
+    /**
+     * 单价含税(售价)
+     */
+    private Boolean slsIsPrWtTax;
+
+    /**
+     * 标准售价
+     */
+    private BigDecimal slsStdPr;
+
+    /**
+     * 最低售价
+     */
+    private BigDecimal minPr;
+
+    /**
+     * 税码(售价)
+     */
+    private String sltTaxId;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 品牌资料
+     */
+    private String tradMrkId;
+
+    /**
+     * 最低销售量
+     */
+    private BigDecimal minSalQty;
+
+    /**
+     * 采购员
+     */
+    private String purPerId;
+
+    /**
+     * 备货属性
+     */
+    private String cuBhsx;
+}

+ 63 - 0
ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/RemoteThirdpartyProductService.java

@@ -0,0 +1,63 @@
+package org.dromara.product.api.thirdparty.domain;
+
+import io.swagger.v3.oas.annotations.Operation;
+import org.dromara.common.core.domain.R;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductDetailBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductStockBo;
+import org.dromara.product.api.thirdparty.domain.dto.*;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.List;
+
+/**
+ * 第三方调用商品接口
+ * @author
+ * @date 2026/6/11 上午11:34
+ */
+public interface RemoteThirdpartyProductService {
+
+    /**
+     * 产品列表查询
+     */
+    List<ThirdpartyProductDto> getListProducts( ThirdpartyProductBo bo, Integer pageNum, Integer pageSize) ;
+
+    /**
+    * 产品数量
+    * */
+    Long getProductCount(ThirdpartyProductBo bo) ;
+
+    /**
+     * 产品表体接口
+     */
+    ThirdpartyProductDto getProductBody( ThirdpartyProductDetailBo bo) ;
+
+    /**
+     * 产品详情查询
+     */
+    ThirdpartyProductDetailDto getProductDetails( ThirdpartyProductDetailBo bo) ;
+
+    /**
+     * 产品价格查询
+     */
+    ThirdpartyProductPriceDto getPrice( ThirdpartyProductDetailBo bo) ;
+
+
+    /**
+     * 商品查询价格(批量)
+     */
+
+    List<ThirdpartyProductPriceDto> getBatchPrice( ThirdpartyProductDetailBo bo) ;
+
+    /**
+     * 商品库存查询
+     */
+
+    ThirdpartyProductStockDto getProductStock( ThirdpartyProductStockBo bo) ;
+
+    /**
+     * 产品状态查询
+     */
+    ThirdpartyProductStatusDto getStatus( ThirdpartyProductDetailBo bo);
+}

+ 1 - 2
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/bo/ThirdpartyProductBo.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductBo.java

@@ -1,10 +1,9 @@
-package org.dromara.external.api.thirdparty.domain.bo;
+package org.dromara.product.api.thirdparty.domain.bo;
 
 import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
-import java.math.BigDecimal;
 
 /**
  * @author

+ 1 - 1
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/bo/ThirdpartyProductBrandBo.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductBrandBo.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.bo;
+package org.dromara.product.api.thirdparty.domain.bo;
 
 import lombok.Data;
 

+ 1 - 2
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/bo/ThirdpartyProductDetailBo.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductDetailBo.java

@@ -1,7 +1,6 @@
-package org.dromara.external.api.thirdparty.domain.bo;
+package org.dromara.product.api.thirdparty.domain.bo;
 
 import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 
 import java.io.Serial;

+ 1 - 1
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/bo/ThirdpartyProductStockBo.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductStockBo.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.bo;
+package org.dromara.product.api.thirdparty.domain.bo;
 
 import lombok.Data;
 

+ 1 - 1
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/bo/ThirdpartyProductUnitBo.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/bo/ThirdpartyProductUnitBo.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.bo;
+package org.dromara.product.api.thirdparty.domain.bo;
 
 import lombok.Data;
 

+ 1 - 1
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductBrandDto.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductBrandDto.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.dto;
+package org.dromara.product.api.thirdparty.domain.dto;
 
 import lombok.Data;
 

+ 3 - 3
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductCateDto.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductCateDto.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.dto;
+package org.dromara.product.api.thirdparty.domain.dto;
 
 import lombok.Data;
 
@@ -29,10 +29,10 @@ public class ThirdpartyProductCateDto implements Serializable {
     /**
      * 分类层级
      */
-    private Integer catLevel;
+    private Integer classLevel;
 
     /**
      * 分类名称
      */
-    private String name;
+    private String categoryName;
 }

+ 221 - 0
ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductDetailDto.java

@@ -0,0 +1,221 @@
+package org.dromara.product.api.thirdparty.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 产品详情响应数据
+ *
+ * @author
+ * @date 2026/4/9
+ */
+@Data
+public class ThirdpartyProductDetailDto implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 产品名称
+     */
+    private String itemName;
+
+    /**
+     * 品牌id
+     */
+    private Long brandId;
+
+    /**
+     * 品牌名称
+     */
+    private String brandName;
+
+    /**
+     * 顶级分类id
+     */
+    private Long topCategoryId;
+
+    /**
+     * 中级分类id
+     */
+    private Long mediumCategoryId;
+
+    /**
+     * 底层分类id
+     */
+    private Long bottomCategoryId;
+
+    /**
+     * 分类名称(底级分类)
+     */
+    private String categoryName;
+
+    /**
+     * 顶级分类名称
+     */
+    private String topCategoryName;
+
+    /**
+     * 中级分类名称
+     */
+    private String mediumCategoryName;
+
+    /**
+     * 底级分类名称
+     */
+    private String bottomCategoryName;
+
+    /**
+     * 规格代码
+     */
+    private String specificationsCode;
+
+    /**
+     * 单位id
+     */
+    private String unitId;
+
+    /**
+     * 单位名称(如:件、箱、千克等)
+     */
+    private String unitName;
+
+    /**
+     * 产品图片URL
+     */
+    private String productImage;
+
+    /**
+     * 商品多图
+     * */
+    private String imageUrl;
+
+
+    /**
+     * 是否自营(1=是,0=否)
+     */
+    private Integer isSelf;
+
+    /**
+     * 市场价
+     * */
+    private BigDecimal marketPrice;
+
+    /**
+     * 官网价格
+     */
+    private BigDecimal memberPrice;
+
+    /**
+     * 协议价价格
+     * */
+    private BigDecimal externalPrice;
+
+    /**
+     * 规格型号
+     */
+    private String specification;
+
+    /**
+     * UPC(S)条码
+     */
+    private String barCoding;
+
+    /**
+     * 发票名称
+     */
+    private String invoiceName;
+
+    /**
+     * 发票规格
+     */
+    private String invoiceSpecs;
+
+    /**
+     * 包装规格
+     */
+    private String packagingSpec;
+
+    /**
+     * 参考链接
+     */
+    private String referenceLink;
+
+    /**
+     * 商品重量
+     */
+    private String productWeight;
+
+    /**
+     * 重量单位
+     */
+    private String weightUnit;
+
+    /**
+     * 商品体积
+     */
+    private String productVolume;
+
+    /**
+     * 体积单位
+     */
+    private String volumeUnit;
+
+    /**
+     * 商品详情
+     */
+    private String pcDetail;
+
+    /**
+     * 税率
+     */
+    private BigDecimal taxRate;
+
+    /**
+     * 币种
+     */
+    private String currency;
+
+    /**
+     * 最低起订量
+     */
+    private Long minOrderQuantity;
+
+    /**
+     * 是否可定制 0:不可定制 1:可定制
+     * */
+    private String isCustomize;
+
+    /**
+     * 总库存
+     * */
+    private Long totalInventory;
+    /**
+     * 当前可用库存
+     * */
+    private Long nowInventory;
+
+    /**
+     * 商品属性值(JSON字符串)
+     */
+    private String attributesList;
+
+    /**
+     * 自定义商品属性(JSON字符串)
+     * */
+    private String diyAttributesList;
+
+    /**
+     * 税收编码id
+     *
+     * */
+    private Long taxationId;
+    /**
+     * 税收编码code
+     *
+     * */
+    private Long taxationCode;
+
+}

+ 1 - 2
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductDto.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductDto.java

@@ -1,11 +1,10 @@
-package org.dromara.external.api.thirdparty.domain.dto;
+package org.dromara.product.api.thirdparty.domain.dto;
 
 import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
 import java.math.BigDecimal;
-import java.util.List;
 
 /**
  * 商品数据

+ 1 - 1
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductPriceDto.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductPriceDto.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.dto;
+package org.dromara.product.api.thirdparty.domain.dto;
 
 import lombok.Data;
 

+ 1 - 1
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductStatusDto.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductStatusDto.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.dto;
+package org.dromara.product.api.thirdparty.domain.dto;
 
 import lombok.Data;
 

+ 2 - 2
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductStockDto.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductStockDto.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.dto;
+package org.dromara.product.api.thirdparty.domain.dto;
 
 import lombok.Data;
 
@@ -39,5 +39,5 @@ public class ThirdpartyProductStockDto implements Serializable {
     /**
      * 库存数量
      */
-    private Integer num;
+    private Long num;
 }

+ 1 - 1
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/thirdparty/domain/dto/ThirdpartyProductUnitDto.java → ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/thirdparty/domain/dto/ThirdpartyProductUnitDto.java

@@ -1,4 +1,4 @@
-package org.dromara.external.api.thirdparty.domain.dto;
+package org.dromara.product.api.thirdparty.domain.dto;
 
 import lombok.Data;
 

+ 35 - 0
ruoyi-auth/src/main/java/org/dromara/auth/form/SerialNumberLoginBody.java

@@ -0,0 +1,35 @@
+package org.dromara.auth.form;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.domain.model.LoginBody;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * 客户编号+联系人的登录名称登录
+ * @author
+ * @date 2026/6/11 上午10:06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SerialNumberLoginBody extends LoginBody {
+    /**
+    * 客户编号
+    * */
+    @NotBlank(message = "客户编号不能为空")
+    private String customerNo;
+
+    /**
+    * 登录名称
+    * */
+    @NotBlank(message = "登录名称不能为空")
+    private String loginName;
+
+    /**
+    * 密码
+    * */
+    @NotBlank(message = "密码不能为空")
+    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
+    private String password;
+}

+ 58 - 36
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SerialNumberStrategy.java

@@ -3,14 +3,18 @@ package org.dromara.auth.service.impl;
 import cn.dev33.satoken.stp.StpUtil;
 import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.crypto.digest.BCrypt;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.zhyd.oauth.model.AuthResponse;
 import me.zhyd.oauth.model.AuthUser;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.auth.domain.vo.LoginVo;
+import org.dromara.auth.form.SerialNumberLoginBody;
 import org.dromara.auth.form.SocialLoginBody;
 import org.dromara.auth.service.IAuthStrategy;
+import org.dromara.auth.service.SysLoginService;
+import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.StreamUtils;
 import org.dromara.common.core.utils.ValidatorUtils;
@@ -19,6 +23,8 @@ import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.common.social.config.properties.SocialProperties;
 import org.dromara.common.social.utils.SocialUtils;
 import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.customer.api.RemoteCustomerContactService;
+import org.dromara.customer.api.RemoteCustomerService;
 import org.dromara.system.api.RemoteSocialService;
 import org.dromara.system.api.RemoteUserService;
 import org.dromara.system.api.domain.vo.RemoteClientVo;
@@ -30,7 +36,7 @@ import java.util.List;
 import java.util.Optional;
 
 /**
- * 第三方授权策略
+ * 客户编号+联系人的登录名称登录
  *
  * @author thiszhc is 三三
  */
@@ -40,12 +46,18 @@ import java.util.Optional;
 public class SerialNumberStrategy implements IAuthStrategy {
 
     private final SocialProperties socialProperties;
+    private final SysLoginService loginService;
 
     @DubboReference
     private RemoteSocialService remoteSocialService;
     @DubboReference
     private RemoteUserService remoteUserService;
 
+    @DubboReference
+    private RemoteCustomerService remoteCustomerService;
+
+    @DubboReference
+    private RemoteCustomerContactService remoteCustomerContactService;
     /**
      * 登录-第三方授权登录
      *
@@ -54,61 +66,71 @@ public class SerialNumberStrategy implements IAuthStrategy {
      */
     @Override
     public LoginVo login(String body, RemoteClientVo client) {
-        SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
+
+        return new LoginVo();
+    }
+
+    /**
+     * 客户登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    @Override
+    public LoginVo clientLogin(String body, RemoteClientVo client) {
+        SerialNumberLoginBody loginBody = JsonUtils.parseObject(body, SerialNumberLoginBody.class);
         ValidatorUtils.validate(loginBody);
-        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
-            loginBody.getSource(), loginBody.getSocialCode(),
-            loginBody.getSocialState(), socialProperties);
-        if (!response.ok()) {
-            throw new ServiceException(response.getMsg());
-        }
-        AuthUser authUserData = response.getData();
 
-        List<RemoteSocialVo> list = remoteSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
-        if (CollUtil.isEmpty(list)) {
-            throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
+        String tenantId = loginBody.getTenantId();
+
+        // 通过客户编号获取客户id
+        Long customerId = remoteCustomerService.selectCustomerIdByCustomerNo(loginBody.getCustomerNo());
+        if (customerId == null) {
+            loginService.recordLogininfor(tenantId, loginBody.getLoginName(), Constants.LOGIN_FAIL, "客户未注册审核");
+            throw new ServiceException("客户未注册审核");
         }
-        RemoteSocialVo socialVo;
-        if (TenantHelper.isEnable()) {
-            Optional<RemoteSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
-            if (opt.isEmpty()) {
-                throw new ServiceException("对不起,你没有权限登录当前租户!");
-            }
-            socialVo = opt.get();
-        } else {
-            socialVo = list.get(0);
+
+        // 通过客户id查询联系人userId
+        Long contactUserId = remoteCustomerContactService.selectContactUserIdByCustomerIdAndLoginName(customerId, loginBody.getLoginName());
+        if (contactUserId == null) {
+            loginService.recordLogininfor(tenantId, loginBody.getLoginName(), Constants.LOGIN_FAIL, "用户名或密码错误");
+            throw new ServiceException("用户名或密码错误");
         }
 
-        LoginUser loginUser = remoteUserService.getUserInfo(socialVo.getUserId(), socialVo.getTenantId());
+        // 通过userId获取用户信息
+        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
+            LoginUser user = remoteUserService.getUserInfo(contactUserId, tenantId);
+            // 校验密码
+            loginService.checkLogin(org.dromara.common.core.enums.LoginType.PASSWORD, tenantId, loginBody.getLoginName(),
+                () -> !BCrypt.checkpw(loginBody.getPassword(), user.getPassword()));
+            return user;
+        });
+
+        // 设置客户ID
+        loginUser.setCustomerId(customerId);
         loginUser.setClientKey(client.getClientKey());
         loginUser.setDeviceType(client.getDeviceType());
+
+        // 配置登录参数
         SaLoginParameter model = new SaLoginParameter();
         model.setDeviceType(client.getDeviceType());
-        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
-        // 例如: 后台用户30分钟过期 app用户1天过期
         model.setTimeout(client.getTimeout());
         model.setActiveTimeout(client.getActiveTimeout());
         model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+
         // 生成token
         LoginHelper.login(loginUser, model);
 
+        // 构建返回结果
         LoginVo loginVo = new LoginVo();
         loginVo.setAccessToken(StpUtil.getTokenValue());
         loginVo.setExpireIn(StpUtil.getTokenTimeout());
         loginVo.setClientId(client.getClientId());
-        return loginVo;
-    }
 
-    /**
-     * 客户登录
-     *
-     * @param body   登录对象
-     * @param client 授权管理视图对象
-     * @return 登录验证信息
-     */
-    @Override
-    public LoginVo clientLogin(String body, RemoteClientVo client) {
-        return null;
+        loginService.recordLogininfor(tenantId, loginBody.getLoginName(), Constants.LOGIN_SUCCESS, "登录成功");
+
+        return loginVo;
     }
 
     @Override

+ 19 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteCustomerContactServiceImpl.java

@@ -1,11 +1,14 @@
 package org.dromara.customer.dubbo;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.dromara.customer.api.RemoteCustomerContactService;
 import org.dromara.customer.api.domain.vo.RemoteCustomerContactVo;
+import org.dromara.customer.domain.CustomerContact;
 import org.dromara.customer.domain.vo.CustomerContactVo;
 import org.dromara.customer.service.ICustomerContactService;
 import org.springframework.stereotype.Service;
@@ -41,4 +44,20 @@ public class RemoteCustomerContactServiceImpl implements RemoteCustomerContactSe
                 e -> BeanUtil.toBean(e.getValue(), RemoteCustomerContactVo.class)
             ));
     }
+
+    /**
+     * 根据客户id和登录名称查询联系人userId
+     *
+     * @param customerId
+     * @param loginName
+     */
+    @Override
+    public Long selectContactUserIdByCustomerIdAndLoginName(Long customerId, String loginName) {
+        CustomerContact one = customerContactService.getOne(Wrappers.lambdaQuery(CustomerContact.class)
+            .eq(CustomerContact::getCustomerId, customerId)
+            .eq(CustomerContact::getCustomLoginName, loginName)
+            .last("limit 1")
+        );
+        return ObjectUtil.isNotEmpty( one) ? one.getUserId() : null;
+    }
 }

+ 16 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteCustomerServiceImpl.java

@@ -2,6 +2,7 @@ package org.dromara.customer.dubbo;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -181,4 +182,19 @@ public class RemoteCustomerServiceImpl implements RemoteCustomerService {
     public String customerInfoJson(Long customerId) {
         return customerInfoService.customerInfoJson(customerId);
     }
+
+    /**
+     * 通过客户编号查询客户Id
+     *
+     * @param customerNo
+     */
+    @Override
+    public Long selectCustomerIdByCustomerNo(String customerNo) {
+        CustomerInfo one = customerInfoService.getOne(Wrappers.lambdaQuery(CustomerInfo.class)
+            .eq(CustomerInfo::getCustomerNo, customerNo)
+            .eq(CustomerInfo::getStatus,"0")
+            .last("limit 1")
+        );
+        return ObjectUtil.isNotEmpty( one) ? one.getId() : null;
+    }
 }

+ 20 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SupplierInfoMapper.java

@@ -1,9 +1,13 @@
 package org.dromara.customer.mapper;
 
 import org.dromara.customer.domain.SupplierInfo;
+import org.dromara.customer.domain.vo.SupplierAddressVo;
+import org.dromara.customer.domain.vo.SupplierBankVo;
 import org.dromara.customer.domain.vo.SupplierInfoVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 
+import java.util.List;
+
 /**
  * 供应商信息Mapper接口
  *
@@ -12,4 +16,20 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
  */
 public interface SupplierInfoMapper extends BaseMapperPlus<SupplierInfo, SupplierInfoVo> {
 
+    /**
+     * 根据供应商ID查询地址列表
+     *
+     * @param supplierId 供应商ID
+     * @return 地址列表
+     */
+    List<SupplierAddressVo> selectAddrListBySupplierId(Long supplierId);
+
+    /**
+     * 根据供应商ID查询银行账户列表
+     *
+     * @param supplierId 供应商ID
+     * @return 银行账户列表
+     */
+    List<SupplierBankVo> selectBankListBySupplierId(Long supplierId);
+
 }

+ 280 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SupplierInfoServiceImpl.java

@@ -22,6 +22,7 @@ import org.dromara.common.core.constant.GlobalConstants;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -36,6 +37,7 @@ import org.dromara.customer.mapper.*;
 import org.dromara.customer.service.*;
 import org.dromara.customer.utils.qcc.QccUtils;
 import org.dromara.customer.utils.qcc.domain.CompanyInfoResponse;
+import org.dromara.external.api.service.RemoteErpPushService;
 import org.dromara.product.api.RemoteCategoryService;
 import org.dromara.product.api.RemoteProductService;
 import org.dromara.product.api.domain.RemoteProductBrand;
@@ -76,6 +78,8 @@ public class SupplierInfoServiceImpl extends ServiceImpl<SupplierInfoMapper, Sup
 
     private final ISupplierInfoTemporaryService supplierInfoTemporaryService;
 
+    private final IIndustryCategoryService industryCategoryService;
+
     private final ObjectMapper objectMapper;
 
     private final SupplyAreaMapper supplyAreaMapper;
@@ -116,6 +120,8 @@ public class SupplierInfoServiceImpl extends ServiceImpl<SupplierInfoMapper, Sup
 
     @DubboReference
     private RemoteSupplierLevelService remoteSupplierLevelService;
+    @DubboReference
+    private RemoteErpPushService remoteErpPushService;
 
 
     /**
@@ -251,6 +257,9 @@ public class SupplierInfoServiceImpl extends ServiceImpl<SupplierInfoMapper, Sup
         if (supplierInfoVo.getSupplyStatus() == SupplierStatusEnum.PENDING_REVIEW.getCode() && bo.getSupplyStatus() == SupplierStatusEnum.OFFICIAL_SUPPLIER.getCode()) {
             SupplierInfo update = MapstructUtils.convert(bo, SupplierInfo.class);
             update.setCooperative(1L);
+            //同步供应商信息到ERP
+            this.syncSupplierToErp(bo.getId());
+            update.setPushStatus(1L);
             baseMapper.updateById(update);
         }
         //待审核  -> 审核未通过
@@ -414,6 +423,277 @@ public class SupplierInfoServiceImpl extends ServiceImpl<SupplierInfoMapper, Sup
         SupplierInfo update = MapstructUtils.convert(bo, SupplierInfo.class);
         return baseMapper.updateById(update) > 0;
     }
+    /**
+     * 同步供应商到erp
+     * */
+    private boolean syncSupplierToErp(Long supplierId) throws JsonProcessingException {
+        try {
+            // 1. 查询供应商信息
+            SupplierInfoVo supplierVo = baseMapper.selectVoById(supplierId);
+            if (supplierVo == null) {
+                log.error("同步供应商到ERP失败:供应商不存在,supplierId={}", supplierId);
+                return false;
+            }
+
+            // 2. 查询供应商ERP信息
+            SupplierErpInfo erpInfo = supplierErpInfoMapper.selectOne(
+                new LambdaQueryWrapper<SupplierErpInfo>()
+                    .eq(SupplierErpInfo::getSupplierId, supplierId)
+            );
+
+            // 3. 构建 SupInfo 对象(主表信息)
+            org.dromara.customer.api.erp.domain.push.SupInfo supInfo = buildSupInfo(supplierVo, erpInfo);
+
+            // 4. 查询并构建 BusConn 列表(联系人信息)
+            List<org.dromara.customer.api.erp.domain.push.BusConnDetail> busConnList = buildBusConnList(supplierId);
+
+            // 5. 查询并构建 AddrInfo 列表(地址信息)
+            List<org.dromara.customer.api.erp.domain.push.AddrInfo> addrInfoList = buildAddrInfoList(supplierId);
+
+            // 6. 查询并构建 PayMoeInf 列表(银行账户信息)
+            List<org.dromara.customer.api.erp.domain.push.PayMoeInf> payMoeInfList = buildPayMoeInfList(supplierId);
+
+            // 7. 构建最终的推送数据结构
+            Map<String, Object> pushDataMap = new HashMap<>();
+            pushDataMap.put("SupInfo", supInfo);
+            pushDataMap.put("BusConn", busConnList);
+            pushDataMap.put("AddrInfo", addrInfoList);
+            pushDataMap.put("PayMoeInfo", payMoeInfList);
+
+            // 8. 转换为JSON字符串
+            String supplierJson = objectMapper.writeValueAsString(pushDataMap);
+
+            // 9. 判断是新增还是更新(根据pushStatus字段)
+            boolean isUpdate = supplierVo.getPushStatus() != null && supplierVo.getPushStatus() == 1L;
+
+            // 10. 调用远程ERP推送服务
+            remoteErpPushService.pushSupplierData(supplierJson, isUpdate);
+
+            log.info("成功同步供应商到ERP:supplierId={}, isUpdate={}", supplierId, isUpdate);
+            return true;
+
+        } catch (Exception e) {
+            log.error("同步供应商到ERP异常:supplierId={}", supplierId, e);
+            throw new ServiceException("同步供应商到ERP失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 构建 SupInfo 对象
+     */
+    private org.dromara.customer.api.erp.domain.push.SupInfo buildSupInfo(SupplierInfoVo supplierVo, SupplierErpInfo erpInfo) {
+        org.dromara.customer.api.erp.domain.push.SupInfo supInfo = new org.dromara.customer.api.erp.domain.push.SupInfo();
+
+        // 基本信息映射
+        supInfo.setSupInfoId(supplierVo.getSupplierNo()); // 编号 -> 供应商编号
+        supInfo.setSupNm(supplierVo.getEnterpriseName()); // 名称 -> 企业名称
+        supInfo.setSrtNm(supplierVo.getShortName()); // 简称
+        supInfo.setTaxChkNo(supplierVo.getSocialCreditCode()); // 统一社会信用代码
+        supInfo.setEmail(supplierVo.getMailbox()); // 电子邮件
+        supInfo.setURL(supplierVo.getUrl()); // 网址
+        supInfo.setFaxNum(supplierVo.getFax()); // 传真号码
+        supInfo.setBusphone(supplierVo.getFixedPhone()); // 办公电话
+        supInfo.setAddr(supplierVo.getOfficeAddress()); // 联系地址
+        supInfo.setType(String.valueOf(supplierVo.getSupplierType())); // 类别 -> 供应商类型
+        supInfo.setCountryId(supplierVo.getBusinessProvince()); // 国家/地区 -> 省份(注册地址)
+        supInfo.setPrvnId(supplierVo.getOfficeProvince()); // 省份 -> 办公地址省
+        supInfo.setCityId(supplierVo.getOfficeCity()); // 城市 -> 办公地址市
+        supInfo.setInvAddr(supplierVo.getInvoiceAddress()); // 发票地址
+
+        // ERP信息映射
+        if (erpInfo != null) {
+            // 采购人员 - 需要通过remoteErpStaffService查询
+            if (erpInfo.getPurchaseId() != null) {
+                Map<Long, String> staffNameMap = remoteErpStaffService.selectStaffNameByIds(Collections.singleton(erpInfo.getPurchaseId()));
+                String staffName = staffNameMap.get(erpInfo.getPurchaseId());
+                if (StringUtils.isNotBlank(staffName)) {
+                    RemoteErpStaffVo staffVo = remoteErpStaffService.selectStaffByStaffNameOne(staffName);
+                    if (staffVo != null) {
+                        supInfo.setBPPurPer(staffVo.getStaffCode()); // 采购人员编码
+                        supInfo.setBPPerNm(staffVo.getStaffName()); // 负责人
+                    }
+                }
+            }
+
+            // 资本额 - 从工商信息获取
+            if (StringUtils.isNotBlank(supplierVo.getRegisteredCapital())) {
+                try {
+                    supInfo.setCapSum(new java.math.BigDecimal(supplierVo.getRegisteredCapital()));
+                } catch (NumberFormatException e) {
+                    log.warn("注册资本格式错误: {}", supplierVo.getRegisteredCapital());
+                }
+            }
+
+            // 行业别
+            if (supplierVo.getIndustrCategory() != null) {
+                IndustryCategory industryCategory = industryCategoryService.getById(supplierVo.getIndustrCategory());
+                if(industryCategory != null){
+                    supInfo.setTradTy(industryCategory.getIndustryCode());
+                }
+
+            }
+
+            // QQ/MSN - 暂无对应字段,留空
+
+            // 单价含税
+            supInfo.setIsPrWTax("1".equals(erpInfo.getUnitPrice()));
+
+            // 付款条件、付款天数、结账日等 - 从结算方式解析
+            // 这里需要根据实际的结算方式字段进行解析
+            if (StringUtils.isNotBlank(erpInfo.getSettlementMethod())) {
+                // TODO: 根据实际业务规则解析结算方式
+            }
+
+            // 公司
+            if (supplierVo.getOwnedCompany() != null) {
+                String companyCode = remoteComCompanyService.selectCompanyCodeById(supplierVo.getOwnedCompany());
+                if (StringUtils.isNotBlank(companyCode)) {
+                    supInfo.setCU_comCo(companyCode);
+                }
+            }
+
+            // 采购开票类型
+            supInfo.setCU_MakeIr(erpInfo.getPurchaseInvoiceNo());
+
+            // 供应品牌
+            supInfo.setCU_supplie(supplierVo.getOperatingBrand());
+
+            // 供应商等级
+            if (StringUtils.isNotBlank(supplierVo.getCooperateLevel())) {
+                supInfo.setGadId(supplierVo.getCooperateLevel());
+            }
+        }
+
+        // 有效期从/至
+        if (supplierVo.getValidityFromDate() != null) {
+            supInfo.setValidityFromDate(Integer.parseInt(DateUtils.parseDateToStr(org.dromara.common.core.enums.FormatsType.YYYYMMDD, supplierVo.getValidityFromDate())));
+        }
+        if (supplierVo.getValidityToDate() != null) {
+            supInfo.setValidityToDate(Integer.parseInt(DateUtils.parseDateToStr(org.dromara.common.core.enums.FormatsType.YYYYMMDD, supplierVo.getValidityToDate())));
+        }
+
+        return supInfo;
+    }
+
+    /**
+     * 构建 BusConn 列表(联系人信息)
+     */
+    private List<org.dromara.customer.api.erp.domain.push.BusConnDetail> buildBusConnList(Long supplierId) {
+        List<org.dromara.customer.api.erp.domain.push.BusConnDetail> busConnList = new ArrayList<>();
+
+        // 查询供应商联系人列表
+        List<SupplierContactVo> contactList = supplierContactMapper.selectVoList(
+            new LambdaQueryWrapper<SupplierContact>()
+                .eq(SupplierContact::getSupplierId, supplierId)
+                .orderByAsc(SupplierContact::getId)
+        );
+
+        if (CollUtil.isEmpty(contactList)) {
+            return busConnList;
+        }
+
+        int rowNum = 1;
+        for (SupplierContactVo contact : contactList) {
+            org.dromara.customer.api.erp.domain.push.BusConnDetail busConn = new org.dromara.customer.api.erp.domain.push.BusConnDetail();
+
+            busConn.setSupInfoId(contact.getSupplierNo()); // 编号
+            busConn.setConnPer(contact.getUserName()); // 联系人
+            busConn.setRowNo(rowNum++); // 序号
+            busConn.setRowCd(rowNum - 1); // 标识号(使用序号作为标识号)
+            busConn.setIsMainPer("1".equals(contact.getIsPrimaryContact())); // 主要联系人
+            busConn.setSex(0); // 性别(默认0,因为SupplierContactVo中没有gender字段)
+            busConn.setDept(contact.getDepartmentNo()); // 部门
+            busConn.setJobFun(contact.getPosition()); // 职务
+            busConn.setOffTel(contact.getOfficePhone()); // 办公电话
+            // 分机号 - 暂无对应字段
+            busConn.setFaxMain(null); // 传真 - 暂无对应字段
+            busConn.setEMail(null); // E-mail - 暂无对应字段
+            busConn.setConnTelph(contact.getPhone()); // 联系电话
+            busConn.setMobiPhn(contact.getPhone()); // 手机
+            busConn.setConnAddr(contact.getConnAddr()); // 联系地址
+
+            busConnList.add(busConn);
+        }
+
+        return busConnList;
+    }
+
+    /**
+     * 构建 AddrInfo 列表(地址信息)
+     */
+    private List<org.dromara.customer.api.erp.domain.push.AddrInfo> buildAddrInfoList(Long supplierId) {
+        List<org.dromara.customer.api.erp.domain.push.AddrInfo> addrInfoList = new ArrayList<>();
+
+        // 查询供应商地址列表
+        List<SupplierAddressVo> addrList = baseMapper.selectAddrListBySupplierId(supplierId);
+
+        if (CollUtil.isEmpty(addrList)) {
+            return addrInfoList;
+        }
+
+        int rowNum = 1;
+        for (SupplierAddressVo address : addrList) {
+            org.dromara.customer.api.erp.domain.push.AddrInfo addrInfo = new org.dromara.customer.api.erp.domain.push.AddrInfo();
+
+            addrInfo.setSupInfoId(address.getSupplierNo()); // 编号
+            addrInfo.setAddrNo(address.getAddressNo()); // 地址编号
+            // 拼接完整地址
+            StringBuilder fullAddr = new StringBuilder();
+            if (StringUtils.isNotBlank(address.getShippingProvincial())) {
+                fullAddr.append(address.getShippingProvincial());
+            }
+            if (StringUtils.isNotBlank(address.getShippingCity())) {
+                fullAddr.append(address.getShippingCity());
+            }
+            if (StringUtils.isNotBlank(address.getShippingCounty())) {
+                fullAddr.append(address.getShippingCounty());
+            }
+            if (StringUtils.isNotBlank(address.getShippingAddress())) {
+                fullAddr.append(address.getShippingAddress());
+            }
+            addrInfo.setAddr(fullAddr.toString()); // 地址
+            addrInfo.setRowNo(rowNum++); // 序号
+            addrInfo.setPtc(address.getShippingPostCode()); // 邮政编码
+            addrInfo.setConnPer(address.getShipperName()); // 联系人
+            addrInfo.setConnPos(null); // 联系人职称 - 暂无对应字段
+            addrInfo.setConnTel(address.getShipperPhone()); // 联系电话
+            addrInfo.setIsMainAddi("1".equals(address.getIsSelf())); // 主要送货地址
+
+            addrInfoList.add(addrInfo);
+        }
+
+        return addrInfoList;
+    }
+
+    /**
+     * 构建 PayMoeInf 列表(银行账户信息)
+     */
+    private List<org.dromara.customer.api.erp.domain.push.PayMoeInf> buildPayMoeInfList(Long supplierId) {
+        List<org.dromara.customer.api.erp.domain.push.PayMoeInf> payMoeInfList = new ArrayList<>();
+
+        // 查询供应商银行账户列表
+        List<SupplierBankVo> bankList = baseMapper.selectBankListBySupplierId(supplierId);
+
+        if (CollUtil.isEmpty(bankList)) {
+            return payMoeInfList;
+        }
+
+        for (SupplierBankVo bank : bankList) {
+            org.dromara.customer.api.erp.domain.push.PayMoeInf payMoeInf = new org.dromara.customer.api.erp.domain.push.PayMoeInf();
+
+            payMoeInf.setSupInfoId(bank.getSupplierNo()); // 编号
+            payMoeInf.setBnAccNo(bank.getBankNo()); // 银行账号
+            payMoeInf.setMainAcc("1".equals(bank.getIsture())); // 主账户(有效性标识)
+            payMoeInf.setAccBn(bank.getBankName()); // 开户银行
+            payMoeInf.setAccNm(bank.getCirclesName()); // 账户名称
+            payMoeInf.setTelNo(bank.getPhone()); // 联系电话
+            // 有效期从/至 - 暂无对应字段,留空
+
+            payMoeInfList.add(payMoeInf);
+        }
+
+        return payMoeInfList;
+    }
 
     /*@Override
     public boolean srmUpdateByBo(SupplierInfoBo bo) throws JsonProcessingException {

+ 51 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/SupplierInfoMapper.xml

@@ -4,4 +4,55 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.dromara.customer.mapper.SupplierInfoMapper">
 
+    <!-- 根据供应商ID查询地址列表 -->
+    <select id="selectAddrListBySupplierId" resultType="org.dromara.customer.domain.vo.SupplierAddressVo">
+        SELECT
+            id,
+            supplier_no AS supplierNo,
+            supplier_id AS supplierId,
+            shipping_company AS shippingCompany,
+            shipper_name AS shipperName,
+            shipper_phone AS shipperPhone,
+            shipping_post_code AS shippingPostCode,
+            shipping_provincial AS shippingProvincial,
+            shipping_city AS shippingCity,
+            shipping_county AS shippingCounty,
+            shipping_address AS shippingAddress,
+            push_status AS pushStatus,
+            is_self AS isSelf,
+            type,
+            address_no AS addressNo,
+            status,
+            remark
+        FROM supplier_address
+        WHERE supplier_id = #{supplierId}
+          AND del_flag = '0'
+        ORDER BY id ASC
+    </select>
+
+    <!-- 根据供应商ID查询银行账户列表 -->
+    <select id="selectBankListBySupplierId" resultType="org.dromara.customer.domain.vo.SupplierBankVo">
+        SELECT
+            id,
+            num,
+            supplier_no AS supplierNo,
+            supplier_id AS supplierId,
+            bank_num AS bankNum,
+            bank_info_no AS bankInfoNo,
+            bank_name AS bankName,
+            bank_no AS bankNo,
+            isture,
+            circles_name AS circlesName,
+            phone,
+            invoice_type_no AS invoiceTypeNo,
+            invoice_type_name AS invoiceTypeName,
+            push_status AS pushStatus,
+            status,
+            remark
+        FROM supplier_bank
+        WHERE supplier_id = #{supplierId}
+          AND del_flag = '0'
+        ORDER BY num ASC
+    </select>
+
 </mapper>

+ 102 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/erp/ErpPushController.java

@@ -1,8 +1,110 @@
 package org.dromara.external.controller.erp;
 
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.R;
+import org.dromara.external.api.erp.domain.ErpAuth;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static kotlin.reflect.jvm.internal.impl.builtins.StandardNames.FqNames.map;
+
 /**
+ * ERP数据推送控制器
  * @author
  * @date 2026/1/15 下午2:38
  */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/erp/push")
+@Slf4j
 public class ErpPushController {
+
+    private final String APPID = "chiesbaab5c67645606cf2";
+
+    private final String AppSecret = "5A7AEB451CE6470614D5066046AC2FA2";
+
+    private final String url = "http://119.97.180.91:890";
+
+    /**
+    * 认证
+    * */
+    public ErpAuth auth() {
+        log.info("认证开始...");
+        //发送请求的utc时间年月日时分秒共14位
+        String time = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
+        String sign = getSign(time);
+
+        Map<String, String> map = new HashMap<>();
+        map.put("appid", APPID);
+        map.put("time", time);
+        map.put("sign", sign);
+        //获取认证信息
+        String post = HttpUtil.post(url + "/esb/api/auth.do", JSONUtil.toJsonStr(map));
+        log.info("认证结果:{}", post);
+        return JSONUtil.toBean(post, ErpAuth.class);
+    }
+
+
+    /**
+    * 获取签名信息
+    **/
+    private String getSign(String time) {
+        //签名,
+        //appid+appsecret+token+time 进行md5加密;32位大写
+        String sign = SecureUtil.md5(APPID + AppSecret + time).toUpperCase();
+        return sign;
+    }
+
+    /**
+    * 新增数据
+    * */
+    @PostMapping("/add")
+    public String add(String progId, String data) {
+        log.info("新增数据开始...");
+        String time = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
+        String sign = getSign(time);
+        ErpAuth auth = this.auth();
+        Map<String, Object> map = new HashMap<>();
+        map.put("token", auth.getToken());
+        map.put("sign", sign);
+        map.put("time", time);
+        map.put("progId",progId);
+        map.put("data", data);
+        String post = HttpUtil.post(url + "/esb/erp/addnew.do", JSONUtil.toJsonStr(map));
+        return "新增数据成功";
+    }
+
+    /**
+    * 更新数据
+    * */
+    @PostMapping("/update")
+    public String update(String progId, String data) {
+        log.info("更新数据开始...");
+        String time = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
+        String sign = getSign(time);
+        ErpAuth auth = this.auth();
+        Map<String, Object> map = new HashMap<>();
+        map.put("token", auth.getToken());
+        map.put("sign", sign);
+        map.put("time", time);
+        map.put("progId",progId);
+        map.put("data", data);
+        String post = HttpUtil.post(url + "/esb/erp/update.do", JSONUtil.toJsonStr(map));
+        return "更新数据成功";
+    }
+
+    public static void main(String[] args) {
+    }
+
 }

+ 6 - 2
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/thirdparty/ThirdpartyCommonController.java

@@ -7,9 +7,12 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.domain.R;
-import org.dromara.external.api.thirdparty.domain.bo.ThirdpartyProductBrandBo;
-import org.dromara.external.api.thirdparty.domain.bo.ThirdpartyProductUnitBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductBrandBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductUnitBo;
 import org.dromara.external.api.thirdparty.domain.dto.*;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductBrandDto;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductCateDto;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductUnitDto;
 import org.dromara.system.api.RemoteComLogisticsCompanyService;
 import org.dromara.system.api.domain.vo.RemoteLogisticsCompanyVo;
 import org.springframework.validation.annotation.Validated;
@@ -34,6 +37,7 @@ public class ThirdpartyCommonController {
     @DubboReference
     private RemoteComLogisticsCompanyService remoteComLogisticsCompanyService;
 
+
     /**
      * 获取区域地址
      */

+ 31 - 15
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/thirdparty/ThirdpartyProductController.java

@@ -4,17 +4,19 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
-import org.dromara.external.api.thirdparty.domain.bo.ThirdpartyProductBo;
-import org.dromara.external.api.thirdparty.domain.bo.ThirdpartyProductDetailBo;
-import org.dromara.external.api.thirdparty.domain.bo.ThirdpartyProductStockBo;
-import org.dromara.external.api.thirdparty.domain.dto.ThirdpartyProductDetailDto;
-import org.dromara.external.api.thirdparty.domain.dto.ThirdpartyProductDto;
-import org.dromara.external.api.thirdparty.domain.dto.ThirdpartyProductPriceDto;
-import org.dromara.external.api.thirdparty.domain.dto.ThirdpartyProductStatusDto;
-import org.dromara.external.api.thirdparty.domain.dto.ThirdpartyProductStockDto;
+import org.dromara.product.api.thirdparty.domain.RemoteThirdpartyProductService;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductDetailBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductStockBo;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductDetailDto;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductDto;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductPriceDto;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductStatusDto;
+import org.dromara.product.api.thirdparty.domain.dto.ThirdpartyProductStockDto;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -34,6 +36,9 @@ import java.util.List;
 @RequestMapping("/api/thirdparty")
 public class ThirdpartyProductController {
 
+    @DubboReference
+    private RemoteThirdpartyProductService remoteThirdpartyProductService;
+
     /**
      * 产品列表查询
      */
@@ -41,7 +46,11 @@ public class ThirdpartyProductController {
     @PostMapping("/product/getListProducts")
     public TableDataInfo<ThirdpartyProductDto> getListProducts(@RequestBody ThirdpartyProductBo bo, PageQuery pageQuery) {
         // TODO: 实现产品列表查询逻辑
-        return TableDataInfo.build();
+        List<ThirdpartyProductDto> listProducts = remoteThirdpartyProductService.getListProducts(bo, pageQuery.getPageNum(), pageQuery.getPageSize());
+        Long productCount = remoteThirdpartyProductService.getProductCount(bo);
+        TableDataInfo<ThirdpartyProductDto> dataInfo = TableDataInfo.build(listProducts);
+        dataInfo.setTotal(productCount);
+        return dataInfo;
     }
 
     /**
@@ -50,8 +59,10 @@ public class ThirdpartyProductController {
     @Operation(summary = "产品表体接口")
     @PostMapping("/product/getProductBody")
     public R<ThirdpartyProductDto> getProductBody(@RequestBody ThirdpartyProductDetailBo bo) {
+
         // TODO: 实现产品表体查询逻辑
-        return R.ok();
+        ThirdpartyProductDto productBody = remoteThirdpartyProductService.getProductBody(bo);
+        return R.ok(productBody);
     }
 
     /**
@@ -61,7 +72,8 @@ public class ThirdpartyProductController {
     @PostMapping("/product/getProductDetails")
     public R<ThirdpartyProductDetailDto> getProductDetails(@RequestBody ThirdpartyProductDetailBo bo) {
         // TODO: 实现产品详情查询逻辑
-        return R.ok();
+        ThirdpartyProductDetailDto productDetails = remoteThirdpartyProductService.getProductDetails(bo);
+        return R.ok(productDetails);
     }
 
     /**
@@ -71,7 +83,8 @@ public class ThirdpartyProductController {
     @PostMapping("/product/getPrice")
     public R<ThirdpartyProductPriceDto> getPrice(@RequestBody ThirdpartyProductDetailBo bo) {
         // TODO: 实现产品价格查询逻辑
-        return R.ok();
+        ThirdpartyProductPriceDto productPrice = remoteThirdpartyProductService.getPrice(bo);
+        return R.ok(productPrice);
     }
 
     /**
@@ -81,7 +94,8 @@ public class ThirdpartyProductController {
     @PostMapping("/product/getBatchPrice")
     public R<List<ThirdpartyProductPriceDto>> getBatchPrice(@RequestBody ThirdpartyProductDetailBo bo) {
         // TODO: 实现批量价格查询逻辑
-        return R.ok();
+        List<ThirdpartyProductPriceDto> batchPrice = remoteThirdpartyProductService.getBatchPrice(bo);
+        return R.ok(batchPrice);
     }
 
     /**
@@ -91,7 +105,8 @@ public class ThirdpartyProductController {
     @PostMapping("/product/getProductStock")
     public R<ThirdpartyProductStockDto> getProductStock(@RequestBody ThirdpartyProductStockBo bo) {
         // TODO: 实现商品库存查询逻辑
-        return R.ok();
+        ThirdpartyProductStockDto productStock = remoteThirdpartyProductService.getProductStock(bo);
+        return R.ok(productStock);
     }
 
     /**
@@ -101,6 +116,7 @@ public class ThirdpartyProductController {
     @PostMapping("/product/getStatus")
     public R<ThirdpartyProductStatusDto> getStatus(@RequestBody ThirdpartyProductDetailBo bo) {
         // TODO: 实现产品状态查询逻辑
-        return R.ok();
+        ThirdpartyProductStatusDto productStatus = remoteThirdpartyProductService.getStatus(bo);
+        return R.ok(productStatus);
     }
 }

+ 3 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/tongji/TongJiPushController.java

@@ -545,6 +545,9 @@ public class TongJiPushController {
         if (trackVo == null){
             return ZCR.fail("5035", "物流信息为空");
         }
+        if(ObjectUtil.isEmpty(trackVo.getDeliveryTrack())){
+            log.error("物流查询 - 响应数据为空,{}", JSONUtil.toJsonStr(trackVo));
+        }
         // 6. 业务参数→JSON→Base64编码(符合文档data字段要求)
         String respBizJson = JSONUtil.toJsonStr(trackVo);
         String respDataBase64 = Base64.encode(respBizJson, StandardCharsets.UTF_8);

+ 12 - 6
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/zhongche/ZhongChePushController.java

@@ -436,7 +436,12 @@ public class ZhongChePushController {
     /**
      * 核心业务:查询商品价格,映射文档返回格式
      */
-    public PricesVo queryGoodsPrice(List<String> goodsIdList) {
+    public PricesVo queryGoodsPrice(List<String> goodsIdList) {        //获取项目配置
+        ExternalItem externalItem = externalItemService.getOne(
+            Wrappers.lambdaQuery(ExternalItem.class)
+                .eq(ExternalItem::getItemKey, "zhongche")
+                .last("LIMIT 1")
+        );
         // 1. 初始化响应结果
         PricesVo pricesVo = new PricesVo();
         pricesVo.setPrices(new ArrayList<>());
@@ -469,6 +474,7 @@ public class ZhongChePushController {
             // 协议价格(price)
             ExternalProduct one = externalProductService.getOne(new LambdaQueryWrapper<>(ExternalProduct.class)
                 .eq(ExternalProduct::getProductNo, goodsId)
+                .eq(ExternalProduct::getItemId, externalItem.getId())
                 .last("LIMIT 1")
             );
             if (one == null) {
@@ -481,17 +487,14 @@ public class ZhongChePushController {
             }
             priceResp.setTaxFreePrice(null);
             priceResp.setTax(BigDecimal.valueOf(0.13));
-            // 税收编码(非必填)
-            priceResp.setTaxCode("107022301");
             // 3.3 添加到响应列表
             pricesVo.getPrices().add(priceResp);
         }
         return pricesVo;
     }
     @GetMapping("/get/queryGoodsPrice")
-    public R getMallOrderNo() {
-
-        PricesVo pricesVo = queryGoodsPrice(List.of("002171604"));
+    public R getMallOrderNo(String productNos) {
+        PricesVo pricesVo = queryGoodsPrice(Arrays.asList(productNos.split(",")));
         return R.ok(pricesVo);
     }
 
@@ -543,6 +546,9 @@ public class ZhongChePushController {
         if (trackVo == null){
             return ZCR.fail("5035", "物流信息为空");
         }
+        if(ObjectUtil.isEmpty(trackVo.getDeliveryTrack())){
+            log.error("物流查询 - 响应数据为空,{}", JSONUtil.toJsonStr(trackVo));
+        }
         // 6. 业务参数→JSON→Base64编码(符合文档data字段要求)
         String respBizJson = JSONUtil.toJsonStr(trackVo);
         String respDataBase64 = Base64.encode(respBizJson, StandardCharsets.UTF_8);

+ 36 - 4
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/dubbo/RemoteErpPushServiceImpl.java

@@ -1,13 +1,24 @@
 package org.dromara.external.dubbo;
 
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
 import org.dromara.external.api.service.RemoteErpPushService;
+import org.dromara.external.controller.erp.ErpPushController;
+import org.springframework.stereotype.Service;
 
 /**
  * @author
  * @date 2026/6/10 下午5:34
  */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@DubboService
 public class RemoteErpPushServiceImpl implements RemoteErpPushService {
 
+    private final ErpPushController erpPushController;
+
     /**
      * 商品数据推送
      *
@@ -16,7 +27,11 @@ public class RemoteErpPushServiceImpl implements RemoteErpPushService {
      */
     @Override
     public void pushProductData(String productJson, boolean isUpdate) {
-
+        if (isUpdate) {
+            erpPushController.update("comProdInfo", productJson);
+        } else {
+            erpPushController.add("comProdInfo", productJson);
+        }
     }
 
     /**
@@ -27,6 +42,11 @@ public class RemoteErpPushServiceImpl implements RemoteErpPushService {
      */
     @Override
     public void pushCustomerData(String customerJson, boolean isUpdate) {
+        if (isUpdate) {
+            erpPushController.update("comCustInfo", customerJson);
+        } else {
+            erpPushController.add("comCustInfo", customerJson);
+        }
 
     }
 
@@ -38,7 +58,11 @@ public class RemoteErpPushServiceImpl implements RemoteErpPushService {
      */
     @Override
     public void pushSupplierData(String supplierJson, boolean isUpdate) {
-
+        if (isUpdate) {
+            erpPushController.update("comSupInfo", supplierJson);
+        } else {
+            erpPushController.add("comSupInfo", supplierJson);
+        }
     }
 
     /**
@@ -49,7 +73,11 @@ public class RemoteErpPushServiceImpl implements RemoteErpPushService {
      */
     @Override
     public void pushOrderData(String orderJson, boolean isUpdate) {
-
+        if (isUpdate) {
+            erpPushController.update("salSO", orderJson);
+        } else {
+            erpPushController.add("salSO", orderJson);
+        }
     }
 
     /**
@@ -60,6 +88,10 @@ public class RemoteErpPushServiceImpl implements RemoteErpPushService {
      */
     @Override
     public void pushAfterSaleData(String afterSaleJson, boolean isUpdate) {
-
+        if (isUpdate) {
+            erpPushController.update("salBkApl", afterSaleJson);
+        } else {
+            erpPushController.add("salBkApl", afterSaleJson);
+        }
     }
 }

+ 32 - 1
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/ZhongChePushStrategy.java

@@ -26,6 +26,7 @@ import org.dromara.external.mapper.ExternalProductMapper;
 import org.dromara.external.service.IExternalProductCategoryService;
 import org.dromara.external.service.IExternalPushPoolLogService;
 import org.dromara.product.api.RemoteProductService;
+import org.dromara.product.api.domain.ProductClassificationDiyDto;
 import org.dromara.product.api.domain.ProductVo;
 import org.dromara.product.api.domain.zhongche.dto.ProductAggregateDto;
 import org.springframework.stereotype.Component;
@@ -336,9 +337,38 @@ public class ZhongChePushStrategy implements ProductPushStrategy {
         try {
             GoodsPropertiesUpdateBo bo = new GoodsPropertiesUpdateBo();
             bo.setAccount(username);
+            List<ProductClassificationDiyDto> productCustomAttribute = remoteProductService.getProductCustomAttribute(products.stream().map(ProductVo::getId).toList());
+            Map<Long, List<ProductClassificationDiyDto>> collect = productCustomAttribute.stream().collect(Collectors.groupingBy(ProductClassificationDiyDto::getProductId));
             bo.setGoods(products.stream().map(item ->{
                 GoodsPropertiesUpdateItem goodsImageUpdateItem = new GoodsPropertiesUpdateItem();
-                goodsImageUpdateItem.setProperties("{\"保质期\":\"12个月\"}");
+                if (collect.containsKey(item.getId())){
+                    List<ProductClassificationDiyDto> productClassificationDiyDtos = collect.get(item.getId());
+                    //格式为{attributeKey:"",attributeValue:""}转换成 {key:value,key:value,key:value|:value}格式
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("{");
+                    for (int i = 0; i < productClassificationDiyDtos.size(); i++) {
+                        ProductClassificationDiyDto e = productClassificationDiyDtos.get(i);
+                        String attributeKey = e.getAttributeKey();
+                        String attributeValue = e.getAttributeValue();
+
+                        // 处理 attributeValue 可能是逗号拼接的情况
+                        if (attributeValue != null && attributeValue.contains(",")) {
+                            // 如果是逗号拼接的值,保持原样或根据需求处理
+                            sb.append("\"").append(attributeKey).append("\":\"").append(attributeValue).append("\"");
+                        } else {
+                            sb.append("\"").append(attributeKey).append("\":\"").append(attributeValue).append("\"");
+                        }
+                        // 添加逗号分隔符(除了最后一个元素)
+                        if (i < productClassificationDiyDtos.size() - 1) {
+                            sb.append(",");
+                        }
+                    }
+                    sb.append("}");
+                    goodsImageUpdateItem.setProperties(sb.toString());
+                }else {
+                    goodsImageUpdateItem.setProperties("{\"保质期\":\"12个月\"}");
+                }
+
                 goodsImageUpdateItem.setGoodsId(item.getProductNo());
                 return goodsImageUpdateItem;
             }).toList());
@@ -375,6 +405,7 @@ public class ZhongChePushStrategy implements ProductPushStrategy {
                 goodsImageUpdateItem.setGoodsUrl("https://item.xiaoluwebsite.xyz/item?productNo="+item.getProductNo());
                 goodsImageUpdateItem.setThirdUrl(item.getReferenceLink());
                 goodsImageUpdateItem.setTaxCode(externalProduct.getTaxCode());
+                goodsImageUpdateItem.setTax(BigDecimal.valueOf(0.13));
                 goodsImageUpdateItem.setUnit(item.getUnitName());
                 ExternalProductCategory externalProductCategory = productCategoryMap.get(externalProduct.getExternalCategoryId());
                 goodsImageUpdateItem.setStandardCatalogId(externalProductCategory.getCategoryNo());

+ 8 - 8
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/impl/InvoiceApplyMessageHandler.java

@@ -61,22 +61,22 @@ public class InvoiceApplyMessageHandler implements MallMessageHandler {
             //TODO解析TOKEN 获取电商账号
             //先假设一个电商账号 123456
             // 2️、 查询开票申请订单列表(分页)
-            String account = "123456";
-            List<InvoiceOrder> invoiceOrders = queryAllInvoiceOrders(account, applyNo);
-
-            if (CollUtil.isEmpty(invoiceOrders)) {
-                return new MessageVo("0", "开票申请无订单数据");
-            }
+//            String account = "123456";
+//            List<InvoiceOrder> invoiceOrders = queryAllInvoiceOrders(account, applyNo);
+//
+//            if (CollUtil.isEmpty(invoiceOrders)) {
+//                return new MessageVo("0", "开票申请无订单数据");
+//            }
 
             // 3、 和本地数据核对
-            //boolean checkResult = remoteExternalOrderService.checkInvoiceData(invoiceApplyDetailVo, invoiceOrders);
+//            boolean checkResult = remoteExternalOrderService.checkInvoiceData(invoiceApplyDetailVo, invoiceOrders);
 
             /*if (!checkResult) {
                 return new MessageVo("0", "开票数据校验不通过");
             }*/
 
             // 4、更新本地开票申请状态
-            //invoiceService.saveOrUpdateInvoice(invoiceApplyDetailVo, invoiceOrders);
+//            invoiceService.saveOrUpdateInvoice(invoiceApplyDetailVo, invoiceOrders);
 
             return new MessageVo("1", "开票申请处理成功");
 

+ 3 - 3
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/service/impl/ExternalProductServiceImpl.java

@@ -263,9 +263,9 @@ public class ExternalProductServiceImpl  extends ServiceImpl<ExternalProductMapp
                 .eq(ExternalProductCategory::getItemId, itemId)
             );
             Map<Long, ExternalProductCategory> productCategoryMap = productCategoryList.stream().collect(Collectors.toMap(ExternalProductCategory::getId, Function.identity()));
-//            strategy.updateDetail(itemId,productDetails,productMap,productCategoryMap);
-//            strategy.updatePrice(itemId,productDetails,productMap);
-//            strategy.updateProperties(itemId,productDetails,productMap);
+            strategy.updateDetail(itemId,productDetails,productMap,productCategoryMap);
+            strategy.updatePrice(itemId,productDetails,productMap);
+            strategy.updateProperties(itemId,productDetails,productMap);
             strategy.updateImages(itemId,productDetails,productMap);
         }
         return baseMapper.update(Wrappers.lambdaUpdate(ExternalProduct.class)

+ 2 - 2
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/pc/PcOrderController.java

@@ -410,8 +410,8 @@ public class PcOrderController extends BaseController {
 
 
             // 如果远程服务只接受字符串
-            List<ProductVo> productDetails = remoteProductService.getProductDetails(productIds);
-
+//            List<ProductVo> productDetails = remoteProductService.getProductDetails(productIds);
+            List<ProductVo> productDetails = remoteProductService.getProductDetailsV2(productIds);
             // 4. 构建订单商品列表
             List<OrderProductBo> orderProductBos = productDetails.stream()
                 .map(productVo -> {

+ 1 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductBrandController.java

@@ -84,6 +84,7 @@ public class ProductBrandController extends BaseController {
     @RepeatSubmit()
     @PostMapping()
     public R<Void> add(@Validated(AddGroup.class) @RequestBody ProductBrandBo bo) {
+        bo.setDataSource("youyi");
         return toAjax(productBrandService.insertByBo(bo));
     }
 

+ 11 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductCategoryController.java

@@ -4,6 +4,7 @@ import java.util.List;
 
 import cn.hutool.core.lang.tree.Tree;
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
@@ -96,6 +97,16 @@ public class ProductCategoryController extends BaseController {
     @RepeatSubmit()
     @PostMapping()
     public R<Void> add(@Validated(AddGroup.class) @RequestBody ProductCategoryBo bo) {
+        //获取上级
+        ProductCategory parent = productCategoryService.getById(bo.getParentId());
+        if (parent != null ) {
+            bo.setClassLevel(parent.getClassLevel() + 1);
+            bo.setAncestors(parent.getAncestors() + "," + parent.getId());
+        }
+        if (ObjectUtil.isNotEmpty(bo.getClassLevel()) && bo.getClassLevel() > 3) {
+            throw new RuntimeException("类目层级不能大于3");
+        }
+        bo.setDataSource("youyi");
         return toAjax(productCategoryService.insertByBo(bo));
     }
 

+ 85 - 3
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/IndexProductController.java

@@ -1,16 +1,23 @@
 package org.dromara.product.controller.pc;
 
 import cn.hutool.core.lang.tree.Tree;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
+import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
+import org.dromara.product.domain.*;
 import org.dromara.product.domain.bo.*;
 import org.dromara.product.domain.vo.*;
 import org.dromara.product.esmapper.ProductEsMapper;
+import org.dromara.product.mapper.ProtocolProductsMapper;
 import org.dromara.product.service.*;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -70,6 +77,14 @@ public class IndexProductController {
     private final IProductAttributesService productAttributeService;
     //商品es
     private final ProductEsMapper productEsMapper;
+    //分类绑定
+    private final IProductCategoryBindingService productCategoryBindingService;
+
+    //协议
+    private final IProtocolInfoService protocolInfoService;
+    //协议商品
+    private final IProtocolProductsService protocolProductsService;
+    private final IClientSiteService clientSiteService;
 
     public static void main(String[] args) {
         String string = HttpUtil.get("http://127.0.0.1:8080/product/indexProduct/getEsProductList");
@@ -109,7 +124,8 @@ public class IndexProductController {
     * */
     @GetMapping("getProductDetail/{productId}")
     public R<ProductBaseVo> getProductDetail(@PathVariable Long productId) {
-        return R.ok(productBaseService.queryById(productId));
+        ProductBaseVo productBaseVo = productBaseService.queryById(productId);
+        return R.ok(handlingProductDetail(productBaseVo));
     }
 
     /**
@@ -121,11 +137,68 @@ public class IndexProductController {
             .eq(ProductBaseVo::getProductNo, productNo)
         );
         if (productBaseVo != null) {
-            return R.ok(productBaseService.queryById(productBaseVo.getId()));
+            ProductBaseVo productBaseVo1 = productBaseService.queryById(productBaseVo.getId());
+
+            return R.ok(handlingProductDetail(productBaseVo1));
         }
         return R.ok(productBaseVo);
     }
 
+    private ProductBaseVo handlingProductDetail(ProductBaseVo productBaseVo) {
+        //首先需要登录
+        if(LoginHelper.isLogin()){
+            //如果是企业购
+            Long customerId = LoginHelper.getLoginUser().getCustomerId();
+            if(ObjectUtil.isNotEmpty(customerId)){
+                //查询是否是大客户
+                ClientSite one = clientSiteService.getOne(Wrappers.lambdaQuery(ClientSite.class)
+                    .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
+                    .eq(ClientSite::getStatus, "0")
+                    .last("limit 1")
+                );
+                if(ObjectUtil.isNotEmpty(one)){
+                    //是大客户则查询协议商品池,获取对应的分类和协议价
+                    ProtocolInfo protocolInfo = protocolInfoService.getOne(Wrappers.lambdaQuery(ProtocolInfo.class)
+                        .eq(ProtocolInfo::getCustomerId, customerId)
+                        .orderByDesc(ProtocolInfo::getCreateTime)
+                        .last("limit 1")
+                    );
+                    if(ObjectUtil.isEmpty(protocolInfo)){
+                        throw new ServiceException("协议信息不存在");
+                    }
+                    ProtocolProducts protocolProducts = protocolProductsService.getOne(Wrappers.lambdaQuery(ProtocolProducts.class)
+                        .eq(ProtocolProducts::getProtocolId, protocolInfo.getId())
+                        .eq(ProtocolProducts::getProductId, productBaseVo.getId())
+                    );
+                    if(ObjectUtil.isEmpty(protocolProducts)){
+                        throw new ServiceException("协议商品不存在");
+                    }
+                    productBaseVo.setMemberPrice(ObjectUtil.isNotEmpty(protocolProducts.getAgreementPrice())
+                        ? protocolProducts.getAgreementPrice() : productBaseVo.getMemberPrice());
+                    //分类处理
+                    ProductCategoryBinding categoryBinding = productCategoryBindingService.getOne(Wrappers.lambdaQuery(ProductCategoryBinding.class)
+                        .eq(ProductCategoryBinding::getProductId, productBaseVo.getId())
+                        .eq(ProductCategoryBinding::getPlatform, 4L)
+                        .last("limit 1"));
+                    productBaseVo.setTopCategoryId(categoryBinding.getTopCategoryId());
+                    productBaseVo.setMediumCategoryId(categoryBinding.getMediumCategoryId());
+                    productBaseVo.setBottomCategoryId(categoryBinding.getBottomCategoryId());
+                }else {
+                    //分类处理
+                    ProductCategoryBinding categoryBinding = productCategoryBindingService.getOne(Wrappers.lambdaQuery(ProductCategoryBinding.class)
+                        .eq(ProductCategoryBinding::getProductId, productBaseVo.getId())
+                        .eq(ProductCategoryBinding::getPlatform, 3L)
+                        .last("limit 1"));
+                    productBaseVo.setTopCategoryId(categoryBinding.getTopCategoryId());
+                    productBaseVo.setMediumCategoryId(categoryBinding.getMediumCategoryId());
+                    productBaseVo.setBottomCategoryId(categoryBinding.getBottomCategoryId());
+                }
+
+            }
+        }
+        return productBaseVo;
+    }
+
     /**
     * 获取商品预览界面
     * */
@@ -137,7 +210,6 @@ public class IndexProductController {
     }
 
 
-
     /**
      * 查询产品分类树
      * */
@@ -146,6 +218,16 @@ public class IndexProductController {
         return R.ok(productCategoryService.selectCategoryTreeList(bo));
     }
 
+    /**
+    * 获取分类下的的属性
+    * */
+    @GetMapping("getProductCategoryAttributeList/{categoryId}")
+    public R<List<ProductAttributes>> getProductCategoryAttributeList(@PathVariable Long categoryId) {
+        LambdaQueryWrapper<ProductAttributes> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(ProductAttributes::getCategoryId, categoryId);
+        return R.ok(productAttributeService.list(wrapper));
+    }
+
     /**
     * 查看分类列表
     * */

+ 5 - 2
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/PcProductBo.java

@@ -81,7 +81,10 @@ public class PcProductBo {
     * */
     private String sortOrder;
 
-
-
+    /**
+     * 商品属性列表(前端传值格式: 属性名:属性值|属性名:属性值)
+     * 例如: 测试:123|商品123:123
+     */
+    private String attributesList;
 
 }

+ 6 - 43
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductBaseImportVo.java

@@ -126,17 +126,16 @@ public class ProductBaseImportVo implements Serializable {
     @ExcelProperty(value = "品牌资料名称", index = 16)
     private String brandName;
 
-    /**
-     * 单位名称
-     */
-    @ExcelProperty(value = "单位名称", index = 17)
-    private String unitName;
-
     /**
      * 单位编号
      */
-    @ExcelProperty(value = "单位编号", index = 18)
+    @ExcelProperty(value = "单位编号", index = 17)
     private String unitNo;
+    /**
+     * 单位名称
+     */
+    @ExcelProperty(value = "单位名称", index = 18)
+    private String unitName;
 
     /**
      * 参考链接
@@ -200,40 +199,4 @@ public class ProductBaseImportVo implements Serializable {
      */
     @ExcelProperty(value = "上下架状态", index = 28)
     private String shelfStatus;
-
-    /**
-     * 主图
-     */
-    @ExcelProperty(value = "主图", index = 29)
-    private String mainImage;
-
-    /**
-     * 轮播图
-     */
-    @ExcelProperty(value = "轮播图", index = 30)
-    private String carouselImages;
-
-    /**
-     * 商品详情
-     */
-    @ExcelProperty(value = "商品详情", index = 31)
-    private String productDetail;
-
-    /**
-     * 售后服务
-     */
-    @ExcelProperty(value = "售后服务", index = 32)
-    private String afterSalesService;
-
-    /**
-     * 服务保障
-     */
-    @ExcelProperty(value = "服务保障", index = 33)
-    private String serviceGuarantee;
-
-    /**
-     * 备注
-     */
-    @ExcelProperty(value = "备注", index = 34)
-    private String remark;
 }

+ 61 - 7
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/dubbo/RemoteProductServiceImpl.java

@@ -16,13 +16,9 @@ import org.apache.dubbo.config.annotation.DubboService;
 import org.apache.seata.common.metadata.namingserver.Unit;
 import org.dromara.common.core.domain.zhongche.domain.Prices;
 import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.product.api.RemoteProductService;
-import org.dromara.product.api.domain.ProductCategoryRemoteVo;
-import org.dromara.product.api.domain.ProductChangeLogApiVo;
-import org.dromara.product.api.domain.ProductVo;
-import org.dromara.product.api.domain.SiteProductRemoteBo;
-import org.dromara.product.api.domain.SiteProductRemoteResult;
-import org.dromara.product.api.domain.SiteProductRemoteVo;
+import org.dromara.product.api.domain.*;
 import org.dromara.product.api.domain.zhongche.dto.OrderProductDto;
 import org.dromara.product.api.domain.zhongche.dto.ProductAggregateDto;
 import org.dromara.product.api.domain.zhongche.dto.StocksResult;
@@ -33,7 +29,6 @@ import org.dromara.product.domain.*;
 import org.dromara.product.domain.bo.SiteProductBo;
 import org.dromara.product.domain.vo.*;
 import org.dromara.product.domain.bo.ProductCategoryBo;
-import org.dromara.product.api.domain.RemoteProductBrand;
 import org.dromara.product.domain.bo.ProductBaseBo;
 import org.dromara.product.domain.bo.ProductChangeLogBo;
 import org.dromara.product.service.*;
@@ -81,6 +76,11 @@ public class RemoteProductServiceImpl implements RemoteProductService {
 
     private final IProductPhotosService productPhotosService;
 
+    private final IClientSiteService clientSiteService;
+
+    private final IProtocolInfoService protocolInfoService;
+
+    private final IProtocolProductsService protocolProductsService;
     /**
      * 获取商品详情
      *
@@ -123,6 +123,47 @@ public class RemoteProductServiceImpl implements RemoteProductService {
         return BeanUtil.copyToList(productBaseVos, ProductVo.class);
     }
 
+    /**
+     * 获取多个商品详情 v2
+     *
+     * @param productIds
+     * @return
+     */
+    @Override
+    public List<ProductVo> getProductDetailsV2(List<Long> productIds) {
+        List<ProductBaseVo> productBaseVos = productBaseService.selectProductBaseVoList(productIds);
+        if(LoginHelper.isLogin()){
+            if(Objects.equals(LoginHelper.getLoginUser().getUserSonType(),"3")){
+                //查询是否是大客户
+                ClientSite one = clientSiteService.getOne(Wrappers.lambdaQuery(ClientSite.class)
+                    .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
+                    .eq(ClientSite::getStatus, "0")
+                    .last("limit 1")
+                );
+                if (ObjectUtil.isNotEmpty(one)){
+                    //如果是大客户只查询客户协议池中的商品
+                    //获取协议
+                    ProtocolInfo protocolInfo = protocolInfoService.getOne(Wrappers.lambdaQuery(ProtocolInfo.class)
+                        .eq(ProtocolInfo::getCustomerId, LoginHelper.getLoginUser().getCustomerId())
+                        .orderByDesc(ProtocolInfo::getCreateTime)
+                        .last("limit 1")
+                    );
+                    //获取协议池
+                    List<ProtocolProducts> protocolProductsVos = protocolProductsService.list(Wrappers.lambdaQuery(ProtocolProducts.class)
+                        .eq(ProtocolProducts::getProtocolId, protocolInfo.getId())
+                    );
+                    if (CollUtil.isNotEmpty(protocolProductsVos)) {
+                        Map<Long, BigDecimal> map = protocolProductsVos.stream().collect(Collectors.toMap(ProtocolProducts::getProductId, ProtocolProducts::getAgreementPrice));
+                        productBaseVos.forEach(pcProductVo -> {
+                            pcProductVo.setMemberPrice(map.get(pcProductVo.getId()));
+                        });
+                    }
+                }
+            }
+        }
+        return BeanUtil.copyToList(productBaseVos, ProductVo.class);
+    }
+
     /**
      * 获取多个商品详情
      *
@@ -133,6 +174,19 @@ public class RemoteProductServiceImpl implements RemoteProductService {
         return List.of();
     }
 
+    /**
+     * 获取商品自定义属性
+     *
+     * @param productIds
+     */
+    @Override
+    public List<ProductClassificationDiyDto> getProductCustomAttribute(List<Long> productIds) {
+        List<ProductClassificationDiy> classificationDiyList = productClassificationDiyService.list(Wrappers.lambdaQuery(ProductClassificationDiy.class)
+            .in(ProductClassificationDiy::getProductId, productIds)
+        );
+
+        return BeanUtil.copyToList(classificationDiyList, ProductClassificationDiyDto.class);
+    }
 
     /**
      * 获取商品变更记录

+ 173 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/dubbo/RemoteThirdpartyProductServiceImpl.java

@@ -0,0 +1,173 @@
+package org.dromara.product.dubbo;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.product.api.thirdparty.domain.RemoteThirdpartyProductService;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductDetailBo;
+import org.dromara.product.api.thirdparty.domain.bo.ThirdpartyProductStockBo;
+import org.dromara.product.api.thirdparty.domain.dto.*;
+import org.dromara.product.domain.ProductBase;
+import org.dromara.product.domain.bo.ProductBaseBo;
+import org.dromara.product.domain.vo.ProductBaseVo;
+import org.dromara.product.service.IProductBaseService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author
+ * @date 2026/6/11 上午11:38
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@DubboService
+public class RemoteThirdpartyProductServiceImpl implements RemoteThirdpartyProductService {
+
+    private final IProductBaseService productBaseService;
+
+    /**
+     * 产品列表查询
+     *
+     * @param bo
+     * @param pageNum
+     * @param pageSize
+     */
+    @Override
+    public List<ThirdpartyProductDto> getListProducts(ThirdpartyProductBo bo, Integer pageNum, Integer pageSize) {
+        ProductBaseBo productBaseBo = new ProductBaseBo();
+        productBaseBo.setProductNos(bo.getProductNo());
+        productBaseBo.setBottomCategoryId(bo.getCategoryId());
+        productBaseBo.setItemName(bo.getItemName());
+        productBaseBo.setBrandId(bo.getBrandId());
+        TableDataInfo<ProductBaseVo> productBaseVoTableDataInfo = productBaseService.queryPageList(productBaseBo, new PageQuery(pageSize, pageNum));
+        List<ProductBaseVo> productBaseVos = productBaseVoTableDataInfo.getRows();
+        if(ObjectUtil.isEmpty(productBaseVos)){
+            return List.of();
+        }
+        return BeanUtil.copyToList(productBaseVos, ThirdpartyProductDto.class);
+    }
+
+    /**
+     * 产品数量
+     *
+     * @param bo
+     */
+    @Override
+    public Long getProductCount(ThirdpartyProductBo bo) {
+        ProductBaseBo productBaseBo = new ProductBaseBo();
+        productBaseBo.setProductNos(bo.getProductNo());
+        productBaseBo.setBottomCategoryId(bo.getCategoryId());
+        productBaseBo.setItemName(bo.getItemName());
+        productBaseBo.setBrandId(bo.getBrandId());
+        TableDataInfo<ProductBaseVo> productBaseVoTableDataInfo = productBaseService.queryPageList(productBaseBo, new PageQuery(1, 1));
+        return productBaseVoTableDataInfo.getTotal();
+    }
+
+    /**
+     * 产品表体接口
+     *
+     * @param bo
+     */
+    @Override
+    public ThirdpartyProductDto getProductBody(ThirdpartyProductDetailBo bo) {
+        //先通过商品编号查询Id,再查询数据
+        ProductBase productBase = productBaseService.getOne(Wrappers.lambdaQuery(ProductBase.class)
+            .eq(ProductBase::getProductNo, bo.getProductNo())
+            .last("limit 1")
+        );
+        if (ObjectUtil.isEmpty(productBase)){
+            return null;
+        }
+        ProductBaseVo productBaseVo = productBaseService.queryById(productBase.getId());
+        return BeanUtil.copyProperties(productBaseVo, ThirdpartyProductDto.class);
+    }
+
+    /**
+     * 产品详情查询
+     *
+     * @param bo
+     */
+    @Override
+    public ThirdpartyProductDetailDto getProductDetails(ThirdpartyProductDetailBo bo) {
+        //先通过商品编号查询Id,再查询数据
+        ProductBase productBase = productBaseService.getOne(Wrappers.lambdaQuery(ProductBase.class)
+            .eq(ProductBase::getProductNo, bo.getProductNo())
+            .last("limit 1")
+        );
+        if (ObjectUtil.isEmpty(productBase)){
+            return null;
+        }
+        ProductBaseVo productBaseVo = productBaseService.queryById(productBase.getId());
+        return BeanUtil.copyProperties(productBaseVo, ThirdpartyProductDetailDto.class);
+    }
+
+    /**
+     * 产品价格查询
+     *
+     * @param bo
+     */
+    @Override
+    public ThirdpartyProductPriceDto getPrice(ThirdpartyProductDetailBo bo) {
+        //先通过商品编号查询Id,再查询数据
+        ProductBase productBase = productBaseService.getOne(Wrappers.lambdaQuery(ProductBase.class)
+            .eq(ProductBase::getProductNo, bo.getProductNo())
+            .last("limit 1")
+        );
+        if (ObjectUtil.isEmpty(productBase)){
+            return null;
+        }
+        ProductBaseVo productBaseVo = productBaseService.queryById(productBase.getId());
+        return BeanUtil.copyProperties(productBaseVo, ThirdpartyProductPriceDto.class);
+    }
+
+    /**
+     * 商品查询价格(批量)
+     *
+     * @param bo
+     */
+    @Override
+    public List<ThirdpartyProductPriceDto> getBatchPrice(ThirdpartyProductDetailBo bo) {
+        
+        return List.of();
+    }
+
+    /**
+     * 商品库存查询
+     *
+     * @param bo
+     */
+    @Override
+    public ThirdpartyProductStockDto getProductStock(ThirdpartyProductStockBo bo) {
+        //先通过商品编号查询Id,再查询数据
+        ProductBase productBase = productBaseService.getOne(Wrappers.lambdaQuery(ProductBase.class)
+            .eq(ProductBase::getProductNo, bo.getProductNo())
+            .last("limit 1")
+        );
+        if (ObjectUtil.isEmpty(productBase)){
+            return null;
+        }
+        ProductBaseVo productBaseVo = productBaseService.queryById(productBase.getId());
+        ThirdpartyProductStockDto thirdpartyProductStockDto = new ThirdpartyProductStockDto();
+        thirdpartyProductStockDto.setProductNo(productBaseVo.getProductNo());
+        thirdpartyProductStockDto.setNum(productBaseVo.getTotalInventory());
+        return thirdpartyProductStockDto;
+    }
+
+    /**
+     * 产品状态查询
+     *
+     * @param bo
+     */
+    @Override
+    public ThirdpartyProductStatusDto getStatus(ThirdpartyProductDetailBo bo) {
+        return null;
+    }
+}

+ 37 - 32
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/listener/ProductBaseImportListener.java

@@ -106,17 +106,23 @@ public class ProductBaseImportListener extends AnalysisEventListener<ProductBase
             }
 
             // 检查产品编号是否已存在
-            List<ProductBase> existingList = productBaseService.list(Wrappers.<ProductBase>lambdaQuery()
-                .eq(ProductBase::getProductNo, importVo.getProductNo()));
+            ProductBase existing = productBaseService.getOne(Wrappers.<ProductBase>lambdaQuery()
+                .eq(ProductBase::getProductNo, importVo.getProductNo())
+                .last("limit 1")
+            );
+
+            boolean isUpdate = false;
 
-            if (ObjectUtil.isNotEmpty(existingList)) {
-                failureNum++;
-                failureMsg.append("<br/>").append(failureNum).append("、第 ").append(rowNum).append(" 行,产品编号 ").append(importVo.getProductNo()).append(" 已存在");
-                return;
-            }
 
             // 构建产品基础信息Bo
             ProductBaseBo productBo = new ProductBaseBo();
+            if (ObjectUtil.isNotEmpty(existing)) {
+                // 产品编号已存在,执行更新操作
+                ProductBase existingProduct = existing;
+                productBo.setId(existingProduct.getId());
+                isUpdate = true;
+            }
+
             productBo.setProductNo(importVo.getProductNo());
             productBo.setItemName(importVo.getProductName());
             productBo.setSpecificationsCode(importVo.getSpecificationsCode());
@@ -130,17 +136,19 @@ public class ProductBaseImportListener extends AnalysisEventListener<ProductBase
             productBo.setTaxRate(importVo.getTaxRate());
             productBo.setReferenceLink(importVo.getReferenceLink());
             productBo.setMarketPrice(importVo.getMarketPrice());
+            productBo.setMemberPrice(importVo.getOfficialPrice());
             productBo.setMinSellingPrice(importVo.getMinSellingPrice());
             productBo.setPurchasingPrice(importVo.getPurchasingPrice());
             productBo.setMaxPurchasePrice(importVo.getMaxPurchasePrice());
-            productBo.setAfterSalesService(importVo.getAfterSalesService());
-            productBo.setServiceGuarantee(importVo.getServiceGuarantee());
-            productBo.setRemark(importVo.getRemark());
 
             // 处理分类信息
             if (ObjectUtil.isNotEmpty(importVo.getCategoryNo())) {
                 // 根据分类编号查询分类ID
-                ProductCategory bottomCategory = productCategoryService.getOne(Wrappers.lambdaQuery(ProductCategory.class).eq(ProductCategory::getCategoryNo, importVo.getCategoryNo()));
+                ProductCategory bottomCategory = productCategoryService.getOne(Wrappers.lambdaQuery(ProductCategory.class)
+                    .eq(ProductCategory::getCategoryNo, importVo.getCategoryNo())
+                    .eq(ProductCategory::getPlatform,0L)
+                    .last("limit 1")
+                );
                 if (ObjectUtil.isNotEmpty(bottomCategory)) {
                     ProductCategory mediumCategory = productCategoryService.getById(bottomCategory.getParentId());
                     // 根据分类层级设置对应的分类ID
@@ -158,7 +166,11 @@ public class ProductBaseImportListener extends AnalysisEventListener<ProductBase
             // 处理品牌信息
             if (ObjectUtil.isNotEmpty(importVo.getBrandNo())) {
                 // 根据品牌编号查询品牌ID
-                ProductBrand productBrand = productBrandService.getOne(Wrappers.lambdaQuery(ProductBrand.class).eq(ProductBrand::getBrandNo, importVo.getBrandNo()));
+                ProductBrand productBrand = productBrandService.getOne(Wrappers.lambdaQuery(ProductBrand.class)
+                    .eq(ProductBrand::getBrandNo, importVo.getBrandNo())
+                    .eq(ProductBrand::getDataSource,"youyi")
+                    .last("limit 1")
+                );
                 if (ObjectUtil.isNotEmpty(productBrand)) {
                     productBo.setBrandId(productBrand.getId());
                 } else {
@@ -171,7 +183,11 @@ public class ProductBaseImportListener extends AnalysisEventListener<ProductBase
             // 处理单位信息
             if (ObjectUtil.isNotEmpty(importVo.getUnitNo())) {
                 // 根据单位编号查询单位ID
-                ProductUnit productUnit = productUnitService.getOne(Wrappers.lambdaQuery(ProductUnit.class).eq(ProductUnit::getUnitNo, importVo.getUnitNo()));
+                ProductUnit productUnit = productUnitService.getOne(Wrappers.lambdaQuery(ProductUnit.class)
+                    .eq(ProductUnit::getUnitNo, importVo.getUnitNo())
+                    .eq(ProductUnit::getDataSource,"youyi")
+                    .last("limit 1")
+                );
                 if (ObjectUtil.isNotEmpty(productUnit)) {
                     productBo.setUnitId(String.valueOf(productUnit.getId()));
                 } else {
@@ -216,31 +232,20 @@ public class ProductBaseImportListener extends AnalysisEventListener<ProductBase
                 productBo.setMinOrderQuantity(importVo.getMinOrderQuantity());
             }
 
-            // 处理主图
-            if (ObjectUtil.isNotEmpty(importVo.getMainImage())) {
-                productBo.setProductImage(importVo.getMainImage());
-            }
 
-            // 处理轮播图(多图)
-            if (ObjectUtil.isNotEmpty(importVo.getCarouselImages())) {
-                productBo.setImageUrl(importVo.getCarouselImages());
-            }
-
-            // 处理商品详情(PC端)
-            if (ObjectUtil.isNotEmpty(importVo.getProductDetail())) {
-                productBo.setPcDetail(importVo.getProductDetail());
-                productBo.setMobileDetail(importVo.getProductDetail());
-            }
 
-
-
-            // 调用Service进行新增
+            // 调用Service进行新增或更新
             ValidatorUtils.validate(productBo);
             productBo.setCreateBy(LoginHelper.getUserId());
-            productBaseService.insertByBo(productBo);
+            if (isUpdate) {
+                productBaseService.updateByBo(productBo);
+                successMsg.append("<br/>").append(successNum).append("、产品编号 ").append(importVo.getProductNo()).append(" 更新成功");
+            } else {
+                productBaseService.insertByBo(productBo);
+                successMsg.append("<br/>").append(successNum).append("、产品编号 ").append(importVo.getProductNo()).append(" 导入成功");
+            }
 
             successNum++;
-            successMsg.append("<br/>").append(successNum).append("、产品编号 ").append(importVo.getProductNo()).append(" 导入成功");
 
         } catch (Exception e) {
             failureNum++;

+ 7 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IProductBaseService.java

@@ -102,6 +102,13 @@ public interface IProductBaseService extends IService<ProductBase>{
      */
     ProductBaseVo updateProductShelfState(Long productId);
 
+    /**
+     * 同步商品到ERP
+     *
+     * @param productId 商品id
+     */
+    void syncProductToErp(Long productId,boolean isUpdate);
+
     /**
      * 分页查询推荐商品列表(联表查询分类名称)
      *

+ 239 - 200
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBaseServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -23,7 +24,9 @@ import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.easyes.core.biz.EsPageInfo;
 import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
 import org.dromara.external.api.domain.ExternalProductDto;
+import org.dromara.external.api.service.RemoteErpPushService;
 import org.dromara.external.api.service.RemoteExternalProductService;
+import org.dromara.product.api.erp.domain.push.ComProdInfo;
 import org.dromara.product.domain.*;
 import org.dromara.product.domain.bo.*;
 import org.dromara.product.domain.vo.*;
@@ -73,6 +76,8 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
     private final ProductCustomizationMapper customizationMapper;
     //产品分类Mapper
     private final ProductCategoryMapper categoryMapper;
+    //产品分类Mapper
+    private final ProductCategoryBindingMapper categoryBindingMapper;
     //获取分类绑定的商品
     private final ProductCategoryBindingMapper productCategoryBindingMapper;
     //品牌信息
@@ -85,6 +90,8 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
     private final ProductDecorationFloorLinkMapper decorationFloorLinkMapper;
     //工业装修楼层
     private final ProductIndustrialFloorLinkMapper industrialFloorLinkMapper;
+    //协议
+    private final ProtocolInfoMapper protocolInfoMapper;
     //协议商品
     private final ProtocolProductsMapper protocolProductsMapper;
     //产品diy属性
@@ -109,6 +116,8 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
 
     @DubboReference
     private RemoteExternalProductService externalProductService;
+    @DubboReference
+    private RemoteErpPushService erpPushService;
 
 
     /**
@@ -479,21 +488,97 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         LambdaEsQueryWrapper<ProductBaseVo> wrapper = new LambdaEsQueryWrapper<ProductBaseVo>()
             .eq(ProductBaseVo::getProductStatus, 1)
             ;
+        if(LoginHelper.isLogin()){
+            if(Objects.equals(LoginHelper.getLoginUser().getUserSonType(),"3")){
+                //查询是否是大客户
+                ClientSite one = clientSiteMapper.selectOne(Wrappers.lambdaQuery(ClientSite.class)
+                    .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
+                    .eq(ClientSite::getStatus, "0")
+                    .last("limit 1")
+                );
+                if (ObjectUtil.isNotEmpty(one)){
+                    //如果是大客户只查询客户协议池中的商品
+                    //获取协议池
+                    List<ProtocolProductsVo> protocolProductsVos = protocolProductsMapper.selectVoList(Wrappers.lambdaQuery(ProtocolProducts.class)
+                        .eq(ProtocolProducts::getCustomerId, LoginHelper.getLoginUser().getCustomerId())
+                    );
+                    if (ObjectUtil.isEmpty(protocolProductsVos)){
+                        wrapper.eq(ProductBaseVo::getId, 0L);
+                    }else{
+                        wrapper.in(ProductBaseVo::getId, protocolProductsVos.stream().map(ProtocolProductsVo::getProductId).distinct().toList());
+                    }
+                }else{
+                    //如果是企业购用户只查询企业购的商品
+                    //获取客户商品池
+                    List<ClientSiteProductVo> clientSiteProductVos = clientSiteProductMapper.selectVoList(Wrappers.lambdaQuery(ClientSiteProduct.class)
+                        .eq(ClientSiteProduct::getClientId, LoginHelper.getLoginUser().getCustomerId())
+                    );
+                    if (ObjectUtil.isEmpty(clientSiteProductVos)){
+                        wrapper.eq(ProductBaseVo::getId, 0L);
+                    }else {
+                        wrapper.in(ProductBaseVo::getId, clientSiteProductVos.stream().map(ClientSiteProductVo::getProductId).distinct().toList());
+                    }
+                }
+            }
+        }
         if(ObjectUtil.isNotEmpty(bo.getProductNo())){
             wrapper.eq(ProductBaseVo::getProductNo, bo.getProductNo());
         }
         if (ObjectUtil.isNotEmpty(bo.getBrandId())) {
             wrapper.eq(ProductBaseVo::getBrandId, bo.getBrandId());
         }
-//        if (ObjectUtil.isNotEmpty(bo.getTopCategoryId())) {
-//            wrapper.eq(ProductBaseVo::getTopCategoryId, bo.getTopCategoryId());
-//        }
-//        if (ObjectUtil.isNotEmpty(bo.getMediumCategoryId())) {
-//            wrapper.eq(ProductBaseVo::getMediumCategoryId, bo.getMediumCategoryId());
-//        }
-//        if (ObjectUtil.isNotEmpty(bo.getBottomCategoryId())) {
-//            wrapper.eq(ProductBaseVo::getBottomCategoryId, bo.getBottomCategoryId());
-//        }
+        if (ObjectUtil.isNotEmpty(bo.getTopCategoryId())) {
+            //获取分类所属的平台
+            ProductCategory productCategory = categoryMapper.selectById(bo.getTopCategoryId());
+            Long platform = productCategory.getPlatform();
+            if (platform == 0) {
+                wrapper.eq(ProductBaseVo::getTopCategoryId, bo.getTopCategoryId());
+            }else{
+                List<ProductCategoryBindingVo> productCategoryBindingVos = productCategoryBindingMapper.selectVoList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
+                    .eq(ProductCategoryBinding::getTopCategoryId, bo.getTopCategoryId())
+                );
+                if (CollUtil.isEmpty(productCategoryBindingVos)) {
+                    wrapper.eq(ProductBaseVo::getId,0);
+                }else{
+                    wrapper.in(ProductBaseVo::getId, productCategoryBindingVos.stream().map(ProductCategoryBindingVo::getProductId).distinct().toList());
+                }
+            }
+        }
+        if (ObjectUtil.isNotEmpty(bo.getMediumCategoryId())) {
+
+            //获取分类所属的平台
+            ProductCategory productCategory = categoryMapper.selectById(bo.getMediumCategoryId());
+            Long platform = productCategory.getPlatform();
+            if (platform == 0) {
+                wrapper.eq(ProductBaseVo::getMediumCategoryId, bo.getMediumCategoryId());
+            }else{
+                List<ProductCategoryBindingVo> productCategoryBindingVos = productCategoryBindingMapper.selectVoList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
+                    .eq(ProductCategoryBinding::getMediumCategoryId, bo.getMediumCategoryId())
+                );
+                if (CollUtil.isEmpty(productCategoryBindingVos)) {
+                    wrapper.eq(ProductBaseVo::getId,0);
+                }else{
+                    wrapper.in(ProductBaseVo::getId, productCategoryBindingVos.stream().map(ProductCategoryBindingVo::getProductId).distinct().toList());
+                }
+            }
+        }
+        if (ObjectUtil.isNotEmpty(bo.getBottomCategoryId())) {
+            //获取分类所属的平台
+            ProductCategory productCategory = categoryMapper.selectById(bo.getBottomCategoryId());
+            Long platform = productCategory.getPlatform();
+            if (platform == 0) {
+                wrapper.eq(ProductBaseVo::getBottomCategoryId, bo.getBottomCategoryId());
+            }else{
+                List<ProductCategoryBindingVo> productCategoryBindingVos = productCategoryBindingMapper.selectVoList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
+                    .eq(ProductCategoryBinding::getBottomCategoryId, bo.getBottomCategoryId())
+                );
+                if (CollUtil.isEmpty(productCategoryBindingVos)) {
+                    wrapper.eq(ProductBaseVo::getId,0);
+                }else{
+                    wrapper.in(ProductBaseVo::getId, productCategoryBindingVos.stream().map(ProductCategoryBindingVo::getProductId).distinct().toList());
+                }
+            }
+        }
         if (ObjectUtil.isNotEmpty(bo.getIsCustomize())) {
             wrapper.eq(ProductBaseVo::getIsCustomize, bo.getIsCustomize());
         }
@@ -553,9 +638,66 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
             }
         }
 
+        // 处理商品属性查询
+        if (ObjectUtil.isNotEmpty(bo.getAttributesList())) {
+            parseAndApplyAttributesFilter(wrapper, bo.getAttributesList());
+        }
+
         return wrapper;
     }
 
+    /**
+     * 解析并应用商品属性过滤器
+     * 前端传值格式: 属性名:属性值|属性名:属性值
+     * 例如: 测试:123|商品123:123
+     * 数据库存储格式: {"测试":"456","商品123":["123","456"]}
+     *
+     * @param wrapper ES查询包装器
+     * @param attributesListStr 前端传来的属性字符串
+     */
+    private void parseAndApplyAttributesFilter(LambdaEsQueryWrapper<ProductBaseVo> wrapper, String attributesListStr) {
+        try {
+            // 解析前端传来的属性字符串,格式:属性名:属性值|属性名:属性值
+            String[] attributePairs = attributesListStr.split("\\|");
+
+            if (attributePairs.length == 0) {
+                return;
+            }
+
+            // 构建ES查询条件,需要匹配所有属性(AND关系)
+            for (String pair : attributePairs) {
+                if (!pair.contains(":")) {
+                    continue;
+                }
+
+                String[] keyValue = pair.split(":", 2);
+                if (keyValue.length != 2) {
+                    continue;
+                }
+
+                String attributeName = keyValue[0].trim();
+                String attributeValue = keyValue[1].trim();
+
+                if (ObjectUtil.isEmpty(attributeName) || ObjectUtil.isEmpty(attributeValue)) {
+                    continue;
+                }
+
+                // 由于attributes_list是JSON字符串,需要使用ES的script查询或wildcard查询
+                // 这里使用wildcard模糊匹配JSON中的键值对
+                // 匹配两种情况:
+                // 1. 字符串值:"属性名":"属性值"
+                // 2. 数组值:"属性名":["属性值",...]
+                wrapper.and(w -> w
+                    .like(ProductBaseVo::getAttributesList, "\"" + attributeName + "\":\"" + attributeValue + "\"")
+                    .or()
+                    .like(ProductBaseVo::getAttributesList, "\"" + attributeName + "\":[\"" + attributeValue + "\"")
+                );
+            }
+        } catch (Exception e) {
+            log.error("解析商品属性失败: {}", e.getMessage(), e);
+        }
+    }
+
     /**
      * PC端商品列表降级查询
      */
@@ -647,7 +789,6 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
             saveProductDetail(bo, productId);
             // 7. 同步到ES索引
             syncToES(productId);
-
             log.info("成功新增产品,ID: {}, 名称: {}", productId, bo.getItemName());
             return true;
 
@@ -709,7 +850,6 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
 
             // 7. 同步到ES索引
             syncToES(productId);
-
             log.info("成功更新产品,ID: {}, 名称: {}", productId, bo.getItemName());
             return true;
 
@@ -1281,8 +1421,6 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
             if (vo != null) {
                 // 先尝试更新,如果不存在则插入
                 try {
-                    vo.setAttributesList(null);
-                    vo.setDiyAttributesList(null);
                     vo.setPcDetail(null);
                     vo.setMobileDetail(null);
                     esMapper.updateById(vo);
@@ -1299,6 +1437,62 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         }
     }
 
+    /**
+    * 同步产品到Erp
+    * */
+    public void syncProductToErp(Long productId,boolean isUpdate) {
+        try {
+            // 查询完整的产品信息(包含关联表数据)
+            ProductBaseVo vo = queryById(productId);
+            if (vo != null) {
+                // 映射字段到 ERP 推送对象
+                ComProdInfo erpProduct = new ComProdInfo();
+                erpProduct.setProdId(vo.getProductNo());
+                erpProduct.setProdNm(vo.getItemName());
+                erpProduct.setUid(vo.getUnitId());
+                erpProduct.setProdCatgId(vo.getBottomCategoryId() != null ? vo.getBottomCategoryId().toString() : null);
+                erpProduct.setTypeId(vo.getProductCategory() != null ? vo.getProductCategory().toString() : null);
+                erpProduct.setProdSpec(vo.getSpecification());
+                erpProduct.setBarCd(vo.getBarCoding());
+                erpProduct.setUseMultUi(false); // 默认不使用多单位
+
+                // 产品状态映射:1=已上架,0=下架 -> ERP 状态可能需要转换,这里暂存字符串
+                erpProduct.setProdSts(vo.getProductStatus() != null ? vo.getProductStatus().toString() : "0");
+
+                erpProduct.setInvNm(vo.getInvoiceName());
+                erpProduct.setInvSpec(vo.getInvoiceSpecs());
+
+                // 价格相关
+                erpProduct.setPurIsPrWtTax(true); // 假设进价含税,可根据业务调整
+                erpProduct.setPurStdPr(vo.getPurchasingPrice());
+                erpProduct.setHighPurPr(vo.getMaxPurchasePrice());
+                erpProduct.setSupId(vo.getSupplierNo());
+                erpProduct.setPurTaxId(vo.getTaxCode());
+
+                erpProduct.setSlsIsPrWtTax(true); // 假设售价含税
+                erpProduct.setSlsStdPr(vo.getMemberPrice());
+                erpProduct.setMinPr(vo.getMinSellingPrice());
+                erpProduct.setSltTaxId(vo.getTaxCode());
+
+                erpProduct.setRemark(vo.getRemark());
+                erpProduct.setTradMrkId(vo.getBrandId() != null ? vo.getBrandId().toString() : null);
+                erpProduct.setMinSalQty(vo.getMinOrderQuantity() != null ? BigDecimal.valueOf(vo.getMinOrderQuantity()) : null);
+                erpProduct.setPurPerId(vo.getPurchaseManagerNo());
+                erpProduct.setCuBhsx("1"); // 备货属性,根据实际业务设置
+
+                // 调用远程服务推送
+                String productJson = JSONUtil.toJsonStr(erpProduct);
+                erpPushService.pushProductData(productJson, isUpdate);
+
+                log.debug("成功{}ERP产品索引,产品ID: {}", isUpdate ? "更新" : "插入", productId);
+            }
+        } catch (Exception e) {
+            // Erp同步失败不影响主业务,只记录日志
+            log.error("同步产品到Erp失败,产品ID: {}, 错误: {}", productId, e.getMessage(), e);
+        }
+    }
+
+
     /**
      * 从ES索引中批量删除产品
      *
@@ -1514,196 +1708,10 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
      */
     @Override
     public TableDataInfo<PcProductVo> getPcProductPage(PcProductBo bo, PageQuery pageQuery) {
-        //获取当前登录用户的商品池
-//        List<Long> productIds = new ArrayList<>();
-//        Map<Long, BigDecimal> agreementPriceMap = new HashMap<>();
-//
-//        //是否是大客户
-//        if(ObjectUtil.isNotEmpty(LoginHelper.getLoginUser().getCustomerId())) {
-//            ClientSiteVo clientSiteVo = clientSiteMapper.selectVoOne(Wrappers.lambdaQuery(ClientSite.class)
-//                .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
-//                .orderByDesc(ClientSite::getCreateTime)
-//                .last("limit 1")
-//            );
-//            if (ObjectUtil.isNotEmpty(clientSiteVo)) {
-//                Long isDiy = clientSiteVo.getIsDiy();
-//                if (ObjectUtil.isNotEmpty(isDiy) && isDiy == 1) {
-//                    //获取协议商品池商品
-//                    List<ProtocolProducts> protocolProducts = protocolProductsMapper.selectList(Wrappers.lambdaQuery(ProtocolProducts.class)
-//                        .eq(ProtocolProducts::getCustomerId, LoginHelper.getLoginUser().getCustomerId())
-//                        .eq(ProtocolProducts::getAuditStatus, 2)
-//                    );
-//                    if (CollUtil.isNotEmpty(protocolProducts)) {
-//                        agreementPriceMap = protocolProducts.stream().collect(Collectors.toMap(ProtocolProducts::getProductId, ProtocolProducts::getAgreementPrice));
-//                        productIds = protocolProducts.stream().map(ProtocolProducts::getProductId).toList();
-//                    }
-//                }
-//            }
-//        }else {
-//            //获取企业的商品池商品
-//            if(Objects.equals(LoginHelper.getLoginUser().getUserSonType(),"3")) {
-//                List<ClientSiteProduct> clientSiteProducts = clientSiteProductMapper.selectList();
-//                if (CollUtil.isNotEmpty(clientSiteProducts)) {
-//                    productIds = clientSiteProducts.stream().map(ClientSiteProduct::getProductId).toList();
-//                }
-//            }
-//        }
-
-//        //是否是企业购
-//        QueryWrapper<ProductBase> lqw = Wrappers.query(ProductBase.class);
-//        if(Objects.equals(LoginHelper.getLoginUser().getUserSonType(),"3")){
-//            if (ObjectUtil.isNotEmpty(productIds) ){
-//                lqw.in("b.id", productIds);
-//            }else {
-//                lqw.isNull("b.id");
-//            }
-//            if (ObjectUtil.isNotEmpty(bo.getTopCategoryId())){
-//                //获取分类绑定的商品
-//                List<ProductCategoryBinding> productCategoryBindings = productCategoryBindingMapper.selectList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
-//                    .eq(ProductCategoryBinding::getTopCategoryId, bo.getTopCategoryId())
-//                );
-//                if (CollUtil.isNotEmpty(productCategoryBindings)) {
-//                    lqw.in("b.id", productCategoryBindings.stream().map(ProductCategoryBinding::getProductId).toList());
-//                }else {
-//                    lqw.isNull("b.id");
-//                }
-//            }
-//            if (ObjectUtil.isNotEmpty(bo.getMediumCategoryId())){
-//                //获取分类绑定的商品
-//                List<ProductCategoryBinding> productCategoryBindings = productCategoryBindingMapper.selectList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
-//                    .eq(ProductCategoryBinding::getMediumCategoryId, bo.getMediumCategoryId())
-//                );
-//                if (CollUtil.isNotEmpty(productCategoryBindings)) {
-//                    lqw.in("b.id", productCategoryBindings.stream().map(ProductCategoryBinding::getProductId).toList());
-//                }else {
-//                    lqw.isNull("b.id");
-//                }
-//            }
-//            if (ObjectUtil.isNotEmpty(bo.getBottomCategoryId())){
-//                //获取分类绑定的商品
-//                List<ProductCategoryBinding> productCategoryBindings = productCategoryBindingMapper.selectList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
-//                    .eq(ProductCategoryBinding::getBottomCategoryId, bo.getBottomCategoryId())
-//                );
-//                if (CollUtil.isNotEmpty(productCategoryBindings)) {
-//                    lqw.in("b.id", productCategoryBindings.stream().map(ProductCategoryBinding::getProductId).toList());
-//                }else {
-//                    lqw.isNull("b.id");
-//                }
-//            }
-//        }
-//
-//        if(ObjectUtil.isNotEmpty( bo.getIds())){
-//            lqw.in("b.id", bo.getIds().split(","));
-//        }
-//
-//        if (ObjectUtil.isNotEmpty(bo.getBrandIds())){
-//            lqw.in("b.brand_id", bo.getBrandIds().split(","));
-//        }
-//        if (ObjectUtil.isNotEmpty(bo.getCategoryIds())){
-//            lqw.in("b.bottom_category_id", bo.getCategoryIds().split(","));
-//        }
-//        lqw.ge(ObjectUtil.isNotEmpty(pageQuery.getFirstSeenId()) && pageQuery.getWay() == 0,"b.id", pageQuery.getFirstSeenId());
-//        lqw.gt(ObjectUtil.isNotEmpty(pageQuery.getLastSeenId()) && pageQuery.getWay() == 1,"b.id", pageQuery.getLastSeenId());
-//        lqw.eq(ObjectUtil.isNotEmpty(bo.getIsSelf()), "b.is_self", bo.getIsSelf());
-////        lqw.eq("b.product_status", 1);
-//        lqw.and(ObjectUtil.isNotEmpty(bo.getSearchKeyword())
-//            , (queryWrapper) -> queryWrapper.and(qw ->
-//                qw.like("b.item_name", bo.getSearchKeyword())
-//                    .or().like("tc.category_name", bo.getSearchKeyword())
-//                    .or().like("mc.category_name", bo.getSearchKeyword())
-//                    .or().like("bc.category_name", bo.getSearchKeyword())
-//                    .or().like("br.brand_name", bo.getSearchKeyword())
-//            )
-//        );
-//        lqw.like(StringUtils.isNotBlank(bo.getProductNo()), "b.product_no", bo.getProductNo());
-//        lqw.eq(ObjectUtil.isNotEmpty(bo.getBrandId()), "b.brand_id", bo.getBrandId());
-//        lqw.eq(ObjectUtil.isNotEmpty(bo.getTopCategoryId()), "b.top_category_id", bo.getTopCategoryId());
-//        lqw.eq(ObjectUtil.isNotEmpty(bo.getMediumCategoryId()), "b.medium_category_id", bo.getMediumCategoryId());
-//        lqw.eq(ObjectUtil.isNotEmpty(bo.getBottomCategoryId()), "b.bottom_category_id", bo.getBottomCategoryId());
-//        lqw.eq(ObjectUtil.isNotEmpty(bo.getIsCustomize()), "e.is_customize", bo.getIsCustomize());
-//        if(bo.getPriceRange() != null){
-//            //价格区间 1:1-100 2:100-500 3:500-1000 4:1000以上
-//            switch (bo.getPriceRange()) {
-//                case "1":
-//                    lqw.between("p.market_price", 1, 100);
-//                case "2":
-//                    lqw.between("p.market_price", 100, 500);
-//                case "3":
-//                    lqw.between("p.market_price", 500, 1000);
-//                case "4":
-//                    lqw.ge("p.market_price", 1000);
-//            }
-//        }
-//        if(bo.getSortField() != null && bo.getSortOrder() != null){
-//            String[] sortFields = bo.getSortField().split(",");
-//            String[] sortOrders = bo.getSortOrder().split(",");
-//            for (int i = 0; i < sortFields.length; i++) {
-//                switch (sortFields[i]) {
-//                    case "1":
-//                        lqw.orderBy( true, sortOrders[i].equals("Asc"),"b.id");
-//                    case "2":
-//                        lqw.orderBy( true, sortOrders[i].equals("Asc"),"p.total_inventory");
-//                    case "3":
-//                        lqw.orderBy( true, sortOrders[i].equals("Asc"),"p.market_price");
-//                }
-//            }
-//        }
-
         // 使用ES分页查询
         try {
             LambdaEsQueryWrapper<ProductBaseVo> esQueryWrapper = buildEsQueryWrapperForPc(bo);
-            if(LoginHelper.isLogin()){
-                if(Objects.equals(LoginHelper.getLoginUser().getUserSonType(),"3")){
-//                if (ObjectUtil.isNotEmpty(productIds) ){
-//                    esQueryWrapper.in(ProductBaseVo::getId, productIds);
-//                }else {
-//                    esQueryWrapper.eq(ProductBaseVo::getId, 0L);
-//                }
-                    if (ObjectUtil.isNotEmpty(bo.getTopCategoryId())){
-                        //获取分类绑定的商品
-                        List<ProductCategoryBinding> productCategoryBindings = productCategoryBindingMapper.selectList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
-                            .eq(ProductCategoryBinding::getTopCategoryId, bo.getTopCategoryId())
-                        );
-                        if (CollUtil.isNotEmpty(productCategoryBindings)) {
-                            esQueryWrapper.in(ProductBaseVo::getId, productCategoryBindings.stream().map(ProductCategoryBinding::getProductId).toList());
-                        }else {
-                            esQueryWrapper.eq(ProductBaseVo::getId, 0L);
-                        }
-                    }
-                    if (ObjectUtil.isNotEmpty(bo.getMediumCategoryId())){
-                        //获取分类绑定的商品
-                        List<ProductCategoryBinding> productCategoryBindings = productCategoryBindingMapper.selectList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
-                            .eq(ProductCategoryBinding::getMediumCategoryId, bo.getMediumCategoryId())
-                        );
-                        if (CollUtil.isNotEmpty(productCategoryBindings)) {
-                            esQueryWrapper.in(ProductBaseVo::getId, productCategoryBindings.stream().map(ProductCategoryBinding::getProductId).toList());
-                        }else {
-                            esQueryWrapper.eq(ProductBaseVo::getId, 0L);
-                        }
-                    }
-                    if (ObjectUtil.isNotEmpty(bo.getBottomCategoryId())){
-                        //获取分类绑定的商品
-                        List<ProductCategoryBinding> productCategoryBindings = productCategoryBindingMapper.selectList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
-                            .eq(ProductCategoryBinding::getBottomCategoryId, bo.getBottomCategoryId())
-                        );
-                        if (CollUtil.isNotEmpty(productCategoryBindings)) {
-                            esQueryWrapper.in(ProductBaseVo::getId, productCategoryBindings.stream().map(ProductCategoryBinding::getProductId).toList());
-                        }else {
-                            esQueryWrapper.eq(ProductBaseVo::getId, 0L);
-                        }
-                    }
-                }else {
-                    if (ObjectUtil.isNotEmpty(bo.getTopCategoryId())) {
-                        esQueryWrapper.eq(ProductBaseVo::getTopCategoryId, bo.getTopCategoryId());
-                    }
-                    if (ObjectUtil.isNotEmpty(bo.getMediumCategoryId())) {
-                        esQueryWrapper.eq(ProductBaseVo::getMediumCategoryId, bo.getMediumCategoryId());
-                    }
-                    if (ObjectUtil.isNotEmpty(bo.getBottomCategoryId())) {
-                        esQueryWrapper.eq(ProductBaseVo::getBottomCategoryId, bo.getBottomCategoryId());
-                    }
-                }
-            }
+
             // 检查ES索引是否存在
             if (!esMapper.existsIndex("productbasevo")) {
                 log.warn("ES索引 [productbasevo] 不存在,降级到数据库查询");
@@ -1715,6 +1723,36 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
 
             if (CollUtil.isNotEmpty(esPageInfo.getList())) {
                 List<PcProductVo> pcProductVos = BeanUtil.copyToList(esPageInfo.getList(), PcProductVo.class);
+                //如果是大客户则需要将价格改为协议价
+                if(LoginHelper.isLogin()){
+                    if(Objects.equals(LoginHelper.getLoginUser().getUserSonType(),"3")){
+                        //查询是否是大客户
+                        ClientSite one = clientSiteMapper.selectOne(Wrappers.lambdaQuery(ClientSite.class)
+                            .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
+                            .eq(ClientSite::getStatus, "0")
+                            .last("limit 1")
+                        );
+                        if (ObjectUtil.isNotEmpty(one)){
+                            //如果是大客户只查询客户协议池中的商品
+                            //获取协议
+                            ProtocolInfo protocolInfo = protocolInfoMapper.selectOne(Wrappers.lambdaQuery(ProtocolInfo.class)
+                                .eq(ProtocolInfo::getCustomerId, LoginHelper.getLoginUser().getCustomerId())
+                                .orderByDesc(ProtocolInfo::getCreateTime)
+                                .last("limit 1")
+                            );
+                            //获取协议池
+                            List<ProtocolProducts> protocolProductsVos = protocolProductsMapper.selectList(Wrappers.lambdaQuery(ProtocolProducts.class)
+                                .eq(ProtocolProducts::getProtocolId, protocolInfo.getId())
+                            );
+                            if (CollUtil.isNotEmpty(protocolProductsVos)) {
+                                Map<Long, BigDecimal> map = protocolProductsVos.stream().collect(Collectors.toMap(ProtocolProducts::getProductId, ProtocolProducts::getAgreementPrice));
+                                pcProductVos.forEach(pcProductVo -> {
+                                    pcProductVo.setMemberPrice(map.get(pcProductVo.getId()));
+                                });
+                            }
+                        }
+                    }
+                }
                 TableDataInfo<PcProductVo> tableDataInfo = TableDataInfo.build(pcProductVos);
                 tableDataInfo.setTotal(esPageInfo.getTotal());
                 return tableDataInfo;
@@ -1728,6 +1766,7 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         return TableDataInfo.build();
     }
 
+
     public static void main(String[] args) {
         Date oldPlanStartAt = DateUtil.parse("2026-03-18 00:00:00");
         Date oldPlanEndAt = DateUtil.parse("2026-04-18 00:00:00");

+ 55 - 23
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBrandServiceImpl.java

@@ -56,6 +56,10 @@ public class ProductBrandServiceImpl  extends ServiceImpl<ProductBrandMapper, Pr
 
     private final ProductBrandEsMapper esMapper;
 
+    private final ProductCategoryMapper productCategoryMapper;
+
+    private final ProductCategoryBindingMapper productCategoryBindingMapper;
+
     private final ProductRecommendLinkMapper productRecommendLinkMapper;
 
     private final ProductSpecialLinkMapper productSpecialLinkMapper;
@@ -312,31 +316,59 @@ public class ProductBrandServiceImpl  extends ServiceImpl<ProductBrandMapper, Pr
      */
     @Override
     public TableDataInfo<ProductBrandVo> getBrandByCategoryList(Long categoryId,String name,String initial,PageQuery pageQuery) {
-//        List<Long> brandIds = null;
-//        if(ObjectUtil.isNotEmpty(categoryId) || ObjectUtil.isNotEmpty(name)){
-//            List<ProductBaseVo> productBaseVos = productEsMapper.selectList(new LambdaEsQueryWrapper<ProductBaseVo>()
-//                .select(ProductBaseVo::getBrandId)
-//                .eq(ObjectUtil.isNotEmpty(categoryId),ProductBaseVo::getTopCategoryId, categoryId)
-//                .like(ObjectUtil.isNotEmpty(name),ProductBaseVo::getBrandName, name)
-//            );
-//            if (CollUtil.isEmpty(productBaseVos)) {
-//                return TableDataInfo.build();
-//            }
-//            brandIds = productBaseVos.stream().map(ProductBaseVo::getBrandId).toList();
-//        }
+        List<Long> brandIds = null;
+        if (ObjectUtil.isNotEmpty(categoryId) || ObjectUtil.isNotEmpty(name)) {
+            //获取分类所属的平台
+            ProductCategory productCategory = productCategoryMapper.selectById(categoryId);
+            Long platform = productCategory.getPlatform();
+
+            if (platform == 0) {
+                //当分类所属的是平台时
+                List<ProductBaseVo> productBaseVos = productEsMapper.selectList(new LambdaEsQueryWrapper<ProductBaseVo>()
+                    .select(ProductBaseVo::getBrandId)
+                    .eq(ObjectUtil.isNotEmpty(categoryId), ProductBaseVo::getTopCategoryId, categoryId)
+                    .eq(ProductBaseVo::getProductStatus, 1)
+                    .like(ObjectUtil.isNotEmpty(name), ProductBaseVo::getBrandName, name)
+                );
+                if (CollUtil.isEmpty(productBaseVos)) {
+                    return TableDataInfo.build();
+                }
+                brandIds = productBaseVos.stream().map(ProductBaseVo::getBrandId).distinct().toList();
+            } else {
+                //当分类所属的是其他站点时,获取分类绑定商品表
+                List<ProductCategoryBindingVo> productCategoryBindingVos = productCategoryBindingMapper.selectVoList(Wrappers.lambdaQuery(ProductCategoryBinding.class)
+                    .eq(ProductCategoryBinding::getTopCategoryId, categoryId)
+
+                );
+                if (CollUtil.isEmpty(productCategoryBindingVos)) {
+                    return TableDataInfo.build();
+                }
+                List<Long> productIds = productCategoryBindingVos.stream().map(ProductCategoryBindingVo::getProductId).toList();
+                List<ProductBaseVo> productBaseVos = productEsMapper.selectList(new LambdaEsQueryWrapper<ProductBaseVo>()
+                    .select(ProductBaseVo::getBrandId)
+                    .in(ObjectUtil.isNotEmpty(productIds), ProductBaseVo::getId, productIds)
+                    .eq(ProductBaseVo::getProductStatus, 1)
+                    .like(ObjectUtil.isNotEmpty(name), ProductBaseVo::getBrandName, name)
+                );
+                if (CollUtil.isEmpty(productBaseVos)) {
+                    return TableDataInfo.build();
+                }
+                brandIds = productBaseVos.stream().map(ProductBaseVo::getBrandId).distinct().toList();
+            }
+        }
 
-        LambdaQueryWrapper<ProductBrand> lqw = Wrappers.lambdaQuery(ProductBrand.class);
-//        if(ObjectUtil.isNotEmpty(categoryId) || ObjectUtil.isNotEmpty(name)){
-//            if(ObjectUtil.isNotEmpty(brandIds)){
-//                lqw.in(ProductBrand::getId,brandIds);
-//            }else {
-//                lqw.isNull(ProductBrand::getId);
-//            }
-//        }
-        lqw.eq(ObjectUtil.isNotEmpty(initial),ProductBrand::getBrandInitials,initial);
+            LambdaQueryWrapper<ProductBrand> lqw = Wrappers.lambdaQuery(ProductBrand.class);
+            if (ObjectUtil.isNotEmpty(categoryId) || ObjectUtil.isNotEmpty(name)) {
+                if (ObjectUtil.isNotEmpty(brandIds)) {
+                    lqw.in(ProductBrand::getId, brandIds);
+                } else {
+                    lqw.isNull(ProductBrand::getId);
+                }
+            }
+            lqw.eq(ObjectUtil.isNotEmpty(initial), ProductBrand::getBrandInitials, initial);
 //        lqw.eq(ProductBrand::getIsShow,1);
-        Page<ProductBrandVo> productBrandVos = baseMapper.selectVoPage(pageQuery.build(),lqw);
-        return TableDataInfo.build(productBrandVos);
+            Page<ProductBrandVo> productBrandVos = baseMapper.selectVoPage(pageQuery.build(), lqw);
+            return TableDataInfo.build(productBrandVos);
     }
 
     /**

+ 11 - 5
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductPoolAuditServiceImpl.java

@@ -35,6 +35,7 @@ import org.dromara.product.domain.vo.ProductListVo;
 import org.dromara.product.domain.vo.ProductPoolLinkAuditVo;
 import org.dromara.product.esmapper.ProductEsMapper;
 import org.dromara.product.mapper.*;
+import org.dromara.product.service.IProductBaseService;
 import org.dromara.system.api.RemoteUserService;
 import org.springframework.stereotype.Service;
 import org.dromara.product.domain.bo.ProductPoolAuditBo;
@@ -79,6 +80,8 @@ public class ProductPoolAuditServiceImpl  extends ServiceImpl<ProductPoolAuditMa
     //采购项目产品关联
     private final ProcurementProgramProductMapper procurementProgramProductMapper;
 
+    private final IProductBaseService productBaseService;
+
     @DubboReference
     private RemoteCustomerService remoteCustomerService;
 
@@ -262,11 +265,11 @@ public class ProductPoolAuditServiceImpl  extends ServiceImpl<ProductPoolAuditMa
     @Transactional(rollbackFor = Exception.class)
     public Boolean insertByBo(ProductPoolAuditBo bo) {
         ProductPoolAudit add = MapstructUtils.convert(bo, ProductPoolAudit.class);
-        
+
         // 生成6位数的审核编号
         String poolAuditNo = generatePoolAuditNo();
         add.setPoolAuditNo(poolAuditNo);
-        
+
         validEntityBeforeSave(add);
         boolean flag = baseMapper.insert(add) > 0;
         if (flag) {
@@ -325,7 +328,7 @@ public class ProductPoolAuditServiceImpl  extends ServiceImpl<ProductPoolAuditMa
                 .orderByDesc(ProductPoolAudit::getPoolAuditNo)
                 .last("limit 1")
         );
-        
+
         String nextNo;
         if (maxAudit != null && StringUtils.isNotBlank(maxAudit.getPoolAuditNo())) {
             // 如果存在最大编号,则 +1
@@ -334,12 +337,12 @@ public class ProductPoolAuditServiceImpl  extends ServiceImpl<ProductPoolAuditMa
             // 如果不存在,从 000001 开始
             nextNo = "000001";
         }
-        
+
         // 确保是6位数,不足补零
         if (nextNo.length() < 6) {
             nextNo = String.format("%06d", Long.parseLong(nextNo));
         }
-        
+
         return nextNo;
     }
 
@@ -497,6 +500,9 @@ public class ProductPoolAuditServiceImpl  extends ServiceImpl<ProductPoolAuditMa
                     .set(ProductBaseVo::getIsSelf, 1)
                     .in(ProductBaseVo::getId, updateProductIds)
                 );
+                for (Long updateProductId : updateProductIds) {
+                    productBaseService.syncProductToErp(updateProductId,true);
+                }
             }
             if(ObjectUtil.isNotEmpty(deleteProductIds)){
                 productBaseMapper.update(Wrappers.<ProductBase>lambdaUpdate()

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/pc/IndexSystemController.java

@@ -99,7 +99,7 @@ public class IndexSystemController {
     @GetMapping("/getBottomNav")
     public R<List<PlatformNavigation>> getBottomNav(@RequestParam(value = "title", required = false) String title) {
         List<PlatformNavigation> list = platformNavigationService.list(Wrappers.<PlatformNavigation>lambdaQuery(PlatformNavigation.class)
-            .eq(PlatformNavigation::getNavType, "setting_bottom")
+            .eq(PlatformNavigation::getNavType, "setting_footer")
             .eq(StringUtils.isNotBlank(title), PlatformNavigation::getTitle, title)
             .eq(PlatformNavigation::getIsEnable, SysPlatformYesNo.YES.getCode())
             .orderByAsc(PlatformNavigation::getSort)