订单超时:定时任务与状态管理实战

1. 订单超时概述

在电商系统中,订单超时处理是重要的业务逻辑。当用户下单后未在规定时间内完成支付,系统需要自动取消订单并释放相关资源。本文将详细介绍订单超时处理、定时任务、状态管理、超时策略和订单取消机制的完整解决方案。

1.1 核心功能

  1. 订单超时检测: 定时检测超时订单
  2. 状态管理: 订单状态流转和管理
  3. 超时策略: 灵活的超时时间配置
  4. 订单取消: 自动取消超时订单
  5. 资源释放: 库存释放和优惠券回收

1.2 技术架构

1
2
3
4
5
订单创建 → 超时检测 → 状态更新 → 资源释放
↓ ↓ ↓ ↓
定时任务 → 超时判断 → 订单取消 → 库存释放
↓ ↓ ↓ ↓
状态管理 → 策略配置 → 通知发送 → 数据清理

2. 订单超时配置

2.1 Maven依赖配置

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
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Boot Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Spring Boot Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- Spring Boot Scheduling -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- Redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>

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
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
/**
* 订单超时配置类
*/
@Configuration
public class OrderTimeoutConfig {

@Value("${order.timeout.minutes:30}")
private int timeoutMinutes;

@Value("${order.timeout.check-interval:60000}")
private long checkInterval;

@Value("${order.timeout.batch-size:100}")
private int batchSize;

@Value("${order.timeout.enable-notification:true}")
private boolean enableNotification;

/**
* 订单超时配置属性
*/
@Bean
public OrderTimeoutProperties orderTimeoutProperties() {
return OrderTimeoutProperties.builder()
.timeoutMinutes(timeoutMinutes)
.checkInterval(checkInterval)
.batchSize(batchSize)
.enableNotification(enableNotification)
.build();
}

/**
* 订单超时服务
*/
@Bean
public OrderTimeoutService orderTimeoutService() {
return new OrderTimeoutService(orderTimeoutProperties());
}

/**
* 订单状态管理服务
*/
@Bean
public OrderStatusService orderStatusService() {
return new OrderStatusService(orderTimeoutProperties());
}

/**
* 定时任务调度器
*/
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("order-timeout-");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(60);
scheduler.initialize();
return scheduler;
}
}

/**
* 订单超时配置属性
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderTimeoutProperties {
private int timeoutMinutes;
private long checkInterval;
private int batchSize;
private boolean enableNotification;

// 超时策略配置
private Map<String, Integer> timeoutStrategies = new HashMap<>();

// 通知配置
private boolean enableEmailNotification = true;
private boolean enableSmsNotification = true;
private boolean enablePushNotification = true;

// 资源释放配置
private boolean enableInventoryRelease = true;
private boolean enableCouponRelease = true;
private boolean enablePointRelease = true;

// 清理配置
private int cleanupDays = 30;
private boolean enableAutoCleanup = true;
}

3. 数据模型定义

3.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
/**
* 订单模型
*/
@Entity
@Table(name = "orders")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "order_no", nullable = false, unique = true)
private String orderNo;

@Column(name = "user_id", nullable = false)
private Long userId;

@Column(name = "status", nullable = false)
private String status; // PENDING, PAID, CANCELLED, TIMEOUT, COMPLETED

@Column(name = "total_amount", nullable = false, precision = 15, scale = 2)
private BigDecimal totalAmount;

@Column(name = "payment_amount", precision = 15, scale = 2)
private BigDecimal paymentAmount;

@Column(name = "discount_amount", precision = 15, scale = 2)
private BigDecimal discountAmount;

@Column(name = "create_time", nullable = false)
private LocalDateTime createTime;

@Column(name = "pay_time")
private LocalDateTime payTime;

@Column(name = "timeout_time")
private LocalDateTime timeoutTime;

@Column(name = "cancel_time")
private LocalDateTime cancelTime;

@Column(name = "cancel_reason")
private String cancelReason;

@Column(name = "version", nullable = false)
private Long version;

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<OrderItem> orderItems;
}

/**
* 订单项模型
*/
@Entity
@Table(name = "order_items")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderItem {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "order_id", nullable = false)
private Long orderId;

@Column(name = "product_id", nullable = false)
private Long productId;

@Column(name = "product_name", nullable = false)
private String productName;

@Column(name = "quantity", nullable = false)
private Integer quantity;

@Column(name = "price", nullable = false, precision = 15, scale = 2)
private BigDecimal price;

