背景
依旧是项目中的一个小功能,不过面试中被经常问及,于是就决定自己提取出来,做一个小demo。
一、简单理解
SpringTask是Spring提供的一种轻量级定时任务调度框架,可以用来定时或者周期性的执行一些方法。使用的场景很多,比如每天定时统计数据、定时清理无效数据、超时订单自动取消等。
二、实现步骤
1、环境搞好
创建Spring Boot项目,配置好开始依赖
简单建订单表
create table orders
(
id int auto_increment comment '订单id主键'
primary key,
status int default 1 not null comment '订单状态 1待付款,2待接单,3已取消',
number varchar(50) null comment '订单号',
user_id int not null comment '下单用户',
order_time datetime not null comment '下单时间',
checkout_time datetime null comment '结账时间',
cancel_reason varchar(255) null comment '订单取消原因',
cancel_time datetime null comment '订单取消时间'
)
comment '基于SpringTask的超时订单取消的订单demo表';
配置yml文件
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/my_blog_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false
username: #写你的数据库用户名
password: #写你的数据库密码
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
map-underscore-to-camel-case: true
定义实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Orders {
//标准的javabean
//私有属性
//全参和无参构造
//公共的get和set方法
//订单id
private Integer id;
//订单状态
private Integer status;
//订单号
private String number;
//下单用户id
private Integer userId;
//下单时间
private LocalDateTime orderTime;
//结账时间
private LocalDateTime checkoutTime;
//订单取消原因
private String cancelReason;
//订单取消时间
private LocalDateTime cancelTime;
}
2、搭建三层结构
Controller层
@Slf4j
@RestController
@RequestMapping("/user/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 用户下单
* @param userId
* @return
*/
@PostMapping("/submit/{userId}")
public String sumbit(@PathVariable Integer userId){
log.info("用户下单:{}", userId);
orderService.sumbit(userId);
return "sucess";
}
}
Service层
//接口
public interface OrderService {
void sumbit(Integer userId);
}
//实现类
@Service
public class OrderServiceImpl implements OrderService{
@Autowired
private OrderMapper orderMapper;
@Override
public void sumbit(Integer userId) {
//创建订单
Orders order = new Orders();
order.setStatus(1);//设置订单状态
order.setNumber(String.valueOf(System.currentTimeMillis()));//设置订单号
order.setUserId(userId);//设置订单用户id
order.setOrderTime(LocalDateTime.now());//设置下单时间
orderMapper.insert(order);
}
}
mapper层
@Mapper
public interface OrderMapper {
@Insert("insert into orders (status, number, user_id, order_time) " +
"values (#{status}, #{number}, #{userId}, #{orderTime})")
void insert(Orders order);
}
可以看出代码非常的简陋啊🥲
3、定义定时任务方法
第一步:在启动类上添加@EnableScheduling注解
第二步:定义定时任务类
@Component
@Slf4j
public class SpringTask {
@Autowired
private OrderMapper orderMapper;
@Scheduled(cron = "*/20 * * * * ?")
public void method(){
log.info("周期处理支付超时订单,当前时间:{}", LocalDateTime.now());
//获取超时订单(这里在SQL语句中设置超时时间为30分钟)
List<Orders> timeoutOrders = orderMapper.getTimeoutOrders(1);
//取消超时订单
timeoutOrders.forEach(order -> {
log.info("取消订单:{}", order.getId());
order.setStatus(3);
order.setCancelReason("订单支付超时,订单取消");
order.setCancelTime(LocalDateTime.now());
orderMapper.update(order);
});
}
}
第三步:在OrderMapper添加方法
@Update("update orders set status = #{status},cancel_reason = #{cancelReason},cancel_time = #{cancelTime} " +
"where id = #{id}" )
void update(Orders order);
@Select("select * from orders where status = #{status} and order_time < date_sub(now(), interval 1 minute)")
List<Orders> getTimeoutOrders(Integer status);
三、测试
为了测试方便,将订单超时时间设置为一分钟,定时任务执行频率为10秒一次。
开启项目,使用Apifox接口测试工具发出五条下单请求,分别为五个不同用户。间隔随机时间请求,观察控制台执行情况和表数据变化情况。
可以看到,测试是成功的。同时注意到,订单取消时间并不是准确的一分钟取消,而是由定时任务执行时间决定,这是因为使用cron表达式是“对齐系统时间刻度的”,它只是在规定的周期时间内执行。
四、总结
本篇实现的是一个很简单的功能,所以篇幅较短。实际业务场景中使用SpringTask肯定要结合多方面来考虑,比如超时订单取消后,如果有库存值就要回调库存,这里就牵扯到了事务问题;比如并发下的用户同一时间下单和订单超时取消该如何处理等等。
最后
声明:本文仅作为学习记录分享使用,内容基于作者当前技术认知整理,如需要参考请自行决定价值度,若有不准确或描述不清之处,欢迎评论区指正,我会在能力范围内修正完善,谢谢阅读!🙏
作者说:其实此篇应该完稿在上个月中旬左右,不过因本人大四求职问题一直拖到现在,额,更多的是自己懒的原因吧。上一篇的文章我本都不抱有被人阅读的想法,结果到现在也有几十的阅读量,应该是平台的保底机制发力了,不管怎么样,内心还是很开心的,水平由读者定义,价值是自我的肯定。
本篇还有一些实践过程中碰到的问题和学习收获,这个我打算做一个续篇。
最近读到一些鸡汤分享一下:做事不等待,想到就去做(对付拖延)、不做完美主义者,快速行动(没有最充分的准备,实践认识再实践)