前言

订单超时自动取消作为电商系统的核心功能之一,直接影响着库存管理和用户体验。通过合理的订单超时处理策略和定时任务,能够构建一个高效、稳定、可扩展的订单超时取消系统,确保系统的稳定运行。本文从订单超时处理策略到定时任务,从基础实现到企业级应用,系统梳理订单超时自动取消的完整解决方案。

一、订单超时自动取消架构设计

1.1 订单超时处理整体架构

1.2 订单超时处理策略架构

二、订单超时处理策略实现

2.1 定时任务扫描策略

2.1.1 定时任务扫描服务

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
* 订单超时定时任务扫描服务
*/
@Service
public class OrderTimeoutScanService {

@Autowired
private OrderRepository orderRepository;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private RabbitTemplate rabbitTemplate;

@Autowired
private InventoryService inventoryService;

@Autowired
private NotificationService notificationService;

private final String ORDER_TIMEOUT_CACHE_PREFIX = "order_timeout:";
private final int BATCH_SIZE = 100; // 批量处理大小
private final int TIMEOUT_MINUTES = 30; // 超时时间30分钟

/**
* 定时扫描超时订单
*/
@Scheduled(fixedDelay = 60000) // 每分钟执行一次
public void scanTimeoutOrders() {
try {
log.info("开始扫描超时订单");

// 1. 查询超时订单
List<Order> timeoutOrders = findTimeoutOrders();

if (timeoutOrders.isEmpty()) {
log.info("没有发现超时订单");
return;
}

log.info("发现{}个超时订单", timeoutOrders.size());

// 2. 批量处理超时订单
batchProcessTimeoutOrders(timeoutOrders);

log.info("超时订单扫描完成");

} catch (Exception e) {
log.error("扫描超时订单失败", e);
}
}

/**
* 查询超时订单
*/
private List<Order> findTimeoutOrders() {
try {
// 计算超时时间点
LocalDateTime timeoutTime = LocalDateTime.now().minusMinutes(TIMEOUT_MINUTES);

// 查询超时订单
List<Order> timeoutOrders = orderRepository.findTimeoutOrders(timeoutTime);

return timeoutOrders;

} catch (Exception e) {
log.error("查询超时订单失败", e);
return new ArrayList<>();
}
}

/**
* 批量处理超时订单
*/
private void batchProcessTimeoutOrders(List<Order> timeoutOrders) {
try {
// 分批处理
for (int i = 0; i < timeoutOrders.size(); i += BATCH_SIZE) {
int endIndex = Math.min(i + BATCH_SIZE, timeoutOrders.size());
List<Order> batch = timeoutOrders.subList(i, endIndex);

// 异步处理每批订单
CompletableFuture.runAsync(() -> {
processBatchTimeoutOrders(batch);
});
}

} catch (Exception e) {
log.error("批量处理超时订单失败", e);
}
}

/**
* 处理一批超时订单
*/
private void processBatchTimeoutOrders(List<Order> orders) {
try {
for (Order order : orders) {
try {
processTimeoutOrder(order);
} catch (Exception e) {
log.error("处理超时订单失败: {}", order.getOrderId(), e);
}
}

} catch (Exception e) {
log.error("处理批量超时订单失败", e);
}
}

/**
* 处理单个超时订单
*/
private void processTimeoutOrder(Order order) {
try {
// 1. 检查订单状态
if (!isOrderTimeout(order)) {
return;
}

// 2. 更新订单状态
updateOrderStatus(order);

// 3. 释放库存
releaseInventory(order);

// 4. 发送通知
sendTimeoutNotification(order);

// 5. 记录日志
recordTimeoutLog(order);

log.info("处理超时订单成功: {}", order.getOrderId());

} catch (Exception e) {
log.error("处理超时订单失败: {}", order.getOrderId(), e);
}
}

/**
* 检查订单是否超时
*/
private boolean isOrderTimeout(Order order) {
try {
// 检查订单状态
if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
return false;
}

// 检查订单创建时间
LocalDateTime timeoutTime = LocalDateTime.now().minusMinutes(TIMEOUT_MINUTES);
if (order.getCreatedTime().isAfter(timeoutTime)) {
return false;
}

return true;

} catch (Exception e) {
log.error("检查订单超时状态失败", e);
return false;
}
}

/**
* 更新订单状态
*/
private void updateOrderStatus(Order order) {
try {
// 更新订单状态为已取消
order.setStatus(OrderStatus.CANCELLED);
order.setCancelTime(LocalDateTime.now());
order.setCancelReason("订单超时自动取消");
order.setUpdatedTime(LocalDateTime.now());

// 保存订单
orderRepository.save(order);

// 更新缓存
updateOrderCache(order);

} catch (Exception e) {
log.error("更新订单状态失败", e);
throw new OrderTimeoutException("更新订单状态失败", e);
}
}

