肖路 před 2 týdny
rodič
revize
02e7cf9537
61 změnil soubory, kde provedl 3256 přidání a 24 odebrání
  1. 32 0
      ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/domain/ExternalItemDto.java
  2. 14 0
      ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/service/RemoteExternalItemService.java
  3. 6 0
      ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/service/RemoteExternalProductService.java
  4. 6 0
      ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/RemoteProductService.java
  5. 2 1
      ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlatformDataScopeInterceptor.java
  6. 31 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/ExternalProductController.java
  7. 1 1
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/zhongche/ZhongChePullController.java
  8. 2 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/ExternalItem.java
  9. 5 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/ExternalProduct.java
  10. 2 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/ExternalProductCategory.java
  11. 3 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/bo/ExternalItemBo.java
  12. 5 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/bo/ExternalProductBo.java
  13. 1 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/vo/ExternalItemVo.java
  14. 83 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/vo/ExternalProductImportVo.java
  15. 5 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/vo/ExternalProductVo.java
  16. 39 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/dubbo/RemoteExternalItemServiceImpl.java
  17. 19 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/dubbo/RemoteExternalProductServiceImpl.java
  18. 27 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/ProductPushStrategy.java
  19. 34 9
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/impl/ZhongChePushStrategy.java
  20. 34 2
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/impl/ZhongZhiPushStrategy.java
  21. 222 0
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/listener/ExternalProductImportListener.java
  22. 4 4
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/util/DataMigration.java
  23. 16 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductBaseController.java
  24. 156 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductPoolAuditController.java
  25. 123 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductPoolLinkAuditController.java
  26. 18 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/IndexProductController.java
  27. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductExtend.java
  28. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPool.java
  29. 86 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPoolAudit.java
  30. 87 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPoolLinkAudit.java
  31. 5 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPriceInventory.java
  32. 22 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductBaseBo.java
  33. 26 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolAuditBatchBo.java
  34. 132 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolAuditBo.java
  35. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolBo.java
  36. 92 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolLinkAuditBo.java
  37. 5 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPriceInventoryBo.java
  38. 16 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductBaseVo.java
  39. 129 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPoolAuditVo.java
  40. 99 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPoolLinkAuditVo.java
  41. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPoolVo.java
  42. 5 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPriceInventoryVo.java
  43. 15 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/dubbo/RemoteProductServiceImpl.java
  44. 15 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/mapper/ProductPoolAuditMapper.java
  45. 15 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/mapper/ProductPoolLinkAuditMapper.java
  46. 9 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IProductBaseService.java
  47. 99 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IProductPoolAuditService.java
  48. 79 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IProductPoolLinkAuditService.java
  49. 42 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBaseServiceImpl.java
  50. 600 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductPoolAuditServiceImpl.java
  51. 179 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductPoolLinkAuditServiceImpl.java
  52. 7 0
      ruoyi-modules/ruoyi-product/src/main/resources/mapper/product/ProductPoolAuditMapper.xml
  53. 7 0
      ruoyi-modules/ruoyi-product/src/main/resources/mapper/product/ProductPoolLinkAuditMapper.xml
  54. 106 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysTaxCodeController.java
  55. 76 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTaxCode.java
  56. 83 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTaxCodeBo.java
  57. 93 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTaxCodeVo.java
  58. 15 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTaxCodeMapper.java
  59. 70 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTaxCodeService.java
  60. 141 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaxCodeServiceImpl.java
  61. 7 0
      ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTaxCodeMapper.xml

+ 32 - 0
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/domain/ExternalItemDto.java

@@ -0,0 +1,32 @@
+package org.dromara.external.api.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author
+ * @date 2026/3/27 上午12:30
+ */
+@Data
+public class ExternalItemDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 项目id
+     */
+    private Long id;
+    /**
+     * 项目负责人id
+     * */
+    private Long purchaseId;
+
+    /**
+     * 项目logo
+     */
+    private String logo;
+    /**
+     * 项目名
+     */
+    private String itemName;
+}

+ 14 - 0
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/service/RemoteExternalItemService.java

@@ -0,0 +1,14 @@
+package org.dromara.external.api.service;
+
+import org.dromara.external.api.domain.ExternalItemDto;
+
+/**
+ * @author
+ * @date 2026/3/27 上午12:30
+ */
+public interface RemoteExternalItemService {
+    /**
+    * 获取项目信息
+    * */
+    ExternalItemDto getItemInfo(Long itemId);
+}

+ 6 - 0
ruoyi-api/ruoyi-api-external/src/main/java/org/dromara/external/api/service/RemoteExternalProductService.java

@@ -20,4 +20,10 @@ public interface RemoteExternalProductService {
     * */
     List<ExternalProductDto> getExternalProductList(Long itemId);
 
+    /**
+    * 更新产品
+    * */
+    void syncExternalProduct(List<ExternalProductDto> productIds);
+
+
 }

+ 6 - 0
ruoyi-api/ruoyi-api-product/src/main/java/org/dromara/product/api/RemoteProductService.java

@@ -112,5 +112,11 @@ public interface RemoteProductService {
     boolean unlockShoppingCart(Long productId, String accountName);
 
 
+    /**
+     * 根据商品编号查询商品ID
+     * @param productNo
+     * @return
+     */
+    Long getProductIdProductNo(String productNo);
 
 }

+ 2 - 1
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlatformDataScopeInterceptor.java

@@ -106,7 +106,8 @@ public class PlatformDataScopeInterceptor implements Interceptor {
         "statement_invoice",
         "statement_invoice_detail",
         "statement_invoice_product",
-        "external_"
+        "external_",
+        "sys_tax_code"
         // 注意:前缀匹配需特殊处理(如 qrtz_),见 isIgnoreTable 方法
     ));
 

+ 31 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/ExternalProductController.java

@@ -1,6 +1,7 @@
 package org.dromara.external.controller;
 
 import java.sql.Wrapper;
+import java.util.ArrayList;
 import java.util.List;
 
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -10,9 +11,13 @@ import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import org.dromara.external.domain.ExternalProduct;
 import org.dromara.external.handler.ProductPushStrategyFactory;
+import org.dromara.external.listener.ExternalProductImportListener;
+import org.dromara.external.domain.vo.ExternalProductImportVo;
 import org.dromara.product.api.domain.ProductVo;
+import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.multipart.MultipartFile;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
 import org.dromara.common.log.annotation.Log;
 import org.dromara.common.web.core.BaseController;
@@ -22,6 +27,7 @@ import org.dromara.common.core.validate.AddGroup;
 import org.dromara.common.core.validate.EditGroup;
 import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.excel.core.ExcelResult;
 import org.dromara.external.domain.vo.ExternalProductVo;
 import org.dromara.external.domain.bo.ExternalProductBo;
 import org.dromara.external.service.IExternalProductService;
@@ -185,6 +191,31 @@ public class ExternalProductController extends BaseController {
         return R.ok();
     }
 
+    /**
+     * 导入对外部推送商品数据
+     *
+     * @param file 导入文件
+     */
+    @Log(title = "对外部推送商品", businessType = BusinessType.IMPORT)
+    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
+        ExcelResult<ExternalProductImportVo> result = ExcelUtil.importExcel(
+            file.getInputStream(),
+            ExternalProductImportVo.class,
+            new ExternalProductImportListener(externalProductService)
+        );
+        return R.ok(result.getAnalysis());
+    }
+
+    /**
+     * 获取导入模板
+     */
+    @Log(title = "对外部推送商品", businessType = BusinessType.IMPORT)
+    @PostMapping("/importTemplate")
+    public void importTemplate(HttpServletResponse response) {
+        ExcelUtil.exportExcel(new ArrayList<>(), "对外部推送商品模板", ExternalProductImportVo.class, response);
+    }
+
 
 
 }

+ 1 - 1
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/zhongche/ZhongChePullController.java

