|
@@ -248,6 +248,9 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private RedissonClient redissonClient;
|
|
private RedissonClient redissonClient;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private com.fs.hisStore.service.IFsStoreProductGroupBuyService groupBuyService;
|
|
|
|
|
+
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private com.fs.common.core.redis.service.ActivityStockService activityStockService;
|
|
private com.fs.common.core.redis.service.ActivityStockService activityStockService;
|
|
|
|
|
|
|
@@ -851,7 +854,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if(cartParam.getProductType() != null && (cartParam.getProductType() == 6 || cartParam.getProductType() == 7) ){//更新金额
|
|
|
|
|
|
|
+ if(cartParam.getProductType() != null && (cartParam.getProductType() == 6 || cartParam.getProductType() == 7 || cartParam.getProductType() == 8) ){//更新金额
|
|
|
for (FsStoreCartQueryVO c : carts){
|
|
for (FsStoreCartQueryVO c : carts){
|
|
|
//获取对应商品金额
|
|
//获取对应商品金额
|
|
|
FsStoreProductActivity activity = activityMapper.selectActivityByProductIdAndSpecId(c.getProductId(), c.getProductAttrValueId());
|
|
FsStoreProductActivity activity = activityMapper.selectActivityByProductIdAndSpecId(c.getProductId(), c.getProductAttrValueId());
|
|
@@ -861,7 +864,18 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
//更新购物车信息
|
|
//更新购物车信息
|
|
|
FsStoreCartScrm cartScrm = new FsStoreCartScrm();
|
|
FsStoreCartScrm cartScrm = new FsStoreCartScrm();
|
|
|
cartScrm.setId(c.getId());
|
|
cartScrm.setId(c.getId());
|
|
|
- BigDecimal price = cartParam.getProductType() == 7?activity.getDiscountPrice():activity.getFlashPrice();
|
|
|
|
|
|
|
+ // 按活动类型取价:7=折扣价 / 8=团购价 / 6=秒杀价
|
|
|
|
|
+ BigDecimal price;
|
|
|
|
|
+ if (cartParam.getProductType() == 7) {
|
|
|
|
|
+ price = activity.getDiscountPrice();
|
|
|
|
|
+ } else if (cartParam.getProductType() == 8) {
|
|
|
|
|
+ price = activity.getGroupPrice();
|
|
|
|
|
+ if (price == null) {
|
|
|
|
|
+ return R.error("操作失败,团购价未设置!");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ price = activity.getFlashPrice();
|
|
|
|
|
+ }
|
|
|
cartScrm.setChangePrice(price);
|
|
cartScrm.setChangePrice(price);
|
|
|
c.setPrice(price);
|
|
c.setPrice(price);
|
|
|
c.setChangePrice(price);
|
|
c.setChangePrice(price);
|
|
@@ -1347,61 +1361,86 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
public R createActivityOrder(long userId, FsStoreOrderCreateParam param) {
|
|
public R createActivityOrder(long userId, FsStoreOrderCreateParam param) {
|
|
|
Long associatedId = null;
|
|
Long associatedId = null;
|
|
|
Integer orderType = param.getOrderType();
|
|
Integer orderType = param.getOrderType();
|
|
|
|
|
+ int activityDeductNum = 1; // Redis库存扣减数量(仅秒杀/限时折扣使用,异常时用于回滚)
|
|
|
|
|
+ Long reservedGroupBuyId = null; // 团购下单阶段预占到的团ID,异常时用于释放名额
|
|
|
|
|
|
|
|
- // 1. 活动参数校验
|
|
|
|
|
- if (orderType == null || (orderType != 6 && orderType != 7)) {
|
|
|
|
|
|
|
+ //活动参数校验(6=秒杀 7=限时折扣 8=限时团购)
|
|
|
|
|
+ if (orderType == null || (orderType != 6 && orderType != 7 && orderType != 8)) {
|
|
|
return R.error("无效的活动类型");
|
|
return R.error("无效的活动类型");
|
|
|
}
|
|
}
|
|
|
if (param.getAssociatedId() == null || param.getAssociatedId() <= 0) {
|
|
if (param.getAssociatedId() == null || param.getAssociatedId() <= 0) {
|
|
|
return R.error("活动ID不能为空");
|
|
return R.error("活动ID不能为空");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 2. 校验活动时间和状态
|
|
|
|
|
- com.fs.common.core.redis.service.ActivityValidateResult validateResult =
|
|
|
|
|
- activityStockService.validateActivityWithDetail(orderType, param.getAssociatedId());
|
|
|
|
|
- if (!validateResult.isValid()) {
|
|
|
|
|
- return R.error(validateResult.getMessage());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 3. 确保Redis活动信息和规格库存已初始化(活动无独立库存,用规格库存)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ //取活动信息
|
|
|
FsStoreProductActivity activityInfo = activityService.selectFsStoreProductActivityById(param.getAssociatedId());
|
|
FsStoreProductActivity activityInfo = activityService.selectFsStoreProductActivityById(param.getAssociatedId());
|
|
|
if (activityInfo == null) {
|
|
if (activityInfo == null) {
|
|
|
return R.error("活动信息不存在");
|
|
return R.error("活动信息不存在");
|
|
|
}
|
|
}
|
|
|
- activityStockService.initActivityInfo(
|
|
|
|
|
- param.getAssociatedId(), 1,
|
|
|
|
|
- activityInfo.getStartTime().getTime(), activityInfo.getEndTime().getTime(),
|
|
|
|
|
- activityInfo.getProductId(), activityInfo.getSpecId(), null
|
|
|
|
|
- );
|
|
|
|
|
- if (activityInfo.getSpecId() != null && activityInfo.getSpecStock() != null) {
|
|
|
|
|
- activityStockService.initProductSpecStock(activityInfo.getSpecId(), activityInfo.getSpecStock());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 3.5 提前获取购物车,计算活动商品总件数,确保Lua扣减数量与DB扣减数量一致
|
|
|
|
|
- String preCartIds = redisCache.getCacheObject("orderKey:" + param.getOrderKey());
|
|
|
|
|
- List<FsStoreCartQueryVO> preCarts = null;
|
|
|
|
|
- int activityDeductNum = 1; // 默认扣减1件
|
|
|
|
|
- if (preCartIds != null) {
|
|
|
|
|
- preCarts = redisCache.getCacheObject("orderCarts:" + param.getOrderKey());
|
|
|
|
|
- if (preCarts != null) {
|
|
|
|
|
- activityDeductNum = 0;
|
|
|
|
|
- for (FsStoreCartQueryVO cart : preCarts) {
|
|
|
|
|
- if (cart.getCartNum() != null) {
|
|
|
|
|
- activityDeductNum += cart.getCartNum();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (orderType == 8) {
|
|
|
|
|
+ // 限时团购:不走 Redis/Lua 预扣,库存交由后续 deStockIncSale 走 DB 统一扣减
|
|
|
|
|
+ long nowTs = System.currentTimeMillis();
|
|
|
|
|
+ if (activityInfo.getStatus() == null || activityInfo.getStatus() != 1) {
|
|
|
|
|
+ return R.error("活动已下架");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (activityInfo.getStartTime() == null || nowTs < activityInfo.getStartTime().getTime()) {
|
|
|
|
|
+ return R.error("活动尚未开始");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (activityInfo.getEndTime() == null || nowTs > activityInfo.getEndTime().getTime()) {
|
|
|
|
|
+ return R.error("活动已结束");
|
|
|
|
|
+ }
|
|
|
|
|
+ associatedId = param.getAssociatedId();
|
|
|
|
|
+
|
|
|
|
|
+ // 下单时先预占一个团的名额:优先找未满未过期的老团拼团,都没有就自己单独开团
|
|
|
|
|
+ reservedGroupBuyId = groupBuyService.reserveGroupSlot(associatedId, userId);
|
|
|
|
|
+ if (reservedGroupBuyId == null) {
|
|
|
|
|
+ return R.error("团购名额预占失败,请稍后重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 秒杀/限时折扣:保持原有 Redis + Lua 预扣路径
|
|
|
|
|
+ //校验活动时间和状态
|
|
|
|
|
+ com.fs.common.core.redis.service.ActivityValidateResult validateResult =
|
|
|
|
|
+ activityStockService.validateActivityWithDetail(orderType, param.getAssociatedId());
|
|
|
|
|
+ if (!validateResult.isValid()) {
|
|
|
|
|
+ return R.error(validateResult.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //确保Redis活动信息和规格库存已初始化(活动无独立库存,用规格库存)
|
|
|
|
|
+ activityStockService.initActivityInfo(
|
|
|
|
|
+ param.getAssociatedId(), 1,
|
|
|
|
|
+ activityInfo.getStartTime().getTime(), activityInfo.getEndTime().getTime(),
|
|
|
|
|
+ activityInfo.getProductId(), activityInfo.getSpecId(), null
|
|
|
|
|
+ );
|
|
|
|
|
+ if (activityInfo.getSpecId() != null && activityInfo.getSpecStock() != null) {
|
|
|
|
|
+ activityStockService.initProductSpecStock(activityInfo.getSpecId(), activityInfo.getSpecStock());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //提前获取购物车,计算活动商品总件数,确保Lua扣减数量与DB扣减数量一致
|
|
|
|
|
+ String preCartIds = redisCache.getCacheObject("orderKey:" + param.getOrderKey());
|
|
|
|
|
+ List<FsStoreCartQueryVO> preCarts = null;
|
|
|
|
|
+ if (preCartIds != null) {
|
|
|
|
|
+ preCarts = redisCache.getCacheObject("orderCarts:" + param.getOrderKey());
|
|
|
|
|
+ if (preCarts != null) {
|
|
|
|
|
+ activityDeductNum = 0;
|
|
|
|
|
+ for (FsStoreCartQueryVO cart : preCarts) {
|
|
|
|
|
+ if (cart.getCartNum() != null) {
|
|
|
|
|
+ activityDeductNum += cart.getCartNum();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (activityDeductNum <= 0) {
|
|
|
|
|
+ activityDeductNum = 1;
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- if (activityDeductNum <= 0) {
|
|
|
|
|
- activityDeductNum = 1;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- // 4. Lua原子扣减活动库存(已移除 getStock() 预检查——先查再扣存在竞态窗口,Lua脚本本身会原子判断库存是否充足)
|
|
|
|
|
- boolean deductSuccess = activityStockService.deductStock(orderType, param.getAssociatedId(), activityDeductNum);
|
|
|
|
|
- if (!deductSuccess) {
|
|
|
|
|
- return R.error("活动商品已售罄,请稍后重试");
|
|
|
|
|
|
|
+ //Lua原子扣减活动库存(已移除 getStock() 预检查——先查再扣存在竞态窗口,Lua脚本本身会原子判断库存是否充足)
|
|
|
|
|
+ boolean deductSuccess = activityStockService.deductStock(orderType, param.getAssociatedId(), activityDeductNum);
|
|
|
|
|
+ if (!deductSuccess) {
|
|
|
|
|
+ return R.error("活动商品已售罄,请稍后重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ associatedId = param.getAssociatedId();
|
|
|
}
|
|
}
|
|
|
- associatedId = param.getAssociatedId();
|
|
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
FsUserCompanyUser fsUserCompanyUser = fsUserCompanyUserMapper.selectFsUserCompanyUserByUserId(userId);
|
|
FsUserCompanyUser fsUserCompanyUser = fsUserCompanyUserMapper.selectFsUserCompanyUserByUserId(userId);
|
|
@@ -1618,6 +1657,10 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
//活动商品只扣Redis(已在上文扣减),DB延后扣减
|
|
//活动商品只扣Redis(已在上文扣减),DB延后扣减
|
|
|
// storeOrder.setAssociatedId
|
|
// storeOrder.setAssociatedId
|
|
|
storeOrder.setAssociatedId(associatedId);
|
|
storeOrder.setAssociatedId(associatedId);
|
|
|
|
|
+ // 团购订单:回写下单时预占到的团ID,支付回调就按这个团落详情
|
|
|
|
|
+ if (reservedGroupBuyId != null) {
|
|
|
|
|
+ storeOrder.setGroupBuyId(reservedGroupBuyId);
|
|
|
|
|
+ }
|
|
|
Integer flag = fsStoreOrderMapper.insertFsStoreOrder(storeOrder);
|
|
Integer flag = fsStoreOrderMapper.insertFsStoreOrder(storeOrder);
|
|
|
if (flag == 0) {
|
|
if (flag == 0) {
|
|
|
return R.error("订单创建失败");
|
|
return R.error("订单创建失败");
|
|
@@ -1741,14 +1784,24 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
return R.error("订单已过期");
|
|
return R.error("订单已过期");
|
|
|
}
|
|
}
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- // Redis已扣减成功,订单创建过程异常,必须回滚Redis库存
|
|
|
|
|
- try {
|
|
|
|
|
- activityStockService.rollbackStock(orderType, param.getAssociatedId(), activityDeductNum);
|
|
|
|
|
- log.info("订单创建异常,Redis库存已回滚,associatedId={}, orderType={}, 回滚数量={}",
|
|
|
|
|
- param.getAssociatedId(), orderType, activityDeductNum);
|
|
|
|
|
- } catch (Exception rollbackEx) {
|
|
|
|
|
- log.error("订单创建异常后Redis库存回滚失败!associatedId={},orderType={},需要人工处理",
|
|
|
|
|
- param.getAssociatedId(), orderType, rollbackEx);
|
|
|
|
|
|
|
+ // 团购未走 Redis 预扣,无需回滚 Redis;秒杀/限时折扣 Redis 已扣减成功,订单创建异常必须回滚
|
|
|
|
|
+ if (orderType != null && orderType != 8) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ activityStockService.rollbackStock(orderType, param.getAssociatedId(), activityDeductNum);
|
|
|
|
|
+ log.info("订单创建异常,Redis库存已回滚,associatedId={}, orderType={}, 回滚数量={}",
|
|
|
|
|
+ param.getAssociatedId(), orderType, activityDeductNum);
|
|
|
|
|
+ } catch (Exception rollbackEx) {
|
|
|
|
|
+ log.error("订单创建异常后Redis库存回滚失败!associatedId={},orderType={},需要人工处理",
|
|
|
|
|
+ param.getAssociatedId(), orderType, rollbackEx);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 团购订单:下单预占到的名额因订单异常无效,要把名额还回去,免得团被卡死
|
|
|
|
|
+ if (orderType != null && orderType == 8 && reservedGroupBuyId != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ groupBuyService.releaseGroupSlot(reservedGroupBuyId);
|
|
|
|
|
+ } catch (Exception releaseEx) {
|
|
|
|
|
+ log.error("订单创建异常后释放团购名额失败,groupBuyId={},需人工处理", reservedGroupBuyId, releaseEx);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
log.error("活动订单创建异常,orderType={}, associatedId={}", orderType, param.getAssociatedId(), e);
|
|
log.error("活动订单创建异常,orderType={}, associatedId={}", orderType, param.getAssociatedId(), e);
|
|
|
throw e;
|
|
throw e;
|
|
@@ -1897,6 +1950,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
this.refundCoupon(order);
|
|
this.refundCoupon(order);
|
|
|
//退回库存
|
|
//退回库存
|
|
|
this.refundStock(order);
|
|
this.refundStock(order);
|
|
|
|
|
+ // 团购订单取消时把占的位置还回去,SQL 内部只对未成团的团生效,已成团的不会动
|
|
|
|
|
+ releaseGroupSlotIfNeeded(order);
|
|
|
fsStoreOrderMapper.cancelOrder(orderId);
|
|
fsStoreOrderMapper.cancelOrder(orderId);
|
|
|
//添加记录
|
|
//添加记录
|
|
|
orderStatusService.create(order.getId(), OrderLogEnum.CANCEL_ORDER.getValue(),
|
|
orderStatusService.create(order.getId(), OrderLogEnum.CANCEL_ORDER.getValue(),
|
|
@@ -2767,6 +2822,15 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("创建限购商品失败:{}", e.getMessage());
|
|
log.error("创建限购商品失败:{}", e.getMessage());
|
|
|
}
|
|
}
|
|
|
|
|
+ // 限时团购(activity_type=8):支付成功后才真正加入/新建团,并回写 group_buy_id
|
|
|
|
|
+ // 自身事务独立,异常吃掉不影响支付回调主流程(汇付需 return SUCCESS 避免重试)
|
|
|
|
|
+ if (order.getOrderType() != null && order.getOrderType() == 8) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ groupBuyService.handleAfterPay(order);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("团购订单支付后处理失败,orderId={}", order.getId(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
return "SUCCESS";
|
|
return "SUCCESS";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -3133,11 +3197,18 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
if (order.getStatus() != 1 && order.getStatus() != 2) {
|
|
if (order.getStatus() != 1 && order.getStatus() != 2) {
|
|
|
return R.error("非法操作");
|
|
return R.error("非法操作");
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ Integer originalRefundStatus = order.getRefundStatus();
|
|
|
if (erpConfig.getErpOpen() != null
|
|
if (erpConfig.getErpOpen() != null
|
|
|
&& erpConfig.getErpOpen() == 1
|
|
&& erpConfig.getErpOpen() == 1
|
|
|
&& order.getExtendOrderId() == null
|
|
&& order.getExtendOrderId() == null
|
|
|
&& !CloudHostUtils.hasCloudHostName("康年堂")) {
|
|
&& !CloudHostUtils.hasCloudHostName("康年堂")) {
|
|
|
- return R.error("暂未推送至erp,请稍后再试!");
|
|
|
|
|
|
|
+ boolean isUnformedGroupOrder = order.getOrderType() != null
|
|
|
|
|
+ && order.getOrderType() == 8
|
|
|
|
|
+ && order.getGroupBuyId() != null;
|
|
|
|
|
+ if (!isUnformedGroupOrder) {
|
|
|
|
|
+ return R.error("暂未推送至erp,请稍后再试!");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
if (StringUtils.isNotEmpty(order.getExtendOrderId())) {
|
|
if (StringUtils.isNotEmpty(order.getExtendOrderId())) {
|
|
|
ErpRefundUpdateRequest request = new ErpRefundUpdateRequest();
|
|
ErpRefundUpdateRequest request = new ErpRefundUpdateRequest();
|
|
@@ -3180,6 +3251,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
order.setRefundStatus(OrderInfoEnum.REFUND_STATUS_2.getValue());
|
|
order.setRefundStatus(OrderInfoEnum.REFUND_STATUS_2.getValue());
|
|
|
fsStoreOrderMapper.updateFsStoreOrder(order);
|
|
fsStoreOrderMapper.updateFsStoreOrder(order);
|
|
|
|
|
|
|
|
|
|
+ // 团购已付款订单退款:同时回滚 join_num + paid_num。
|
|
|
|
|
+ if (originalRefundStatus == null || originalRefundStatus == 0) {
|
|
|
|
|
+ releasePaidGroupSlotIfNeeded(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
//退库存
|
|
//退库存
|
|
|
//获取订单下的商品
|
|
//获取订单下的商品
|
|
|
List<FsStoreOrderItemVO> orderItemVOS = fsStoreOrderItemMapper.selectFsStoreOrderItemListByOrderId(order.getId());
|
|
List<FsStoreOrderItemVO> orderItemVOS = fsStoreOrderItemMapper.selectFsStoreOrderItemListByOrderId(order.getId());
|
|
@@ -3189,8 +3265,12 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
refundActivityStock(order, orderItemVOS);
|
|
refundActivityStock(order, orderItemVOS);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 团购订单(orderType=8)下单时走的是 deStockIncSale 直接扣 fs_store_product_attr_value_scrm 规格库存(stock-/sales+),
|
|
|
|
|
+ // 没走 isAfterSales 的售后流程;超时自动退款 / 直接退款路径下 isAfterSales 还是 0,
|
|
|
|
|
+ // 不无条件回的话 fs_store_product_attr_value_scrm 的 stock 就泄漏了。
|
|
|
|
|
+ boolean isGroupBuyOrder = order.getOrderType() != null && order.getOrderType() == 8;
|
|
|
for (FsStoreOrderItemVO vo : orderItemVOS) {
|
|
for (FsStoreOrderItemVO vo : orderItemVOS) {
|
|
|
- if (vo.getIsAfterSales() == 1) {
|
|
|
|
|
|
|
+ if (vo.getIsAfterSales() == 1 || isGroupBuyOrder) {
|
|
|
productService.incProductStock(vo.getNum(), vo.getProductId(), vo.getProductAttrValueId());
|
|
productService.incProductStock(vo.getNum(), vo.getProductId(), vo.getProductAttrValueId());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -4032,6 +4112,51 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
|
|
|
&& order.getAssociatedId() > 0;
|
|
&& order.getAssociatedId() > 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 团购订单未付款取消时还回占的名额。
|
|
|
|
|
+ * <p>仅对 orderType=8 且已有 groupBuyId 的订单生效;
|
|
|
|
|
+ * Mapper 层的 SQL 从来只改 status=0 的未成团团,已成团的订单调下来不会误伤。</p>
|
|
|
|
|
+ * <p>异常不中断主流程,打个错误日志给运维看就行。</p>
|
|
|
|
|
+ */
|
|
|
|
|
+ private void releaseGroupSlotIfNeeded(FsStoreOrderScrm order) {
|
|
|
|
|
+ if (order == null || order.getOrderType() == null || order.getOrderType() != 8) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long groupBuyId = order.getGroupBuyId();
|
|
|
|
|
+ if (groupBuyId == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ groupBuyService.releaseGroupSlot(groupBuyId);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("团购订单释放名额失败,orderId={},groupBuyId={},需人工核查",
|
|
|
|
|
+ order.getId(), groupBuyId, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 已付款订单退款时还名额:同时回滚 join_num + paid_num。
|
|
|
|
|
+ * <p>跟 {@link #releaseGroupSlotIfNeeded} 的区别是这个用于"已付款"场景(refundOrderMoney),
|
|
|
|
|
+ * 前者用于"未付款取消"场景(cancelOrder)。paid_num 只有在已付款路径才加过 1,
|
|
|
|
|
+ * 退款时需要同步扣回,不然 paid_num 会虚高让后续团员被误判成团。</p>
|
|
|
|
|
+ * <p>SQL 层做了 status=0 保护,团已成团/已失败时空转不会误改,调用方无需自己判。</p>
|
|
|
|
|
+ */
|
|
|
|
|
+ private void releasePaidGroupSlotIfNeeded(FsStoreOrderScrm order) {
|
|
|
|
|
+ if (order == null || order.getOrderType() == null || order.getOrderType() != 8) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long groupBuyId = order.getGroupBuyId();
|
|
|
|
|
+ if (groupBuyId == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ groupBuyService.releasePaidGroupSlot(groupBuyId);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("团购订单退款释放名额失败,orderId={},groupBuyId={},需人工核查",
|
|
|
|
|
+ order.getId(), groupBuyId, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 活动订单退款时回滚Redis规格库存(Lua原子操作)。
|
|
* 活动订单退款时回滚Redis规格库存(Lua原子操作)。
|
|
|
* 异常不中断退款主流程,但记录错误日志以便人工排查。
|
|
* 异常不中断退款主流程,但记录错误日志以便人工排查。
|