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>