秒杀下单接口设计
1、新建秒杀产品,设置秒杀产品属性:秒杀开始时间、秒杀结束时间、秒杀价格、秒杀库存等属性
2、秒杀开始前10分钟,将秒杀产品信息同步到Redis
3、秒杀开始,根据秒杀时间及秒杀实时库存判断秒杀是否成功
4、定时更新秒杀时间结束、秒杀库存售罄的秒杀产品状态
/**
*
* @Description: 秒杀订单
*
* @author [ wenfengSAT@163.com ] on [2020年4月11日下午4:19:43]
* @Modified By: [修改人] on [修改日期] for [修改说明]
*
*/
@Slf4j
@Service(version = "1.0.0")
public class SeckillOrderImpl implements SeckillOrderService {
@Autowired
private SeckillComponentService seckillComponentService;
/**
*
* @Description:秒杀下单
*
* @author [ wenfengSAT@163.com ]
* @Date [2020年4月11日下午4:25:54]
* @param req
* @return
*
*/
@Override
public CreateSeckillOrderResp createSeckillOrder(CreateSeckillOrderReq req) {
seckillComponentService.createBlockingQueue().add(req);
CompletableFuture<CreateSeckillOrderResp> future = CompletableFuture.supplyAsync(() -> {
try {
return seckillComponentService.seckillDealResult();
} catch (Exception e) {
log.error("creatSeckillOrder error:[{}]", e);
return new CreateSeckillOrderResp(ErrorCode.CREATE_SECKILL_ORDER_FIAL);
}
});
try {
CreateSeckillOrderResp response = future.get();
return response;
} catch (Exception e) {
log.error("createSeckillOrder error:[{}]", e);
return new CreateSeckillOrderResp(ErrorCode.CREATE_SECKILL_ORDER_FIAL);
}
}
}
/**
*
* @Description: 秒杀组件
*
* @author [ wenfengSAT@163.com ] on [2020年4月11日下午4:15:24]
* @Modified By: [修改人] on [修改日期] for [修改说明]
*
*/
@Slf4j
@Service
public class SeckillComponentService {
private static final BlockingQueue<CreateSeckillOrderReq> queue = new LinkedBlockingQueue<CreateSeckillOrderReq>();
public BlockingQueue<CreateSeckillOrderReq> createBlockingQueue() {
return queue;
}
@Autowired
private MapperFactory mapperFactory;
@Autowired
private OrderService orderService;
/**
*
* @Description: 秒杀处理结果
*
* @author [ wenfengSAT@163.com ]
* @Date [2020年4月12日上午10:02:29]
* @return
* @throws Exception
*
*/
@Transactional
public CreateSeckillOrderResp seckillDealResult() throws Exception {
log.debug("seckillDealResult start.");
if (CollUtil.isNotEmpty(queue)) {
CreateSeckillOrderReq req = queue.take();
return creatOrder(req);
}
return new CreateSeckillOrderResp(ErrorCode.CREATE_SECKILL_ORDER_FIAL);
}
/**
*
* @Description: 创建订单
*
* @author [ wenfengSAT@163.com ]
* @Date [2020年4月11日下午4:53:39]
* @param req
* @return
*
*/
public CreateSeckillOrderResp creatOrder(CreateSeckillOrderReq req) {
CreatSeckillOrderReqBody body = req.getBody();
String userId = mapperFactory.getApiTokenUtil().getTokenMsgByKey(req.getTspReqheader().getToken(),
ApiTokenUtil.AUTH_TOKEN_ACCTID_KEY);
boolean validateFlag = validateSign(body);
if (!validateFlag) {
return new CreateSeckillOrderResp(ErrorCode.SIGN_VALIDATE_ERROR);
}
String productId = body.getProductId();
String seckillId = body.getSeckillId();
int selNumber = Integer.parseInt(body.getSelNumber());
body.setUserId(userId);
ProductAttr productAttr = mapperFactory.getProductAttrMapper().queryProductByProductId(productId);
if (Objects.isNull(productAttr)) {
return new CreateSeckillOrderResp(ErrorCode.CREATE_ORDER_ERROR);
}
// 检查库存
String seckillStockKey = SeckillConst.seckillStockKey + seckillId + SeckillConst.UNDERLINE_SYMBOL
+ productAttr.getProductCode();
boolean checkStockFlag = decrStock(seckillStockKey, selNumber);
if (!checkStockFlag) {
return new CreateSeckillOrderResp(ErrorCode.PRODUCT_STOCK_IS_ZERO);
}
String orderNo = IdUtil.getSnowflake(0, 0).nextIdStr();
Date now = new Date();
// 存储订单主表信息
CreateOrderReqBodyMsg msg = BeanConvertUtils.convert(body, CreateOrderReqBodyMsg.class);
int result = orderService.saveOrderHeader(msg, orderNo, now, productAttr,
OrderSourceEnum.SECKILL_ORDER.getValue());
String orderDetailId = IdUtil.getSnowflake(0, 0).nextIdStr();
// 存储订单明细信息
int resultDetail = orderService.saveOrderDetails(orderNo, now, productAttr, orderDetailId, msg.getSelNumber());
// 存储订单扩展属性信息
int resultDetailSpec = orderService.saveOrderDetailSpec(msg, productAttr, orderDetailId);
// 保存订单-秒杀活动关系
saveSeckillOrder(orderNo, seckillId);
if (result < 1 && resultDetail < 1 && resultDetailSpec < 1) {
return new CreateSeckillOrderResp(ErrorCode.CREATE_ORDER_ERROR);
}
CreateSeckillOrderResp resp = new CreateSeckillOrderResp();
CreateOrderRespBodyMsg createOrderResult = new CreateOrderRespBodyMsg();
createOrderResult.setOrderNo(orderNo);
resp.setCreateOrderRespBodyMsg(createOrderResult);
return resp;
}
/**
*
* @Description: 保存订单-秒杀活动关系
*
* @author [ wenfengSAT@163.com ]
* @Date [2020年7月5日下午2:56:45]
* @param orderId
* @param seckillId
*
*/
public void saveSeckillOrder(String orderId, String seckillId) {
Map<String, String> param = new HashMap<String, String>();
param.put("orderId", orderId);
param.put("seckillId", seckillId);
mapperFactory.getOrderHeaderMapper().insertSeckillOrder(param);
}
/**
*
* @Description: 扣减库存
*
* @author [ wenfengSAT@163.com ]
* @Date [2020年4月12日下午2:42:45]
* @param key-库存key
* @param num-扣减库存数量
* @return
*
*/
public boolean decrStock(String key, int num) {
int operatNum = 0;
for (int i = 0; i < num; i++) {
long value = mapperFactory.getRedisService().decr(key);
if (value < 0) {
mapperFactory.getRedisService().incr(key);
break;
}
operatNum++;
}
if (operatNum < num) {
resetStock(key, operatNum);
return false;
}
return true;
}
/**
*
* @Description: 还原秒杀库存
*
* @author [ wenfengSAT@163.com ]
* @Date [2020年4月12日下午2:42:18]
* @param key-库存key
* @param num-还原库存数量
*
*/
public void resetStock(String key, int num) {
IntStream.range(0, num).forEach(index -> {
mapperFactory.getRedisService().incr(key);
});
}
/**
*
* @Description: 验签
*
* @author [ wenfengSAT@163.com ]
* @Date [2020年4月12日上午10:40:55]
* @param body
* @return
*
*/
public boolean validateSign(CreatSeckillOrderReqBody body) {
String productId = body.getProductId();
String productSelPrice = body.getProductSelPrice();
String userId = body.getUserId();
String selNumber = body.getSelNumber();
String productName = body.getProductName();
String carNO = body.getCarNO();
String isPayIntegral = body.getIsPayIntegral();
String sign = body.getSign();
Digester sha1 = new Digester(DigestAlgorithm.SHA256);
StringBuffer sb = new StringBuffer();
sb.append("productId").append("=").append(productId);
sb.append("&").append("userId").append("=").append(userId);
sb.append("&").append("productName").append("=").append(productName);
sb.append("&").append("productSelPrice").append("=").append(productSelPrice);
sb.append("&").append("selNumber").append("=").append(selNumber);
if (sha1.digestHex(sb.toString()).equals(sign)) {
return true;
}
return false;
}
}