@Column(name = "total_price", nullable = false, precision = 15, scale = 2)
private BigDecimal totalPrice;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", insertable = false, updatable = false)
private Order order;
}

/**
* 订单超时记录模型
*/
@Entity
@Table(name = "order_timeout_records")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderTimeoutRecord {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "order_id", nullable = false)
private Long orderId;

@Column(name = "order_no", nullable = false)
private String orderNo;

@Column(name = "user_id", nullable = false)
private Long userId;

@Column(name = "timeout_type", nullable = false)
private String timeoutType; // PAYMENT, CONFIRM, DELIVERY

@Column(name = "timeout_minutes", nullable = false)
private Integer timeoutMinutes;

@Column(name = "create_time", nullable = false)
private LocalDateTime createTime;

@Column(name = "timeout_time", nullable = false)
private LocalDateTime timeoutTime;

@Column(name = "process_time")
private LocalDateTime processTime;

@Column(name = "process_status", nullable = false)
private String processStatus; // PENDING, PROCESSING, COMPLETED, FAILED

@Column(name = "process_result")
private String processResult;

@Column(name = "error_message")
private String errorMessage;
}

/**
* 订单超时策略模型
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderTimeoutStrategy {
private String orderType;
private int timeoutMinutes;
private String timeoutAction; // CANCEL, NOTIFY, EXTEND
private boolean enableNotification;
private String notificationTemplate;
private boolean enableAutoCancel;
private int cancelDelayMinutes;
}

/**
* 订单超时处理结果
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderTimeoutResult {
private boolean success;
private Long orderId;
private String orderNo;
private String action; // CANCELLED, NOTIFIED, EXTENDED
private String message;
private LocalDateTime processTime;
private List<String> releasedResources;
}

4. 订单超时服务

4.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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
/**
* 订单超时服务
*/
@Service
public class OrderTimeoutService {

private final OrderTimeoutProperties properties;
private final OrderRepository orderRepository;
private final OrderTimeoutRecordRepository timeoutRecordRepository;
private final OrderStatusService orderStatusService;
private final InventoryService inventoryService;
private final CouponService couponService;
private final NotificationService notificationService;

public OrderTimeoutService(OrderTimeoutProperties properties) {
this.properties = properties;
this.orderRepository = null; // 注入
this.timeoutRecordRepository = null; // 注入
this.orderStatusService = null; // 注入
this.inventoryService = null; // 注入
this.couponService = null; // 注入
this.notificationService = null; // 注入
}

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

// 1. 获取待处理的超时订单
List<Order> timeoutOrders = getTimeoutOrders();

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

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

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

log.info("超时订单处理完成");

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

/**
* 处理超时订单
* @param orders 超时订单列表
*/
public void processTimeoutOrders(List<Order> orders) {
for (Order order : orders) {
try {
processTimeoutOrder(order);
} catch (Exception e) {
log.error("处理超时订单失败: orderId={}", order.getId(), e);
}
}
}

/**
* 处理单个超时订单
* @param order 订单
*/
public OrderTimeoutResult processTimeoutOrder(Order order) {
try {
// 1. 创建超时记录
OrderTimeoutRecord record = createTimeoutRecord(order);

// 2. 更新记录状态为处理中
record.setProcessStatus("PROCESSING");
record.setProcessTime(LocalDateTime.now());
timeoutRecordRepository.save(record);

// 3. 执行超时处理逻辑
OrderTimeoutResult result = executeTimeoutAction(order);

// 4. 更新记录状态
record.setProcessStatus(result.isSuccess() ? "COMPLETED" : "FAILED");
record.setProcessResult(result.getAction());
record.setErrorMessage(result.getMessage());
timeoutRecordRepository.save(record);

return result;

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

return OrderTimeoutResult.builder()
.success(false)
.orderId(order.getId())
.orderNo(order.getOrderNo())
.message("处理失败: " + e.getMessage())
.processTime(LocalDateTime.now())
.build();
}
}

/**
* 执行超时处理动作
* @param order 订单
* @return 处理结果
*/
private OrderTimeoutResult executeTimeoutAction(Order order) {
try {
// 1. 获取超时策略
OrderTimeoutStrategy strategy = getTimeoutStrategy(order);

// 2. 执行超时动作
switch (strategy.getTimeoutAction()) {
case "CANCEL":
return cancelTimeoutOrder(order, strategy);
case "NOTIFY":
return notifyTimeoutOrder(order, strategy);
case "EXTEND":
return extendTimeoutOrder(order, strategy);
default:
return cancelTimeoutOrder(order, strategy);
}

} catch (Exception e) {
log.error("执行超时处理动作失败: orderId={}", order.getId(), e);
throw new RuntimeException("执行超时处理动作失败", e);
}
}

/**
* 取消超时订单
* @param order 订单
* @param strategy 超时策略
* @return 处理结果
*/
private OrderTimeoutResult cancelTimeoutOrder(Order order, OrderTimeoutStrategy strategy) {
try {
// 1. 更新订单状态
order.setStatus("TIMEOUT");
order.setCancelTime(LocalDateTime.now());
order.setCancelReason("订单超时自动取消");
orderRepository.save(order);

// 2. 释放库存
List<String> releasedResources = new ArrayList<>();
if (properties.isEnableInventoryRelease()) {
releaseInventory(order);
releasedResources.add("库存");
}

// 3. 释放优惠券
if (properties.isEnableCouponRelease()) {
releaseCoupons(order);
releasedResources.add("优惠券");
}

// 4. 释放积分
if (properties.isEnablePointRelease()) {
releasePoints(order);
releasedResources.add("积分");
}

// 5. 发送通知
if (strategy.isEnableNotification() && properties.isEnableNotification()) {
sendTimeoutNotification(order, "订单超时取消");
}

return OrderTimeoutResult.builder()
.success(true)
.orderId(order.getId())
.orderNo(order.getOrderNo())
.action("CANCELLED")
.message("订单超时取消成功")
.processTime(LocalDateTime.now())
.releasedResources(releasedResources)
.build();

} catch (Exception e) {
log.error("取消超时订单失败: orderId={}", order.getId(), e);
throw new RuntimeException("取消超时订单失败", e);
}
}

/**
* 通知超时订单
* @param order 订单
* @param strategy 超时策略
* @return 处理结果
*/
private OrderTimeoutResult notifyTimeoutOrder(Order order, OrderTimeoutStrategy strategy) {
try {
// 1. 发送超时通知
if (strategy.isEnableNotification()) {
sendTimeoutNotification(order, "订单即将超时");
}

// 2. 延长超时时间
if (strategy.getCancelDelayMinutes() > 0) {
extendTimeoutTime(order, strategy.getCancelDelayMinutes());
}

return OrderTimeoutResult.builder()
.success(true)
.orderId(order.getId())
.orderNo(order.getOrderNo())
.action("NOTIFIED")
.message("订单超时通知发送成功")
.processTime(LocalDateTime.now())
.build();

} catch (Exception e) {
log.error("通知超时订单失败: orderId={}", order.getId(), e);
throw new RuntimeException("通知超时订单失败", e);
}
}

/**
* 延长超时订单
* @param order 订单
* @param strategy 超时策略
* @return 处理结果
*/
private OrderTimeoutResult extendTimeoutOrder(Order order, OrderTimeoutStrategy strategy) {
try {
// 1. 延长超时时间
extendTimeoutTime(order, strategy.getCancelDelayMinutes());

// 2. 发送延长通知
if (strategy.isEnableNotification()) {
sendTimeoutNotification(order, "订单超时时间已延长");
}

return OrderTimeoutResult.builder()
.success(true)
.orderId(order.getId())
.orderNo(order.getOrderNo())
.action("EXTENDED")
.message("订单超时时间延长成功")
.processTime(LocalDateTime.now())
.build();

} catch (Exception e) {
log.error("延长超时订单失败: orderId={}", order.getId(), e);
throw new RuntimeException("延长超时订单失败", e);
}
}

/**
* 获取超时订单
* @return 超时订单列表
*/
private List<Order> getTimeoutOrders() {
LocalDateTime timeoutThreshold = LocalDateTime.now().minusMinutes(properties.getTimeoutMinutes());

return orderRepository.findTimeoutOrders(timeoutThreshold, properties.getBatchSize());
}

/**
* 创建超时记录
* @param order 订单
* @return 超时记录
*/
private OrderTimeoutRecord createTimeoutRecord(Order order) {
OrderTimeoutRecord record = OrderTimeoutRecord.builder()
.orderId(order.getId())
.orderNo(order.getOrderNo())
.userId(order.getUserId())
.timeoutType("PAYMENT")
.timeoutMinutes(properties.getTimeoutMinutes())
.createTime(LocalDateTime.now())
.timeoutTime(order.getTimeoutTime())
.processStatus("PENDING")
.build();

return timeoutRecordRepository.save(record);
}

/**
* 获取超时策略
* @param order 订单
* @return 超时策略
*/
private OrderTimeoutStrategy getTimeoutStrategy(Order order) {
// 根据订单类型获取超时策略
String orderType = determineOrderType(order);

return OrderTimeoutStrategy.builder()
.orderType(orderType)
.timeoutMinutes(properties.getTimeoutMinutes())
.timeoutAction("CANCEL")
.enableNotification(properties.isEnableNotification())
.enableAutoCancel(true)
.cancelDelayMinutes(0)
.build();
}

/**
* 确定订单类型
* @param order 订单
* @return 订单类型
*/
private String determineOrderType(Order order) {
// 根据订单金额、商品类型等确定订单类型
if (order.getTotalAmount().compareTo(new BigDecimal("1000")) > 0) {
return "HIGH_VALUE";
} else if (order.getTotalAmount().compareTo(new BigDecimal("100")) > 0) {
return "MEDIUM_VALUE";
} else {
return "LOW_VALUE";
}
}

/**
* 释放库存
* @param order 订单
*/
private void releaseInventory(Order order) {
for (OrderItem item : order.getOrderItems()) {
inventoryService.releaseInventory(item.getProductId(), item.getQuantity());
}
}

/**
* 释放优惠券
* @param order 订单
*/
private void releaseCoupons(Order order) {
// 实现优惠券释放逻辑
couponService.releaseCoupons(order.getId());
}

/**
* 释放积分
* @param order 订单
*/
private void releasePoints(Order order) {
// 实现积分释放逻辑
// pointService.releasePoints(order.getId());
}

/**
* 发送超时通知
* @param order 订单
* @param message 通知消息
*/
private void sendTimeoutNotification(Order order, String message) {
if (properties.isEnableNotification()) {
notificationService.sendOrderTimeoutNotification(order, message);
}
}

/**
* 延长超时时间
* @param order 订单
* @param extendMinutes 延长时间(分钟)
*/
private void extendTimeoutTime(Order order, int extendMinutes) {
order.setTimeoutTime(order.getTimeoutTime().plusMinutes(extendMinutes));
orderRepository.save(order);
}
}

