瀏覽代碼

商城限购和库存检查

yuhongqi 2 周之前
父節點
當前提交
8508d6d745

+ 1 - 1
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -484,7 +484,7 @@ public class MallStoreTask
     //每天执行一次
     public void syncExpress()
     {
-        List<Long> ids =fsStoreOrderMapper.selectSyncExpressIds();
+        List<Long> ids =fsStoreOrderMapper.selectSyncExpressIdsNoDate();
 
         for (Long id : ids) {
             FsStoreOrderExpressEditParam param =new FsStoreOrderExpressEditParam();

+ 1 - 0
fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java

@@ -28,4 +28,5 @@ public class StoreConfig implements Serializable {
     private Boolean isWeChatShipping;//是否开启微信发货
     private Boolean scanCodeDiscountEnabled;//是否开启扫码立减金
     private BigDecimal scanCodeDiscountAmount;//扫码立减金额
+    private Boolean checkStock;//是否检查库存,默认关闭
 }

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java

@@ -992,6 +992,9 @@ public interface FsStoreOrderScrmMapper
     @Select("select id from fs_store_order_scrm where status = 2  and DATE(pay_time)>='2023-11-1' and DATE(create_time)<='2024-2-7' and company_id is null ")
     List<Long> selectSyncExpressIds();
 
+    @Select("select id from fs_store_order_scrm where status = 2  and DATE(pay_time)>='2023-11-1' and company_id is null ")
+    List<Long> selectSyncExpressIdsNoDate();
+
 
     List<Long> selectAddTuiMoney();
 

+ 6 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductAttrValueScrmMapper.java

@@ -76,6 +76,12 @@ public interface FsStoreProductAttrValueScrmMapper
     List<FsStoreProductAttrValueScrm> selectFsStoreProductAttrValueByProductId(Long productId);
     @Select("select ifnull(stock,0) from fs_store_product_attr_value_scrm where  id=#{productAttrValueId}")
     int selectFsStoreProductStockById(Long productAttrValueId);
+    
+    /**
+     * 使用行锁查询规格库存
+     */
+    @Select("select ifnull(stock,0) from fs_store_product_attr_value_scrm where id=#{productAttrValueId} for update")
+    Integer selectProductAttrStockForUpdate(@Param("productAttrValueId") Long productAttrValueId);
 
     @Update("update fs_store_product_attr_value_scrm set stock=stock-#{num},sales=sales+#{num}" +
             " where id=#{productAttrValueId} and stock >= #{num} ")

+ 6 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java

@@ -255,6 +255,12 @@ public interface FsStoreProductScrmMapper
     @Update("update fs_store_product_scrm set stock=stock-#{num}, sales=sales+#{num}" +
             " where product_id=#{productId} and stock >= #{num}")
     int decProductAttrStock(@Param("productId")Long productId, @Param("num")Integer cartNum);
+    
+    /**
+     * 使用行锁查询商品库存
+     */
+    @Select("select stock from fs_store_product_scrm where product_id=#{productId} for update")
+    Integer selectProductStockForUpdate(@Param("productId") Long productId);
     @Update("update fs_store_product_scrm set stock=stock+#{num}, sales=sales-#{num}" +
             " where product_id=#{productId}")
     int incStockDecSales( @Param("num")Long num, @Param("productId")Long productId);

+ 40 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -99,6 +99,9 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
 
     @Autowired
     private ConfigUtil configUtil;
+    
+    @Autowired
+    private com.fs.system.service.ISysConfigService configService;
 
     @Autowired
     @Qualifier("hzOMSErpGoodsServiceImpl")
@@ -1046,8 +1049,45 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         return fsStoreProductMapper.selectFsStoreProductByIdQuery(productId,storeId,medicalMallConfig);
     }
 
+    /**
+     * 检查库存(使用行锁)
+     */
+    private void checkStockWithLock(Long productId, Long productAttrValueId, Integer cartNum) {
+        // 检查商品库存
+        Integer productStock = fsStoreProductMapper.selectProductStockForUpdate(productId);
+        if (productStock == null || productStock < cartNum) {
+            FsStoreProductScrm product = fsStoreProductMapper.selectFsStoreProductById(productId);
+            String productName = product != null ? product.getProductName() : "商品";
+            throw new CustomException(productName + "库存不足,当前库存:" + (productStock != null ? productStock : 0) + ",需要数量:" + cartNum);
+        }
+        
+        // 如果有规格,检查规格库存
+        if (productAttrValueId != null) {
+            Integer attrStock = fsStoreProductAttrValueMapper.selectProductAttrStockForUpdate(productAttrValueId);
+            if (attrStock == null || attrStock < cartNum) {
+                FsStoreProductAttrValueScrm attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(productAttrValueId);
+                String sku = attrValue != null ? attrValue.getSku() : "规格";
+                throw new CustomException("商品规格" + sku + "库存不足,当前库存:" + (attrStock != null ? attrStock : 0) + ",需要数量:" + cartNum);
+            }
+        }
+    }
+    
     @Override
     public void decProductStock(Long productId, Long productAttrValueId, Integer cartNum) {
+        // 检查配置是否开启库存检查
+        try {
+            String configJson = configService.selectConfigByKey("his.store");
+            if (StringUtils.isNotEmpty(configJson)) {
+                com.fs.hisStore.config.StoreConfig config = com.alibaba.fastjson.JSON.parseObject(configJson, com.fs.hisStore.config.StoreConfig.class);
+                if (config != null && Boolean.TRUE.equals(config.getCheckStock())) {
+                    // 使用行锁检查库存
+                    checkStockWithLock(productId, productAttrValueId, cartNum);
+                }
+            }
+        } catch (Exception e) {
+            log.error("检查库存配置失败", e);
+        }
+        
         //处理属性sku
         fsStoreProductAttrValueMapper.decProductAttrStock(productAttrValueId,cartNum);
         fsStoreProductMapper.decProductAttrStock(productId,cartNum);

+ 20 - 16
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -1654,6 +1654,8 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 userService.subLiveTuiMoney(liveOrder);
             }
         }
