学习记录:使用SpringTask定时处理超时未支付订单-demo

33 阅读4分钟

背景

依旧是项目中的一个小功能,不过面试中被经常问及,于是就决定自己提取出来,做一个小demo。

一、简单理解

SpringTask是Spring提供的一种轻量级定时任务调度框架,可以用来定时或者周期性的执行一些方法。使用的场景很多,比如每天定时统计数据、定时清理无效数据、超时订单自动取消等。

二、实现步骤

1、环境搞好

创建Spring Boot项目,配置好开始依赖

Snipaste_2025-12-12_10-10-34.png

简单建订单表

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接口测试工具发出五条下单请求,分别为五个不同用户。间隔随机时间请求,观察控制台执行情况和表数据变化情况。 Snipaste_2026-01-02_17-57-59.png 可以看到,测试是成功的。同时注意到,订单取消时间并不是准确的一分钟取消,而是由定时任务执行时间决定,这是因为使用cron表达式是“对齐系统时间刻度的”,它只是在规定的周期时间内执行。

四、总结

本篇实现的是一个很简单的功能,所以篇幅较短。实际业务场景中使用SpringTask肯定要结合多方面来考虑,比如超时订单取消后,如果有库存值就要回调库存,这里就牵扯到了事务问题;比如并发下的用户同一时间下单和订单超时取消该如何处理等等。

最后

声明:本文仅作为学习记录分享使用,内容基于作者当前技术认知整理,如需要参考请自行决定价值度,若有不准确或描述不清之处,欢迎评论区指正,我会在能力范围内修正完善,谢谢阅读!🙏

作者说:其实此篇应该完稿在上个月中旬左右,不过因本人大四求职问题一直拖到现在,额,更多的是自己懒的原因吧。上一篇的文章我本都不抱有被人阅读的想法,结果到现在也有几十的阅读量,应该是平台的保底机制发力了,不管怎么样,内心还是很开心的,水平由读者定义,价值是自我的肯定。

本篇还有一些实践过程中碰到的问题和学习收获,这个我打算做一个续篇。

最近读到一些鸡汤分享一下:做事不等待,想到就去做(对付拖延)、不做完美主义者,快速行动(没有最充分的准备,实践认识再实践)