From 23e3602b4f3ebde8665c3ab3a56ef81b8d671e0d Mon Sep 17 00:00:00 2001
From: zepengdev <lzpsmith@outlook.com>
Date: Wed, 17 Sep 2025 19:17:21 +0800
Subject: [PATCH] feat: 增加商品库存设置功能

---
 oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java  |   19 ---
 oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductInventoryUpdateRequest.java |   13 ++
 oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java          |   16 ++
 oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductStockMapper.java                |   23 ++++
 oying-system/src/main/java/com/oying/modules/pc/product/service/ProductService.java                   |    7 +
 oying-system/src/main/java/com/oying/modules/pc/product/service/ProductInventoryService.java          |   11 ++
 oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductInventoryServiceImpl.java |   59 +++++++++++
 oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductInventoryController.java          |   54 ++++++++++
 oying-system/src/main/resources/mapper/pc/product/ProductStockMapper.xml                              |   49 +++++++++
 9 files changed, 231 insertions(+), 20 deletions(-)

diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductInventoryUpdateRequest.java b/oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductInventoryUpdateRequest.java
new file mode 100644
index 0000000..2bc2000
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductInventoryUpdateRequest.java
@@ -0,0 +1,13 @@
+package com.oying.modules.pc.product.domain.dto;
+
+import lombok.Data;
+
+@Data
+public class ProductInventoryUpdateRequest {
+
+    private Long productId;
+    private Integer quantity;
+    private Integer amount;
+    private Long version;
+
+}
diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductStockMapper.java b/oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductStockMapper.java
new file mode 100644
index 0000000..227cea8
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductStockMapper.java
@@ -0,0 +1,23 @@
+package com.oying.modules.pc.product.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.oying.modules.pc.product.domain.Product;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface ProductStockMapper extends BaseMapper<Product> {
+
+    List<Product> selectLowStockProducts(@Param("threshold") Integer threshold);
+
+    int updateStock(@Param("productId") Long productId, @Param("quantity") Integer quantity, @Param("version") Long version);
+
+    int increaseStock(@Param("productId") Long productId, @Param("amount") Integer amount, @Param("version") Long version);
+
+    int decreaseStock(@Param("productId") Long productId, @Param("amount") Integer amount, @Param("version") Long version);
+
+    int batchUpdateStock(List<Product> products);
+
+}
diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductInventoryController.java b/oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductInventoryController.java
new file mode 100644
index 0000000..7d5d4ca
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductInventoryController.java
@@ -0,0 +1,54 @@
+package com.oying.modules.pc.product.rest;
+
+import com.oying.modules.pc.product.domain.Product;
+import com.oying.modules.pc.product.domain.dto.ProductInventoryUpdateRequest;
+import com.oying.modules.pc.product.service.ProductInventoryService;
+import com.oying.utils.R;
+import io.swagger.annotations.Api;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Optional;
+
+/**
+ * @author lzp
+ * @date 2025-09-10
+ **/
+@RestController
+@RequiredArgsConstructor
+@Api(tags = "商品库存")
+@RequestMapping("/api/pc/product/{productId}/inventory")
+public class ProductInventoryController {
+
+    private final ProductInventoryService productInventoryService;
+
+    @GetMapping
+    public ResponseEntity<?> getStockInfo(@PathVariable Long productId) {
+        return ResponseEntity.ok(R.success(productInventoryService.getProductById(productId)));
+    }
+
+    @PutMapping
+    public ResponseEntity<?> setStock(@PathVariable Long productId, @RequestBody ProductInventoryUpdateRequest request) {
+        Product product = productInventoryService.setStockQuantity(Optional.ofNullable(request.getProductId()).orElse(productId), request.getQuantity(), request.getVersion());
+        return ResponseEntity.ok(R.success(product));
+    }
+
+    @PostMapping("/increase")
+    public ResponseEntity<?> increaseStock(@PathVariable Long productId, @RequestBody ProductInventoryUpdateRequest request) {
+        Product product = productInventoryService.increaseStock(Optional.ofNullable(request.getProductId()).orElse(productId), request.getAmount());
+        return ResponseEntity.ok(R.success(product));
+    }
+
+    @PostMapping("/decrease")
+    public ResponseEntity<?> decreaseStock(@PathVariable Long productId, @RequestBody ProductInventoryUpdateRequest request) {
+        Product product = productInventoryService.decreaseStock(Optional.ofNullable(request.getProductId()).orElse(productId), request.getAmount());
+        return ResponseEntity.ok(R.success(product));
+    }
+
+    @GetMapping("/sufficient")
+    public ResponseEntity<?> checkStockSufficient(@PathVariable Long productId, @RequestParam Integer requiredAmount) {
+        boolean isSufficient = productInventoryService.isStockSufficient(productId, requiredAmount);
+        return ResponseEntity.ok(R.success(isSufficient));
+    }
+}
diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/service/ProductInventoryService.java b/oying-system/src/main/java/com/oying/modules/pc/product/service/ProductInventoryService.java
new file mode 100644
index 0000000..c425722
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/service/ProductInventoryService.java
@@ -0,0 +1,11 @@
+package com.oying.modules.pc.product.service;
+
+import com.oying.modules.pc.product.domain.Product;
+
+public interface ProductInventoryService {
+    Product getProductById(Long id);
+    Product setStockQuantity(Long product, Integer quantity, Long version);
+    Product increaseStock(Long product, Integer amount);
+    Product decreaseStock(Long product, Integer amount);
+    boolean isStockSufficient(Long productId, Integer requiredAmount);
+}
diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/service/ProductService.java b/oying-system/src/main/java/com/oying/modules/pc/product/service/ProductService.java
index a610f1d..026338f 100644
--- a/oying-system/src/main/java/com/oying/modules/pc/product/service/ProductService.java
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/service/ProductService.java
@@ -33,6 +33,13 @@
     List<Product> queryAll(ProductQueryCriteria criteria);
 
     /**
+     * 根据ID查询数据
+     * @param productId ID
+     * @return product
+     */
+    Product getProduct(Long productId);
+
+    /**
     * 创建
     * @param resources /
     */
diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductInventoryServiceImpl.java b/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductInventoryServiceImpl.java
new file mode 100644
index 0000000..896cb23
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductInventoryServiceImpl.java
@@ -0,0 +1,59 @@
+package com.oying.modules.pc.product.service.impl;
+
+import com.oying.modules.pc.product.domain.Product;
+import com.oying.modules.pc.product.mapper.ProductStockMapper;
+import com.oying.modules.pc.product.service.ProductInventoryService;
+import com.oying.modules.pc.product.service.ProductService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+public class ProductInventoryServiceImpl implements ProductInventoryService {
+
+    private final ProductStockMapper productStockMapper;
+    private final ProductService productService;
+
+    @Override
+    public Product getProductById(Long productId) {
+        return productService.getProduct(productId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Product setStockQuantity(Long productId, Integer quantity, Long version) {
+        Product existingProduct = this.getProductById(productId);
+        productStockMapper.updateStock(productId, quantity, existingProduct.getVersion());
+        return this.getProductById(productId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Product increaseStock(Long productId, Integer amount) {
+        Product existingProduct = this.getProductById(productId);
+        productStockMapper.increaseStock(productId, amount, existingProduct.getVersion());
+        return getProductById(productId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Product decreaseStock(Long productId, Integer amount) {
+        Product existingProduct = this.getProductById(productId);
+        if (existingProduct.getStockQuantity() < amount) {
+            throw new RuntimeException("库存不足,当前库存: " + existingProduct.getStockQuantity() + ", 请求数量: " + amount);
+        }
+        productStockMapper.decreaseStock(productId, amount, existingProduct.getVersion());
+        return getProductById(productId);
+    }
+
+    @Override
+    public boolean isStockSufficient(Long productId, Integer requiredAmount) {
+        if (requiredAmount == null || requiredAmount <= 0) {
+            throw new IllegalArgumentException("需求数量必须大于0");
+        }
+        Product existingProduct = this.getProductById(productId);
+        return existingProduct.getStockQuantity() >= requiredAmount;
+    }
+
+}
diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java b/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java
index 24ed038..e4eef20 100644
--- a/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java
@@ -1,11 +1,9 @@
 package com.oying.modules.pc.product.service.impl;
 
 import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.core.util.ObjUtil;
 import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.oying.exception.BadRequestException;
-import com.oying.exception.EntityNotFoundException;
 import com.oying.modules.pc.common.core.constrant.AuditStatusEnum;
 import com.oying.modules.pc.product.converter.ProductImageAssembler;
 import com.oying.modules.pc.product.converter.ProductLabelAssembler;
@@ -30,7 +28,6 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -77,7 +74,7 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void update(ProductMerchantUpdateRequest request) {
-        Product existingProduct = this.findOrThrow(request.getProductId());
+        Product existingProduct = productService.getProduct(request.getProductId());
         this.validateApprovedStatus(existingProduct.getShelfStatus());
         this.processImagesUpdate(request);
         this.processLabelsUpdate(request);
@@ -88,18 +85,16 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void updateImages(ProductMerchantUpdateRequest request) {
-        Product existingProduct = this.findOrThrow(request.getProductId());
+        Product existingProduct = productService.getProduct(request.getProductId());
         this.validateApprovedStatus(existingProduct.getShelfStatus());
-        this.findOrThrow(request.getProductId());
         this.processImagesUpdate(request);
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void updateLabels(ProductMerchantUpdateRequest request) {
-        Product existingProduct = this.findOrThrow(request.getProductId());
+        Product existingProduct = productService.getProduct(request.getProductId());
         this.validateApprovedStatus(existingProduct.getShelfStatus());
-        this.findOrThrow(request.getProductId());
         this.processImagesUpdate(request);
     }
 
@@ -160,14 +155,6 @@
         } catch (Exception e) {
             log.error("处理商品审核结果异常", e);
         }
-    }
-
-    private Product findOrThrow(Long productId) {
-        Product existingProduct = productService.getById(productId);
-        if (ObjUtil.isEmpty(existingProduct)) {
-            throw new EntityNotFoundException(Product.class, "id", Optional.ofNullable(productId).map(Object::toString).orElse("null"));
-        }
-        return existingProduct;
     }
 
     private void processImagesUpdate(ProductMerchantUpdateRequest request) {
diff --git a/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java b/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java
index 879e105..5e609ea 100644
--- a/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java
+++ b/oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java
@@ -1,8 +1,10 @@
 package com.oying.modules.pc.product.service.impl;
 
+import cn.hutool.core.util.ObjUtil;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.oying.exception.EntityNotFoundException;
 import com.oying.modules.pc.product.domain.Product;
 import com.oying.modules.pc.product.domain.dto.ProductQueryCriteria;
 import com.oying.modules.pc.product.mapper.ProductMapper;
@@ -16,10 +18,7 @@
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * @author lzp
@@ -43,6 +42,15 @@
     }
 
     @Override
+    public Product getProduct(Long productId) {
+        Product existingProduct = getById(productId);
+        if (ObjUtil.isEmpty(existingProduct)) {
+            throw new EntityNotFoundException(Product.class, "id", Optional.ofNullable(productId).map(Object::toString).orElse("null"));
+        }
+        return existingProduct;
+    }
+
+    @Override
     @Transactional(rollbackFor = Exception.class)
     public void create(Product resources) {
         productMapper.insert(resources);
diff --git a/oying-system/src/main/resources/mapper/pc/product/ProductStockMapper.xml b/oying-system/src/main/resources/mapper/pc/product/ProductStockMapper.xml
new file mode 100644
index 0000000..c712189
--- /dev/null
+++ b/oying-system/src/main/resources/mapper/pc/product/ProductStockMapper.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.oying.modules.pc.product.mapper.ProductStockMapper">
+
+    <!-- 更新库存 -->
+    <update id="updateStock">
+        UPDATE pc_product SET
+            stock_quantity = #{quantity},
+            version = version + 1
+        WHERE
+            product_id = #{productId} AND version = #{version}
+    </update>
+
+    <!-- 批量更新库存 -->
+    <update id="batchUpdateStock" parameterType="list">
+        <foreach collection="list" item="item" index="index" separator=";">
+            UPDATE products
+            SET stock_quantity = #{item.quantity}, version = version + 1
+            WHERE
+            product_id = #{item.productId} AND version = #{item.version}
+        </foreach>
+    </update>
+
+    <!-- 增加库存 -->
+    <update id="increaseStock">
+        UPDATE pc_product SET
+            stock_quantity = stock_quantity + #{amount},
+            version = version + 1
+        WHERE
+            product_id = #{productId} AND version = #{version}
+    </update>
+
+    <!-- 减少库存 -->
+    <update id="decreaseStock">
+        UPDATE pc_product SET
+            stock_quantity = stock_quantity - #{amount},
+            version = version + 1
+        WHERE
+            product_id = #{productId} AND version = #{version}
+    </update>
+
+    <!-- 查询最低库存商品 -->
+    <select id="selectLowStockProducts">
+        SELECT * FROM products
+        WHERE stock_quantity <![CDATA[ <= ]]> #{threshold}
+        ORDER BY stock_quantity ASC
+    </select>
+
+</mapper>
\ No newline at end of file

--
Gitblit v1.9.3