5. 订单状态管理服务

5.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
/**
* 订单状态管理服务
*/
@Service
public class OrderStatusService {

private final OrderTimeoutProperties properties;
private final OrderRepository orderRepository;

public OrderStatusService(OrderTimeoutProperties properties) {
this.properties = properties;
this.orderRepository = null; // 注入
}

/**
* 更新订单状态
* @param orderId 订单ID
* @param newStatus 新状态
* @param reason 状态变更原因
* @return 更新结果
*/
public boolean updateOrderStatus(Long orderId, String newStatus, String reason) {
try {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new BusinessException("订单不存在"));

// 验证状态转换是否合法
if (!isValidStatusTransition(order.getStatus(), newStatus)) {
throw new BusinessException("无效的状态转换: " + order.getStatus() + " -> " + newStatus);
}

// 更新订单状态
order.setStatus(newStatus);
order.setVersion(order.getVersion() + 1);

// 根据状态设置相应的时间字段
switch (newStatus) {
case "PAID":
order.setPayTime(LocalDateTime.now());
break;
case "CANCELLED":
case "TIMEOUT":
order.setCancelTime(LocalDateTime.now());
order.setCancelReason(reason);
break;
case "COMPLETED":
// 设置完成时间
break;
}

orderRepository.save(order);

log.info("订单状态更新成功: orderId={}, status={}", orderId, newStatus);

return true;

} catch (Exception e) {
log.error("更新订单状态失败: orderId={}, status={}", orderId, newStatus, e);
return false;
}
}