/**
* 释放库存
*/
private void releaseInventory(Order order) {
try {
// 异步释放库存
CompletableFuture.runAsync(() -> {
try {
inventoryService.releaseInventory(order.getOrderId());
log.info("释放库存成功: {}", order.getOrderId());
} catch (Exception e) {
log.error("释放库存失败: {}", order.getOrderId(), e);
}
});

} catch (Exception e) {
log.error("释放库存失败", e);
}
}

/**
* 发送超时通知
*/
private void sendTimeoutNotification(Order order) {
try {
// 构建通知消息
OrderTimeoutNotification notification = new OrderTimeoutNotification();
notification.setOrderId(order.getOrderId());
notification.setUserId(order.getUserId());
notification.setOrderAmount(order.getAmount());
notification.setTimeoutTime(LocalDateTime.now());
notification.setNotificationType("ORDER_TIMEOUT");

// 发送到消息队列
rabbitTemplate.convertAndSend("order.timeout.notification.queue", notification);

} catch (Exception e) {
log.error("发送超时通知失败", e);
}
}

/**
* 记录超时日志
*/
private void recordTimeoutLog(Order order) {
try {
OrderTimeoutLog timeoutLog = new OrderTimeoutLog();
timeoutLog.setOrderId(order.getOrderId());
timeoutLog.setUserId(order.getUserId());
timeoutLog.setOrderAmount(order.getAmount());
timeoutLog.setTimeoutTime(LocalDateTime.now());
timeoutLog.setProcessTime(LocalDateTime.now());

// 异步记录日志
CompletableFuture.runAsync(() -> {
try {
orderRepository.saveTimeoutLog(timeoutLog);
} catch (Exception e) {
log.error("记录超时日志失败", e);
}
});

} catch (Exception e) {
log.error("记录超时日志失败", e);
}
}

/**
* 更新订单缓存
*/
private void updateOrderCache(Order order) {
try {
String cacheKey = ORDER_TIMEOUT_CACHE_PREFIX + order.getOrderId();
redisTemplate.opsForValue().set(cacheKey, order, Duration.ofHours(1));

} catch (Exception e) {
log.error("更新订单缓存失败", e);
}
}
}

2.2 延迟队列策略

2.2.1 延迟队列服务

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/**
* 订单超时延迟队列服务
*/
@Service
public class OrderTimeoutDelayQueueService {

@Autowired
private RabbitTemplate rabbitTemplate;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private OrderRepository orderRepository;

private final String ORDER_TIMEOUT_DELAY_QUEUE = "order.timeout.delay.queue";
private final String ORDER_TIMEOUT_CACHE_PREFIX = "order_timeout_delay:";
private final int TIMEOUT_MINUTES = 30; // 超时时间30分钟

/**
* 添加订单到延迟队列
*/
public void addOrderToDelayQueue(Order order) {
try {
// 1. 验证订单
validateOrder(order);

// 2. 构建延迟消息
OrderTimeoutDelayMessage message = buildDelayMessage(order);

// 3. 发送延迟消息
sendDelayMessage(message);

// 4. 记录延迟队列信息
recordDelayQueueInfo(order);

log.info("订单添加到延迟队列成功: {}", order.getOrderId());

} catch (Exception e) {
log.error("添加订单到延迟队列失败: {}", order.getOrderId(), e);
}
}

/**
* 验证订单
*/
private void validateOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("订单不能为空");
}

if (order.getOrderId() == null) {
throw new IllegalArgumentException("订单ID不能为空");
}

if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new IllegalArgumentException("订单状态不正确");
}
}

/**
* 构建延迟消息
*/
private OrderTimeoutDelayMessage buildDelayMessage(Order order) {
OrderTimeoutDelayMessage message = new OrderTimeoutDelayMessage();
message.setOrderId(order.getOrderId());
message.setUserId(order.getUserId());
message.setOrderAmount(order.getAmount());
message.setCreatedTime(order.getCreatedTime());
message.setTimeoutMinutes(TIMEOUT_MINUTES);
message.setMessageId(UUID.randomUUID().toString());
message.setTimestamp(new Date());

return message;
}

/**
* 发送延迟消息
*/
private void sendDelayMessage(OrderTimeoutDelayMessage message) {
try {
// 设置消息属性
MessageProperties properties = new MessageProperties();
properties.setExpiration(String.valueOf(TIMEOUT_MINUTES * 60 * 1000)); // 30分钟
properties.setMessageId(message.getMessageId());
properties.setTimestamp(new Date());

// 创建消息
Message rabbitMessage = new Message(JSON.toJSONBytes(message), properties);

// 发送到延迟队列
rabbitTemplate.send(ORDER_TIMEOUT_DELAY_QUEUE, rabbitMessage);

} catch (Exception e) {
log.error("发送延迟消息失败", e);
throw new OrderTimeoutException("发送延迟消息失败", e);
}
}

