Преглед изворни кода

更新库存并发超发bug

release_toaliyun_real
xiaohanzi пре 6 година
родитељ
комит
3faa3ab223
14 измењених фајлова са 415 додато и 125 уклоњено
  1. +9
    -2
      mallinkAdmin/src/main/java/com/iformall/controller/market/WxCouponController.java
  2. +2
    -2
      mallinkCApi/src/main/java/com/iformall/controller/WxCouponChannelController.java
  3. +6
    -1
      mallinkMQConsumer/src/main/java/com/iformall/mq/MqBaseConsumer.java
  4. +31
    -0
      mallinkService/src/main/java/com/iformall/domain/po/msg/UpdateCouponStockMsg.java
  5. +1
    -1
      mallinkService/src/main/java/com/iformall/enums/EnumCacheKey.java
  6. +1
    -0
      mallinkService/src/main/java/com/iformall/enums/EnumMsgMqTopic.java
  7. +1
    -0
      mallinkService/src/main/java/com/iformall/enums/EnumMsgRecordType.java
  8. +1
    -1
      mallinkService/src/main/java/com/iformall/mapper/WxCouponMapper.java
  9. +1
    -1
      mallinkService/src/main/java/com/iformall/service/WxCouponService.java
  10. +1
    -1
      mallinkService/src/main/java/com/iformall/service/impl/WxCouponSendServiceImpl.java
  11. +2
    -2
      mallinkService/src/main/java/com/iformall/service/impl/WxCouponServiceImpl.java
  12. +276
    -112
      mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java
  13. +35
    -0
      mallinkService/src/main/java/com/iformall/service/msg/impl/UpdateCouponStockMsgServiceImpl.java
  14. +48
    -2
      mallinkService/src/main/java/com/iformall/utils/RedisLock.java

+ 9
- 2
mallinkAdmin/src/main/java/com/iformall/controller/market/WxCouponController.java Прегледај датотеку

@@ -17,6 +17,8 @@ import com.iformall.service.WxCouponPasswordService;
import com.iformall.service.WxCouponService;
import com.iformall.service.WxFlowService;
import com.iformall.utils.DateUtils;
import com.iformall.utils.RedisLock;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -46,6 +48,8 @@ public class WxCouponController extends BaseController {
private WxCouponChannelMapper wxCouponChannelMapper;
@Autowired
private WxFlowService wxFlowService;
@Autowired
RedisLock redisLock;

@ApiOperation("分页列表接口")
@GetMapping("list")
@@ -95,7 +99,7 @@ public class WxCouponController extends BaseController {
if (resultData.code != 200) {
return resultData;
}
redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory());
//启动投放审批
if(wxCoupon.getFlowParams() !=null && wxCoupon.getFlowParams().size()>0) {
wxCoupon.getFlowParams().put("businessId",wxCoupon.getId());
@@ -124,7 +128,7 @@ public class WxCouponController extends BaseController {
return new ResultData(ResultData.ERROR, "有活动正在上架,请先下架。");
}
}
redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory());
//启动审批流
if (wxCoupon.getFlowParams() != null && wxCoupon.getFlowParams().size() > 0) {
logger.info("------coupon.update().businessType:"+wxCoupon.getFlowParams().get("businessType"));
@@ -139,6 +143,7 @@ public class WxCouponController extends BaseController {
wxCoupon.setPutApplyStatus(EnumRentContractAppStatus.APPLYING.getCode());
}
}
return wxCouponService.saveOrUpdate(wxCoupon);
}

@@ -246,7 +251,9 @@ public class WxCouponController extends BaseController {
updateCoupon.setStockApplyStatus(EnumRentContractAppStatus.APPLYING.getCode());
updateCoupon.setUpdateDate(new Date());
wxCouponService.update(updateCoupon);
redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory());
} else {
redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory());
return wxCouponService.updateCouponStockAndEndTime(wxCoupon);
}



+ 2
- 2
mallinkCApi/src/main/java/com/iformall/controller/WxCouponChannelController.java Прегледај датотеку