/**
* 验证状态转换是否合法
* @param currentStatus 当前状态
* @param newStatus 新状态
* @return 是否合法
*/
private boolean isValidStatusTransition(String currentStatus, String newStatus) {
Map<String, List<String>> validTransitions = new HashMap<>();

validTransitions.put("PENDING", Arrays.asList("PAID", "CANCELLED", "TIMEOUT"));
validTransitions.put("PAID", Arrays.asList("COMPLETED", "CANCELLED"));
validTransitions.put("CANCELLED", Arrays.asList());
validTransitions.put("TIMEOUT", Arrays.asList());
validTransitions.put("COMPLETED", Arrays.asList());

List<String> allowedStatuses = validTransitions.get(currentStatus);
return allowedStatuses != null && allowedStatuses.contains(newStatus);
}

/**
* 获取订单状态历史
* @param orderId 订单ID
* @return 状态历史
*/
public List<OrderStatusHistory> getOrderStatusHistory(Long orderId) {
// 实现获取订单状态历史的逻辑
return new ArrayList<>();
}

/**
* 检查订单是否超时
* @param orderId 订单ID
* @return 是否超时
*/
public boolean isOrderTimeout(Long orderId) {
try {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new BusinessException("订单不存在"));

if (!"PENDING".equals(order.getStatus())) {
return false;
}

LocalDateTime timeoutTime = order.getTimeoutTime();
if (timeoutTime == null) {
timeoutTime = order.getCreateTime().plusMinutes(properties.getTimeoutMinutes());
}

return LocalDateTime.now().isAfter(timeoutTime);

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

/**
* 订单状态历史模型
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderStatusHistory {
private Long id;
private Long orderId;
private String fromStatus;
private String toStatus;
private String reason;
private LocalDateTime changeTime;
private String operator;
}

6. 订单超时控制器

6.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
/**
* 订单超时控制器
*/
@RestController
@RequestMapping("/order-timeout")
public class OrderTimeoutController {

@Autowired
private OrderTimeoutService orderTimeoutService;

@Autowired
private OrderStatusService orderStatusService;

/**
* 手动处理超时订单
*/
@PostMapping("/process/{orderId}")
public ResponseEntity<Map<String, Object>> processTimeoutOrder(@PathVariable Long orderId) {
try {
OrderTimeoutResult result = orderTimeoutService.processTimeoutOrder(
orderRepository.findById(orderId).orElse(null));

Map<String, Object> response = new HashMap<>();
response.put("success", result.isSuccess());
response.put("action", result.getAction());
response.put("message", result.getMessage());
response.put("releasedResources", result.getReleasedResources());

return ResponseEntity.ok(response);

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

Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "处理失败: " + e.getMessage());

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}

/**
* 延长订单超时时间
*/
@PostMapping("/extend/{orderId}")
public ResponseEntity<Map<String, Object>> extendOrderTimeout(
@PathVariable Long orderId,
@RequestParam int extendMinutes) {
try {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new BusinessException("订单不存在"));

// 延长超时时间
order.setTimeoutTime(order.getTimeoutTime().plusMinutes(extendMinutes));
orderRepository.save(order);

Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "订单超时时间延长成功");
response.put("newTimeoutTime", order.getTimeoutTime());

return ResponseEntity.ok(response);

} catch (Exception e) {
log.error("延长订单超时时间失败: orderId={}", orderId, e);

Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "延长失败: " + e.getMessage());

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}