/**
* 记录延迟队列信息
*/
private void recordDelayQueueInfo(Order order) {
try {
String cacheKey = ORDER_TIMEOUT_CACHE_PREFIX + order.getOrderId();
OrderDelayQueueInfo queueInfo = new OrderDelayQueueInfo();
queueInfo.setOrderId(order.getOrderId());
queueInfo.setUserId(order.getUserId());
queueInfo.setAddTime(LocalDateTime.now());
queueInfo.setTimeoutTime(LocalDateTime.now().plusMinutes(TIMEOUT_MINUTES));
queueInfo.setStatus("PENDING");

redisTemplate.opsForValue().set(cacheKey, queueInfo, Duration.ofMinutes(TIMEOUT_MINUTES + 5));

} catch (Exception e) {
log.error("记录延迟队列信息失败", e);
}
}

/**
* 处理延迟队列消息
*/
@RabbitListener(queues = "order.timeout.delay.queue")
public void handleDelayMessage(OrderTimeoutDelayMessage message) {
try {
log.info("处理延迟队列消息: {}", message.getOrderId());

// 1. 检查订单状态
if (!isOrderStillPending(message.getOrderId())) {
log.info("订单状态已变更,跳过处理: {}", message.getOrderId());
return;
}

// 2. 处理超时订单
processTimeoutOrder(message);

// 3. 更新延迟队列信息
updateDelayQueueInfo(message.getOrderId());

log.info("延迟队列消息处理完成: {}", message.getOrderId());

} catch (Exception e) {
log.error("处理延迟队列消息失败: {}", message.getOrderId(), e);
}
}

/**
* 检查订单是否仍为待支付状态
*/
private boolean isOrderStillPending(String orderId) {
try {
Order order = orderRepository.findByOrderId(orderId);
return order != null && order.getStatus() == OrderStatus.PENDING_PAYMENT;

} catch (Exception e) {
log.error("检查订单状态失败", e);
return false;
}
}

/**
* 处理超时订单
*/
private void processTimeoutOrder(OrderTimeoutDelayMessage message) {
try {
// 1. 更新订单状态
updateOrderStatus(message.getOrderId());

// 2. 释放库存
releaseInventory(message.getOrderId());

// 3. 发送通知
sendTimeoutNotification(message);

// 4. 记录日志
recordTimeoutLog(message);

} catch (Exception e) {
log.error("处理超时订单失败", e);
}
}

/**
* 更新订单状态
*/
private void updateOrderStatus(String orderId) {
try {
Order order = orderRepository.findByOrderId(orderId);
if (order != null) {
order.setStatus(OrderStatus.CANCELLED);
order.setCancelTime(LocalDateTime.now());
order.setCancelReason("订单超时自动取消");
order.setUpdatedTime(LocalDateTime.now());

orderRepository.save(order);
}

} catch (Exception e) {
log.error("更新订单状态失败", e);
}
}

/**
* 释放库存
*/
private void releaseInventory(String orderId) {
try {
// 异步释放库存
CompletableFuture.runAsync(() -> {
try {
inventoryService.releaseInventory(orderId);
} catch (Exception e) {
log.error("释放库存失败", e);
}
});

} catch (Exception e) {
log.error("释放库存失败", e);
}
}

/**
* 发送超时通知
*/
private void sendTimeoutNotification(OrderTimeoutDelayMessage message) {
try {
OrderTimeoutNotification notification = new OrderTimeoutNotification();
notification.setOrderId(message.getOrderId());
notification.setUserId(message.getUserId());
notification.setOrderAmount(message.getOrderAmount());
notification.setTimeoutTime(LocalDateTime.now());
notification.setNotificationType("ORDER_TIMEOUT");

rabbitTemplate.convertAndSend("order.timeout.notification.queue", notification);

} catch (Exception e) {
log.error("发送超时通知失败", e);
}
}

/**
* 记录超时日志
*/
private void recordTimeoutLog(OrderTimeoutDelayMessage message) {
try {
OrderTimeoutLog timeoutLog = new OrderTimeoutLog();
timeoutLog.setOrderId(message.getOrderId());
timeoutLog.setUserId(message.getUserId());
timeoutLog.setOrderAmount(message.getOrderAmount());
timeoutLog.setTimeoutTime(LocalDateTime.now());
timeoutLog.setProcessTime(LocalDateTime.now());
timeoutLog.setProcessType("DELAY_QUEUE");

CompletableFuture.runAsync(() -> {
try {
orderRepository.saveTimeoutLog(timeoutLog);
} catch (Exception e) {
log.error("记录超时日志失败", e);
}
});

} catch (Exception e) {
log.error("记录超时日志失败", e);
}
}

