抽奖系统-抽奖
文章目录
- 前言
- 抽奖异步接口实现-MQ生产者
- 抽奖异步接口实现-MQ消费者
- 消费者& 校验抽奖请求
- 扭转状态思路
- 状态扭转实现1
- 扭转状态实现2
- 扭转状态实现3
- 扭转状态实现4
- 保存中奖记录
- 发送消息服务
- 抽奖回滚实现
- 测试
- 正向流程
- 消息回滚
- 消息重发
- 查询中奖记录
- 查询中奖记录
- 前端
- 项目部署
- 总结
前言
抽奖异步接口实现-MQ生产者
[请求] /draw-prize POST
{"winnerList":[{"userId":15,"userName":"胡⼀博"},{"userId":21,"userName":"范闲"}],"activityId":23,"prizeId":13,"prizeTiers":"FIRST_PRIZE","winningTime":"2024-05-21T11:55:10.000Z"
}
[响应]
{"code": 200,"data": true,"msg": ""}
controller
service:把数据丢给MQ
测试
抽奖异步接口实现-MQ消费者
消费者& 校验抽奖请求
mapper
扭转状态思路
一个奖品的数量是一次性全部抽取完的,而且一个人只能抽取一个奖品
人员数量大于奖品数量
状态扭转实现1
扭转状态实现2
扭转状态实现3
活动状态改变
奖品状态改变
活动状态扭转
保存为缓存
扭转状态实现4
然后开始测试
然后就是并没有问题
保存中奖记录
测试OK
发送消息服务
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
</dependency>
还有配置
## 邮件 ##
spring.mail.host=smtp.qq.com
spring.mail.username=发送者邮箱
# 你的授权码:邮箱设置-》第三⽅服务-》开启IMAP/SMTP服务-》获取授权码
spring.mail.password=你的授权码
spring.mail.default-encoding=UTF-8
然后是配置一个util
@Component
@Slf4j
public class MailUtil {private static final Logger logger =LoggerFactory.getLogger(MailUtil.class);@Value(value = "${spring.mail.username}")private String from;@Autowiredprivate JavaMailSender mailSender;public Boolean sendSampleMail(String to, String subject, String context) {SimpleMailMessage message = new SimpleMailMessage();message.setFrom(from);message.setTo(to);message.setSubject(subject);message.setText(context);try {mailSender.send(message);} catch (Exception e) {logger.error("向{}发送邮件失败!", to, e);return false;}return true;}
}
然后开始实现中奖的发送信息
我们异步发送邮箱和手机短信,由于手机号短信我用不了,所以我们就发两次模拟异步发消息
异步发消息的意思就是多线程的意思
配置线程池
## 线程池 ##
async.executor.thread.core_pool_size=10
async.executor.thread.max_pool_size=20
async.executor.thread.queue_capacity=20
async.executor.thread.name.prefix=async-service-
然后是线程池配置类
@Configuration
@EnableAsync//启动异步的方法
@Data
public class ExecutorConfig {@Value("${async.executor.thread.core_pool_size}")private int corePoolSize;@Value("${async.executor.thread.max_pool_size}")private int maxPoolSize;@Value("${async.executor.thread.queue_capacity}")private int queueCapacity;@Value("${async.executor.thread.name.prefix}")private String namePrefix;@Bean(name = "asyncServiceExecutor")public ThreadPoolTaskExecutor asyncServiceExecutor(){ThreadPoolTaskExecutor threadPoolTaskExecutor = newThreadPoolTaskExecutor();threadPoolTaskExecutor.setCorePoolSize(corePoolSize);threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);threadPoolTaskExecutor.setQueueCapacity(queueCapacity);threadPoolTaskExecutor.setKeepAliveSeconds(3);threadPoolTaskExecutor.setThreadNamePrefix(namePrefix);// rejection-policy:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执⾏任务,⽽是由调⽤者所在的线程来执⾏threadPoolTaskExecutor.setRejectedExecutionHandler(newThreadPoolExecutor.AbortPolicy());//加载threadPoolTaskExecutor.initialize();return threadPoolTaskExecutor;}
}
抽奖回滚实现
就是发送了异常后,要把以前的处理给恢复,比如扭转回去状态
然后就是保存中奖者名单的时候,要删去保存的名单
发送短信那里,没有对数据库进行修改,所以不要抛出异常和回滚
因为状态扭转的时候是用了事务的
所以奖品和人员的状态要么一起变,要么一起不变,如果变了的话,就要回滚状态了
奖品状态是否需要改变:
改变奖品状态
中奖状态是否需要改变:
回滚中奖者信息
然后就是开始测试了
测试
正向流程
修改一下,这里的校验参数这里不用抛异常,这里只是为了检验参数而已,看要不要进行抽奖,防止多来几次请求,如果传进来的参数不对,那么就false
真正要抛异常的地方是那些对数据库进行修改的地方
这样对参数进行校验,也可以保证幂等性了,多次请求结果一样,不然第二次一样请求就报错了,然后回滚,相当于都白干了
消息回滚
直接new一个异常
什么都没有
而且有六个异常,说明重发了
注意邮箱的配置也要修改一下,运行不起来了
消息重发
就是定义死信队列
就是定义死信队列,交换机,和对应的绑定
然后是普通队列把消息转到死信队列
这样死信队列就有数据了
注意记得把原来的额普通队列删了,因为属性改变了
然后死信队列就有消息了,然后给死信队列也搞一个消费者,进行相关处理之后,比如把这种不能处理的消息存入数据库,或者干嘛,这里我们就不处理了,最后重新发给普通队列的,让普通队列的消费者消化
查询中奖记录
[请求] /winning-records/show POST
{"activityId":23
}
[响应]
{"code": 200,"data": [{"winnerId": 15,"winnerName": "胡⼀博","prizeName": "华为⼿机","prizeTier": "⼀等奖","winningTime": "2024-05-21T11:55:10.000+00:00"},{"winnerId": 21,"winnerName": "范闲","prizeName": "华为⼿机","prizeTier": "⼀等奖","winningTime": "2024-05-21T11:55:10.000+00:00"}],"msg": ""
}
查询中奖记录
前端
注意改一下对应的属性,因为和前端对不上
比如在这里
就会直接跳过这个抽取的界面
然后就是修修改改代码
注意不同方法可以换行,便于调试
然后就可以成功了
项目部署
记得改mysql密码,图片地址,redis端口等等
然后就是打包的时候过滤掉测试部分
<maven.test.skip>true</maven.test.skip>这个就表示打包跳过测试
执行sql脚本
mysql -u root -p
source 绝对路径下的sql文件