/**
* 获取超时订单列表
*/
@GetMapping("/timeout-orders")
public ResponseEntity<Map<String, Object>> getTimeoutOrders(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
try {
List<Order> timeoutOrders = orderRepository.findTimeoutOrders(
LocalDateTime.now().minusMinutes(30), size);

Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("orders", timeoutOrders);
response.put("total", timeoutOrders.size());

return ResponseEntity.ok(response);

} catch (Exception e) {
log.error("获取超时订单列表失败", e);

Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "获取失败: " + e.getMessage());

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}

/**
* 获取订单超时记录
*/
@GetMapping("/records/{orderId}")
public ResponseEntity<Map<String, Object>> getOrderTimeoutRecords(@PathVariable Long orderId) {
try {
List<OrderTimeoutRecord> records = timeoutRecordRepository.findByOrderId(orderId);

Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("records", records);

return ResponseEntity.ok(response);

} catch (Exception e) {
log.error("获取订单超时记录失败: orderId={}", orderId, e);

Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "获取失败: " + e.getMessage());

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}
}

7. 总结

通过订单超时处理的实现,我们成功构建了一个完整的订单超时管理系统。关键特性包括:

7.1 核心优势

  1. 订单超时检测: 定时检测超时订单
  2. 状态管理: 订单状态流转和管理
  3. 超时策略: 灵活的超时时间配置
  4. 订单取消: 自动取消超时订单
  5. 资源释放: 库存释放和优惠券回收

7.2 最佳实践

  1. 定时任务: 高效的定时任务调度
  2. 状态管理: 完善的状态转换控制
  3. 超时策略: 灵活的超时处理策略
  4. 资源管理: 可靠的资源释放机制
  5. 监控告警: 全面的监控和告警机制

这套订单超时方案不仅能够提供自动化的订单超时处理,还包含了状态管理、资源释放、通知机制等核心功能,是电商系统的重要业务组件。