zepengdev
2025-09-17 23e3602b4f3ebde8665c3ab3a56ef81b8d671e0d
feat: 增加商品库存设置功能
6 files added
3 files modified
251 ■■■■■ changed files
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductInventoryUpdateRequest.java 13 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductStockMapper.java 23 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductInventoryController.java 54 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductInventoryService.java 11 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductService.java 7 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductInventoryServiceImpl.java 59 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java 19 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java 16 ●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/product/ProductStockMapper.xml 49 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductInventoryUpdateRequest.java
New file
@@ -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;
}
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductStockMapper.java
New file
@@ -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);
}
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductInventoryController.java
New file
@@ -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));
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductInventoryService.java
New file
@@ -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);
}
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 /
    */
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductInventoryServiceImpl.java
New file
@@ -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;
    }
}
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) {
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);
oying-system/src/main/resources/mapper/pc/product/ProductStockMapper.xml
New file
@@ -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>