/**
* 更新延迟队列信息
*/
private void updateDelayQueueInfo(String orderId) {
try {
String cacheKey = ORDER_TIMEOUT_CACHE_PREFIX + orderId;
OrderDelayQueueInfo queueInfo = (OrderDelayQueueInfo) redisTemplate.opsForValue().get(cacheKey);

if (queueInfo != null) {
queueInfo.setStatus("PROCESSED");
queueInfo.setProcessTime(LocalDateTime.now());
redisTemplate.opsForValue().set(cacheKey, queueInfo, Duration.ofHours(1));
}

} catch (Exception e) {
log.error("更新延迟队列信息失败", e);
}
}
}

2.3 Redis过期策略

2.3.1 Redis过期服务

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/**
* 订单超时Redis过期服务
*/
@Service
public class OrderTimeoutRedisService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private OrderRepository orderRepository;

@Autowired
private RabbitTemplate rabbitTemplate;

private final String ORDER_TIMEOUT_REDIS_PREFIX = "order_timeout_redis:";
private final int TIMEOUT_MINUTES = 30; // 超时时间30分钟

/**
* 设置订单超时
*/
public void setOrderTimeout(Order order) {
try {
// 1. 验证订单
validateOrder(order);

// 2. 设置Redis过期
setRedisExpiration(order);

// 3. 设置过期回调
setExpirationCallback(order);

log.info("设置订单超时成功: {}", order.getOrderId());

} catch (Exception e) {
log.error("设置订单超时失败: {}", order.getOrderId(), e);
}
}

/**
* 验证订单
*/
private void validateOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("订单不能为空");
}

if (order.getOrderId() == null) {
throw new IllegalArgumentException("订单ID不能为空");
}

if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new IllegalArgumentException("订单状态不正确");
}
}

/**
* 设置Redis过期
*/
private void setRedisExpiration(Order order) {
try {
String cacheKey = ORDER_TIMEOUT_REDIS_PREFIX + order.getOrderId();

// 存储订单信息
OrderTimeoutInfo timeoutInfo = new OrderTimeoutInfo();
timeoutInfo.setOrderId(order.getOrderId());
timeoutInfo.setUserId(order.getUserId());
timeoutInfo.setOrderAmount(order.getAmount());
timeoutInfo.setCreatedTime(order.getCreatedTime());
timeoutInfo.setTimeoutMinutes(TIMEOUT_MINUTES);

// 设置过期时间
redisTemplate.opsForValue().set(cacheKey, timeoutInfo, Duration.ofMinutes(TIMEOUT_MINUTES));

} catch (Exception e) {
log.error("设置Redis过期失败", e);
throw new OrderTimeoutException("设置Redis过期失败", e);
}
}

/**
* 设置过期回调
*/
private void setExpirationCallback(Order order) {
try {
// 使用Redis的键空间通知功能
// 需要在Redis配置中启用键空间通知: notify-keyspace-events Ex

String callbackKey = ORDER_TIMEOUT_REDIS_PREFIX + order.getOrderId();

// 监听键过期事件
redisTemplate.getConnectionFactory().getConnection().setConfig("notify-keyspace-events", "Ex");

} catch (Exception e) {
log.error("设置过期回调失败", e);
}
}

/**
* 处理Redis过期事件
*/
@EventListener
public void handleRedisExpiration(RedisKeyExpiredEvent event) {
try {
String expiredKey = new String(event.getKeyspace());

if (expiredKey.startsWith(ORDER_TIMEOUT_REDIS_PREFIX)) {
String orderId = expiredKey.substring(ORDER_TIMEOUT_REDIS_PREFIX.length());

log.info("检测到订单超时: {}", orderId);

// 处理超时订单
processTimeoutOrder(orderId);
}

} catch (Exception e) {
log.error("处理Redis过期事件失败", e);
}
}

/**
* 处理超时订单
*/
private void processTimeoutOrder(String orderId) {
try {
// 1. 检查订单状态
if (!isOrderStillPending(orderId)) {
log.info("订单状态已变更,跳过处理: {}", orderId);
return;
}

// 2. 更新订单状态
updateOrderStatus(orderId);

// 3. 释放库存
releaseInventory(orderId);

// 4. 发送通知
sendTimeoutNotification(orderId);

// 5. 记录日志
recordTimeoutLog(orderId);

log.info("处理超时订单完成: {}", orderId);

} catch (Exception e) {
log.error("处理超时订单失败: {}", orderId, e);
}
}

