用户取消订单Java微服务后端架构实战

1. 架构概述

用户取消订单系统是电商平台和租赁平台的核心模块,需要支持订单取消校验、商品返库存、优惠券取消使用和订单状态更新等关键功能。本篇文章将深入讲解如何基于Java微服务架构实现一个高性能、高可用、数据一致性的用户取消订单系统。

1.1 系统架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
用户端 → 用户网关 → 订单服务 → 目录服务/用户服务

身份认证

判断订单是否允许取消

如果不允许,返回不允许取消提示

如果允许:
商品返库存
优惠券取消标记请求
优惠券取消使用处理
订单取消
返回取消信息

1.2 核心组件

  • 用户网关(User Gateway):负责用户请求的接入、身份认证、请求路由
  • 订单服务(Order Service):负责订单管理、订单取消校验、订单取消处理、流程编排
  • 目录服务(Catalog Service):负责商品库存管理、商品返库存
  • 用户服务(User Service):负责优惠券管理、优惠券取消使用
  • 数据库(MySQL):持久化订单信息、商品库存信息、优惠券信息
  • 分布式锁(Redisson):保证订单取消和库存返还的并发安全
  • 分布式事务(Seata):保证取消流程的原子性和一致性

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
/**
* 用户网关服务
* 负责用户请求的接入、身份认证、请求路由
*/
@RestController
@RequestMapping("/api/user/gateway")
@Slf4j
public class UserGatewayController {

@Autowired
private AuthService authService;

@Autowired
private OrderServiceClient orderServiceClient;

/**
* 用户取消订单请求
* 流程:身份认证 → 判断订单是否允许取消 → 商品返库存 →
* 优惠券取消标记 → 订单取消 → 返回取消信息
*/
@PostMapping("/order/cancel")
public Result<OrderCancelResult> cancelOrder(
@RequestHeader("Authorization") String token,
@RequestBody OrderCancelRequest request) {

try {
// 1. 身份认证
UserInfo userInfo = authService.authenticate(token);
if (userInfo == null) {
return Result.error("身份认证失败");
}

// 2. 设置用户ID
request.setUserId(userInfo.getUserId());

// 3. 调用订单服务取消订单
OrderCancelResult result = orderServiceClient.cancelOrder(request);

if (!result.isSuccess()) {
return Result.error(result.getMessage());
}

log.info("用户取消订单成功: userId={}, orderId={}, orderNo={}",
userInfo.getUserId(), request.getOrderId(), result.getOrderNo());

return Result.success(result);

} catch (Exception e) {
log.error("用户取消订单失败: orderId={}, error={}",
request.getOrderId(), e.getMessage(), e);
return Result.error("取消订单失败: " + e.getMessage());
}
}
}

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
/**
* 身份认证服务
*/
@Service
@Slf4j
public class AuthService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private UserServiceClient userServiceClient;

/**
* 身份认证
* 验证Token有效性
*/
public UserInfo authenticate(String token) {
try {
// 1. 从Token中解析用户信息
String userId = parseToken(token);
if (StringUtils.isEmpty(userId)) {
return null;
}

// 2. 从缓存中获取用户信息
String userCacheKey = "user:info:" + userId;
UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(userCacheKey);

if (userInfo != null) {
return userInfo;
}

// 3. 缓存未命中,调用用户服务查询
userInfo = userServiceClient.getUserInfo(userId);

// 4. 将用户信息写入缓存
if (userInfo != null) {
redisTemplate.opsForValue().set(userCacheKey, userInfo, 30, TimeUnit.MINUTES);
}

return userInfo;

} catch (Exception e) {
log.error("身份认证失败: token={}, error={}", token, e.getMessage(), e);
return null;
}
}

/**
* 解析Token
*/
private String parseToken(String token) {
// JWT Token解析逻辑
if (StringUtils.isEmpty(token) || !token.startsWith("Bearer ")) {
return null;
}

String jwtToken = token.substring(7);
// 解析JWT获取userId
return extractUserIdFromJWT(jwtToken);
}

