最近做一个小小小小商城,有一个需求就是需要在订单24小时内未付款将订单取消的需求,本来想着弄个定时任务随便搞一搞就完事了,但是本着以用户体验至上的原则,还是好好做吧。

在Java的并发包里有一个java.util.concurrent.DelayQueue,要用这个队列必须要有一个类实现java.util.concurrent.Delayed接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Data
public class OrderCancelDelayedImpl implements Delayed {

public OrderCancelDelayedImpl(Long id, LocalDate executeTime) {
this.id = id;
this.executeTime = executeTime;
}

private Long id;
private LocalDateTime executeTime;

@Override
public long getDelay(TimeUnit unit) {
return executeTime.isBefore(LocalDateTime.now()) ? -1 : 1;
}

@Override
public int compareTo(Delayed o) {
if (this == o) {
return 0;
}
if (o instanceof OrderCancelDelayedImpl) {
return this.executeTime.compareTo(((OrderCancelDelayedImpl) o).getExecuteTime());
} else {
throw new RuntimeException("this is a error message");
}
}
}

主要实现两个方法:

  • getDelay:官方给的解释Returns the remaining delay associated with this object, in the given time unit.,简单来说就是用来判断什么时候出列。返回值大于0不出列,反之出列。
  • compareTo:这个没啥说的就是做个比较看看是不是放重了。

在这个类中有两个属性:

  • id:主要记录要取消那个订单。
  • executeTime:执行取消订单的时间,前期主要作用是判断执不执行。

其中的属性可以根据自己的需求来添加。

然后是创建一个一个队列

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 订单相关配置
* @author 阮雪峰
* @date 2018/11/1 12:09
*/
@Configuration
public class OrderConfig {

@Bean
public DelayQueue<OrderCancelDelayedImpl> delayQueue() {
return new DelayQueue<>();
}
}

然后是在什么时候将DelayedImpl放入队列,我的需求是确认订单后24小时未付款取消订单,需要在订单创建完成之后将订单信息放入队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Slf4j
@Service("orderService")
public class OrderServiceImpl{

//省略。。。

public void create(Order order) {
//省略其他代码
LocalDateTime now = LocalDateTime.now();
order.setCreateTime(now);
orderDao.insert(order);
delayQueue.put(new OrderCancelDelayedImpl(order.getId(), now));
}
}

然后我们需要启动一个线程来取消超过24小时未付款的订单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 取消订单线程
* @author 阮雪峰
* @date 2018/11/1 12:05
*/
@Slf4j
public class CancelOrderThread extends Thread {

@Resource(name = "orderService")
private OrderService orderService;

@Resource(name = "delayQueue")
private DelayQueue<OrderCancelDelayedImpl> delayQueue;

@Override
public void run() {
OrderCancelDelayedImpl delayed;
while (true) {
try {

delayed = delayQueue.take();
orderService.cancel(new Order(delayed.getOrderId()));
log.info("------ id为" + delayed.getOrderId() + "订单已被取消 ------");

} catch (InterruptedException e) {
if (delayed != null) {
log.error("------ id为" + delayed.getOrderId() + "订单取消失败 ------", e);
}
}
}
}
}

然后再OrderConfig注册CancelOrderThread

1
2
3
4
@Bean
public CancelOrderThread cancelOrderThread() {
return new CancelOrderThread();
}

最后则是在启动服务器的时候启动该线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 服务启动完成后开始启动取消订单线程
* @author 阮雪峰
* @date 2018/11/1 12:24
*/
@Slf4j
@Component
public class ServerStartedListenerCancelOrderThread implements ApplicationListener<ContextRefreshedEvent> {

@Resource(name = "cancelOrderThread")
private CancelOrderThread cancelOrderThread;

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
cancelOrderThread.start();
log.info("------ 取消订单线程开始工作 ------");
}
}
}