/**
* 检查订单是否仍为待支付状态
*/
private boolean isOrderStillPending(String orderId) {
try {
Order order = orderRepository.findByOrderId(orderId);
return order != null && order.getStatus() == OrderStatus.PENDING_PAYMENT;

} catch (Exception e) {
log.error("检查订单状态失败", e);
return false;
}
}

/**
* 更新订单状态
*/
private void updateOrderStatus(String orderId) {
try {
Order order = orderRepository.findByOrderId(orderId);
if (order != null) {
order.setStatus(OrderStatus.CANCELLED);
order.setCancelTime(LocalDateTime.now());
order.setCancelReason("订单超时自动取消");
order.setUpdatedTime(LocalDateTime.now());

orderRepository.save(order);
}

} catch (Exception e) {
log.error("更新订单状态失败", e);
}
}

/**
* 释放库存
*/
private void releaseInventory(String orderId) {
try {
CompletableFuture.runAsync(() -> {
try {
inventoryService.releaseInventory(orderId);
} catch (Exception e) {
log.error("释放库存失败", e);
}
});

} catch (Exception e) {
log.error("释放库存失败", e);
}
}

/**
* 发送超时通知
*/
private void sendTimeoutNotification(String orderId) {
try {
Order order = orderRepository.findByOrderId(orderId);
if (order != null) {
OrderTimeoutNotification notification = new OrderTimeoutNotification();
notification.setOrderId(orderId);
notification.setUserId(order.getUserId());
notification.setOrderAmount(order.getAmount());
notification.setTimeoutTime(LocalDateTime.now());
notification.setNotificationType("ORDER_TIMEOUT");

rabbitTemplate.convertAndSend("order.timeout.notification.queue", notification);
}

} catch (Exception e) {
log.error("发送超时通知失败", e);
}
}

/**
* 记录超时日志
*/
private void recordTimeoutLog(String orderId) {
try {
Order order = orderRepository.findByOrderId(orderId);
if (order != null) {
OrderTimeoutLog timeoutLog = new OrderTimeoutLog();
timeoutLog.setOrderId(orderId);
timeoutLog.setUserId(order.getUserId());
timeoutLog.setOrderAmount(order.getAmount());
timeoutLog.setTimeoutTime(LocalDateTime.now());
timeoutLog.setProcessTime(LocalDateTime.now());
timeoutLog.setProcessType("REDIS_EXPIRATION");

CompletableFuture.runAsync(() -> {
try {
orderRepository.saveTimeoutLog(timeoutLog);
} catch (Exception e) {
log.error("记录超时日志失败", e);
}
});
}

} catch (Exception e) {
log.error("记录超时日志失败", e);
}
}
}

三、企业级订单超时取消方案

3.1 订单超时管理服务

3.1.1 订单超时管理服务

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* 订单超时管理服务
*/
@Service
public class OrderTimeoutManagementService {

@Autowired
private OrderTimeoutScanService scanService;

@Autowired
private OrderTimeoutDelayQueueService delayQueueService;

@Autowired
private OrderTimeoutRedisService redisService;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final String ORDER_TIMEOUT_MANAGEMENT_CACHE_PREFIX = "order_timeout_management:";
private final long ORDER_TIMEOUT_MANAGEMENT_CACHE_EXPIRE = 3600; // 1小时

/**
* 智能选择超时处理策略
*/
public void processOrderTimeout(Order order, OrderTimeoutStrategy strategy) {
try {
// 1. 验证订单
validateOrder(order);

// 2. 选择处理策略
OrderTimeoutStrategy selectedStrategy = selectTimeoutStrategy(order, strategy);

// 3. 执行处理策略
executeTimeoutStrategy(order, selectedStrategy);

// 4. 记录处理统计
recordTimeoutStatistics(order, selectedStrategy);

log.info("订单超时处理完成: {}, 策略: {}", order.getOrderId(), selectedStrategy);

} catch (Exception e) {
log.error("订单超时处理失败: {}", order.getOrderId(), e);
}
}

/**
* 选择超时处理策略
*/
private OrderTimeoutStrategy selectTimeoutStrategy(Order order, OrderTimeoutStrategy strategy) {
try {
if (strategy != null) {
return strategy;
}

// 根据订单特征选择策略
if (order.getAmount().compareTo(new BigDecimal("1000")) > 0) {
return OrderTimeoutStrategy.DELAY_QUEUE; // 大额订单使用延迟队列
} else if (order.getAmount().compareTo(new BigDecimal("100")) > 0) {
return OrderTimeoutStrategy.REDIS_EXPIRATION; // 中等金额使用Redis过期
} else {
return OrderTimeoutStrategy.SCHEDULED_SCAN; // 小额订单使用定时扫描
}

} catch (Exception e) {
log.error("选择超时处理策略失败", e);
return OrderTimeoutStrategy.SCHEDULED_SCAN; // 默认策略
}
}

/**
* 执行超时处理策略
*/
private void executeTimeoutStrategy(Order order, OrderTimeoutStrategy strategy) {
try {
switch (strategy) {
case SCHEDULED_SCAN:
// 定时扫描策略不需要额外处理,由定时任务自动处理
break;
case DELAY_QUEUE:
delayQueueService.addOrderToDelayQueue(order);
break;
case REDIS_EXPIRATION:
redisService.setOrderTimeout(order);
break;
default:
throw new IllegalArgumentException("不支持的超时处理策略: " + strategy);
}

} catch (Exception e) {
log.error("执行超时处理策略失败", e);
throw new OrderTimeoutException("执行超时处理策略失败", e);
}
}

/**
* 验证订单
*/
private void validateOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("订单不能为空");
}