+        // 删除限购记录
+        deletePurchaseLimitRecordsForLiveOrder(order);
 
         return R.ok();
     }
@@ -3661,6 +3663,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         this.payConfirm(1, null, payment.getPayCode(), payment.getTradeNo(), payment.getBankSerialNo(), payment.getBankSerialNo());
     }
 
+    public void deStockIncSale(List<FsStoreCartQueryVO> cartInfo) {
+        for (FsStoreCartQueryVO storeCartVO : cartInfo) {
+            fsStoreProductService.decProductStock(storeCartVO.getProductId(),
+                    storeCartVO.getProductAttrValueId(), storeCartVO.getCartNum());
+        }
+    }
 
     @Override
     @Transactional(rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
@@ -3697,20 +3705,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         Integer purchaseNum = Integer.parseInt(liveOrder.getTotalNum());
         checkPurchaseLimitForLiveOrder(userId, liveOrder.getProductId(), purchaseNum);
 
-        FsStoreProductAttrValueScrm attrValue = null;
-        if (!Objects.isNull(liveOrder.getAttrValueId())) {
-            attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(liveOrder.getAttrValueId());
-        }
-        if (attrValue != null) {
-            attrValue.setStock(attrValue.getStock() - Integer.parseInt(liveOrder.getTotalNum()));
-            attrValue.setSales(attrValue.getSales() + Integer.parseInt(liveOrder.getTotalNum()));
-            fsStoreProductAttrValueMapper.updateFsStoreProductAttrValue(attrValue);
-        }
-        // 更改店铺库存
-        fsStoreProduct.setStock(fsStoreProduct.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
-        fsStoreProduct.setSales(fsStoreProduct.getSales()+Integer.parseInt(liveOrder.getTotalNum()));
-        fsStoreProductScrmMapper.incStockDecSales(Long.valueOf("-" + liveOrder.getTotalNum()),fsStoreProduct.getProductId());
-
+        // 使用 deStockIncSale 方法处理库存和销量
+        deStockIncSale(liveOrder.getProductId(), liveOrder.getAttrValueId(), purchaseNum);
+        FsStoreProductAttrValueScrm attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(liveOrder.getAttrValueId());
         // 更新直播间库存
         goods.setStock(goods.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
         goods.setSales(goods.getSales()+Integer.parseInt(liveOrder.getTotalNum()));
@@ -3986,8 +3983,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             goods.setStock(goods.getStock()+Long.parseLong(liveOrder.getTotalNum()));
             // 更新商品库存
             liveGoodsMapper.updateLiveGoods(goods);
-            // 删除限购记录
-            deletePurchaseLimitRecordsForLiveOrder(liveOrder);
+
             // 退券
             this.refundCoupon(order);
             TemplateBean templateBean = TemplateBean.builder()
@@ -4011,6 +4007,14 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
      * @param productId 商品ID
      * @param num 购买数量
      */
+    /**
+     * 减库存加销量(直播订单)
+     */
+    private void deStockIncSale(Long productId, Long attrValueId, Integer num) {
+        // 使用 productService.decProductStock 方法,该方法已包含库存检查逻辑
+        fsStoreProductService.decProductStock(productId, attrValueId, num);
+    }
+    
     private void checkPurchaseLimitForLiveOrder(Long userId, Long productId, Integer num) {
         // 查询商品信息
         FsStoreProductScrm product = fsStoreProductService.selectFsStoreProductById(productId);