概述
抢红包功能作为几大高并发场景中典型,应该如何实现?
分析
参考微信抢红包功能,将抢红包分成一下几个步骤:
- 发红包;主要填写红包信息,生成红包记录
- 红包支付回调;用户发红包支付成功后,收到微信支付付款成功的回调,生成指定数量的红包。
- 抢红包;用户并发抢红包。
- 拆红包;记录用户抢红包记录,转账抢到的红包金额。
效果展示
项目使用sessionId模拟用户,示例打开俩个浏览器窗口模拟两个用户。
设计开发
表结构设计
红包记录在 redpacket
表中,用户领取红包详情记录在 redpacket_detail
表中。
CREATE DATABASE `redpacket`;
use `redpacket`;
CREATE TABLE `redpacket`.`redpacket` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`packet_no` varchar(32) NOT NULL COMMENT '订单号',
`amount` decimal(5,2) NOT NULL COMMENT '红包金额最高10000.00元',
`num` int(11) NOT NULL COMMENT '红包数量',
`order_status` int(4) NOT NULL DEFAULT '0' COMMENT '订单状态:0初始、1待支付、2支付成功、3取消',
`pay_seq` varchar(32) DEFAULT NULL COMMENT '支付流水号',
`create_time` datetime NOT NULL COMMENT '创建时间',
`user_id` varchar(32) NOT NULL COMMENT '用户ID',
`update_time` datetime NOT NULL COMMENT '更新时间',
`pay_time` datetime DEFAULT NULL COMMENT '支付时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='红包订单表';
CREATE TABLE `redpacket`.`redpacket_detail` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`packet_id` bigint(20) NOT NULL COMMENT '红包ID',
`amount` decimal(5,2) NOT NULL COMMENT '红包金额',
`received` int(1) NOT NULL DEFAULT '0' COMMENT '是否领取0未领取、1已领取',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
`user_id` varchar(32) DEFAULT NULL COMMENT '领取用户',
`packet_no` varchar(32) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='红包详情表';
复制代码
发红包设计
用户需要填写红包金额、红包数量、备注信息等,生成红包记录,微信收银台下单,返回用户支付。
public RedPacket generateRedPacket(ReqSendRedPacketsVO data,String userId) {
final BigDecimal amount = data.getAmount();
//红包数量
final Integer num = data.getNum();
//初始化订单
final RedPacket redPacket = new RedPacket();
redPacket.setPacketNo(UUID.randomUUID().toString().replace("-", ""));
redPacket.setAmount(amount);
redPacket.setNum(num);
redPacket.setUserId(userId);
Date now = new Date();
redPacket.setCreateTime(now);
redPacket.setUpdateTime(now);
int i = redPacketMapper.insertSelective(redPacket);
if (i != 1) {
throw new ServiceException("生成红包出错", ExceptionType.SYS_ERR);
}
//模拟收银台下单
String paySeq = UUID.randomUUID().toString().replace("-", "");
//拿到收银台下单结果,更新订单为待支付状态
redPacket.setOrderStatus(1);//待支付
redPacket.setPaySeq(paySeq);
i = redPacketMapper.updateByPrimaryKeySelective(redPacket);
if (i