if (order.getOrderId() == null) {
throw new IllegalArgumentException("订单ID不能为空");
}

if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new IllegalArgumentException("订单状态不正确");
}
}

/**
* 记录超时统计
*/
private void recordTimeoutStatistics(Order order, OrderTimeoutStrategy strategy) {
try {
OrderTimeoutStatistics statistics = new OrderTimeoutStatistics();
statistics.setOrderId(order.getOrderId());
statistics.setUserId(order.getUserId());
statistics.setOrderAmount(order.getAmount());
statistics.setStrategy(strategy);
statistics.setProcessTime(LocalDateTime.now());

// 异步记录统计
CompletableFuture.runAsync(() -> {
try {
orderRepository.saveTimeoutStatistics(statistics);
} catch (Exception e) {
log.error("记录超时统计失败", e);
}
});

} catch (Exception e) {
log.error("记录超时统计失败", e);
}
}

/**
* 获取订单超时统计
*/
public OrderTimeoutStatisticsResult getOrderTimeoutStatistics(Date startTime, Date endTime) {
try {
OrderTimeoutStatisticsResult result = new OrderTimeoutStatisticsResult();
result.setStartTime(startTime);
result.setEndTime(endTime);

// 统计超时订单数量
result.setTotalTimeoutOrders(1000L); // 实际应用中需要从数据库统计

// 统计策略使用情况
Map<OrderTimeoutStrategy, Long> strategyCount = new HashMap<>();
strategyCount.put(OrderTimeoutStrategy.SCHEDULED_SCAN, 400L);
strategyCount.put(OrderTimeoutStrategy.DELAY_QUEUE, 300L);
strategyCount.put(OrderTimeoutStrategy.REDIS_EXPIRATION, 300L);
result.setStrategyCount(strategyCount);

// 统计处理成功率
result.setSuccessRate(0.98); // 98%

// 统计平均处理时间
result.setAverageProcessTime(5000.0); // 5秒

return result;

} catch (Exception e) {
log.error("获取订单超时统计失败", e);
throw new OrderTimeoutException("获取订单超时统计失败", e);
}
}
}

四、性能优化与监控

4.1 性能优化

4.1.1 订单超时性能优化

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* 订单超时性能优化服务
*/
@Service
public class OrderTimeoutPerformanceOptimizationService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private CaffeineCache localCache;

private final String ORDER_TIMEOUT_PERFORMANCE_CACHE_PREFIX = "order_timeout_performance:";

/**
* 优化订单超时处理性能
*/
public OrderTimeoutOptimizationResult optimizeOrderTimeoutPerformance(OrderTimeoutOptimizationRequest request) {
try {
OrderTimeoutOptimizationResult result = new OrderTimeoutOptimizationResult();
result.setRequestId(request.getRequestId());
result.setStartTime(new Date());

// 1. 分析订单超时模式
OrderTimeoutPatternAnalysis patternAnalysis = analyzeOrderTimeoutPattern(request);
result.setPatternAnalysis(patternAnalysis);

// 2. 优化处理策略
OrderTimeoutStrategyOptimizationResult strategyOptimization = optimizeOrderTimeoutStrategy(request, patternAnalysis);
result.setStrategyOptimization(strategyOptimization);

// 3. 优化缓存策略
OrderTimeoutCacheOptimizationResult cacheOptimization = optimizeOrderTimeoutCacheStrategy(request, patternAnalysis);
result.setCacheOptimization(cacheOptimization);

result.setStatus(OrderTimeoutOptimizationStatus.COMPLETED);
result.setEndTime(new Date());

return result;

} catch (Exception e) {
log.error("优化订单超时处理性能失败", e);
throw new OrderTimeoutException("优化订单超时处理性能失败", e);
}
}