@@ -57,7 +57,7 @@ public class ZhongChePullController {
     // 中车提供的配置(替换为真实值)
     private static final String CLIENT_ID = "KFZnKGiDsJ7";
     private static final String PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgQu0H97EPqkgz1YS5LkzZNmkG3mS5Er8rJ2LSoJtuOlGgCgYIKoEcz1UBgi2hRANCAARP6NYwTHpW2QTL8A2f2hpgunEpDVkJBhErBQPLqNS/Si5Q+9I9wUpCYdk1EvB5Hw6yzkE4bYk5IZM1j+/SnNFn"; // 电商平台私钥
-    private static final String ZC_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAET+jWMEx6VtkEy/ANn9oaYLpxKQ1ZCQYRKwUDy6jUv0ouUPvSPcFKQmHZNRLweR8Oss5BOG2JOSGTNY/v0pzRZw=="; // 中车公钥
+    private static final String ZC_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE9ITEKJdH9o1K9AeQYY7zNMo/q5/cdce+9jbawURTPEpBKAx4VkB+lRkb5e5YL+Be4pPM464rPvLyfqGNJvL6uQ=="; // 中车公钥
     //测试环境
     // 中车地区查询接口地址(替换为真实域名)
 //    private static final String AREA_QUERY_URL = "https://supply-test.crrcgo.cc/mallapi/";

+ 2 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/ExternalItem.java

@@ -27,6 +27,8 @@ public class ExternalItem extends TenantEntity {
     @TableId(value = "id")
     private Long id;
 
+    private String itemNo;
+
     /**
      * 项目负责人id
      * */

+ 5 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/ExternalProduct.java

@@ -111,4 +111,9 @@ public class ExternalProduct extends TenantEntity {
      * 起订量
      * */
     private Integer minOrderQuantity;
+
+    /**
+     * 计价规则(0 一品一价,1 按类目折扣率报价)
+     */
+    private Integer pricingRule;
 }

+ 2 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/ExternalProductCategory.java

@@ -100,4 +100,6 @@ public class ExternalProductCategory extends TenantEntity {
     private BigDecimal discountRate;
 
 
+
+
 }

+ 3 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/bo/ExternalItemBo.java

@@ -25,6 +25,9 @@ public class ExternalItemBo extends BaseEntity {
      */
     private Long id;
 
+    private String itemNo;
+
+
     /**
      * 项目负责人id
      * */

+ 5 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/bo/ExternalProductBo.java

@@ -103,5 +103,10 @@ public class ExternalProductBo extends BaseEntity {
     * */
     private Integer minOrderQuantity;
 
+    /**
+     * 计价规则(0 一品一价,1 按类目折扣率报价)
+     */
+    private Integer pricingRule;
+
 
 }

+ 1 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/vo/ExternalItemVo.java

@@ -35,6 +35,7 @@ public class ExternalItemVo implements Serializable {
     @ExcelProperty(value = "项目id")
     private Long id;
 
+    private String itemNo;
     /**
      * 项目负责人id
      * */

+ 83 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/vo/ExternalProductImportVo.java

@@ -0,0 +1,83 @@
+package org.dromara.external.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 对外部推送商品导入视图对象
+ *
+ * @author LionLi
+ * @date 2026-01-08
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class ExternalProductImportVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 序号
+     */
+    @ExcelProperty(value = "序号", index = 0)
+    private Integer serialNumber;
+
+    /**
+     * 项目编号
+     */
+    @ExcelProperty(value = "项目编号", index = 1)
+    private String itemNo;
+
+    /**
+     * 产品编号
+     */
+    @ExcelProperty(value = "产品编号", index = 2)
+    private String productNo;
+
+    /**
+     * 市场价
+     */
+    @ExcelProperty(value = "市场价", index = 3)
+    private BigDecimal marketPrice;
+
+    /**
+     * 官网价
+     */
+    @ExcelProperty(value = "官网价", index = 4)
+    private BigDecimal officialPrice;
+
+    /**
+     * 协议价
+     */
+    @ExcelProperty(value = "协议价", index = 5)
+    private BigDecimal agreementPrice;
+
+    /**
+     * 计价规则(0 一品一价,1 按类目折扣率报价)
+     */
+    @ExcelProperty(value = "计价规则(0 一品一价,1 按类目折扣率报价)", index = 6)
+    private Integer pricingRule;
+
+    /**
+     * 项目分类编码
+     */
+    @ExcelProperty(value = "项目分类编码", index = 7)
+    private String categoryNo;
+
+    /**
+     * 起订量
+     */
+    @ExcelProperty(value = "起订量", index = 8)
+    private Integer minOrderQuantity;
+
+    /**
+     * 上架状态(0 上架,1 下架)
+     */
+    @ExcelProperty(value = "上架状态(0 上架,1 下架)", index = 9)
+    private Integer shelfStatus;
+}

+ 5 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/domain/vo/ExternalProductVo.java

@@ -443,5 +443,10 @@ public class ExternalProductVo implements Serializable {
      * */
     private Integer availableInventory;
 
+    /**
+     * 计价规则(0 一品一价,1 按类目折扣率报价)
+     */
+    private Integer pricingRule;
+
 
 }

+ 39 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/dubbo/RemoteExternalItemServiceImpl.java

@@ -0,0 +1,39 @@
+package org.dromara.external.dubbo;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.external.api.domain.ExternalItemDto;
+import org.dromara.external.api.service.RemoteExternalItemService;
+import org.dromara.external.domain.vo.ExternalItemVo;
+import org.dromara.external.service.IExternalItemService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author
+ * @date 2026/1/14 下午5:02
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@DubboService
+public class RemoteExternalItemServiceImpl implements RemoteExternalItemService {
+    private final IExternalItemService externalItemService;
+
+    /**
+     * 获取项目信息
+     *
+     * @param itemId
+     */
+    @Override
+    public ExternalItemDto getItemInfo(Long itemId) {
+        ExternalItemVo externalItemVo = externalItemService.queryById(itemId);
+
+        if(ObjectUtil.isEmpty(externalItemVo)){
+            return null;
+        }
+        return BeanUtil.toBean(externalItemVo, ExternalItemDto.class);
+    }
+}

+ 19 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/dubbo/RemoteExternalProductServiceImpl.java

@@ -70,4 +70,23 @@ public class RemoteExternalProductServiceImpl implements RemoteExternalProductSe
 
         return BeanUtil.copyToList(externalProducts, ExternalProductDto.class);
     }
+
+    /**
+     * 更新产品
+     *
+     * @param products
+     */
+    @Override
+    public void syncExternalProduct(List<ExternalProductDto> products) {
+        for (ExternalProductDto product : products) {
+            //不存在则新增存在则跳过
+            if (!externalProductService.exists(Wrappers.lambdaQuery(ExternalProduct.class)
+                .eq(ExternalProduct::getProductId, product.getProductId()))){
+                ExternalProduct externalProduct = BeanUtil.toBean(product, ExternalProduct.class);
+                externalProductService.save(externalProduct);
+            }
+        }
+
+
+    }
 }

+ 27 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/ProductPushStrategy.java

@@ -1,5 +1,7 @@
 package org.dromara.external.handler;
 
+import org.dromara.external.api.zhongche.domain.bo.*;
+import org.dromara.external.api.zhongche.domain.vo.*;
 import org.dromara.external.domain.ExternalProduct;
 
 import java.util.List;
@@ -10,4 +12,29 @@ public interface ProductPushStrategy {
      * 推送商品
      */
     void push( Long itemId,List<ExternalProduct> products);
+
+    /**
+     * 商品价格变更
+     */
+    GoodsPriceUpdateVo updatePrice(Long itemId, GoodsImportBo bo);
+
+    /**
+     * 商品上下架状态变更
+     */
+    GoodsStatusUpdateVo updateStatus(Long itemId, GoodsStatusUpdateBo bo);
+
+    /**
+     * 商品图片变更
+     */
+    GoodsImageUpdateVo updateImages(Long itemId, GoodsImageUpdateBo bo);
+
+    /**
+     * 商品规格信息变更
+     */
+    GoodsUpdateVo updateProperties(Long itemId, GoodsPropertiesUpdateBo bo);
+
+    /**
+     * 商品详情信息变更
+     */
+    GoodsUpdateVo updateDetail(Long itemId, GoodsDetailBo bo);
 }

+ 34 - 9
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/impl/ZhongChePushStrategy.java

@@ -9,8 +9,8 @@ import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.exception.api.ZhongcheException;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.external.api.zhongche.domain.GoodsImportItem;
-import org.dromara.external.api.zhongche.domain.bo.GoodsImportBo;
-import org.dromara.external.api.zhongche.domain.vo.GoodsImportVo;
+import org.dromara.external.api.zhongche.domain.bo.*;
+import org.dromara.external.api.zhongche.domain.vo.*;
 import org.dromara.external.controller.zhongche.ZhongChePullController;
 import org.dromara.external.domain.ExternalProduct;
 import org.dromara.external.domain.ExternalProductCategory;
@@ -89,9 +89,9 @@ public class ZhongChePushStrategy implements ProductPushStrategy {
 
             List<Long> productIds = productVoList.stream()
                 .map(ExternalProductVo::getProductId)
-                .filter(Objects::nonNull) // 过滤空ID,避免远程调用报错
+                .filter(Objects::nonNull) // 过滤空 ID,避免远程调用报错
                 .collect(Collectors.toList());
-            //远程调用获取ProductAggregateDto列表
+            //远程调用获取 ProductAggregateDto 列表
             final Map<Long, ProductAggregateDto> productAggregateMap = new HashMap<>();
 
             if (!CollectionUtils.isEmpty(productIds)) {
@@ -131,7 +131,7 @@ public class ZhongChePushStrategy implements ProductPushStrategy {
             //第三方价格
             goodsImportItem.setThirdPrice(item.getExternalPrice());
             goodsImportItem.setUnit(item.getUnitName());
-            //TODO库存
+            //TODO 库存
             goodsImportItem.setStock(productAggregateMap.get(item.getProductId()).getStock());
             //图片
             String rawImageStr = item.getProductImage();
@@ -152,15 +152,15 @@ public class ZhongChePushStrategy implements ProductPushStrategy {
                 barImgUrlsJson = "[]";
             }
             goodsImportItem.setBarImgUrls(barImgUrlsJson);
-            //TODO商品描述
+            //TODO 商品描述
             goodsImportItem.setDescription(productAggregateMap.get(item.getProductId()).getDescription());
-            //TODO商品规格  默认 颜色:白色
+            //TODO 商品规格  默认 颜色:白色
             goodsImportItem.setProperties("{\"颜色\":\"白色\"}");
-            //TODO是否自营
+            //TODO 是否自营
             goodsImportItem.setIsSelfOperated(item.getIsSelf());
             //税率
             goodsImportItem.setTax(item.getTaxRate());
-            //TODO税收编码   默认填1
+            //TODO 税收编码   默认填 1
             goodsImportItem.setTaxCode("1");
 
             batchGoods.add(goodsImportItem);
@@ -218,6 +218,31 @@ public class ZhongChePushStrategy implements ProductPushStrategy {
         }
     }
 
+    @Override
+    public GoodsPriceUpdateVo updatePrice(Long itemId, GoodsImportBo bo) {
+        return zhongChePullController.egoodsPriceUpdate(bo);
+    }
+
+    @Override
+    public GoodsStatusUpdateVo updateStatus(Long itemId, GoodsStatusUpdateBo bo) {
+        return zhongChePullController.egoodsStatusUpdate(bo);
+    }
+
+    @Override
+    public GoodsImageUpdateVo updateImages(Long itemId, GoodsImageUpdateBo bo) {
+        return zhongChePullController.egoodsImgsUpdate(bo);
+    }
+
+    @Override
+    public GoodsUpdateVo updateProperties(Long itemId, GoodsPropertiesUpdateBo bo) {
+        return zhongChePullController.egoodsPropertiesUpdate(bo);
+    }
+
+    @Override
+    public GoodsUpdateVo updateDetail(Long itemId, GoodsDetailBo bo) {
+        return zhongChePullController.egoodsDetailUpdate(bo);
+    }
+
     // --------------------- 核心判断方法 ---------------------
     /**
      * 判断字符串是否包含中文汉字(核心判断逻辑)

+ 34 - 2
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/handler/impl/ZhongZhiPushStrategy.java

@@ -1,5 +1,7 @@
 package org.dromara.external.handler.impl;
 
+import org.dromara.external.api.zhongche.domain.bo.*;
+import org.dromara.external.api.zhongche.domain.vo.*;
 import org.dromara.external.domain.ExternalProduct;
 import org.dromara.external.handler.ProductPushStrategy;
 import org.springframework.stereotype.Component;
@@ -13,8 +15,38 @@ public class ZhongZhiPushStrategy implements ProductPushStrategy {
     public void push(Long itemId,List<ExternalProduct> products) {
 
         for (ExternalProduct product : products) {
-            // 调用中职API
-            System.out.println("推送到中职平台: " + product.getId());
+            // 调用中职 API
+            System.out.println("推送到中职平台" + product.getId());
         }
     }
+
+    @Override
+    public GoodsPriceUpdateVo updatePrice(Long itemId, GoodsImportBo bo) {
+        // TODO: 实现中职平台价格更新逻辑
+        return null;
+    }
+
+    @Override
+    public GoodsStatusUpdateVo updateStatus(Long itemId, GoodsStatusUpdateBo bo) {
+        // TODO: 实现中职平台状态更新逻辑
+        return null;
+    }
+
+    @Override
+    public GoodsImageUpdateVo updateImages(Long itemId, GoodsImageUpdateBo bo) {
+        // TODO: 实现中职平台图片更新逻辑
+        return null;
+    }
+
+    @Override
+    public GoodsUpdateVo updateProperties(Long itemId, GoodsPropertiesUpdateBo bo) {
+        // TODO: 实现中职平台规格更新逻辑
+        return null;
+    }
+
+    @Override
+    public GoodsUpdateVo updateDetail(Long itemId, GoodsDetailBo bo) {
+        // TODO: 实现中职平台详情更新逻辑
+        return null;
+    }
 }

+ 222 - 0
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/listener/ExternalProductImportListener.java

@@ -0,0 +1,222 @@
+package org.dromara.external.listener;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.idev.excel.context.AnalysisContext;
+import cn.idev.excel.event.AnalysisEventListener;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.ValidatorUtils;
+import org.dromara.common.excel.core.ExcelListener;
+import org.dromara.common.excel.core.ExcelResult;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.external.domain.ExternalItem;
+import org.dromara.external.domain.ExternalProduct;
+import org.dromara.external.domain.ExternalProductCategory;
+import org.dromara.external.domain.bo.ExternalProductBo;
+import org.dromara.external.domain.vo.ExternalProductImportVo;
+import org.dromara.external.service.IExternalItemService;
+import org.dromara.external.service.IExternalProductCategoryService;
+import org.dromara.external.service.IExternalProductService;
+import org.dromara.product.api.RemoteProductService;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 对外部推送商品导入监听器
+ *
+ * @author LionLi
+ * @date 2026-01-08
+ */
+@Slf4j
+public class ExternalProductImportListener extends AnalysisEventListener<ExternalProductImportVo> implements ExcelListener<ExternalProductImportVo> {
+
+    private final IExternalProductService externalProductService;
+    private RemoteProductService remoteProductService;
+    private IExternalItemService externalItemService;
+    private IExternalProductCategoryService externalProductCategoryService;
+
+    private int successNum = 0;
+    private int failureNum = 0;
+    private final StringBuilder successMsg = new StringBuilder();
+    private final StringBuilder failureMsg = new StringBuilder();
+
+    public ExternalProductImportListener() {
+        this.externalProductService = null;
+    }
+
+    public ExternalProductImportListener(IExternalProductService externalProductService) {
+        this.externalProductService = externalProductService;
+        this.remoteProductService = SpringUtils.getBean(RemoteProductService.class);
+        this.externalItemService = SpringUtils.getBean(IExternalItemService.class);
+        this.externalProductCategoryService = SpringUtils.getBean(IExternalProductCategoryService.class);
+    }
+
+    @Override
+    public void invoke(ExternalProductImportVo importVo, AnalysisContext context) {
+        try {
+            // 校验必填字段
+            if (ObjectUtil.isEmpty(importVo.getProductNo())) {
+                failureNum++;
+                failureMsg.append("<br/>").append(failureNum).append("、第 ").append(context.readRowHolder().getRowIndex() + 1).append(" 行,产品编号不能为空");
+                return;
+            }
+
+            if (ObjectUtil.isEmpty(importVo.getItemNo())) {
+                failureNum++;
+                failureMsg.append("<br/>").append(failureNum).append("、第 ").append(context.readRowHolder().getRowIndex() + 1).append(" 行,项目编号不能为空");
+                return;
+            }
+
+            // 如果是按类目折扣率报价,需要校验官网价和分类编码
+            if (importVo.getPricingRule() != null && importVo.getPricingRule() == 1) {
+                if (ObjectUtil.isEmpty(importVo.getOfficialPrice())) {
+                    failureNum++;
+                    failureMsg.append("<br/>").append(failureNum).append("、第 ").append(context.readRowHolder().getRowIndex() + 1).append(" 行,按类目折扣率报价时官网价不能为空");
+                    return;
+                }
+                if (ObjectUtil.isEmpty(importVo.getCategoryNo())) {
+                    failureNum++;
+                    failureMsg.append("<br/>").append(failureNum).append("、第 ").append(context.readRowHolder().getRowIndex() + 1).append(" 行,按类目折扣率报价时项目分类编码不能为空");
+                    return;
+                }
+            } else {
+                // 一品一价时,协议价必填
+                if (ObjectUtil.isEmpty(importVo.getAgreementPrice())) {
+                    failureNum++;
+                    failureMsg.append("<br/>").append(failureNum).append("、第 ").append(context.readRowHolder().getRowIndex() + 1).append(" 行,协议价不能为空");
+                    return;
+                }
+            }
+
+            // 根据产品编号查询是否已存在
+            List<ExternalProduct> existingList = externalProductService.list(Wrappers.<ExternalProduct>lambdaQuery()
+                .eq(ExternalProduct::getProductNo, importVo.getProductNo()));
+
+            // 获取项目 ID(通过项目编号)
+            Long itemId = null;
+            if (ObjectUtil.isNotEmpty(importVo.getItemNo())) {
+                ExternalItem item = externalItemService.getOne(
+                    Wrappers.<ExternalItem>lambdaQuery()
+                        .eq(ExternalItem::getItemNo, importVo.getItemNo())
+                );
+                if (ObjectUtil.isNotEmpty(item)) {
+                    itemId = item.getId();
+                }
+            }
+
+            // 获取分类信息(如果是按类目折扣率报价)
+            Long categoryId = null;
+            BigDecimal discountRate = null;
+            if (importVo.getPricingRule() != null && importVo.getPricingRule() == 1 && ObjectUtil.isNotEmpty(importVo.getCategoryNo())) {
+                ExternalProductCategory category = externalProductCategoryService.getOne(
+                    Wrappers.<ExternalProductCategory>lambdaQuery()
+                        .eq(ExternalProductCategory::getCategoryNo, importVo.getCategoryNo())
+                );
+                if (ObjectUtil.isNotEmpty(category)) {
+                    categoryId = category.getId();
+                    discountRate = category.getDiscountRate();
+                }
+            }
+
+            // 计算协议价:如果是按类目折扣率报价,则协议价 = 官网价 * 折扣率
+            BigDecimal finalAgreementPrice = importVo.getAgreementPrice();
+            if (importVo.getPricingRule() != null && importVo.getPricingRule() == 1 &&
+                ObjectUtil.isNotEmpty(importVo.getOfficialPrice()) && ObjectUtil.isNotEmpty(discountRate)) {
+                finalAgreementPrice = importVo.getOfficialPrice().multiply(discountRate);
+            }
+            //获取产品 ID
+            Long productId = remoteProductService.getProductIdProductNo(importVo.getProductNo());
+
+
+            if (ObjectUtil.isNotEmpty(existingList)) {
+                // 如果已存在,则更新
+                ExternalProduct existing = existingList.get(0);
+                // 校验是否属于同一个项目
+                if (itemId != null && !itemId.equals(existing.getItemId())) {
+                    failureNum++;
+                    failureMsg.append("<br/>").append(failureNum).append("、第 ").append(context.readRowHolder().getRowIndex() + 1).append(" 行,产品编号已存在于其他项目");
+                    return;
+                }
+
+                ExternalProductBo updateBo = new ExternalProductBo();
+                updateBo.setId(existing.getId());
+                updateBo.setProductId(productId);
+                updateBo.setItemId(itemId != null ? itemId : existing.getItemId());
+                updateBo.setCategoryId(categoryId);
+                updateBo.setProductNo(importVo.getProductNo());
+                updateBo.setPricingRule(importVo.getPricingRule());
+                updateBo.setExternalPrice(finalAgreementPrice);
+                updateBo.setMinOrderQuantity(importVo.getMinOrderQuantity());
+                updateBo.setProductStatus(importVo.getShelfStatus());
+                ValidatorUtils.validate(updateBo);
+                updateBo.setUpdateBy(LoginHelper.getUserId());
+                externalProductService.updateByBo(updateBo);
+                successNum++;
+                successMsg.append("<br/>").append(successNum).append("、产品编号 ").append(importVo.getProductNo()).append(" 更新成功");
+            } else {
+                // 如果不存在,则新增
+                ExternalProductBo insertBo = new ExternalProductBo();
+                insertBo.setItemId(itemId);
+                insertBo.setProductId(productId);
+                insertBo.setProductNo(importVo.getProductNo());
+                insertBo.setCategoryId(categoryId);
+                insertBo.setPricingRule(importVo.getPricingRule());
+                insertBo.setExternalPrice(finalAgreementPrice);
+                insertBo.setMinOrderQuantity(importVo.getMinOrderQuantity());
+                insertBo.setProductStatus(importVo.getShelfStatus());
+                ValidatorUtils.validate(insertBo);
+                insertBo.setCreateBy(LoginHelper.getUserId());
+                externalProductService.insertByBo(insertBo);
+                successNum++;
+                successMsg.append("<br/>").append(successNum).append("、产品编号 ").append(importVo.getProductNo()).append(" 导入成功");
+            }
+        } catch (Exception e) {
+            failureNum++;
+            String msg = "<br/>" + failureNum + "、产品编号 " + importVo.getProductNo() + " 导入失败:";
+            String message = e.getMessage();
+            if (e instanceof ConstraintViolationException cvException) {
+                message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
+            }
+            failureMsg.append(msg).append(message);
+            log.error(msg, e);
+        }
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+
+    }
+
+    @Override
+    public ExcelResult<ExternalProductImportVo> getExcelResult() {
+        return new ExcelResult<ExternalProductImportVo>() {
+
+            @Override
+            public String getAnalysis() {
+                if (failureNum > 0) {
+                    failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
+                    throw new ServiceException(failureMsg.toString());
+                } else {
+                    successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
+                }
+                return successMsg.toString();
+            }
+
+            @Override
+            public List<ExternalProductImportVo> getList() {
+                return null;
+            }
+
+            @Override
+            public List<String> getErrorList() {
+                return null;
+            }
+        };
+    }
+}

+ 4 - 4
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/util/DataMigration.java

@@ -11,9 +11,9 @@ public class DataMigration {
 
 
     // SQL Server 连接信息
-    private static final String SQL_SERVER_URL = "jdbc:sqlserver://192.168.1.123:1433;databaseName=yoe_db";
+    private static final String SQL_SERVER_URL = "jdbc:sqlserver://119.97.180.89:1433;databaseName=Yoe365Pro;";
     private static final String SQL_SERVER_USER = "sa";
-    private static final String SQL_SERVER_PASS = "Sql123456";
+    private static final String SQL_SERVER_PASS = "Dhx365!123";
 
     // MySQL 连接信息
     private static final String MYSQL_URL = "jdbc:mysql://127.0.0.1:3306/yoe_product_db?useSSL=false&serverTimezone=UTC";
@@ -851,13 +851,13 @@ public class DataMigration {
                             createdTime = new Timestamp(System.currentTimeMillis());
                         }
                         ins.setTimestamp(idx++, createdTime);
-                        
+
                         Timestamp modifyTime = rs.getTimestamp("modify");
                         if (modifyTime == null) {
                             modifyTime = new Timestamp(System.currentTimeMillis());
                         }
                         ins.setTimestamp(idx++, modifyTime);
-                        
+
                         ins.setString(idx++, null);     // platform_code(若源表无,可设 null 或固定值)
 
                         ins.addBatch();

+ 16 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductBaseController.java

@@ -280,4 +280,20 @@ public class ProductBaseController extends BaseController {
         return productBaseService.getItemProductList(bo, pageQuery);
     }
 
+    /**
+     * 校验 69 码是否重复
+     *
+     * @param upcBarcode 69 码
+     * @param productId 商品 ID(编辑时传入,用于排除自己)
+     */
+    @GetMapping("/checkUpcBarcode")
+    public R<Boolean> checkUpcBarcode(@RequestParam("upcBarcode") String upcBarcode,
+                                      @RequestParam(value = "productId", required = false) Long productId) {
+        Boolean isDuplicate = productBaseService.checkUpcBarcodeDuplicate(upcBarcode, productId);
+        if (isDuplicate) {
+            return R.ok("存在相同 69 码的商品");
+        }
+        return R.ok(isDuplicate);
+    }
+
 }

+ 156 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductPoolAuditController.java

@@ -0,0 +1,156 @@
+package org.dromara.product.controller;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import org.dromara.product.domain.ProductPoolAudit;
+import org.dromara.product.domain.bo.ProductBaseBo;
+import org.dromara.product.domain.bo.ProductPoolAuditBatchBo;
+import org.dromara.product.domain.vo.ProductBaseVo;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.product.domain.vo.ProductPoolAuditVo;
+import org.dromara.product.domain.bo.ProductPoolAuditBo;
+import org.dromara.product.service.IProductPoolAuditService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 产品池审核
+ * 前端访问路由地址为:/product/poolAudit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/poolAudit")
+public class ProductPoolAuditController extends BaseController {
+
+    private final IProductPoolAuditService productPoolAuditService;
+
+    /**
+     * 查询产品池审核列表
+     */
+    //@SaCheckPermission("product:poolAudit:list")
+    @GetMapping("/list")
+    public TableDataInfo<ProductPoolAuditVo> list(ProductPoolAuditBo bo, PageQuery pageQuery) {
+        return productPoolAuditService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出产品池审核列表
+     */
+    //@SaCheckPermission("product:poolAudit:export")
+    @Log(title = "产品池审核", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(ProductPoolAuditBo bo, HttpServletResponse response) {
+        List<ProductPoolAuditVo> list = productPoolAuditService.queryList(bo);
+        ExcelUtil.exportExcel(list, "产品池审核", ProductPoolAuditVo.class, response);
+    }
+
+    /**
+     * 获取产品池审核详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("product:poolAudit:query")
+    @GetMapping("/{id}")
+    public R<ProductPoolAuditVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(productPoolAuditService.queryById(id));
+    }
+
+    /**
+     * 新增产品池审核
+     */
+    //@SaCheckPermission("product:poolAudit:add")
+    @Log(title = "产品池审核", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody ProductPoolAuditBo bo) {
+        return toAjax(productPoolAuditService.insertByBo(bo));
+    }
+
+    /**
+     * 修改产品池审核
+     */
+    //@SaCheckPermission("product:poolAudit:edit")
+    @Log(title = "产品池审核", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ProductPoolAuditBo bo) {
+        return toAjax(productPoolAuditService.updateByBo(bo));
+    }
+
+    /**
+     * 删除产品池审核
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("product:poolAudit:remove")
+    @Log(title = "产品池审核", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(productPoolAuditService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+    * 获取审核池里的商品
+    * */
+    @GetMapping("/getPoolAuditProductPage")
+    public TableDataInfo<ProductBaseVo> getPoolAuditProductPage(ProductBaseBo bo, PageQuery pageQuery) {
+        return productPoolAuditService.getPoolAuditProductPage(bo, pageQuery);
+    }
+
+    /**
+     * 批量审核产品池商品
+     */
+    @Log(title = "产品池审核", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchAudit")
+    public R<Void> batchAudit(@RequestBody ProductPoolAuditBatchBo bo) {
+        return toAjax(productPoolAuditService.batchAuditProducts(
+            bo.getPoolAuditId(),
+            bo.getProductIds(),
+            bo.getAuditStatus(),
+            bo.getReason()
+        ));
+    }
+
+
+
+    /**
+    * 清空审核池
+    * */
+    @Log(title = "清空审核池", businessType = BusinessType.DELETE)
+    @DeleteMapping("/clearPool")
+    public R<Void> clearPool(@NotNull(message = "主键不能为空") @PathVariable("id") Long id) {
+        return toAjax(productPoolAuditService.clearPool(id));
+    }
+
+    /**
+    * 提交审核
+    * */
+    @Log(title = "产品池审核", businessType = BusinessType.UPDATE)
+    @PutMapping("/apply/{id}")
+    public R<Void> apply(@NotNull(message = "主键不能为空") @PathVariable("id") Long id) {
+        productPoolAuditService.update(Wrappers.lambdaUpdate(ProductPoolAudit.class)
+            .set(ProductPoolAudit::getProductReviewStatus, "1")
+            .eq(ProductPoolAudit::getId, id)
+        );
+        return R.ok();
+    }
+}

+ 123 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProductPoolLinkAuditController.java

@@ -0,0 +1,123 @@
+package org.dromara.product.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.product.domain.bo.BatchAddProductBo;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.product.domain.vo.ProductPoolLinkAuditVo;
+import org.dromara.product.domain.bo.ProductPoolLinkAuditBo;
+import org.dromara.product.service.IProductPoolLinkAuditService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 产品池和产品关联审核
+ * 前端访问路由地址为:/product/poolLinkAudit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/poolLinkAudit")
+public class ProductPoolLinkAuditController extends BaseController {
+
+    private final IProductPoolLinkAuditService productPoolLinkAuditService;
+
+    /**
+     * 查询产品池和产品关联审核列表
+     */
+    //@SaCheckPermission("product:poolLinkAudit:list")
+    @GetMapping("/list")
+    public TableDataInfo<ProductPoolLinkAuditVo> list(ProductPoolLinkAuditBo bo, PageQuery pageQuery) {
+        return productPoolLinkAuditService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出产品池和产品关联审核列表
+     */
+    //@SaCheckPermission("product:poolLinkAudit:export")
+    @Log(title = "产品池和产品关联审核", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(ProductPoolLinkAuditBo bo, HttpServletResponse response) {
+        List<ProductPoolLinkAuditVo> list = productPoolLinkAuditService.queryList(bo);
+        ExcelUtil.exportExcel(list, "产品池和产品关联审核", ProductPoolLinkAuditVo.class, response);
+    }
+
+    /**
+     * 获取产品池和产品关联审核详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("product:poolLinkAudit:query")
+    @GetMapping("/{id}")
+    public R<ProductPoolLinkAuditVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(productPoolLinkAuditService.queryById(id));
+    }
+
+    /**
+     * 新增产品池和产品关联审核
+     */
+    //@SaCheckPermission("product:poolLinkAudit:add")
+    @Log(title = "产品池和产品关联审核", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody ProductPoolLinkAuditBo bo) {
+        return toAjax(productPoolLinkAuditService.insertByBo(bo));
+    }
+
+    /**
+     * 修改产品池和产品关联审核
+     */
+    //@SaCheckPermission("product:poolLinkAudit:edit")
+    @Log(title = "产品池和产品关联审核", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ProductPoolLinkAuditBo bo) {
+        return toAjax(productPoolLinkAuditService.updateByBo(bo));
+    }
+
+    /**
+     * 删除产品池和产品关联审核
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("product:poolLinkAudit:remove")
+    @Log(title = "产品池和产品关联审核", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(productPoolLinkAuditService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+     * 批量添加商品到商品池
+     *
+     * @param bo 批量添加参数
+     * @return 操作结果
+     */
+    //@SaCheckPermission("product:poolLink:add")
+    @Log(title = "批量添加商品到商品池", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/batchAdd")
+    public R<Void> batchAdd(@Validated @RequestBody BatchAddProductBo bo) {
+        return toAjax(productPoolLinkAuditService.batchAddProducts(bo));
+    }
+
+
+}

+ 18 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/IndexProductController.java

@@ -6,8 +6,10 @@ import lombok.RequiredArgsConstructor;
 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.easyes.core.conditions.select.LambdaEsQueryWrapper;
 import org.dromara.product.domain.bo.*;
 import org.dromara.product.domain.vo.*;
+import org.dromara.product.esmapper.ProductEsMapper;
 import org.dromara.product.service.*;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -61,6 +63,8 @@ public class IndexProductController {
     private final IProcurementTopicsService procurementTopicsService;
     //特价商品
     private final IProductSpecialLinkService productSpecialLinkService;
+    //商品es
+    private final ProductEsMapper productEsMapper;
 
     public static void main(String[] args) {
         String string = HttpUtil.get("http://127.0.0.1:8080/product/indexProduct/getEsProductList");
@@ -94,6 +98,20 @@ public class IndexProductController {
         return R.ok(productBaseService.queryById(productId));
     }
 
+    /**
+    * 获取商品详情
+    * */
+    @GetMapping("getProductDetailByNo/{productNo}")
+    public R<ProductBaseVo> getProductDetailByNo(@PathVariable String productNo) {
+        ProductBaseVo productBaseVo = productEsMapper.selectOne(new LambdaEsQueryWrapper<ProductBaseVo>()
+            .eq(ProductBaseVo::getProductNo, productNo)
+        );
+        if (productBaseVo != null) {
+            return R.ok(productBaseService.queryById(productBaseVo.getId()));
+        }
+        return R.ok(productBaseVo);
+    }
+
 
 
     /**

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductExtend.java

@@ -40,7 +40,7 @@ public class ProductExtend extends TenantEntity {
     private String invoiceName;
 
     /**
-     * 发票类型
+     * 发票规格
      */
     private String invoiceType;
 

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPool.java

@@ -42,7 +42,7 @@ public class ProductPool extends TenantEntity {
     private Long categoryId;
 
     /**
-     * 产品池类型 0普通产品池,1精选产品池,2协议产品池,3项目产品池,4分类产品池
+     * 产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4分类产品池
      * */
     private Integer type;
 

+ 86 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPoolAudit.java

@@ -0,0 +1,86 @@
+package org.dromara.product.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 产品池审核对象 product_pool_audit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("product_pool_audit")
+public class ProductPoolAudit extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 池名称
+     */
+    private String name;
+
+    /**
+     * 池id
+     */
+    private Long poolId;
+
+    /**
+     * 项目id
+     */
+    private Long itemId;
+
+    /**
+     * 产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4营销产品池
+     */
+    private String type;
+
+    /**
+     * 协议id
+     */
+    private Long protocolId;
+
+    /**
+     * 客户id
+     */
+    private Long customerId;
+
+    /**
+     * 产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回
+     */
+    private String productReviewStatus;
+
+    /**
+     * 审核原因
+     */
+    private String reviewReason;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    private Long auditUserId;
+
+    private Date auditTime;
+
+    /**
+     * 附件
+     * */
+    private String attachment;
+
+
+}

+ 87 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPoolLinkAudit.java

@@ -0,0 +1,87 @@
+package org.dromara.product.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.math.BigDecimal;
+
+/**
+ * 产品池和产品关联审核对象 product_pool_link_audit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("product_pool_link_audit")
+public class ProductPoolLinkAudit extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 所属池 ID
+     */
+    private Long poolId;
+    
+    /**
+     * 项目 id
+     */
+    private Long itemId;
+    
+    /**
+     * 审核池 id
+     */
+    private Long poolAuditId;
+
+    /**
+     * 产品id
+     */
+    private Long productId;
+
+    /**
+     * 产品价格
+     */
+    private BigDecimal productPrice;
+
+    /**
+     * 协议价格
+     */
+    private BigDecimal negotiatedPrice;
+
+    /**
+     * 分类id
+     */
+    private Long categoryId;
+
+    /**
+     * 产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回
+     */
+    private String productReviewStatus;
+
+    /**
+     * 审核原因
+     */
+    private String reviewReason;
+
+    /**
+     * 审核人
+     */
+    private String reviewer;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 5 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductPriceInventory.java

@@ -93,6 +93,10 @@ public class ProductPriceInventory extends TenantEntity {
      * 备注
      */
     private String remark;
-
+    /**
+     * 税收编码id
+     *
+     * */
+    private Long taxationId;
 
 }

+ 22 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductBaseBo.java

@@ -41,6 +41,12 @@ public class ProductBaseBo extends BaseEntity {
     //@NotBlank(message = "产品编号不能为空", groups = {AddGroup.class, EditGroup.class})
     private String productNo;
 
+    /**
+     * 产品编号
+     */
+    //@NotBlank(message = "产品编号不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String productNos;
+
     /**
      * 项目名称
      */
@@ -182,6 +188,11 @@ public class ProductBaseBo extends BaseEntity {
      */
     private String invoiceName;
 
+    /**
+     * 促销标题
+     */
+    private String promotionTitle;
+
     /**
      * 发票规格
      */
@@ -388,6 +399,17 @@ public class ProductBaseBo extends BaseEntity {
     * */
     private Integer pushStatus;
 
+    /**
+     * 税收编码id
+     *
+     * */
+    private Long taxationId;
+
+    /**
+    * 产品池审核id
+    * */
+    private Long poolAuditId;
+
 
 
 }

+ 26 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolAuditBatchBo.java

@@ -0,0 +1,26 @@
+package org.dromara.product.domain.bo;
+
+import lombok.Data;
+
+/**
+ * 产品池批量审核请求参数
+ */
+@Data
+public class ProductPoolAuditBatchBo {
+    /**
+     * 审核池 ID
+     */
+    private Long poolAuditId;
+    /**
+     * 产品 ID 列表
+     */
+    private java.util.List<Long> productIds;
+    /**
+     * 审核状态 2=通过,3=驳回
+     */
+    private String auditStatus;
+    /**
+     * 审核原因
+     */
+    private String reason;
+}

+ 132 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolAuditBo.java

@@ -0,0 +1,132 @@
+package org.dromara.product.domain.bo;
+
+import org.dromara.product.domain.ProductPoolAudit;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+import java.math.BigDecimal;
+
+/**
+ * 产品池审核业务对象 product_pool_audit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = ProductPoolAudit.class, reverseConvertGenerate = false)
+public class ProductPoolAuditBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 池名称
+     */
+    private String name;
+
+    /**
+     * 池id
+     */
+    //@NotNull(message = "池id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long poolId;
+
+    /**
+     * 项目id
+     */
+    //@NotNull(message = "项目id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long itemId;
+
+    /**
+     * 产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4营销产品池
+     */
+    //@NotBlank(message = "产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4营销产品池不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String type;
+
+    /**
+     * 协议id
+     */
+    //@NotNull(message = "协议id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long protocolId;
+
+    /**
+     * 客户id
+     */
+    //@NotNull(message = "客户id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long customerId;
+
+    /**
+     * 产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回
+     */
+    private String productReviewStatus;
+
+    /**
+     * 审核原因
+     */
+    //@NotBlank(message = "审核原因不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String reviewReason;
+
+    /**
+     * 备注
+     */
+    //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+    private Long auditUserId;
+
+    private Date auditTime;
+
+    /**
+    * 附件
+    * */
+    private String attachment;
+
+    /**
+     * 产品列表
+     * */
+    private List<ProductItem> products;
+
+    /**
+     * 产品项
+     * */
+    @Data
+    public static class ProductItem implements Serializable {
+
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 商品 ID
+         */
+        @NotNull(message = "商品 ID 不能为空")
+        private Long productId;
+
+        /**
+         * 产品价格
+         */
+        private BigDecimal productPrice;
+
+        /**
+         * 协议价格
+         */
+        private BigDecimal negotiatedPrice;
+
+        /**
+         * 分类 id
+         */
+        private Long categoryId;
+    }
+
+
+}

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolBo.java

@@ -40,7 +40,7 @@ public class ProductPoolBo extends BaseEntity {
     private Long categoryId;
 
     /**
-    * 产品池类型 0普通产品池,1精选产品池,2协议产品池,3项目产品池,4分类产品池
+    * 产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4分类产品池
     * */
     private Integer type;
 

+ 92 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPoolLinkAuditBo.java

@@ -0,0 +1,92 @@
+package org.dromara.product.domain.bo;
+
+import org.dromara.product.domain.ProductPoolLinkAudit;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 产品池和产品关联审核业务对象 product_pool_link_audit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = ProductPoolLinkAudit.class, reverseConvertGenerate = false)
+public class ProductPoolLinkAuditBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 所属池ID
+     */
+    //@NotNull(message = "所属池ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long poolId;
+
+    /**
+     * 审核池id
+     */
+    //@NotNull(message = "审核池id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long poolAuditId;
+
+    /**
+     * 产品id
+     */
+    private Long productId;
+
+    /**
+     * 产品价格
+     */
+    //@NotNull(message = "产品价格不能为空", groups = { AddGroup.class, EditGroup.class })
+    private BigDecimal productPrice;
+
+    /**
+     * 协议价格
+     */
+    //@NotNull(message = "协议价格不能为空", groups = { AddGroup.class, EditGroup.class })
+    private BigDecimal negotiatedPrice;
+
+    /**
+     * 分类id
+     */
+    //@NotNull(message = "分类id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long categoryId;
+
+    /**
+     * 产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回
+     */
+    //@NotBlank(message = "产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String productReviewStatus;
+
+    /**
+     * 审核原因
+     */
+    //@NotBlank(message = "审核原因不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String reviewReason;
+
+    /**
+     * 审核人
+     */
+    //@NotBlank(message = "审核人不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String reviewer;
+
+    /**
+     * 备注
+     */
+    //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+
+
+}

+ 5 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductPriceInventoryBo.java

@@ -94,5 +94,9 @@ public class ProductPriceInventoryBo extends BaseEntity {
     //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
     private String remark;
 
-
+    /**
+     * 税收编码id
+     *
+     * */
+    private Long taxationId;
 }

+ 16 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductBaseVo.java

@@ -522,4 +522,20 @@ public class ProductBaseVo implements Serializable {
      * 对接状态 0未对接,1已对接
      */
     private Integer connectStatus;
+
+    /**
+    * 税收编码id
+     *
+    * */
+    private Long taxationId;
+
+    /**
+     * 促销标题
+     */
+    private String promotionTitle;
+
+    /**
+    * 产品池审核商品id
+    * */
+    private Long poolAuditProductId;
 }

+ 129 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPoolAuditVo.java

@@ -0,0 +1,129 @@
+package org.dromara.product.domain.vo;
+
+import org.dromara.product.domain.ProductPoolAudit;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * 产品池审核视图对象 product_pool_audit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = ProductPoolAudit.class)
+public class ProductPoolAuditVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 池名称
+     */
+    @ExcelProperty(value = "池名称")
+    private String name;
+
+    /**
+     * 池id
+     */
+    @ExcelProperty(value = "池id")
+    private Long poolId;
+
+    /**
+     * 项目id
+     */
+    @ExcelProperty(value = "项目id")
+    private Long itemId;
+
+    /**
+     * 产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4营销产品池
+     */
+    @ExcelProperty(value = "产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4营销产品池")
+    private String type;
+
+    /**
+     * 协议id
+     */
+    @ExcelProperty(value = "协议id")
+    private Long protocolId;
+
+    /**
+     * 客户id
+     */
+    @ExcelProperty(value = "客户id")
+    private Long customerId;
+
+    /**
+     * 产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回
+     */
+    @ExcelProperty(value = "产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回")
+    private String productReviewStatus;
+
+    /**
+     * 审核原因
+     */
+    @ExcelProperty(value = "审核原因")
+    private String reviewReason;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+    * 创建人名称
+    * */
+    @ExcelProperty(value = "创建人名称")
+    private String createByName;
+
+    /**
+    * 审核人名称
+    * */
+    @ExcelProperty(value = "审核人名称")
+    private String auditByName;
+
+    /**
+     * 项目/协议/自营产品池名称(根据类型回显)
+     * */
+    @ExcelProperty(value = "产品池名称")
+    private String poolName;
+
+    private Long auditUserId;
+
+    private Date auditTime;
+
+    private Long createBy;
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 附件
+     * */
+    private String attachment;
+
+    /**
+    * 商品ids
+    * */
+    private List<Long> productIds;
+
+
+
+}

+ 99 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPoolLinkAuditVo.java

@@ -0,0 +1,99 @@
+package org.dromara.product.domain.vo;
+
+import org.dromara.product.domain.ProductPoolLinkAudit;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+
+
+/**
+ * 产品池和产品关联审核视图对象 product_pool_link_audit
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = ProductPoolLinkAudit.class)
+public class ProductPoolLinkAuditVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 所属池ID
+     */
+    @ExcelProperty(value = "所属池ID")
+    private Long poolId;
+
+    /**
+     * 审核池id
+     */
+    @ExcelProperty(value = "审核池id")
+    private Long poolAuditId;
+
+    /**
+     * 产品id
+     */
+    @ExcelProperty(value = "产品id")
+    private Long productId;
+
+    /**
+     * 产品价格
+     */
+    @ExcelProperty(value = "产品价格")
+    private BigDecimal productPrice;
+
+    /**
+     * 协议价格
+     */
+    @ExcelProperty(value = "协议价格")
+    private BigDecimal negotiatedPrice;
+
+    /**
+     * 分类id
+     */
+    @ExcelProperty(value = "分类id")
+    private Long categoryId;
+
+    /**
+     * 产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回
+     */
+    @ExcelProperty(value = "产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回")
+    private String productReviewStatus;
+
+    /**
+     * 审核原因
+     */
+    @ExcelProperty(value = "审核原因")
+    private String reviewReason;
+
+    /**
+     * 审核人
+     */
+    @ExcelProperty(value = "审核人")
+    private String reviewer;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPoolVo.java

@@ -51,7 +51,7 @@ public class ProductPoolVo implements Serializable {
     private Long categoryId;
 
     /**
-     * 产品池类型 0普通产品池,1精选产品池,2协议产品池,3项目产品池,4分类产品池
+     * 产品池类型 0自营产品池,1精选产品池,2协议产品池,3项目产品池,4分类产品池
      * */
     private Integer type;
 

+ 5 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductPriceInventoryVo.java

@@ -107,5 +107,9 @@ public class ProductPriceInventoryVo implements Serializable {
     @ExcelProperty(value = "备注")
     private String remark;
 
-
+    /**
+     * 税收编码id
+     *
+     * */
+    private Long taxationId;
 }

+ 15 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/dubbo/RemoteProductServiceImpl.java

@@ -8,6 +8,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.math3.stat.descriptive.summary.Product;
@@ -638,5 +639,19 @@ public class RemoteProductServiceImpl implements RemoteProductService {
         return update;
     }
 
+    /**
+     * 根据商品编号查询商品ID
+     *
+     * @param productNo
+     * @return
+     */
+    @Override
+    public Long getProductIdProductNo(String productNo) {
+        ProductBase productBase = productBaseService.getOne(Wrappers.lambdaQuery(ProductBase.class)
+            .eq(ProductBase::getProductNo, productNo)
+            .last("limit 1")
+        );
 
+        return Objects.isNull(productBase) ? null : productBase.getId();
+    }
 }

+ 15 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/mapper/ProductPoolAuditMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.product.mapper;
+
+import org.dromara.product.domain.ProductPoolAudit;
+import org.dromara.product.domain.vo.ProductPoolAuditVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 产品池审核Mapper接口
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+public interface ProductPoolAuditMapper extends BaseMapperPlus<ProductPoolAudit, ProductPoolAuditVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/mapper/ProductPoolLinkAuditMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.product.mapper;
+
+import org.dromara.product.domain.ProductPoolLinkAudit;
+import org.dromara.product.domain.vo.ProductPoolLinkAuditVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 产品池和产品关联审核Mapper接口
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+public interface ProductPoolLinkAuditMapper extends BaseMapperPlus<ProductPoolLinkAudit, ProductPoolLinkAuditVo> {
+
+}

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

@@ -259,4 +259,13 @@ public interface IProductBaseService extends IService<ProductBase>{
     * 获取项目商品列表
     * */
     TableDataInfo<ProductBaseVo> getItemProductList(ProductBaseBo bo, PageQuery pageQuery);
+
+    /**
+     * 根据 69 码校验是否有重复商品
+     *
+     * @param upcBarcode 69 码
+     * @param productId 商品 ID(编辑时传入,用于排除自己)
+     * @return 是否存在重复的 69 码
+     */
+    Boolean checkUpcBarcodeDuplicate(String upcBarcode, Long productId);
 }

+ 99 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IProductPoolAuditService.java

@@ -0,0 +1,99 @@
+package org.dromara.product.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.product.domain.ProductPoolAudit;
+import org.dromara.product.domain.bo.ProductBaseBo;
+import org.dromara.product.domain.vo.ProductBaseVo;
+import org.dromara.product.domain.vo.ProductPoolAuditVo;
+import org.dromara.product.domain.bo.ProductPoolAuditBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 产品池审核Service接口
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+public interface IProductPoolAuditService extends IService<ProductPoolAudit>{
+
+    /**
+     * 查询产品池审核
+     *
+     * @param id 主键
+     * @return 产品池审核
+     */
+    ProductPoolAuditVo queryById(Long id);
+
+    /**
+     * 分页查询产品池审核列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 产品池审核分页列表
+     */
+    TableDataInfo<ProductPoolAuditVo> queryPageList(ProductPoolAuditBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的产品池审核列表
+     *
+     * @param bo 查询条件
+     * @return 产品池审核列表
+     */
+    List<ProductPoolAuditVo> queryList(ProductPoolAuditBo bo);
+
+    /**
+     * 新增产品池审核
+     *
+     * @param bo 产品池审核
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(ProductPoolAuditBo bo);
+
+    /**
+     * 修改产品池审核
+     *
+     * @param bo 产品池审核
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(ProductPoolAuditBo bo);
+
+    /**
+     * 校验并批量删除产品池审核信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 获取产品池产品审核列表
+     *
+     * @param bo 查询条件
+     * @return 产品池审核列表
+     */
+    TableDataInfo<ProductBaseVo> getPoolAuditProductPage(ProductBaseBo bo, PageQuery pageQuery);
+
+    /**
+     * 批量审核产品池商品
+     *
+     * @param poolAuditId 审核池 ID
+     * @param productIds 产品 ID 列表
+     * @param auditStatus 审核状态 2=通过,3=驳回
+     * @param reason 审核原因
+     * @return 是否审核成功
+     */
+    Boolean batchAuditProducts(Long poolAuditId, List<Long> productIds, String auditStatus, String reason);
+
+    /**
+     * 清空审核池
+     *
+     * @param id 审核池 ID
+     * @return 是否清空成功
+     */
+    int clearPool(Long id);
+}

+ 79 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IProductPoolLinkAuditService.java

@@ -0,0 +1,79 @@
+package org.dromara.product.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.product.domain.ProductPoolLinkAudit;
+import org.dromara.product.domain.bo.BatchAddProductBo;
+import org.dromara.product.domain.vo.ProductPoolLinkAuditVo;
+import org.dromara.product.domain.bo.ProductPoolLinkAuditBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 产品池和产品关联审核Service接口
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+public interface IProductPoolLinkAuditService extends IService<ProductPoolLinkAudit>{
+
+    /**
+     * 查询产品池和产品关联审核
+     *
+     * @param id 主键
+     * @return 产品池和产品关联审核
+     */
+    ProductPoolLinkAuditVo queryById(Long id);
+
+    /**
+     * 分页查询产品池和产品关联审核列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 产品池和产品关联审核分页列表
+     */
+    TableDataInfo<ProductPoolLinkAuditVo> queryPageList(ProductPoolLinkAuditBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的产品池和产品关联审核列表
+     *
+     * @param bo 查询条件
+     * @return 产品池和产品关联审核列表
+     */
+    List<ProductPoolLinkAuditVo> queryList(ProductPoolLinkAuditBo bo);
+
+    /**
+     * 新增产品池和产品关联审核
+     *
+     * @param bo 产品池和产品关联审核
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(ProductPoolLinkAuditBo bo);
+
+    /**
+     * 修改产品池和产品关联审核
+     *
+     * @param bo 产品池和产品关联审核
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(ProductPoolLinkAuditBo bo);
+
+    /**
+     * 校验并批量删除产品池和产品关联审核信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 批量添加产品
+     *
+     * @param bo 批量添加产品参数
+     * @return 添加结果
+     */
+    Boolean batchAddProducts(BatchAddProductBo bo);
+}

+ 42 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBaseServiceImpl.java

@@ -227,6 +227,8 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         extendWrapper.eq(ProductExtend::getProductId, id);
         ProductExtend extend = extendMapper.selectOne(extendWrapper);
         if (extend != null) {
+            vo.setPromotionTitle(extend.getPromotionTitle());
+            vo.setSpecification(extend.getSpecificationsCode());
             vo.setInvoiceName(extend.getInvoiceName());
             vo.setInvoiceSpec(extend.getInvoiceSpecs());
             vo.setUpcBarcode(extend.getBarCoding());
@@ -262,6 +264,7 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
             vo.setPurchasePrice(priceInventory.getPurchasingPrice());
             vo.setEstimatedPurchasePrice(priceInventory.getMaxPurchasePrice());
             vo.setTaxRate(priceInventory.getTaxRate());
+            vo.setTaxationId(priceInventory.getTaxationId());
             vo.setCurrency(priceInventory.getCurrency());
             vo.setMinOrderQuantity(priceInventory.getMinOrderQuantity());
             vo.setTotalInventory(priceInventory.getTotalInventory());
@@ -408,6 +411,9 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         if (ObjectUtil.isNotEmpty(bo.getIds())){
             productBaseVoLambdaEsQueryWrapper.in(ProductBaseVo::getId, bo.getIds().split(","));
         }
+        if (ObjectUtil.isNotEmpty(bo.getProductNos())){
+            productBaseVoLambdaEsQueryWrapper.in(ProductBaseVo::getProductNo, bo.getProductNos().split(","));
+        }
         if (ObjectUtil.isNotEmpty(bo.getBrandIds())){
             productBaseVoLambdaEsQueryWrapper.in(ProductBaseVo::getBrandId, bo.getBrandIds().split(","));
         }
@@ -748,8 +754,11 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         extend.setProductId(productId);
 
         // 映射扩展字段
+        extend.setPromotionTitle(extend.getPromotionTitle());
         extend.setInvoiceName(bo.getInvoiceName());
+        extend.setSpecificationsCode(bo.getSpecification());
         extend.setInvoiceSpecs(bo.getInvoiceSpec());
+        extend.setInvoiceType(bo.getInvoiceSpec());
         extend.setBarCoding(bo.getUpcBarcode());
         extend.setProductWeight(bo.getWeight());
         extend.setWeightUnit(bo.getWeightUnit());
@@ -783,6 +792,7 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         priceInventory.setCurrency(bo.getCurrency());
         priceInventory.setTaxRate(bo.getTaxRate());
         priceInventory.setMinOrderQuantity(bo.getMinOrderQuantity());
+        priceInventory.setTaxationId(bo.getTaxationId());
         // 设置默认库存值
         priceInventory.setTotalInventory(0L);
         priceInventory.setNowInventory(0L);
@@ -817,7 +827,10 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
 
         if (existing != null) {
             // 存在则更新(只更新非空字段)
+            if (bo.getPromotionTitle() != null) existing.setPromotionTitle(bo.getPromotionTitle());
             if (bo.getInvoiceName() != null) existing.setInvoiceName(bo.getInvoiceName());
+            if (bo.getSpecification() != null) existing.setSpecificationsCode(bo.getSpecification());
+            if (bo.getInvoiceSpec() != null) existing.setInvoiceType(bo.getInvoiceSpec());
             if (bo.getInvoiceSpec() != null) existing.setInvoiceSpecs(bo.getInvoiceSpec());
             if (bo.getUpcBarcode() != null) existing.setBarCoding(bo.getUpcBarcode());
             if (bo.getWeight() != null) existing.setProductWeight(bo.getWeight());
@@ -861,6 +874,7 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
             if (bo.getCertificatePrice() != null) existing.setMinSellingPrice(bo.getCertificatePrice());
             if (bo.getPurchasePrice() != null) existing.setPurchasingPrice(bo.getPurchasePrice());
             if (bo.getTaxRate() != null) existing.setTaxRate(bo.getTaxRate());
+            if (bo.getTaxationId() != null) existing.setTaxationId(bo.getTaxationId());
             if (bo.getEstimatedPurchasePrice() != null) existing.setMaxPurchasePrice(bo.getEstimatedPurchasePrice());
             if(bo.getTotalInventory() != null) existing.setTotalInventory(bo.getVirtualInventory()+existing.getNowInventory());
             if(bo.getNowInventory() != null) existing.setNowInventory(bo.getNowInventory());
@@ -2201,5 +2215,33 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
 
         return tableDataInfo;
     }
+
+    /**
+     * 根据 69 码校验是否有重复商品
+     *
+     * @param upcBarcode 69 码
+     * @param productId 商品 ID(编辑时传入,用于排除自己)
+     * @return 是否存在重复的 69 码
+     */
+    @Override
+    public Boolean checkUpcBarcodeDuplicate(String upcBarcode, Long productId) {
+        if (ObjectUtil.isEmpty(upcBarcode)) {
+            return false;
+        }
+
+        // 查询 product_extend 表中是否存在相同的 bar_coding
+        LambdaQueryWrapper<ProductExtend> wrapper = Wrappers.lambdaQuery();
+        wrapper.eq(ProductExtend::getBarCoding, upcBarcode);
+        
+        // 如果是编辑操作,需要排除当前商品
+        if (productId != null) {
+            wrapper.ne(ProductExtend::getProductId, productId);
+        }
+        
+        ProductExtend productExtend = extendMapper.selectOne(wrapper);
+        
+        // 如果找到了记录,说明存在重复
+        return productExtend != null;
+    }
 }
 

+ 600 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductPoolAuditServiceImpl.java

@@ -0,0 +1,600 @@
+package org.dromara.product.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.csp.sentinel.transport.endpoint.Protocol;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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.easyes.core.conditions.update.LambdaEsUpdateWrapper;
+import org.dromara.external.api.domain.ExternalItemDto;
+import org.dromara.external.api.domain.ExternalProductDto;
+import org.dromara.external.api.service.RemoteExternalItemService;
+import org.dromara.external.api.service.RemoteExternalProductService;
+import org.dromara.product.domain.*;
+import org.dromara.product.domain.bo.ProductBaseBo;
+import org.dromara.product.domain.vo.ProductBaseVo;
+import org.dromara.product.domain.vo.ProductPoolLinkAuditVo;
+import org.dromara.product.esmapper.ProductEsMapper;
+import org.dromara.product.mapper.*;
+import org.dromara.system.api.RemoteUserService;
+import org.springframework.stereotype.Service;
+import org.dromara.product.domain.bo.ProductPoolAuditBo;
+import org.dromara.product.domain.vo.ProductPoolAuditVo;
+import org.dromara.product.service.IProductPoolAuditService;
+import org.dromara.common.core.exception.ServiceException;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+/**
+ * 产品池审核Service业务层处理
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class ProductPoolAuditServiceImpl  extends ServiceImpl<ProductPoolAuditMapper, ProductPoolAudit> implements IProductPoolAuditService {
+
+    private final ProductPoolAuditMapper baseMapper;
+
+    //产品池和产品关联审核对象
+    private final ProductPoolLinkAuditMapper productPoolLinkAuditMapper;
+    //产品池
+    private final ProductPoolMapper productPoolMapper;
+    //产品池产品关联对象
+    private final ProductPoolLinkMapper productPoolLinkMapper;
+    //客户协议
+    private final ProtocolInfoMapper protocolInfoMapper;
+    //客户协议商品池
+    private final ProtocolProductsMapper protocolProductsMapper;
+    //商品
+    private final ProductEsMapper productEsMapper;
+    //商品
+    private final ProductBaseMapper productBaseMapper;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+    //项目对象
+    @DubboReference
+    private RemoteExternalItemService remoteExternalItemService;
+    //项目商品池
+    @DubboReference
+    private RemoteExternalProductService remoteExternalProductService;
+
+
+    /**
+     * 查询产品池审核
+     *
+     * @param id 主键
+     * @return 产品池审核
+     */
+    @Override
+    public ProductPoolAuditVo queryById(Long id){
+        ProductPoolAuditVo productPoolAuditVo = baseMapper.selectVoById(id);
+        if (productPoolAuditVo != null) {
+            Long createBy = productPoolAuditVo.getCreateBy();
+            if(createBy != null) {
+                String CreateByName = remoteUserService.selectNicknameById(createBy);
+                if(CreateByName != null) {
+                    productPoolAuditVo.setCreateByName(CreateByName);
+                }
+            }
+            Long auditUserId = productPoolAuditVo.getAuditUserId();
+            if(auditUserId != null) {
+                String AuditByName = remoteUserService.selectNicknameById(auditUserId);
+                if(AuditByName != null) {
+                    productPoolAuditVo.setAuditByName(AuditByName);
+                }
+            }
+            // 根据类型回显产品池名称
+            setPoolNameByType(productPoolAuditVo);
+        }
+        return productPoolAuditVo;
+    }
+
+    /**
+     * 分页查询产品池审核列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 产品池审核分页列表
+     */
+    @Override
+    public TableDataInfo<ProductPoolAuditVo> queryPageList(ProductPoolAuditBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<ProductPoolAudit> lqw = buildQueryWrapper(bo);
+        Page<ProductPoolAuditVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        result.getRecords().forEach(item -> {
+            Long createBy = item.getCreateBy();
+            if(createBy != null) {
+                String CreateByName = remoteUserService.selectNicknameById(createBy);
+                if(CreateByName != null) {
+                    item.setCreateByName(CreateByName);
+                }
+            }
+            Long auditUserId = item.getAuditUserId();
+            if(auditUserId != null) {
+                String AuditByName = remoteUserService.selectNicknameById(auditUserId);
+                if(AuditByName != null) {
+                    item.setAuditByName(AuditByName);
+                }
+            }
+            // 根据类型回显产品池名称
+            setPoolNameByType(item);
+        });
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的产品池审核列表
+     *
+     * @param bo 查询条件
+     * @return 产品池审核列表
+     */
+    @Override
+    public List<ProductPoolAuditVo> queryList(ProductPoolAuditBo bo) {
+        LambdaQueryWrapper<ProductPoolAudit> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<ProductPoolAudit> buildQueryWrapper(ProductPoolAuditBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<ProductPoolAudit> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(ProductPoolAudit::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getName()), ProductPoolAudit::getName, bo.getName());
+        lqw.eq(bo.getPoolId() != null, ProductPoolAudit::getPoolId, bo.getPoolId());
+        lqw.eq(bo.getItemId() != null, ProductPoolAudit::getItemId, bo.getItemId());
+        lqw.eq(StringUtils.isNotBlank(bo.getType()), ProductPoolAudit::getType, bo.getType());
+        lqw.eq(bo.getProtocolId() != null, ProductPoolAudit::getProtocolId, bo.getProtocolId());
+        lqw.eq(bo.getCustomerId() != null, ProductPoolAudit::getCustomerId, bo.getCustomerId());
+        lqw.eq(StringUtils.isNotBlank(bo.getProductReviewStatus()), ProductPoolAudit::getProductReviewStatus, bo.getProductReviewStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getReviewReason()), ProductPoolAudit::getReviewReason, bo.getReviewReason());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), ProductPoolAudit::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增产品池审核
+     *
+     * @param bo 产品池审核
+     * @return 是否新增成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean insertByBo(ProductPoolAuditBo bo) {
+        ProductPoolAudit add = MapstructUtils.convert(bo, ProductPoolAudit.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+            // 保存产品池和产品关联审核数据
+            if (bo.getProducts() != null && !bo.getProducts().isEmpty()) {
+                savePoolLinkAudits(bo);
+            }
+        }
+        return flag;
+    }
+
+    /**
+     * 修改产品池审核
+     *
+     * @param bo 产品池审核
+     * @return 是否修改成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateByBo(ProductPoolAuditBo bo) {
+        ProductPoolAudit update = MapstructUtils.convert(bo, ProductPoolAudit.class);
+        validEntityBeforeSave(update);
+        boolean flag = baseMapper.updateById(update) > 0;
+        if (flag) {
+            // 先删除旧的产品关联审核数据
+            productPoolLinkAuditMapper.delete(Wrappers.<ProductPoolLinkAudit>lambdaQuery()
+                .eq(ProductPoolLinkAudit::getPoolAuditId, bo.getId()));
+            
+            // 再保存新的产品关联审核数据
+            if (bo.getProducts() != null && !bo.getProducts().isEmpty()) {
+                savePoolLinkAudits(bo);
+            }
+        }
+        return flag;
+    }
+
+    /**
+     * 根据类型设置产品池名称
+     * 0 自营产品池,1 精选产品池,4 营销产品池 -> 查询 product_pool 表
+     * 2 协议产品池 -> 查询 protocol_info 表
+     * 3 项目产品池 -> 调用远程服务查询外部项目
+     */
+    private void setPoolNameByType(ProductPoolAuditVo vo) {
+        String type = vo.getType();
+        if (type == null) {
+            return;
+        }
+
+        if ("0".equals(type) ) {
+            //自营产品池
+            //获取所有的审核池里的商品id
+            List<ProductPoolLinkAudit> productPoolLinkAudits = productPoolLinkAuditMapper.selectList(Wrappers.<ProductPoolLinkAudit>lambdaQuery()
+                .eq(ProductPoolLinkAudit::getPoolAuditId, vo.getId())
+            );
+            List<Long> productIds = productPoolLinkAudits.stream().map(ProductPoolLinkAudit::getProductId).collect(Collectors.toList());
+            //将商品改为自营
+            productBaseMapper.update(Wrappers.<ProductBase>lambdaUpdate()
+                .set(ProductBase::getIsSelf, 1)
+                .in(ProductBase::getId, productIds)
+            );
+            productEsMapper.update(new LambdaEsUpdateWrapper<ProductBaseVo>()
+                .set(ProductBaseVo::getIsSelf, 1)
+                .in(ProductBaseVo::getId, productIds)
+            );
+        } if ("1".equals(type)) {
+            //精选产品池
+            //获取所有的审核池里的商品id
+            List<ProductPoolLinkAudit> productPoolLinkAudits = productPoolLinkAuditMapper.selectList(Wrappers.<ProductPoolLinkAudit>lambdaQuery()
+                .eq(ProductPoolLinkAudit::getPoolAuditId, vo.getId())
+            );
+            List<Long> productIds = productPoolLinkAudits.stream().map(ProductPoolLinkAudit::getProductId).collect(Collectors.toList());
+            //将商品改为自营
+            productBaseMapper.update(Wrappers.<ProductBase>lambdaUpdate()
+                .set(ProductBase::getProductCategory, 2)
+                .in(ProductBase::getId, productIds)
+            );
+            productEsMapper.update(new LambdaEsUpdateWrapper<ProductBaseVo>()
+                .set(ProductBaseVo::getProductCategory, 2)
+                .in(ProductBaseVo::getId, productIds)
+            );
+        } else if ("2".equals(type)) {
+            // 协议产品池
+            if (vo.getProtocolId() != null) {
+                ProtocolInfo protocolInfo = protocolInfoMapper.selectById(vo.getProtocolId());
+                if (protocolInfo != null) {
+                    vo.setPoolName(protocolInfo.getProtocolNo());
+                }
+            }
+        } else if ("3".equals(type)) {
+            // 项目产品池
+            if (vo.getItemId() != null && remoteExternalItemService != null) {
+                try {
+                    ExternalItemDto itemInfo = remoteExternalItemService.getItemInfo(vo.getItemId());
+                    if (itemInfo != null) {
+                        vo.setPoolName(itemInfo.getItemName());
+                    }
+                } catch (Exception e) {
+                    log.warn("获取项目信息失败:itemId={}", vo.getItemId(), e);
+                }
+            }
+        }else if ("4".equals(type)){
+            //营销产品池
+            if (vo.getPoolId() != null) {
+                ProductPool productPool = productPoolMapper.selectById(vo.getPoolId());
+                if (productPool != null) {
+                    vo.setPoolName(productPool.getName());
+                }
+            }else {
+                log.warn("获取营销产品池信息失败:itemId={}", vo.getPoolId());
+            }
+        }
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(ProductPoolAudit entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 保存产品池和产品关联审核数据
+     *
+     * @param bo 产品审核业务对象
+     */
+    private void savePoolLinkAudits(ProductPoolAuditBo bo) {
+        List<ProductPoolAuditBo.ProductItem> products = bo.getProducts();
+        for (ProductPoolAuditBo.ProductItem product : products) {
+            ProductPoolLinkAudit linkAudit = new ProductPoolLinkAudit();
+            linkAudit.setPoolAuditId(bo.getId());
+            linkAudit.setPoolId(bo.getPoolId());
+            linkAudit.setItemId(bo.getItemId());
+            linkAudit.setProductId(product.getProductId());
+            linkAudit.setProductPrice(product.getProductPrice());
+            linkAudit.setNegotiatedPrice(product.getNegotiatedPrice());
+            linkAudit.setCategoryId(product.getCategoryId());
+            linkAudit.setProductReviewStatus("0"); // 0=待提交
+            linkAudit.setReviewer(String.valueOf(LoginHelper.getUserId()));
+            
+            productPoolLinkAuditMapper.insert(linkAudit);
+        }
+    }
+
+    /**
+     * 校验并批量删除产品池审核信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 获取产品池产品审核列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery
+     * @return 产品池审核列表
+     */
+    @Override
+    public TableDataInfo<ProductBaseVo> getPoolAuditProductPage(ProductBaseBo bo, PageQuery pageQuery) {
+        List<ProductPoolLinkAuditVo> productPoolLinkAuditVos = productPoolLinkAuditMapper.selectVoList(Wrappers.lambdaQuery(ProductPoolLinkAudit.class)
+            .eq(ProductPoolLinkAudit::getPoolAuditId, bo.getPoolAuditId())
+            .eq(ObjectUtil.isNotEmpty(bo.getAuditStatus()),ProductPoolLinkAudit::getProductReviewStatus, bo.getAuditStatus())
+        );
+        if (CollUtil.isEmpty(productPoolLinkAuditVos)) {
+            return TableDataInfo.build();
+        }
+        LambdaEsQueryWrapper<ProductBaseVo> esQueryWrapper = new LambdaEsQueryWrapper<>();
+        esQueryWrapper.in(ProductBaseVo::getId, productPoolLinkAuditVos.stream().map(ProductPoolLinkAuditVo::getProductId).toList())
+            .eq(ObjectUtil.isNotEmpty(bo.getProductNo()), ProductBaseVo::getProductNo, bo.getProductNo())
+            .like(ObjectUtil.isNotEmpty(bo.getItemName()), ProductBaseVo::getItemName, bo.getItemName())
+            .like(ObjectUtil.isNotEmpty(bo.getBrandName()), ProductBaseVo::getBrandName, bo.getBrandName())
+            .eq(bo.getBrandId() != null, ProductBaseVo::getBrandId, bo.getBrandId())
+            .eq(bo.getTopCategoryId() != null, ProductBaseVo::getTopCategoryId, bo.getTopCategoryId())
+            .eq(bo.getMediumCategoryId() != null, ProductBaseVo::getMediumCategoryId, bo.getMediumCategoryId())
+            .eq(bo.getBottomCategoryId() != null, ProductBaseVo::getBottomCategoryId, bo.getBottomCategoryId())
+            .eq(ObjectUtil.isNotEmpty(bo.getUnitId()), ProductBaseVo::getUnitId, bo.getUnitId())
+            .eq(ObjectUtil.isNotEmpty(bo.getProductStatus()), ProductBaseVo::getProductStatus, bo.getProductStatus())
+            .eq(ObjectUtil.isNotEmpty(bo.getIsSelf()), ProductBaseVo::getIsSelf, bo.getIsSelf())
+            .eq(ObjectUtil.isNotEmpty(bo.getProductReviewStatus()), ProductBaseVo::getProductReviewStatus, bo.getProductReviewStatus())
+            .eq(ObjectUtil.isNotEmpty(bo.getDataSource()), ProductBaseVo::getDataSource, bo.getDataSource())
+            .eq(ObjectUtil.isNotEmpty(bo.getProductCategory()), ProductBaseVo::getProductCategory, bo.getProductCategory())
+            .orderByDesc(ProductBaseVo::getCreateTime)
+        ;
+        EsPageInfo<ProductBaseVo> esPageInfo = productEsMapper.pageQuery(esQueryWrapper, pageQuery.getPageNum(), pageQuery.getPageSize());
+        TableDataInfo<ProductBaseVo> tableDataInfo = TableDataInfo.build(esPageInfo.getList());
+        tableDataInfo.setTotal(esPageInfo.getTotal());
+        tableDataInfo.getRows().forEach(item -> {
+            item.setExternalPrice(productPoolLinkAuditVos.stream().filter(o -> o.getProductId().equals(item.getId())).findFirst().get().getProductPrice());
+            item.setAgreementPrice(productPoolLinkAuditVos.stream().filter(o -> o.getProductId().equals(item.getId())).findFirst().get().getNegotiatedPrice());
+            item.setPoolAuditProductId(productPoolLinkAuditVos.stream().filter(o -> o.getProductId().equals(item.getId())).findFirst().get().getId());
+            item.setAuditStatus(Integer.valueOf(productPoolLinkAuditVos.stream().filter(o -> o.getProductId().equals(item.getId())).findFirst().get().getProductReviewStatus()));
+        });
+
+        return tableDataInfo;
+    }
+
+    /**
+     * 批量审核产品池商品
+     *
+     * @param poolAuditId 审核池 ID
+     * @param productIds  产品 ID 列表
+     * @param auditStatus 审核状态 2=通过,3=驳回
+     * @param reason      审核原因
+     * @return 是否审核成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean batchAuditProducts(Long poolAuditId, List<Long> productIds, String auditStatus, String reason) {
+        // 查询审核池信息
+        ProductPoolAudit audit = baseMapper.selectById(poolAuditId);
+        if (audit == null) {
+            throw new ServiceException("审核池不存在");
+        }
+
+        // 查询审核池关联的产品
+        List<ProductPoolLinkAudit> linkAudits = productPoolLinkAuditMapper.selectList(
+            Wrappers.lambdaQuery(ProductPoolLinkAudit.class)
+                .eq(ProductPoolLinkAudit::getPoolAuditId, poolAuditId)
+                .eq(ProductPoolLinkAudit::getProductReviewStatus, "2")
+//                .in(ProductPoolLinkAudit::getProductId, productIds)
+        );
+
+        if (CollUtil.isEmpty(linkAudits)) {
+            throw new ServiceException("未找到待审核的产品");
+        }
+
+        // 获取当前用户 ID(审核人)- 从安全上下文获取
+        Long currentUserId = null;
+        try {
+            currentUserId = LoginHelper.getUserId();
+        } catch (Exception e) {
+            log.warn("无法获取当前用户 ID,使用默认值", e);
+        }
+
+        if ("2".equals(auditStatus)) {
+            // 审核通过:根据类型添加到不同的表
+            handleAuditPass(audit, linkAudits, currentUserId);
+        } else if ("3".equals(auditStatus)) {
+            // 审核驳回:只需要填写原因
+//            handleAuditReject(poolAuditId, productIds, reason, currentUserId);
+        } else {
+            throw new ServiceException("无效的审核状态");
+        }
+
+        // 更新审核池主表状态
+        updateAuditStatus(audit, auditStatus, reason, currentUserId);
+
+        return true;
+    }
+
+    /**
+     * 处理审核通过
+     */
+    private void handleAuditPass(ProductPoolAudit audit, List<ProductPoolLinkAudit> linkAudits, Long currentUserId) {
+        String type = audit.getType();
+
+        if ("0".equals(type) || "1".equals(type) || "4".equals(type)) {
+            // 自营产品池、精选产品池、营销产品池 -> 添加到 product_pool_link
+            addToProductPoolLink(audit, linkAudits, currentUserId);
+        }else if ("".equals(type)) {
+            // 协议产品池 -> 添加到 protocol_products
+            addToProtocolProducts(audit, linkAudits, currentUserId);
+        } else if ("3".equals(type)) {
+            // 项目产品池 -> 调用远程服务添加到外部项目商品池
+            addToExternalProductPool(audit, linkAudits, currentUserId);
+        } else if ("2".equals(type)) {
+            // 协议产品池 -> 添加到 protocol_products
+            addToProtocolProducts(audit, linkAudits, currentUserId);
+        } else if ("3".equals(type)) {
+            // 项目产品池 -> 调用远程服务添加到外部项目商品池
+            addToExternalProductPool(audit, linkAudits, currentUserId);
+        }
+    }
+
+    /**
+     * 添加到自营产品池
+     */
+    private void addToProductPoolLink(ProductPoolAudit audit, List<ProductPoolLinkAudit> linkAudits, Long currentUserId) {
+        List<ProductPoolLink> poolLinks = linkAudits.stream().map(link -> {
+            ProductPoolLink poolLink = new ProductPoolLink();
+            poolLink.setPoolId(audit.getPoolId());
+            poolLink.setItemId(audit.getItemId());
+            poolLink.setCategoryId(link.getCategoryId());
+            poolLink.setProductId(link.getProductId());
+            poolLink.setProductPrice(link.getProductPrice());
+            poolLink.setNegotiatedPrice(link.getNegotiatedPrice());
+            poolLink.setStock(0L); // 初始库存为 0
+            poolLink.setProductReviewStatus("2"); // 审核通过
+            poolLink.setIsShow("1"); // 默认显示
+            poolLink.setIsPoolStatus("1"); // 在池中
+            poolLink.setReviewer(String.valueOf(currentUserId));
+            return poolLink;
+        }).collect(Collectors.toList());
+
+        // 批量插入
+        if (CollUtil.isNotEmpty(poolLinks)) {
+            productPoolLinkMapper.insertBatch(poolLinks);
+        }
+    }
+
+    /**
+     * 添加到协议产品池
+     */
+    private void addToProtocolProducts(ProductPoolAudit audit, List<ProductPoolLinkAudit> linkAudits, Long currentUserId) {
+        //获取协议
+        ProtocolInfo protocolInfo = protocolInfoMapper.selectById(audit.getProtocolId());
+
+        List<ProtocolProducts> products = linkAudits.stream().map(link -> {
+            ProtocolProducts product = new ProtocolProducts();
+            product.setProtocolId(audit.getProtocolId());
+            if (protocolInfo != null) {
+                product.setProtocolNo(protocolInfo.getProtocolNo());
+                product.setDataSource(protocolInfo.getDataSource());
+                product.setCustomerId(protocolInfo.getCustomerId());
+            }
+            //获取协议商品
+            ProtocolProducts protocolProducts = protocolProductsMapper.selectOne(Wrappers.lambdaQuery(ProtocolProducts.class)
+                .eq(ProtocolProducts::getProductId, link.getProductId())
+                .eq(ProtocolProducts::getProtocolId, audit.getProtocolId())
+                .last("LIMIT 1")
+            );
+            product.setProductNo(protocolProducts.getProductNo());
+
+            product.setId(protocolProducts.getProtocolId());
+            product.setAgreementPrice(link.getNegotiatedPrice());
+            product.setAuditStatus(2); // 审核通过
+            product.setStatus("0"); // 正常状态
+            product.setDelFlag("0"); // 未删除
+            return product;
+        }).collect(Collectors.toList());
+
+        // 批量插入 如果存在冲突的记录,则更新
+        if (CollUtil.isNotEmpty(products)) {
+            protocolProductsMapper.insertBatch(products);
+        }
+    }
+
+    /**
+     * 添加到外部项目产品池
+     */
+    private void addToExternalProductPool(ProductPoolAudit audit, List<ProductPoolLinkAudit> linkAudits, Long currentUserId) {
+        //获取项目
+//        ExternalItemDto itemInfo = remoteExternalItemService.getItemInfo(audit.getItemId());
+        List<ExternalProductDto> externalProductDtos = linkAudits.stream().map(link -> {
+            ExternalProductDto product = new ExternalProductDto();
+            product.setItemId(audit.getItemId());
+            product.setProductId(link.getProductId());
+            ProductBaseVo productBaseVo = productEsMapper.selectById(link.getProductId());
+            if (ObjectUtil.isNotEmpty(productBaseVo)) {
+                product.setProductNo(productBaseVo.getProductNo());
+                product.setItemName(productBaseVo.getItemName());
+            }
+            return product;
+        }).collect(Collectors.toList());
+
+        // 调用远程服务添加到外部项目商品池
+        if (remoteExternalProductService != null) {
+                try {
+                    remoteExternalProductService.syncExternalProduct( externalProductDtos);
+                    // 暂时直接记录日志,实际应该调用远程服务
+                    log.info("添加产品到项目池:, ItemId={}", audit.getItemId());
+                } catch (Exception e) {
+                    log.error("添加产品到外部项目池失败", e);
+                    throw new ServiceException("添加产品到项目池失败");
+                }
+
+        }
+    }
+
+    /**
+     * 处理审核驳回
+     */
+    private void handleAuditReject(Long poolAuditId, List<Long> productIds, String reason, Long currentUserId) {
+        // 更新审核关联表的状态和原因
+        for (Long productId : productIds) {
+            productPoolLinkAuditMapper.update(null,
+                Wrappers.lambdaUpdate(ProductPoolLinkAudit.class)
+                    .set(ProductPoolLinkAudit::getProductReviewStatus, "3")
+                    .set(ProductPoolLinkAudit::getReviewReason, reason)
+                    .set(ProductPoolLinkAudit::getReviewer, String.valueOf(currentUserId))
+                    .eq(ProductPoolLinkAudit::getPoolAuditId, poolAuditId)
+                    .eq(ProductPoolLinkAudit::getProductId, productId)
+            );
+        }
+    }
+
+    /**
+     * 更新审核池主表状态
+     */
+    private void updateAuditStatus(ProductPoolAudit audit, String auditStatus, String reason, Long currentUserId) {
+        audit.setProductReviewStatus(auditStatus);
+        audit.setReviewReason(reason);
+        audit.setAuditUserId(currentUserId);
+        audit.setAuditTime(DateUtil.date());
+        baseMapper.updateById(audit);
+    }
+
+    /**
+     * 清空审核池
+     *
+     * @param id 审核池 ID
+     * @return 是否清空成功
+     */
+    @Override
+    public int clearPool(Long id) {
+        return productPoolLinkAuditMapper.delete(Wrappers.lambdaQuery(ProductPoolLinkAudit.class).eq(ProductPoolLinkAudit::getPoolAuditId, id));
+    }
+}
+

+ 179 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductPoolLinkAuditServiceImpl.java

@@ -0,0 +1,179 @@
+package org.dromara.product.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.product.domain.ProductPoolLink;
+import org.dromara.product.domain.bo.BatchAddProductBo;
+import org.springframework.stereotype.Service;
+import org.dromara.product.domain.bo.ProductPoolLinkAuditBo;
+import org.dromara.product.domain.vo.ProductPoolLinkAuditVo;
+import org.dromara.product.domain.ProductPoolLinkAudit;
+import org.dromara.product.mapper.ProductPoolLinkAuditMapper;
+import org.dromara.product.service.IProductPoolLinkAuditService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 产品池和产品关联审核Service业务层处理
+ *
+ * @author LionLi
+ * @date 2026-03-27
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class ProductPoolLinkAuditServiceImpl  extends ServiceImpl<ProductPoolLinkAuditMapper, ProductPoolLinkAudit> implements IProductPoolLinkAuditService {
+
+    private final ProductPoolLinkAuditMapper baseMapper;
+
+    /**
+     * 查询产品池和产品关联审核
+     *
+     * @param id 主键
+     * @return 产品池和产品关联审核
+     */
+    @Override
+    public ProductPoolLinkAuditVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询产品池和产品关联审核列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 产品池和产品关联审核分页列表
+     */
+    @Override
+    public TableDataInfo<ProductPoolLinkAuditVo> queryPageList(ProductPoolLinkAuditBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<ProductPoolLinkAudit> lqw = buildQueryWrapper(bo);
+        Page<ProductPoolLinkAuditVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的产品池和产品关联审核列表
+     *
+     * @param bo 查询条件
+     * @return 产品池和产品关联审核列表
+     */
+    @Override
+    public List<ProductPoolLinkAuditVo> queryList(ProductPoolLinkAuditBo bo) {
+        LambdaQueryWrapper<ProductPoolLinkAudit> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<ProductPoolLinkAudit> buildQueryWrapper(ProductPoolLinkAuditBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<ProductPoolLinkAudit> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(ProductPoolLinkAudit::getId);
+        lqw.eq(bo.getPoolId() != null, ProductPoolLinkAudit::getPoolId, bo.getPoolId());
+        lqw.eq(bo.getPoolAuditId() != null, ProductPoolLinkAudit::getPoolAuditId, bo.getPoolAuditId());
+        lqw.eq(bo.getProductId() != null, ProductPoolLinkAudit::getProductId, bo.getProductId());
+        lqw.eq(bo.getProductPrice() != null, ProductPoolLinkAudit::getProductPrice, bo.getProductPrice());
+        lqw.eq(bo.getNegotiatedPrice() != null, ProductPoolLinkAudit::getNegotiatedPrice, bo.getNegotiatedPrice());
+        lqw.eq(bo.getCategoryId() != null, ProductPoolLinkAudit::getCategoryId, bo.getCategoryId());
+        lqw.eq(StringUtils.isNotBlank(bo.getProductReviewStatus()), ProductPoolLinkAudit::getProductReviewStatus, bo.getProductReviewStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getReviewReason()), ProductPoolLinkAudit::getReviewReason, bo.getReviewReason());
+        lqw.eq(StringUtils.isNotBlank(bo.getReviewer()), ProductPoolLinkAudit::getReviewer, bo.getReviewer());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), ProductPoolLinkAudit::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增产品池和产品关联审核
+     *
+     * @param bo 产品池和产品关联审核
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(ProductPoolLinkAuditBo bo) {
+        ProductPoolLinkAudit add = MapstructUtils.convert(bo, ProductPoolLinkAudit.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改产品池和产品关联审核
+     *
+     * @param bo 产品池和产品关联审核
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(ProductPoolLinkAuditBo bo) {
+        ProductPoolLinkAudit update = MapstructUtils.convert(bo, ProductPoolLinkAudit.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(ProductPoolLinkAudit entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除产品池和产品关联审核信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 批量添加产品
+     *
+     * @param bo 批量添加产品参数
+     * @return 添加结果
+     */
+    @Override
+    public Boolean batchAddProducts(BatchAddProductBo bo) {
+        if (ObjectUtil.isNull(bo.getPoolId()) || ObjectUtil.isEmpty(bo.getProducts())) {
+            return false;
+        }
+
+        // 批量插入商品池关联记录
+        for (BatchAddProductBo.ProductItem product : bo.getProducts()) {
+            ProductPoolLinkAudit poolLink = new ProductPoolLinkAudit();
+            poolLink.setPoolAuditId(bo.getPoolId());
+            poolLink.setProductId(product.getProductId());
+            poolLink.setNegotiatedPrice(product.getAgreementPrice());
+            poolLink.setProductReviewStatus("1"); // 0-待提交
+
+            // 检查是否已存在
+            LambdaQueryWrapper<ProductPoolLinkAudit> wrapper = Wrappers.lambdaQuery();
+            wrapper.eq(ProductPoolLinkAudit::getPoolAuditId, bo.getPoolId())
+                .eq(ProductPoolLinkAudit::getProductId, product.getProductId());
+
+            Long count = baseMapper.selectCount(wrapper);
+            if (count == 0) {
+                baseMapper.insert(poolLink);
+            }
+
+        }
+        return true;
+    }
+}

+ 7 - 0
ruoyi-modules/ruoyi-product/src/main/resources/mapper/product/ProductPoolAuditMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.product.mapper.ProductPoolAuditMapper">
+
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-product/src/main/resources/mapper/product/ProductPoolLinkAuditMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.product.mapper.ProductPoolLinkAuditMapper">
+
+</mapper>

+ 106 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysTaxCodeController.java

@@ -0,0 +1,106 @@
+package org.dromara.system.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.system.domain.vo.SysTaxCodeVo;
+import org.dromara.system.domain.bo.SysTaxCodeBo;
+import org.dromara.system.service.ISysTaxCodeService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 税收编码
+ * 前端访问路由地址为:/system/taxCode
+ *
+ * @author LionLi
+ * @date 2026-03-26
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/taxCode")
+public class SysTaxCodeController extends BaseController {
+
+    private final ISysTaxCodeService sysTaxCodeService;
+
+    /**
+     * 查询税收编码列表
+     */
+    //@SaCheckPermission("system:taxCode:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysTaxCodeVo> list(SysTaxCodeBo bo, PageQuery pageQuery) {
+        return sysTaxCodeService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出税收编码列表
+     */
+    //@SaCheckPermission("system:taxCode:export")
+    @Log(title = "税收编码", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(SysTaxCodeBo bo, HttpServletResponse response) {
+        List<SysTaxCodeVo> list = sysTaxCodeService.queryList(bo);
+        ExcelUtil.exportExcel(list, "税收编码", SysTaxCodeVo.class, response);
+    }
+
+    /**
+     * 获取税收编码详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("system:taxCode:query")
+    @GetMapping("/{id}")
+    public R<SysTaxCodeVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(sysTaxCodeService.queryById(id));
+    }
+
+    /**
+     * 新增税收编码
+     */
+    //@SaCheckPermission("system:taxCode:add")
+    @Log(title = "税收编码", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysTaxCodeBo bo) {
+        return toAjax(sysTaxCodeService.insertByBo(bo));
+    }
+
+    /**
+     * 修改税收编码
+     */
+    //@SaCheckPermission("system:taxCode:edit")
+    @Log(title = "税收编码", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTaxCodeBo bo) {
+        return toAjax(sysTaxCodeService.updateByBo(bo));
+    }
+
+    /**
+     * 删除税收编码
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("system:taxCode:remove")
+    @Log(title = "税收编码", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(sysTaxCodeService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 76 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTaxCode.java

@@ -0,0 +1,76 @@
+package org.dromara.system.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 税收编码对象 sys_tax_code
+ *
+ * @author LionLi
+ * @date 2026-03-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_tax_code")
+public class SysTaxCode extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 父级id
+     */
+    private Long parentId;
+
+    /**
+     * 货物和劳务名称
+     */
+    private String name;
+
+    /**
+     * 商品和服务分类简称
+     */
+    private String abbreviation;
+
+    /**
+     * 税收编码
+     */
+    private String taxationNo;
+
+    /**
+     * 合并编码
+     */
+    private String mergeNo;
+
+    /**
+     * 上级编码
+     */
+    private String parentNo;
+
+    /**
+     * 祖级列表
+     */
+    private String ancestors;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 83 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTaxCodeBo.java

@@ -0,0 +1,83 @@
+package org.dromara.system.domain.bo;
+
+import org.dromara.system.domain.SysTaxCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 税收编码业务对象 sys_tax_code
+ *
+ * @author LionLi
+ * @date 2026-03-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysTaxCode.class, reverseConvertGenerate = false)
+public class SysTaxCodeBo extends BaseEntity {
+
+    /**
+     * 
+     */
+    private Long id;
+
+    /**
+     * 父级id
+     */
+    //@NotNull(message = "父级id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long parentId;
+
+    /**
+     * 货物和劳务名称
+     */
+    //@NotBlank(message = "货物和劳务名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    /**
+     * 商品和服务分类简称
+     */
+    //@NotBlank(message = "商品和服务分类简称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String abbreviation;
+
+    /**
+     * 税收编码
+     */
+    //@NotBlank(message = "税收编码不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String taxationNo;
+
+    /**
+     * 合并编码
+     */
+    //@NotBlank(message = "合并编码不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String mergeNo;
+
+    /**
+     * 上级编码
+     */
+    //@NotBlank(message = "上级编码不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String parentNo;
+
+    /**
+     * 祖级列表
+     */
+    //@NotBlank(message = "祖级列表不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String ancestors;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    //@NotBlank(message = "状态(0正常 1停用)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String status;
+
+    /**
+     * 备注
+     */
+    //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+
+}

+ 93 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTaxCodeVo.java

@@ -0,0 +1,93 @@
+package org.dromara.system.domain.vo;
+
+import org.dromara.system.domain.SysTaxCode;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * 税收编码视图对象 sys_tax_code
+ *
+ * @author LionLi
+ * @date 2026-03-26
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysTaxCode.class)
+public class SysTaxCodeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 
+     */
+    @ExcelProperty(value = "")
+    private Long id;
+
+    /**
+     * 父级id
+     */
+    @ExcelProperty(value = "父级id")
+    private Long parentId;
+
+    /**
+     * 货物和劳务名称
+     */
+    @ExcelProperty(value = "货物和劳务名称")
+    private String name;
+
+    /**
+     * 商品和服务分类简称
+     */
+    @ExcelProperty(value = "商品和服务分类简称")
+    private String abbreviation;
+
+    /**
+     * 税收编码
+     */
+    @ExcelProperty(value = "税收编码")
+    private String taxationNo;
+
+    /**
+     * 合并编码
+     */
+    @ExcelProperty(value = "合并编码")
+    private String mergeNo;
+
+    /**
+     * 上级编码
+     */
+    @ExcelProperty(value = "上级编码")
+    private String parentNo;
+
+    /**
+     * 祖级列表
+     */
+    @ExcelProperty(value = "祖级列表")
+    private String ancestors;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTaxCodeMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.system.mapper;
+
+import org.dromara.system.domain.SysTaxCode;
+import org.dromara.system.domain.vo.SysTaxCodeVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 税收编码Mapper接口
+ *
+ * @author LionLi
+ * @date 2026-03-26
+ */
+public interface SysTaxCodeMapper extends BaseMapperPlus<SysTaxCode, SysTaxCodeVo> {
+
+}

+ 70 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTaxCodeService.java

@@ -0,0 +1,70 @@
+package org.dromara.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.system.domain.SysTaxCode;
+import org.dromara.system.domain.vo.SysTaxCodeVo;
+import org.dromara.system.domain.bo.SysTaxCodeBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 税收编码Service接口
+ *
+ * @author LionLi
+ * @date 2026-03-26
+ */
+public interface ISysTaxCodeService extends IService<SysTaxCode>{
+
+    /**
+     * 查询税收编码
+     *
+     * @param id 主键
+     * @return 税收编码
+     */
+    SysTaxCodeVo queryById(Long id);
+
+    /**
+     * 分页查询税收编码列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 税收编码分页列表
+     */
+    TableDataInfo<SysTaxCodeVo> queryPageList(SysTaxCodeBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的税收编码列表
+     *
+     * @param bo 查询条件
+     * @return 税收编码列表
+     */
+    List<SysTaxCodeVo> queryList(SysTaxCodeBo bo);
+
+    /**
+     * 新增税收编码
+     *
+     * @param bo 税收编码
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(SysTaxCodeBo bo);
+
+    /**
+     * 修改税收编码
+     *
+     * @param bo 税收编码
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(SysTaxCodeBo bo);
+
+    /**
+     * 校验并批量删除税收编码信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 141 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaxCodeServiceImpl.java

@@ -0,0 +1,141 @@
+package org.dromara.system.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.system.domain.bo.SysTaxCodeBo;
+import org.dromara.system.domain.vo.SysTaxCodeVo;
+import org.dromara.system.domain.SysTaxCode;
+import org.dromara.system.mapper.SysTaxCodeMapper;
+import org.dromara.system.service.ISysTaxCodeService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 税收编码Service业务层处理
+ *
+ * @author LionLi
+ * @date 2026-03-26
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class SysTaxCodeServiceImpl  extends ServiceImpl<SysTaxCodeMapper, SysTaxCode> implements ISysTaxCodeService {
+
+    private final SysTaxCodeMapper baseMapper;
+
+    /**
+     * 查询税收编码
+     *
+     * @param id 主键
+     * @return 税收编码
+     */
+    @Override
+    public SysTaxCodeVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询税收编码列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 税收编码分页列表
+     */
+    @Override
+    public TableDataInfo<SysTaxCodeVo> queryPageList(SysTaxCodeBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<SysTaxCode> lqw = buildQueryWrapper(bo);
+        Page<SysTaxCodeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的税收编码列表
+     *
+     * @param bo 查询条件
+     * @return 税收编码列表
+     */
+    @Override
+    public List<SysTaxCodeVo> queryList(SysTaxCodeBo bo) {
+        LambdaQueryWrapper<SysTaxCode> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<SysTaxCode> buildQueryWrapper(SysTaxCodeBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<SysTaxCode> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(SysTaxCode::getId);
+        lqw.eq(bo.getParentId() != null, SysTaxCode::getParentId, bo.getParentId());
+        lqw.like(StringUtils.isNotBlank(bo.getName()), SysTaxCode::getName, bo.getName());
+        lqw.eq(StringUtils.isNotBlank(bo.getAbbreviation()), SysTaxCode::getAbbreviation, bo.getAbbreviation());
+        lqw.eq(StringUtils.isNotBlank(bo.getTaxationNo()), SysTaxCode::getTaxationNo, bo.getTaxationNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getMergeNo()), SysTaxCode::getMergeNo, bo.getMergeNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getParentNo()), SysTaxCode::getParentNo, bo.getParentNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getAncestors()), SysTaxCode::getAncestors, bo.getAncestors());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTaxCode::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), SysTaxCode::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增税收编码
+     *
+     * @param bo 税收编码
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(SysTaxCodeBo bo) {
+        SysTaxCode add = MapstructUtils.convert(bo, SysTaxCode.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改税收编码
+     *
+     * @param bo 税收编码
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(SysTaxCodeBo bo) {
+        SysTaxCode update = MapstructUtils.convert(bo, SysTaxCode.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(SysTaxCode entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除税收编码信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 7 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTaxCodeMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.system.mapper.SysTaxCodeMapper">
+
+</mapper>