
最近上线了一个新平台。问题不断,记录一下3次超发的问题
分析了一下代码。
活动逻辑
发放优惠券使用了支付1分钱认证卡种,如用户使用指定卡种支付后。给用户发放优惠券。认证失败后给用户退款。
事故一
起源
·由于A同事复制 上古代码
事故分析:
没有做预下单扣库存,支付回调时会写入一条领取成功记录。 下单时,会使用如下代码进行判断领取是否达到上限。
由于支付是异步进行的,没有提前锁定库存。导致此代码在正常情况下都会超发。
Integer receiveCount = itJcWxcardReceiveRelService.getReceiveCount(param.getStockId(), redisInfo.getUid(), WebUtils.getTisid());
if (receiveCount >= couadvSetting.getBuylimitnum()) {
//每日购买限制
log.error("每日领取限制");
throw new ShopAlertException(MsgEnums.DAY_OVERFLOW.getMsg());
}
事故二
起源
本次由B对本次活动优惠券下单认证逻辑添加了一层验证。
逻辑如下
本来以为这样改了就没问题了。发到生产后,问题还是继续存在。代码如下:
key = BaseConstant.RECEIVE_LIMIT_WEEK.concat(String.valueOf(tMiniTicketCouponInfo.getWxcardid()));
Long sumCount = stringObjectRedisTemplate.boundValueOps(key).increment(1);
stringObjectRedisTemplate.boundValueOps(key).expireAt(DateUtils.getEndDayOfWeek());
if (sumCount > couadvSetting.getDaylimit()) {
stringObjectRedisTemplate.boundValueOps(key).increment(-1);
throw new ShopAlertException("本周领取数发放上限,请稍后再试");
}
事故三
起源
再次对代码进行分析,发现一个上古代码,取消库存逻辑如下:
boolean update = tradeRecordWxService.update(new LambdaUpdateWrapper<TJcAppTradeRecordWx>()
.set(TJcAppTradeRecordWx::getTrstatus, "2")
.eq(TJcAppTradeRecordWx::getOrderid, orderid);
if (update) {
}
由于取消库存逻辑问题,存在重复取消订单的可能,且支付成功的订单也可能被修改为已取消。
修改上述问题后验证,发现回库存也存在问题 取消后本应该将tradeKey删除防止重复回库存,结果因为疏忽删错了key
导致整个库存校验逻辑失效。
String tradeKey = BaseConstant.USER_RECEIVE_LIMIT_ORDER.concat(tradeRecordWx.getOrderid());
String key = (String) stringObjectRedisTemplate.opsForValue().get(tradeKey);
if (!com.shop.coupons.common.utils.StringUtils.isEmpty(key)){
stringObjectRedisTemplate.boundValueOps(key).increment(-1);
stringObjectRedisTemplate.delete(key);
}
至此,以上问题都被修复。
总结:写代码需要严谨再严谨,由于之前验证过回库存逻辑,后期防止重复回库存,加了一个删除key的逻辑。没有验证就发布导致整个库存校验逻辑失效