/**
* 分析订单超时模式
*/
private OrderTimeoutPatternAnalysis analyzeOrderTimeoutPattern(OrderTimeoutOptimizationRequest request) {
try {
OrderTimeoutPatternAnalysis analysis = new OrderTimeoutPatternAnalysis();
analysis.setRequestId(request.getRequestId());

// 分析超时频率
analysis.setTimeoutFrequency(analyzeOrderTimeoutFrequency(request.getOrderId()));

// 分析订单特征
analysis.setOrderCharacteristics(analyzeOrderCharacteristics(request.getOrderId()));

return analysis;

} catch (Exception e) {
log.error("分析订单超时模式失败", e);
throw new OrderTimeoutException("分析订单超时模式失败", e);
}
}

/**
* 分析订单超时频率
*/
private OrderTimeoutFrequency analyzeOrderTimeoutFrequency(String orderId) {
try {
OrderTimeoutFrequency frequency = new OrderTimeoutFrequency();
frequency.setOrderId(orderId);
frequency.setDailyCount(100);
frequency.setHourlyCount(10);
frequency.setMinuteCount(1);

return frequency;

} catch (Exception e) {
log.error("分析订单超时频率失败", e);
return new OrderTimeoutFrequency();
}
}

/**
* 分析订单特征
*/
private OrderCharacteristics analyzeOrderCharacteristics(String orderId) {
try {
OrderCharacteristics characteristics = new OrderCharacteristics();
characteristics.setOrderId(orderId);
characteristics.setOrderAmount(new BigDecimal("1000"));
characteristics.setOrderType("NORMAL");
characteristics.setPriority(1);

return characteristics;

} catch (Exception e) {
log.error("分析订单特征失败", e);
return new OrderCharacteristics();
}
}

/**
* 优化订单超时策略
*/
private OrderTimeoutStrategyOptimizationResult optimizeOrderTimeoutStrategy(OrderTimeoutOptimizationRequest request,
OrderTimeoutPatternAnalysis analysis) {
try {
OrderTimeoutStrategyOptimizationResult result = new OrderTimeoutStrategyOptimizationResult();
result.setRequestId(request.getRequestId());

// 根据订单特征优化策略
if (analysis.getOrderCharacteristics().getOrderAmount().compareTo(new BigDecimal("1000")) > 0) {
result.setRecommendedStrategy(OrderTimeoutStrategy.DELAY_QUEUE);
result.setRecommendedTimeoutMinutes(30);
result.setRecommendedBatchSize(50);
} else if (analysis.getOrderCharacteristics().getOrderAmount().compareTo(new BigDecimal("100")) > 0) {
result.setRecommendedStrategy(OrderTimeoutStrategy.REDIS_EXPIRATION);
result.setRecommendedTimeoutMinutes(30);
result.setRecommendedBatchSize(100);
} else {
result.setRecommendedStrategy(OrderTimeoutStrategy.SCHEDULED_SCAN);
result.setRecommendedTimeoutMinutes(30);
result.setRecommendedBatchSize(200);
}

return result;

} catch (Exception e) {
log.error("优化订单超时策略失败", e);
throw new OrderTimeoutException("优化订单超时策略失败", e);
}
}

/**
* 优化订单超时缓存策略
*/
private OrderTimeoutCacheOptimizationResult optimizeOrderTimeoutCacheStrategy(OrderTimeoutOptimizationRequest request,
OrderTimeoutPatternAnalysis analysis) {
try {
OrderTimeoutCacheOptimizationResult result = new OrderTimeoutCacheOptimizationResult();
result.setRequestId(request.getRequestId());

// 根据超时频率优化缓存策略
if (analysis.getTimeoutFrequency().getDailyCount() > 100) {
result.setRecommendedCacheExpire(3600); // 1小时
result.setRecommendedCacheSize(1000);
result.setRecommendedCacheStrategy("LRU");
} else if (analysis.getTimeoutFrequency().getDailyCount() > 10) {
result.setRecommendedCacheExpire(1800); // 30分钟
result.setRecommendedCacheSize(500);
result.setRecommendedCacheStrategy("LFU");
} else {
result.setRecommendedCacheExpire(600); // 10分钟
result.setRecommendedCacheSize(100);
result.setRecommendedCacheStrategy("FIFO");
}

return result;

} catch (Exception e) {
log.error("优化订单超时缓存策略失败", e);
throw new OrderTimeoutException("优化订单超时缓存策略失败", e);
}
}
}

4.2 监控告警