@@ -43,7 +43,7 @@ public class WxCouponChannelController extends BaseController {
@Autowired
private WxMerchantService wxMerchantService;

@RedisCache(expireTime = 600)
@RedisCache(expireTime = 100)
@ApiOperation("分页列表接口")
@GetMapping("list")
@ApiImplicitParams({
@@ -101,7 +101,7 @@ public class WxCouponChannelController extends BaseController {
}
}

@RedisCache(expireTime = 600)
@RedisCache(expireTime = 100)
@ApiOperation("分页列表接口")
@GetMapping("change")
public ResultData change(@ModelAttribute WxCouponChannel wxCouponChannel) {


+ 6
- 1
mallinkMQConsumer/src/main/java/com/iformall/mq/MqBaseConsumer.java Прегледај датотеку

@@ -41,7 +41,9 @@ public class MqBaseConsumer {
private FmInsideNotifyPaySuccessMsgServiceImpl fmInsideNotifyPaySuccessMsgService;
@Autowired
private FmInsideNotifyRefundSuccessMsgServiceImpl fmInsideNotifyRefundSuccessMsgService;

@Autowired
UpdateCouponStockMsgServiceImpl updateCouponStockMsgService;


public void doMessage(String message) {
@@ -95,6 +97,9 @@ public class MqBaseConsumer {
// 内部消息 - 微信退款通知
FmInsideNotifyRefundSuccessMsg msg = (FmInsideNotifyRefundSuccessMsg)JsonUtil.readValue(message,FmInsideNotifyRefundSuccessMsg.class);
fmInsideNotifyRefundSuccessMsgService.send(msg);
} else if (EnumMsgRecordType.COUPON_STOCK.getCode().equals(baseMsg.getMsgType())) {
UpdateCouponStockMsg msg = (UpdateCouponStockMsg)JsonUtil.readValue(message,UpdateCouponStockMsg.class);
updateCouponStockMsgService.send(msg);
}
baseMsg.setMsgStatus(EnumMsgRecordStatus.CONSUME_SUCC.getCode());
wxMsgRecordMapper.updateByBaseMsg(baseMsg);


+ 31
- 0
mallinkService/src/main/java/com/iformall/domain/po/msg/UpdateCouponStockMsg.java Прегледај датотеку

@@ -0,0 +1,31 @@
package com.iformall.domain.po.msg;

import cn.binarywang.wx.miniapp.bean.WxMaTemplateData;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

import java.util.List;
import java.util.Map;

/**
* 更新券库存消息
* @author Stormeye
* @date 2019/4/9 13:55
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class UpdateCouponStockMsg extends BaseMsg {
private static final long serialVersionUID = 223232132183760321L;
public static final String OPERATE_TYPE_INCREASE = "increase";
public static final String OPERATE_TYPE_DECREASE = "decrease";

@io.swagger.annotations.ApiModelProperty(value="券Id",name="couponId")
private Long couponId;
@io.swagger.annotations.ApiModelProperty(value="更新数量",name="number")
private long number;
@io.swagger.annotations.ApiModelProperty(value="操作类型 1-增加库存 ,2-减少库存",name="operateType")
private String operateType;
}

+ 1
- 1
mallinkService/src/main/java/com/iformall/enums/EnumCacheKey.java Прегледај датотеку

@@ -9,7 +9,7 @@ import java.util.Map;
public enum EnumCacheKey {

C_INDEX_PAGE_LIST(0, "couponChannelList_"),
COUPON_STOCK(1,"couponStock_")
;
private static Map<Integer,EnumCacheKey> map = new HashMap<Integer,EnumCacheKey>();
static {


+ 1
- 0
mallinkService/src/main/java/com/iformall/enums/EnumMsgMqTopic.java Прегледај датотеку

@@ -5,6 +5,7 @@ package com.iformall.enums;
*/
public enum EnumMsgMqTopic {
DEFAULT("topic-1", "默认"),
STOCK("stock","库存"),
;

public static EnumMsgMqTopic getEnum(String code) {


+ 1
- 0
mallinkService/src/main/java/com/iformall/enums/EnumMsgRecordType.java Прегледај датотеку

@@ -17,6 +17,7 @@ public enum EnumMsgRecordType {
INSIDE_C_LOGIN(102, "C端用户登录"),
INSIDE_NOTIFY_PAY_SUCCESS(103, "微信支付回调"),
INSIDE_NOTIFY_REFUND_SUCCESS(104, "微信退款回调"),
COUPON_STOCK(105, "券库存更新"),
;

public static EnumMsgRecordType getEnum(Integer code) {


+ 1
- 1
mallinkService/src/main/java/com/iformall/mapper/WxCouponMapper.java Прегледај датотеку

@@ -20,7 +20,7 @@ public interface WxCouponMapper extends CommonMapper<WxCoupon, Long> {
WxCouponCVo findVoDetail(@Param("id")Long id);
WxCouponCVo findVoStatusDetail(@Param("id")Long id);

Integer reduceInventory(@Param("id")Long id,@Param("number")Integer number,@Param("remainInventory")Integer remainInventory);
Integer reduceInventory(@Param("id")Long id,@Param("number")Integer number);

Integer backInventory(@Param("id")Long id, @Param("number")Integer number,@Param("remainInventory")Integer remainInventory);



+ 1
- 1
mallinkService/src/main/java/com/iformall/service/WxCouponService.java Прегледај датотеку

@@ -212,7 +212,7 @@ public interface WxCouponService {
* @param number 减少数量
* @param remainInventory 当前库存
*/
void reduceRemainInventory(Long id, Integer number, Integer remainInventory);
void reduceRemainInventory(Long id, Integer number);

/**
* 库存退回


+ 1
- 1
mallinkService/src/main/java/com/iformall/service/impl/WxCouponSendServiceImpl.java Прегледај датотеку

@@ -181,7 +181,7 @@ public class WxCouponSendServiceImpl implements WxCouponSendService {
}

//更新库存
wxCouponService.reduceRemainInventory(wxCouponOfDb.getId(), merchantLnventory, wxCouponOfDb.getRemainInventory());
wxCouponService.reduceRemainInventory(wxCouponOfDb.getId(), merchantLnventory);
}

@Override


+ 2
- 2
mallinkService/src/main/java/com/iformall/service/impl/WxCouponServiceImpl.java Прегледај датотеку

@@ -714,8 +714,8 @@ public class WxCouponServiceImpl implements WxCouponService {

@Override
@Transactional(isolation= Isolation.SERIALIZABLE, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public void reduceRemainInventory(Long id, Integer number, Integer remainInventory) {
int num = wxCouponMapper.reduceInventory(id, number, remainInventory);
public void reduceRemainInventory(Long id, Integer number) {
int num = wxCouponMapper.reduceInventory(id, number);
if (num == 0) {
throw new MallinkException(ErrorCode.ORDER_SAVE_ERR);
}


+ 276
- 112
mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java Прегледај датотеку

@@ -1,5 +1,31 @@
package com.iformall.service.impl;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -10,34 +36,99 @@ import com.iformall.common.IdWorker;
import com.iformall.common.ResultData;
import com.iformall.domain.dto.OrderSaveDto;
import com.iformall.domain.dto.WxSharingOrderDto;
import com.iformall.domain.po.*;
import com.iformall.domain.po.WxAppinfo;
import com.iformall.domain.po.WxCUser;
import com.iformall.domain.po.WxCardInfo;
import com.iformall.domain.po.WxCoupon;
import com.iformall.domain.po.WxCouponChannel;
import com.iformall.domain.po.WxCouponMerchant;
import com.iformall.domain.po.WxCouponOrder;
import com.iformall.domain.po.WxCouponPassword;
import com.iformall.domain.po.WxCouponSend;
import com.iformall.domain.po.WxCreditHistory;
import com.iformall.domain.po.WxMerchant;
import com.iformall.domain.po.WxMerchantBUser;
import com.iformall.domain.po.WxOrder;
import com.iformall.domain.po.WxOrderGroup;
import com.iformall.domain.po.WxOrderPress;
import com.iformall.domain.po.WxPayAccount;
import com.iformall.domain.po.WxPayOrder;
import com.iformall.domain.po.base.TenantEntity;
import com.iformall.domain.po.msg.FmInsideOrderSuccessMsg;
import com.iformall.domain.vo.*;
import com.iformall.enums.*;
import com.iformall.domain.po.msg.UpdateCouponStockMsg;
import com.iformall.domain.vo.WxCouponSendVo;
import com.iformall.domain.vo.WxMerchantVo;
import com.iformall.domain.vo.WxOrderCouponPressVo;
import com.iformall.domain.vo.WxOrderCouponVo;
import com.iformall.domain.vo.WxOrderPayExpVo;
import com.iformall.domain.vo.WxOrderPayVo;
import com.iformall.domain.vo.WxOrderQueryVo;
import com.iformall.enums.EnumAssignTagsTrigger;
import com.iformall.enums.EnumCacheKey;
import com.iformall.enums.EnumCouponChannelStatus;
import com.iformall.enums.EnumCouponConditionType;
import com.iformall.enums.EnumCouponMerchantStatus;
import com.iformall.enums.EnumCouponOrderStatus;
import com.iformall.enums.EnumCouponPasswordStatus;
import com.iformall.enums.EnumCouponPasswordSupport;
import com.iformall.enums.EnumCouponSendSendType;
import com.iformall.enums.EnumCouponSendType;
import com.iformall.enums.EnumCouponStatus;
import com.iformall.enums.EnumCouponTransfer;
import com.iformall.enums.EnumCouponType;
import com.iformall.enums.EnumMerchantStatus;
import com.iformall.enums.EnumMerchantType;
import com.iformall.enums.EnumMsgMqKey;
import com.iformall.enums.EnumMsgMqTag;
import com.iformall.enums.EnumMsgMqTopic;
import com.iformall.enums.EnumMsgRecordType;
import com.iformall.enums.EnumOrderPressType;
import com.iformall.enums.EnumOrderStatus;
import com.iformall.enums.EnumOrderType;
import com.iformall.enums.EnumPayShare;
import com.iformall.enums.EnumPayStatus;
import com.iformall.enums.EnumPayType;
import com.iformall.enums.EnumProfitSharingOrderType;
import com.iformall.enums.EnumScoreType;
import com.iformall.enums.EnumUserType;
import com.iformall.enums.EnumValidStatus;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.*;
import com.iformall.mapper.WxAppinfoMapper;
import com.iformall.mapper.WxCUserBasicInfoMapper;
import com.iformall.mapper.WxCUserMapper;
import com.iformall.mapper.WxCardInfoMapper;
import com.iformall.mapper.WxCouponChannelMapper;
import com.iformall.mapper.WxCouponMapper;
import com.iformall.mapper.WxCouponMerchantMapper;
import com.iformall.mapper.WxCouponOrderMapper;
import com.iformall.mapper.WxCouponPasswordMapper;
import com.iformall.mapper.WxMallMapper;
import com.iformall.mapper.WxMerchantBUserMapper;
import com.iformall.mapper.WxMerchantMapper;
import com.iformall.mapper.WxOrderGroupMapper;
import com.iformall.mapper.WxOrderMapper;
import com.iformall.mapper.WxOrderPressMapper;
import com.iformall.mapper.WxPayAccountMapper;
import com.iformall.mapper.WxPayOrderMapper;
import com.iformall.mq.MqBaseProducer;
import com.iformall.service.*;
import com.iformall.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.iformall.service.ExcelService;
import com.iformall.service.WxAppinfoService;
import com.iformall.service.WxCUserService;
import com.iformall.service.WxCUserTagsService;
import com.iformall.service.WxCouponActionLogService;
import com.iformall.service.WxCouponSendService;
import com.iformall.service.WxCreditHistoryService;
import com.iformall.service.WxMsgLimitService;
import com.iformall.service.WxOrderService;
import com.iformall.service.WxPayOrderService;
import com.iformall.service.WxProfitSharingOrderService;
import com.iformall.service.WxScoreRulesService;
import com.iformall.utils.Constant;
import com.iformall.utils.DateUtils;
import com.iformall.utils.PayUtils;
import com.iformall.utils.PressUtils;
import com.iformall.utils.RedisLock;
import com.iformall.utils.Utility;

@Service
public class WxOrderServiceImpl implements WxOrderService {
@@ -479,94 +570,165 @@ public class WxOrderServiceImpl implements WxOrderService {
}
}


/**
* 减库存
*
* @param user
* @param coupon
* @param couponIdStr
*/
private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) {
long time = System.currentTimeMillis() + RedisLock.TIMEOUT;
String timeStr = String.valueOf(time);
// 库存加锁
if (!redisLock.lock(couponIdStr, timeStr)) {
logger.error("此券被锁定, couponId: " + couponIdStr);
throw new MallinkException(ErrorCode.TOO_MANY_REQUEST);
}

// 检查 优惠券 库存
if (coupon.getRemainInventory() <= 0) {
//解锁
redisLock.unlock(couponIdStr, timeStr);
logger.error("此券库存为0, couponId: " + couponIdStr);
throw new MallinkException(ErrorCode.REMAIN_IS_EMPTY);
}

int count = 0;
// 有价券
if (coupon.getSalePrice() > 0) {
// 检查是否未支付订单
try {
count = getUserOrderCount(user, coupon);
} catch (Exception e) {
//解锁
redisLock.unlock(couponIdStr, timeStr);
throw new MallinkException(ErrorCode.DB_FAIL);
}
if (count > 0) {
//解锁
redisLock.unlock(couponIdStr, timeStr);
logger.error("此券有未支付的订单, couponId: " + couponIdStr + ", count: " + count);
throw new MallinkException(ErrorCode.ORDER_UNPAID);
}
}
//检查拼团中的数量
try {
count = getUserOrderGroupCount(user, coupon);
} catch (Exception e) {
//解锁
redisLock.unlock(couponIdStr, timeStr);
throw new MallinkException(ErrorCode.DB_FAIL);
}
if (count >= coupon.getUseLimitQuantity()) {
//解锁
redisLock.unlock(couponIdStr, timeStr);
logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count);
throw new MallinkException(ErrorCode.ORDER_IS_LIMITED);
}

// 检查用户已购买数量
try {
count = getUserCouponOrderCount(user, coupon);
} catch (Exception e) {
//解锁
redisLock.unlock(couponIdStr, timeStr);
throw new MallinkException(ErrorCode.DB_FAIL);
}
if (count >= coupon.getUseLimitQuantity()) {
//解锁
redisLock.unlock(couponIdStr, timeStr);
logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count);
throw new MallinkException(ErrorCode.ORDER_IS_LIMITED);
}

try {
// 减库存
int num = wxCouponMapper.reduceInventory(coupon.getId(), 1, coupon.getRemainInventory());
if (num <= 0) {
logger.error("此券减库存失败, couponId: " + couponIdStr);
throw new MallinkException(ErrorCode.ORDER_IS_FAIL);
}
} catch (RuntimeException e) {
logger.error("此券减库存失败, couponId: " + couponIdStr);
throw new MallinkException(ErrorCode.ORDER_IS_FAIL);
} finally {
// 解锁
redisLock.unlock(couponIdStr, timeStr);
}
}
* 减库存
*
* @param user
* @param coupon
* @param couponIdStr
*/
private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) {
long redisStock = redisLock.getCouponStock(Long.parseLong(couponIdStr));
if (redisStock<=0) {
throw new MallinkException(ErrorCode.ORDER_IS_FAIL.getCode(),"券库存已为零!");
}
// 检查 优惠券 库存
if (coupon.getRemainInventory() <= 0) {
throw new MallinkException(ErrorCode.REMAIN_IS_EMPTY);
}

int count = 0;
// 有价券
if (coupon.getSalePrice() > 0) {
// 检查是否未支付订单
try {
count = getUserOrderCount(user, coupon);
} catch (Exception e) {
throw new MallinkException(ErrorCode.DB_FAIL);
}
if (count > 0) {
logger.error("此券有未支付的订单, couponId: " + couponIdStr + ", count: " + count);
throw new MallinkException(ErrorCode.ORDER_UNPAID);
}
}
//检查拼团中的数量
try {
count = getUserOrderGroupCount(user, coupon);
} catch (Exception e) {
throw new MallinkException(ErrorCode.DB_FAIL);
}
if (count >= coupon.getUseLimitQuantity()) {
logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count);
throw new MallinkException(ErrorCode.ORDER_IS_LIMITED);
}

// 检查用户已购买数量
try {
count = getUserCouponOrderCount(user, coupon);
} catch (Exception e) {
throw new MallinkException(ErrorCode.DB_FAIL);
}
if (count >= coupon.getUseLimitQuantity()) {
logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count);
throw new MallinkException(ErrorCode.ORDER_IS_LIMITED);
}

try {
//减库存,从redis中取,然后发mq消息同步到数据库
long stock = redisLock.decrease(EnumCacheKey.COUPON_STOCK.getMessage()+coupon.getId(), 1);
if (stock <= 0 ) {
throw new MallinkException(ErrorCode.ORDER_IS_FAIL.getCode(),"券库存已为零!");
}
UpdateCouponStockMsg msg = new UpdateCouponStockMsg();
msg.setCouponId(coupon.getId());
msg.setNumber(1);
msg.setOperateType(UpdateCouponStockMsg.OPERATE_TYPE_DECREASE);
msg.setMsgType(EnumMsgRecordType.COUPON_STOCK.getCode());
mqBaseProducer.sendMessage(msg, EnumMsgMqTopic.STOCK.getCode(), "*", "decrease_stock_"+coupon.getId());
} catch (RuntimeException e) {
logger.error("此券减库存失败, couponId: " + couponIdStr);
throw new MallinkException(ErrorCode.ORDER_IS_FAIL);
}
}

// /**
// * 减库存
// *
// * @param user
// * @param coupon
// * @param couponIdStr
// */
// private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) {
// long time = System.currentTimeMillis() + RedisLock.TIMEOUT;
// String timeStr = String.valueOf(time);
// // 库存加锁
// if (!redisLock.lock(couponIdStr, timeStr)) {
// logger.error("此券被锁定, couponId: " + couponIdStr);
// throw new MallinkException(ErrorCode.TOO_MANY_REQUEST);
// }
//
// // 检查 优惠券 库存
// if (coupon.getRemainInventory() <= 0) {
// //解锁
// redisLock.unlock(couponIdStr, timeStr);
// logger.error("此券库存为0, couponId: " + couponIdStr);
// throw new MallinkException(ErrorCode.REMAIN_IS_EMPTY);
// }
//
// int count = 0;
// // 有价券
// if (coupon.getSalePrice() > 0) {
// // 检查是否未支付订单
// try {
// count = getUserOrderCount(user, coupon);
// } catch (Exception e) {
// //解锁
// redisLock.unlock(couponIdStr, timeStr);
// throw new MallinkException(ErrorCode.DB_FAIL);
// }
// if (count > 0) {
// //解锁
// redisLock.unlock(couponIdStr, timeStr);
// logger.error("此券有未支付的订单, couponId: " + couponIdStr + ", count: " + count);
// throw new MallinkException(ErrorCode.ORDER_UNPAID);
// }
// }
// //检查拼团中的数量
// try {
// count = getUserOrderGroupCount(user, coupon);
// } catch (Exception e) {
// //解锁
// redisLock.unlock(couponIdStr, timeStr);
// throw new MallinkException(ErrorCode.DB_FAIL);
// }
// if (count >= coupon.getUseLimitQuantity()) {
// //解锁
// redisLock.unlock(couponIdStr, timeStr);
// logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count);
// throw new MallinkException(ErrorCode.ORDER_IS_LIMITED);
// }
//
// // 检查用户已购买数量
// try {
// count = getUserCouponOrderCount(user, coupon);
// } catch (Exception e) {
// //解锁
// redisLock.unlock(couponIdStr, timeStr);
// throw new MallinkException(ErrorCode.DB_FAIL);
// }
// if (count >= coupon.getUseLimitQuantity()) {
// //解锁
// redisLock.unlock(couponIdStr, timeStr);
// logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count);
// throw new MallinkException(ErrorCode.ORDER_IS_LIMITED);
// }
//
// try {
// // 减库存
// int num = wxCouponMapper.reduceInventory(coupon.getId(), 1);
// if (num <= 0) {
// logger.error("此券减库存失败, couponId: " + couponIdStr);
// throw new MallinkException(ErrorCode.ORDER_IS_FAIL);
// }
// } catch (RuntimeException e) {
// logger.error("此券减库存失败, couponId: " + couponIdStr);
// throw new MallinkException(ErrorCode.ORDER_IS_FAIL);
// } finally {
// // 解锁
// redisLock.unlock(couponIdStr, timeStr);
// }
// }

@Override
public void stockBack(WxOrder updateOrder) {
@@ -575,7 +737,7 @@ public class WxOrderServiceImpl implements WxOrderService {
Long couponId = updateOrder.getProductId();
String couponIdStr = "" + couponId;
//加锁
long time = System.currentTimeMillis() + RedisLock.TIMEOUT;
long time = System.currentTimeMillis() + 300*RedisLock.TIMEOUT;
String timeStr = String.valueOf(time);

if (!redisLock.lock(couponIdStr, timeStr)) {
@@ -602,6 +764,7 @@ public class WxOrderServiceImpl implements WxOrderService {
logger.error("此券加库存失败, couponId: " + couponIdStr);
throw new MallinkException(ErrorCode.ORDER_IS_FAIL);
}
redisLock.increase(EnumCacheKey.COUPON_STOCK.getMessage()+coupon.getId(), 1);
} catch (Exception e) {
logger.error("数据库更新失败,库存+1失败, e:" + e.getMessage());
throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "库存恢复失败");
@@ -1717,6 +1880,7 @@ public class WxOrderServiceImpl implements WxOrderService {
throw new MallinkException(ErrorCode.SYS_REPEAT_SUBMIT_EXCEPTION);
}
cUserTokenRedisTemplate.opsForValue().set(key, user, 1000, TimeUnit.MILLISECONDS);
order = saveFreeOrderForCoupon(user, coupon, orderSaveDto.getCouponChannelId(), orderSaveDto.getFormId(), null);
if (order != null) {
// 6. 下订单完成,发送内部消息


+ 35
- 0
mallinkService/src/main/java/com/iformall/service/msg/impl/UpdateCouponStockMsgServiceImpl.java Прегледај датотеку

@@ -0,0 +1,35 @@
package com.iformall.service.msg.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.iformall.domain.po.msg.BaseMsg;
import com.iformall.domain.po.msg.UpdateCouponStockMsg;
import com.iformall.enums.EnumPayWay;
import com.iformall.service.WxCouponService;
import com.iformall.service.msg.MsgSendService;

/**
*
* @author Stormeye Wu
* @date 2019/5/10
*/
@Service
public class UpdateCouponStockMsgServiceImpl implements MsgSendService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired
private WxCouponService wxCouponService;

@Override
public void send(BaseMsg baseMsg) throws Exception {
UpdateCouponStockMsg msg = (UpdateCouponStockMsg)baseMsg;
if (null != msg ) {
if (msg.getOperateType().equals(UpdateCouponStockMsg.OPERATE_TYPE_DECREASE)) {
wxCouponService.reduceRemainInventory(msg.getCouponId(), (int)msg.getNumber());
}
}
}
}

+ 48
- 2
mallinkService/src/main/java/com/iformall/utils/RedisLock.java Прегледај датотеку

@@ -8,6 +8,8 @@ import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.iformall.enums.EnumCacheKey;

import java.util.concurrent.TimeUnit;

/**
@@ -24,7 +26,7 @@ public class RedisLock {
private StringRedisTemplate stringRedisTemplate;

public static final int TIMEOUT = 10*1000;//超时时间 10s
/**
* 加锁
* @param key productId - 商品的唯一标志
@@ -102,5 +104,49 @@ public class RedisLock {
logger.error("[Redis分布式锁] 解锁出现异常了,{}",e);
}
}

/**
* 原子性增加
*/
public long increase(String key, long delta) {
try {
return stringRedisTemplate.opsForValue().increment(key, delta);
}catch(Exception e) {
logger.error("[Redis分布式锁] increase error,{}",e);
}
return 0;
}
/**
* 原子性减少
*/
public long decrease(String key, long delta) {
try {
stringRedisTemplate.opsForValue().decrement(key, delta);
}catch(Exception e) {
logger.error("[Redis分布式锁] decrease error,{}",e);
}
return 0;
}
public long getCouponStock(long couponId) {
try {
String longstr = stringRedisTemplate.opsForValue().get(EnumCacheKey.COUPON_STOCK.getMessage()+String.valueOf(couponId));
return Long.parseLong(longstr);
}catch (Exception e) {
logger.error("[Redis分布式锁] getCouponStock error,{}",e);
}
return 0;
}
public void setCouponStock(long couponId,long number) {
try {
stringRedisTemplate.opsForValue().set(EnumCacheKey.COUPON_STOCK.getMessage()+String.valueOf(couponId), String.valueOf(number));
}catch(Exception e) {
logger.error("[Redis分布式锁] setCouponStock error,{}",e);
}
}
}

Loading…
Откажи
Сачувај