private String extractUserIdFromJWT(String jwtToken) {
// JWT解析实现
// 实际实现需要使用JWT库
return "user123";
}
}

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
/**
* 订单服务
* 负责订单管理、订单取消校验、订单取消处理、流程编排
*/
@Service
@Slf4j
public class OrderService {

@Autowired
private OrderMapper orderMapper;

@Autowired
private CatalogServiceClient catalogServiceClient;

@Autowired
private UserServiceClient userServiceClient;

@Autowired
private RedissonClient redissonClient;

/**
* 取消订单
* 完整的取消流程
*/
@GlobalTransactional(rollbackFor = Exception.class)
public OrderCancelResult cancelOrder(OrderCancelRequest request) {
String lockKey = "order:cancel:lock:" + request.getOrderId();
RLock lock = redissonClient.getLock(lockKey);

try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
// 1. 查询订单
Order order = orderMapper.selectById(request.getOrderId());
if (order == null) {
return OrderCancelResult.failed("订单不存在");
}

// 2. 检查订单归属
if (!order.getUserId().equals(request.getUserId())) {
return OrderCancelResult.failed("无权限取消该订单");
}

// 3. 判断订单是否允许取消
if (!isOrderAllowedToCancel(order)) {
return OrderCancelResult.failed("订单状态不允许取消,当前状态: " + order.getStatus());
}

// 4. 商品返库存
StockReturnRequest stockRequest = new StockReturnRequest();
stockRequest.setProductId(order.getProductId());
stockRequest.setQuantity(order.getQuantity());
stockRequest.setOrderId(order.getId());
stockRequest.setOrderNo(order.getOrderNo());

StockReturnResult stockResult = catalogServiceClient.returnStock(stockRequest);
if (!stockResult.isSuccess()) {
return OrderCancelResult.failed("商品返库存失败: " + stockResult.getMessage());
}

// 5. 优惠券取消标记请求
if (StringUtils.isNotEmpty(order.getCouponIds())) {
List<Long> couponIds = Arrays.stream(order.getCouponIds().split(","))
.map(Long::parseLong)
.collect(Collectors.toList());

CouponCancelRequest couponRequest = new CouponCancelRequest();
couponRequest.setUserId(request.getUserId());
couponRequest.setCouponIds(couponIds);
couponRequest.setOrderId(order.getId());
couponRequest.setOrderNo(order.getOrderNo());

CouponCancelResult couponResult = userServiceClient.cancelCoupons(couponRequest);
if (!couponResult.isSuccess()) {
// 回滚库存
catalogServiceClient.rollbackStockReturn(stockRequest);
return OrderCancelResult.failed("优惠券取消失败: " + couponResult.getMessage());
}
}

// 6. 订单取消
order.setStatus(OrderStatus.CANCELLED.getCode());
order.setCancelReason(request.getCancelReason());
order.setCancelTime(new Date());
order.setUpdateTime(new Date());
orderMapper.updateById(order);

log.info("订单取消成功: orderId={}, orderNo={}, userId={}",
order.getId(), order.getOrderNo(), request.getUserId());

// 7. 构建返回结果
OrderCancelResult result = new OrderCancelResult();
result.setSuccess(true);
result.setOrderId(order.getId());
result.setOrderNo(order.getOrderNo());
result.setStatus(OrderStatus.CANCELLED.getCode());
result.setMessage("订单取消成功");

return result;

} else {
return OrderCancelResult.failed("取消订单操作超时,请稍后再试");
}
} catch (Exception e) {
log.error("取消订单失败: orderId={}, userId={}, error={}",
request.getOrderId(), request.getUserId(), e.getMessage(), e);
return OrderCancelResult.failed("取消订单失败: " + e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}

/**
* 判断订单是否允许取消
*/
private boolean isOrderAllowedToCancel(Order order) {
// 只有未支付、已支付但未发货的订单可以取消
String status = order.getStatus();
return OrderStatus.UNPAID.getCode().equals(status) ||
OrderStatus.PAID.getCode().equals(status);
}

/**
* 判断订单是否允许取消(返回详细信息)
*/
public OrderCancelCheckResult checkOrderCancelAllowed(Long orderId, Long userId) {
try {
// 1. 查询订单
Order order = orderMapper.selectById(orderId);
if (order == null) {
return OrderCancelCheckResult.failed("订单不存在");
}

// 2. 检查订单归属
if (!order.getUserId().equals(userId)) {
return OrderCancelCheckResult.failed("无权限取消该订单");
}

// 3. 判断订单是否允许取消
boolean allowed = isOrderAllowedToCancel(order);

OrderCancelCheckResult result = new OrderCancelCheckResult();
result.setAllowed(allowed);
result.setOrderId(orderId);
result.setOrderNo(order.getOrderNo());
result.setCurrentStatus(order.getStatus());
result.setMessage(allowed ? "订单可以取消" : "订单状态不允许取消");

return result;

} catch (Exception e) {
log.error("检查订单是否允许取消失败: orderId={}, error={}",
orderId, e.getMessage(), e);
return OrderCancelCheckResult.failed("检查失败: " + e.getMessage());
}
}
}

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
/**
* 目录服务
* 负责商品库存管理、商品返库存
*/
@Service
@Slf4j
public class CatalogService {

@Autowired
private ProductStockMapper productStockMapper;

@Autowired
private RedissonClient redissonClient;

/**
* 商品返库存
*/
@Transactional(rollbackFor = Exception.class)
public StockReturnResult returnStock(StockReturnRequest request) {
String lockKey = "stock:return:lock:" + request.getProductId();
RLock lock = redissonClient.getLock(lockKey);

try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
// 1. 查询商品库存
ProductStock stock = productStockMapper.selectByProductId(request.getProductId());
if (stock == null) {
return StockReturnResult.failed("商品库存信息不存在");
}

// 2. 返还库存
stock.setAvailableStock(stock.getAvailableStock() + request.getQuantity());
stock.setReservedStock(stock.getReservedStock() - request.getQuantity());
stock.setUpdateTime(new Date());
productStockMapper.updateById(stock);

log.info("商品返库存成功: productId={}, quantity={}, availableStock={}, orderId={}",
request.getProductId(), request.getQuantity(),
stock.getAvailableStock(), request.getOrderId());

// 3. 构建返回结果
StockReturnResult result = new StockReturnResult();
result.setSuccess(true);
result.setProductId(request.getProductId());
result.setReturnedQuantity(request.getQuantity());
result.setRemainingStock(stock.getAvailableStock());
result.setMessage("库存返还成功");

return result;

} else {
return StockReturnResult.failed("库存操作超时,请稍后再试");
}
} catch (Exception e) {
log.error("商品返库存失败: productId={}, quantity={}, orderId={}, error={}",
request.getProductId(), request.getQuantity(),
request.getOrderId(), e.getMessage(), e);
return StockReturnResult.failed("库存返还失败: " + e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}

/**
* 回滚库存返还
*/
@Transactional(rollbackFor = Exception.class)
public void rollbackStockReturn(StockReturnRequest request) {
String lockKey = "stock:return:lock:" + request.getProductId();
RLock lock = redissonClient.getLock(lockKey);

try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
ProductStock stock = productStockMapper.selectByProductId(request.getProductId());
if (stock != null) {
stock.setAvailableStock(stock.getAvailableStock() - request.getQuantity());
stock.setReservedStock(stock.getReservedStock() + request.getQuantity());
stock.setUpdateTime(new Date());
productStockMapper.updateById(stock);

log.info("库存返还回滚成功: productId={}, quantity={}",
request.getProductId(), request.getQuantity());
}
}
} catch (Exception e) {
log.error("库存返还回滚失败: productId={}, quantity={}, error={}",
request.getProductId(), request.getQuantity(), e.getMessage(), e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}

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
/**
* 用户服务
* 负责优惠券管理、优惠券取消使用
*/
@Service
@Slf4j
public class UserService {

@Autowired
private UserCouponMapper userCouponMapper;

@Autowired
private RedissonClient redissonClient;

/**
* 优惠券取消标记请求
* 优惠券取消使用处理
*/
@Transactional(rollbackFor = Exception.class)
public CouponCancelResult cancelCoupons(CouponCancelRequest request) {
String lockKey = "coupon:cancel:lock:" + request.getUserId();
RLock lock = redissonClient.getLock(lockKey);

try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
List<Long> cancelledCouponIds = new ArrayList<>();

// 1. 遍历优惠券ID,取消使用标记
for (Long couponId : request.getCouponIds()) {
// 查询用户优惠券
UserCoupon userCoupon = userCouponMapper.selectByUserIdAndCouponId(
request.getUserId(), couponId);

if (userCoupon == null) {
return CouponCancelResult.failed("优惠券不存在: " + couponId);
}

// 检查优惠券状态
if (!CouponStatus.USED.getCode().equals(userCoupon.getStatus())) {
log.warn("优惠券状态不是已使用,跳过: couponId={}, status={}",
couponId, userCoupon.getStatus());
continue;
}

// 检查是否是该订单使用的优惠券
if (userCoupon.getOrderId() != null &&
!userCoupon.getOrderId().equals(request.getOrderId())) {
log.warn("优惠券不是该订单使用的,跳过: couponId={}, orderId={}",
couponId, request.getOrderId());
continue;
}

// 取消使用标记,恢复为未使用
userCoupon.setStatus(CouponStatus.UNUSED.getCode());
userCoupon.setUseTime(null);
userCoupon.setOrderId(null);
userCoupon.setOrderNo(null);
userCoupon.setOrderAmount(null);
userCoupon.setUpdateTime(new Date());
userCouponMapper.updateById(userCoupon);

cancelledCouponIds.add(couponId);
}

log.info("优惠券取消使用成功: userId={}, orderId={}, cancelledCouponIds={}",
request.getUserId(), request.getOrderId(), cancelledCouponIds);

// 2. 构建返回结果
CouponCancelResult result = new CouponCancelResult();
result.setSuccess(true);
result.setCancelledCouponIds(cancelledCouponIds);
result.setMessage("优惠券取消使用成功");

return result;

} else {
return CouponCancelResult.failed("优惠券操作超时,请稍后再试");
}
} catch (Exception e) {
log.error("优惠券取消使用失败: userId={}, orderId={}, couponIds={}, error={}",
request.getUserId(), request.getOrderId(),
request.getCouponIds(), e.getMessage(), e);
return CouponCancelResult.failed("优惠券取消使用失败: " + e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}

/**
* 优惠券取消使用处理
*/
public CouponCancelProcessResult processCouponCancel(CouponCancelProcessRequest request) {
try {
CouponCancelResult cancelResult = cancelCoupons(request.getCouponCancelRequest());

CouponCancelProcessResult result = new CouponCancelProcessResult();
result.setSuccess(cancelResult.isSuccess());
result.setCancelledCouponIds(cancelResult.getCancelledCouponIds());
result.setMessage(cancelResult.getMessage());

return result;

} catch (Exception e) {
log.error("优惠券取消使用处理失败: error={}", e.getMessage(), e);
return CouponCancelProcessResult.failed("处理失败: " + e.getMessage());
}
}
}

6. 数据模型定义

6.1 订单取消请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 订单取消请求
*/
@Data
public class OrderCancelRequest {
/**
* 用户ID
*/
private Long userId;

/**
* 订单ID
*/
private Long orderId;

/**
* 取消原因
*/
private String cancelReason;
}

6.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
/**
* 订单取消结果
*/
@Data
public class OrderCancelResult {
/**
* 是否成功
*/
private Boolean success;

/**
* 订单ID
*/
private Long orderId;

/**
* 订单号
*/
private String orderNo;

/**
* 订单状态
*/
private String status;

/**
* 消息
*/
private String message;

public static OrderCancelResult failed(String message) {
OrderCancelResult result = new OrderCancelResult();
result.setSuccess(false);
result.setMessage(message);
return result;
}
}

6.3 订单取消检查结果

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
/**
* 订单取消检查结果
*/
@Data
public class OrderCancelCheckResult {
/**
* 是否允许取消
*/
private Boolean allowed;

/**
* 订单ID
*/
private Long orderId;

/**
* 订单号
*/
private String orderNo;

/**
* 当前状态
*/
private String currentStatus;

/**
* 消息
*/
private String message;

public static OrderCancelCheckResult failed(String message) {
OrderCancelCheckResult result = new OrderCancelCheckResult();
result.setAllowed(false);
result.setMessage(message);
return result;
}
}

6.4 库存返还请求

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
/**
* 库存返还请求
*/
@Data
public class StockReturnRequest {
/**
* 商品ID
*/
private Long productId;

/**
* 返还数量
*/
private Integer quantity;

/**
* 订单ID
*/
private Long orderId;

/**
* 订单号
*/
private String orderNo;
}

6.5 库存返还结果

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
/**
* 库存返还结果
*/
@Data
public class StockReturnResult {
/**
* 是否成功
*/
private Boolean success;

/**
* 商品ID
*/
private Long productId;

/**
* 返还数量
*/
private Integer returnedQuantity;

/**
* 剩余库存
*/
private Integer remainingStock;

/**
* 消息
*/
private String message;

public static StockReturnResult failed(String message) {
StockReturnResult result = new StockReturnResult();
result.setSuccess(false);
result.setMessage(message);
return result;
}
}

6.6 优惠券取消请求

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
/**
* 优惠券取消请求
*/
@Data
public class CouponCancelRequest {
/**
* 用户ID
*/
private Long userId;

/**
* 优惠券IDs
*/
private List<Long> couponIds;

/**
* 订单ID
*/
private Long orderId;

/**
* 订单号
*/
private String orderNo;
}

6.7 优惠券取消结果

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
/**
* 优惠券取消结果
*/
@Data
public class CouponCancelResult {
/**
* 是否成功
*/
private Boolean success;

/**
* 已取消的优惠券IDs
*/
private List<Long> cancelledCouponIds;

/**
* 消息
*/
private String message;

public static CouponCancelResult failed(String message) {
CouponCancelResult result = new CouponCancelResult();
result.setSuccess(false);
result.setMessage(message);
return result;
}
}

6.8 订单状态枚举

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
/**
* 订单状态枚举
*/
public enum OrderStatus {
UNPAID("UNPAID", "未支付"),
PAID("PAID", "已支付"),
CANCELLED("CANCELLED", "已取消"),
COMPLETED("COMPLETED", "已完成"),
REFUNDED("REFUNDED", "已退款");

private final String code;
private final String name;

OrderStatus(String code, String name) {
this.code = code;
this.name = name;
}

public String getCode() {
return code;
}

public String getName() {
return name;
}
}

6.9 优惠券状态枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 优惠券状态枚举
*/
public enum CouponStatus {
UNUSED("UNUSED", "未使用"),
USED("USED", "已使用"),
EXPIRED("EXPIRED", "已过期");

private final String code;
private final String name;

CouponStatus(String code, String name) {
this.code = code;
this.name = name;
}

public String getCode() {
return code;
}

public String getName() {
return name;
}
}

7. 数据库Mapper实现

7.1 OrderMapper

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 订单Mapper
*/
@Mapper
public interface OrderMapper extends BaseMapper<Order> {

/**
* 根据订单号查询订单
*/
@Select("SELECT * FROM `order` WHERE order_no = #{orderNo} AND deleted = 0")
Order selectByOrderNo(@Param("orderNo") String orderNo);
}

7.2 UserCouponMapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 用户优惠券Mapper
*/
@Mapper
public interface UserCouponMapper extends BaseMapper<UserCoupon> {

/**
* 根据用户ID和优惠券ID查询
*/
@Select("SELECT * FROM user_coupon " +
"WHERE user_id = #{userId} AND coupon_id = #{couponId} AND deleted = 0")
UserCoupon selectByUserIdAndCouponId(@Param("userId") Long userId,
@Param("couponId") Long couponId);
}

8. 数据库表设计

8.1 订单表(更新字段)

1
2
3
4
-- 订单表增加取消相关字段
ALTER TABLE `order`
ADD COLUMN `cancel_reason` varchar(500) DEFAULT NULL COMMENT '取消原因' AFTER `status`,
ADD COLUMN `cancel_time` datetime DEFAULT NULL COMMENT '取消时间' AFTER `cancel_reason`;

8.2 用户优惠券表(更新字段)

1
2
3
4
5
6
-- 用户优惠券表增加订单相关字段
ALTER TABLE `user_coupon`
ADD COLUMN `order_id` bigint(20) DEFAULT NULL COMMENT '订单ID' AFTER `status`,
ADD COLUMN `order_no` varchar(64) DEFAULT NULL COMMENT '订单号' AFTER `order_id`,
ADD COLUMN `order_amount` decimal(15,2) DEFAULT NULL COMMENT '订单金额' AFTER `order_no`,
ADD KEY `idx_order_id` (`order_id`);

9. 异常处理和回滚机制

9.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
/**
* 订单取消异常处理服务
*/
@Service
@Slf4j
public class OrderCancelExceptionHandler {

@Autowired
private CatalogServiceClient catalogServiceClient;

@Autowired
private UserServiceClient userServiceClient;

/**
* 处理订单取消异常
*/
public void handleCancelException(OrderCancelRequest request,
Exception exception) {
try {
log.error("订单取消异常,开始回滚: orderId={}, error={}",
request.getOrderId(), exception.getMessage());

// 1. 查询订单
Order order = orderMapper.selectById(request.getOrderId());
if (order == null) {
return;
}

// 2. 回滚库存返还
if (order.getProductId() != null && order.getQuantity() != null) {
StockReturnRequest stockRequest = new StockReturnRequest();
stockRequest.setProductId(order.getProductId());
stockRequest.setQuantity(order.getQuantity());
stockRequest.setOrderId(order.getId());

try {
catalogServiceClient.rollbackStockReturn(stockRequest);
} catch (Exception e) {
log.error("回滚库存返还失败: orderId={}, error={}",
request.getOrderId(), e.getMessage(), e);
}
}

// 3. 回滚优惠券取消
if (StringUtils.isNotEmpty(order.getCouponIds())) {
List<Long> couponIds = Arrays.stream(order.getCouponIds().split(","))
.map(Long::parseLong)
.collect(Collectors.toList());

CouponCancelRequest couponRequest = new CouponCancelRequest();
couponRequest.setUserId(request.getUserId());
couponRequest.setCouponIds(couponIds);
couponRequest.setOrderId(order.getId());

try {
// 重新标记优惠券为已使用
userServiceClient.rollbackCouponCancel(couponRequest);
} catch (Exception e) {
log.error("回滚优惠券取消失败: orderId={}, error={}",
request.getOrderId(), e.getMessage(), e);
}
}

} catch (Exception e) {
log.error("订单取消异常处理失败: orderId={}, error={}",
request.getOrderId(), e.getMessage(), e);
}
}
}

10. 性能优化策略

10.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
/**
* 订单状态缓存服务
*/
@Service
@Slf4j
public class OrderStatusCacheService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private OrderMapper orderMapper;

/**
* 获取订单状态(带缓存)
*/
public String getOrderStatus(Long orderId) {
String cacheKey = "order:status:" + orderId;
String status = (String) redisTemplate.opsForValue().get(cacheKey);

if (status != null) {
return status;
}

// 缓存未命中,查询数据库
Order order = orderMapper.selectById(orderId);
if (order != null) {
status = order.getStatus();
// 写入缓存,过期时间10分钟
redisTemplate.opsForValue().set(cacheKey, status, 10, TimeUnit.MINUTES);
}

return status;
}

/**
* 更新订单状态缓存
*/
public void updateOrderStatusCache(Long orderId, String status) {
String cacheKey = "order:status:" + orderId;
redisTemplate.opsForValue().set(cacheKey, status, 10, TimeUnit.MINUTES);
}

/**
* 删除订单状态缓存
*/
public void deleteOrderStatusCache(Long orderId) {
String cacheKey = "order:status:" + orderId;
redisTemplate.delete(cacheKey);
}
}