4.2.1 订单超时监控指标

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
* 订单超时监控指标
*/
@Component
public class OrderTimeoutMetrics {

private final MeterRegistry meterRegistry;

public OrderTimeoutMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}

/**
* 记录订单超时次数
*/
public void recordOrderTimeoutCount(String strategy, String status) {
Counter.builder("order_timeout.count")
.description("订单超时次数")
.tag("strategy", strategy)
.tag("status", status)
.register(meterRegistry)
.increment();
}

/**
* 记录订单超时处理时间
*/
public void recordOrderTimeoutProcessTime(String strategy, String status, long duration) {
Timer.builder("order_timeout.process.time")
.description("订单超时处理时间")
.tag("strategy", strategy)
.tag("status", status)
.register(meterRegistry)
.record(duration, TimeUnit.MILLISECONDS);
}

/**
* 记录订单超时成功率
*/
public void recordOrderTimeoutSuccessRate(String strategy, double successRate) {
Gauge.builder("order_timeout.success.rate")
.description("订单超时成功率")
.tag("strategy", strategy)
.register(meterRegistry, successRate);
}

/**
* 记录订单超时失败率
*/
public void recordOrderTimeoutFailureRate(String strategy, double failureRate) {
Gauge.builder("order_timeout.failure.rate")
.description("订单超时失败率")
.tag("strategy", strategy)
.register(meterRegistry, failureRate);
}

/**
* 记录订单超时吞吐量
*/
public void recordOrderTimeoutThroughput(String strategy, double throughput) {
Gauge.builder("order_timeout.throughput")
.description("订单超时吞吐量")
.tag("strategy", strategy)
.register(meterRegistry, throughput);
}

/**
* 记录订单超时异常次数
*/
public void recordOrderTimeoutExceptionCount(String strategy, String exceptionType) {
Counter.builder("order_timeout.exception.count")
.description("订单超时异常次数")
.tag("strategy", strategy)
.tag("exception_type", exceptionType)
.register(meterRegistry)
.increment();
}
}

4.2.2 订单超时告警规则

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# prometheus-rules.yml
groups:
- name: order_timeout_alerts
rules:
- alert: HighOrderTimeoutProcessTime
expr: order_timeout_process_time{quantile="0.95"} > 10000
for: 2m
labels:
severity: warning
annotations:
summary: "订单超时处理时间过长"
description: "订单超时处理时间P95超过10秒,当前值: {{ $value }}ms"

- alert: HighOrderTimeoutFailureRate
expr: order_timeout_failure_rate > 0.05
for: 2m
labels:
severity: warning
annotations:
summary: "订单超时失败率过高"
description: "订单超时失败率超过5%,当前值: {{ $value }}"

- alert: LowOrderTimeoutThroughput
expr: order_timeout_throughput < 10
for: 5m
labels:
severity: warning
annotations:
summary: "订单超时吞吐量过低"
description: "订单超时吞吐量低于10次/秒,当前值: {{ $value }}"

- alert: HighOrderTimeoutExceptionCount
expr: rate(order_timeout_exception_count[5m]) > 5
for: 2m
labels:
severity: critical
annotations:
summary: "订单超时异常次数过多"
description: "订单超时异常频率超过5次/分钟,当前值: {{ $value }}"

- alert: OrderTimeoutServiceDown
expr: up{job="order-timeout-service"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "订单超时服务宕机"
description: "订单超时服务已宕机超过1分钟"

五、总结

订单超时自动取消作为电商系统的核心功能之一,通过合理的订单超时处理策略和定时任务,能够构建一个高效、稳定、可扩展的订单超时取消系统。本文从订单超时处理策略到定时任务,从基础实现到企业级应用,系统梳理了订单超时自动取消的完整解决方案。

5.1 关键要点

  1. 处理策略:通过多种处理策略实现不同场景下的订单超时取消需求
  2. 定时任务:通过定时任务扫描、延迟队列、Redis过期等方式处理超时订单
  3. 性能优化:通过批量处理、异步处理、缓存优化等手段优化处理性能
  4. 监控告警:建立完善的监控体系,及时发现和处理问题
  5. 企业级方案:提供完整的企业级部署和监控方案

5.2 最佳实践

  1. 策略选择:根据订单金额、处理频率、系统负载选择合适的处理策略
  2. 批量处理:合理使用批量处理,提高处理效率
  3. 异步处理:通过异步处理提高系统响应性能
  4. 监控告警:建立完善的监控体系,确保订单超时处理服务稳定运行
  5. 容错处理:实现完善的容错机制,确保系统稳定性

通过以上措施,可以构建一个高效、稳定、可扩展的订单超时自动取消系统,为企业的各种业务场景提供订单超时处理支持。