diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java b/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java index dfae59e..9038bb1 100644 --- a/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java +++ b/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java @@ -31,6 +31,10 @@ public class GltTicketOrder implements Serializable { @Schema(description = "用户水票ID") private Integer userTicketId; + @Schema(description = "订单编号") + @TableField(exist = false) + private String orderNo; + @Schema(description = "门店ID") private Integer storeId; diff --git a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml index a38798e..b45594c 100644 --- a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml +++ b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml @@ -10,13 +10,15 @@ u.nickname, u.phone, u.avatar, d.name as receiverName, d.phone as receiverPhone, d.province as receiverProvince, d.city as receiverCity, d.region as receiverRegion, - d.address as receiverAddress, d.full_address as receiverFullAddress, d.lat as receiverLat, d.lng as receiverLng + d.address as receiverAddress, d.full_address as receiverFullAddress, d.lat as receiverLat, d.lng as receiverLng, + f.order_no as orderNo FROM glt_ticket_order a LEFT JOIN shop_store b ON a.store_id = b.id LEFT JOIN shop_store_warehouse w ON a.warehouse_id = w.id LEFT JOIN shop_store_rider c ON a.rider_id = c.user_id LEFT JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id LEFT JOIN shop_user_address d ON a.address_id = d.id + LEFT JOIN glt_user_ticket f ON a.user_ticket_id = f.id diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java b/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java index 9d3f303..7fe522f 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonFormat; import java.io.Serializable; import io.swagger.v3.oas.annotations.media.Schema; @@ -101,6 +102,7 @@ public class ShopGoods implements Serializable { private BigDecimal secondDividend; @Schema(description = "库存计算方式(10下单减库存 20付款减库存)") + @JsonAlias({"cdeductStockType"}) private Integer deductStockType; @Schema(description = "交付方式(0不启用)") diff --git a/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java b/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java index 337b32c..ed8b43c 100644 --- a/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java +++ b/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java @@ -32,6 +32,8 @@ import java.util.Map; @Slf4j @Service public class OrderBusinessService { + private static final int DEDUCT_STOCK_TYPE_ORDER = 10; // 下单减库存 + private static final int DEDUCT_STOCK_TYPE_PAY = 20; // 付款减库存 @Resource private ShopOrderService shopOrderService; @@ -713,6 +715,15 @@ public class OrderBusinessService { */ private void deductStock(OrderCreateRequest request) { for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) { + // 付款减库存的商品:创建订单时不扣库存 + ShopGoods goodsForType = shopGoodsService.getById(item.getGoodsId()); + Integer deductStockType = goodsForType != null ? goodsForType.getDeductStockType() : null; + if (deductStockType != null && deductStockType == DEDUCT_STOCK_TYPE_PAY) { + log.debug("跳过下单扣库存(付款减库存) - goodsId={}, skuId={}, qty={}", + item.getGoodsId(), item.getSkuId(), item.getQuantity()); + continue; + } + if (item.getSkuId() != null) { // 多规格商品,扣减SKU库存 ShopGoodsSku sku = shopGoodsSkuService.getById(item.getSkuId()); diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/OrderCancelServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/OrderCancelServiceImpl.java index 46901d2..f327838 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/OrderCancelServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/OrderCancelServiceImpl.java @@ -24,6 +24,7 @@ import java.util.List; @Slf4j @Service public class OrderCancelServiceImpl implements OrderCancelService { + private static final int DEDUCT_STOCK_TYPE_PAY = 20; // 付款减库存:下单不扣库存,因此未支付取消无需回退 @Autowired private ShopOrderService shopOrderService; @@ -182,6 +183,20 @@ public class OrderCancelServiceImpl implements OrderCancelService { } for (ShopOrderGoods orderGood : orderGoods) { + // 付款减库存的商品:创建订单时未扣库存,未支付取消时无需回退(避免库存被“加多”) + try { + ShopGoods goods = shopGoodsService.getById(orderGood.getGoodsId()); + if (goods != null && goods.getDeductStockType() != null && goods.getDeductStockType() == DEDUCT_STOCK_TYPE_PAY) { + log.debug("跳过未支付取消的库存回退(付款减库存) - orderId={}, goodsId={}, skuId={}, qty={}", + order.getOrderId(), orderGood.getGoodsId(), orderGood.getSkuId(), orderGood.getTotalNum()); + continue; + } + } catch (Exception e) { + // 查不到商品或查询异常时,保持原有回退逻辑,避免出现“库存无法回退”的更坏情况 + log.warn("读取商品扣库存方式失败,继续执行库存回退 - orderId={}, goodsId={}", + order.getOrderId(), orderGood.getGoodsId(), e); + } + if (orderGood.getSkuId() != null && orderGood.getSkuId() > 0) { // 多规格商品,恢复SKU库存 restoreSkuStock(orderGood); diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java index 24322b7..9f91ffb 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java @@ -1,8 +1,10 @@ package com.gxwebsoft.shop.service.impl; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.common.core.context.TenantContext; import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.config.CertificateProperties; import com.gxwebsoft.common.core.utils.*; @@ -86,10 +88,13 @@ public class ShopOrderServiceImpl extends ServiceImpl 0) { markCouponAsUsed(order); @@ -816,6 +829,90 @@ public class ShopOrderServiceImpl extends ServiceImpl orderGoodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId()); + if (CollectionUtils.isEmpty(orderGoodsList)) { + return; + } + + TenantContext.runIgnoreTenant(() -> { + for (ShopOrderGoods og : orderGoodsList) { + if (og == null || og.getGoodsId() == null) { + continue; + } + int qty = og.getTotalNum() == null ? 0 : og.getTotalNum(); + if (qty <= 0) { + continue; + } + + ShopGoods goods = shopGoodsService.getById(og.getGoodsId()); + Integer deductStockType = goods != null ? goods.getDeductStockType() : null; + if (deductStockType == null || deductStockType != DEDUCT_STOCK_TYPE_PAY) { + continue; + } + + try { + if (og.getSkuId() != null && og.getSkuId() > 0) { + // 多规格:扣 SKU 库存 + boolean updated = shopGoodsSkuService.update( + new LambdaUpdateWrapper() + .eq(ShopGoodsSku::getId, og.getSkuId()) + .eq(ShopGoodsSku::getTenantId, order.getTenantId()) + .apply("IFNULL(stock,0) >= {0}", qty) + .setSql("stock = IFNULL(stock,0) - " + qty) + ); + if (!updated) { + log.warn("支付成功后扣SKU库存失败 - tenantId={}, orderId={}, skuId={}, goodsId={}, qty={}", + order.getTenantId(), order.getOrderId(), og.getSkuId(), og.getGoodsId(), qty); + // 兜底:库存不足时至少将库存置0,避免出现“明明已售出但库存仍大于0”的情况 + shopGoodsSkuService.update( + new LambdaUpdateWrapper() + .eq(ShopGoodsSku::getId, og.getSkuId()) + .eq(ShopGoodsSku::getTenantId, order.getTenantId()) + .apply("IFNULL(stock,0) < {0}", qty) + .setSql("stock = 0") + ); + } + } else { + // 单规格:扣商品库存 + boolean updated = shopGoodsService.update( + new LambdaUpdateWrapper() + .eq(ShopGoods::getGoodsId, og.getGoodsId()) + .eq(ShopGoods::getTenantId, order.getTenantId()) + .apply("IFNULL(stock,0) >= {0}", qty) + .setSql("stock = IFNULL(stock,0) - " + qty) + ); + if (!updated) { + log.warn("支付成功后扣商品库存失败 - tenantId={}, orderId={}, goodsId={}, qty={}", + order.getTenantId(), order.getOrderId(), og.getGoodsId(), qty); + // 兜底:库存不足时至少将库存置0,避免出现“明明已售出但库存仍大于0”的情况 + shopGoodsService.update( + new LambdaUpdateWrapper() + .eq(ShopGoods::getGoodsId, og.getGoodsId()) + .eq(ShopGoods::getTenantId, order.getTenantId()) + .apply("IFNULL(stock,0) < {0}", qty) + .setSql("stock = 0") + ); + } + } + } catch (Exception e) { + log.warn("支付成功后扣库存异常 - tenantId={}, orderId={}, goodsId={}, skuId={}, qty={}", + order.getTenantId(), order.getOrderId(), og.getGoodsId(), og.getSkuId(), qty, e); + } + } + }); + } + /** * 标记优惠券为已使用 */ @@ -1452,43 +1549,41 @@ public class ShopOrderServiceImpl extends ServiceImpl