11. 总结

本文详细介绍了用户取消订单的Java微服务架构实现,包括:

  1. 用户网关服务:负责用户请求接入、身份认证、请求路由
  2. 订单服务:负责订单管理、订单取消校验、订单取消处理、流程编排
  3. 目录服务:负责商品库存管理、商品返库存
  4. 用户服务:负责优惠券管理、优惠券取消使用
  5. 取消流程
    • 用户取消订单请求
    • 身份认证
    • 判断订单是否允许取消
    • 如果不允许,返回不允许取消提示
    • 如果允许:
      • 商品返库存
      • 优惠券取消标记请求
      • 优惠券取消使用处理
      • 订单取消
      • 返回取消信息
  6. 分布式事务:使用Seata保证取消流程的原子性和一致性
  7. 异常处理:完整的异常处理和回滚机制
  8. 库存管理:支持库存返还和回滚

该架构具有以下优势:

  • 数据一致性:分布式事务保证取消流程的原子性
  • 高性能:分布式锁保证并发安全,支持高并发取消
  • 高可用:数据库持久化,支持数据恢复
  • 可扩展:微服务架构,支持水平扩展
  • 完整性:完整的订单取消校验、库存返还、优惠券取消流程
  • 可靠性:异常处理和回滚机制,保证数据一致性

通过本文的实战代码,可以快速搭建一个高性能、高可用、数据一致性的用户取消订单系统。