|
@Service
@Slf4j
@Transactional
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private OrderDelayQueueService delayQueueService;
@Autowired
private OrderExpireZSetService zSetService;
@Autowired
private ApplicationEventPublisher eventPublisher;
private static final String ORDER_LOCK_PREFIX = "order:lock:";
private static final String ORDER_EXPIRE_KEY_PREFIX = "order:expire:";
private static final int ORDER_EXPIRE_MINUTES = 30; // 30分钟未支付过期
/**
* 创建订单
*/
public Order createOrder(Long userId, BigDecimal amount) {
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(userId);
order.setAmount(amount);
order.setStatus(OrderStatus.PENDING_PAYMENT);
order.setExpireTime(LocalDateTime.now().plusMinutes(ORDER_EXPIRE_MINUTES));
order = orderRepository.save(order);
// 设置Redis过期
setOrderExpire(order.getOrderNo());
// 添加到延时队列
delayQueueService.addToDelayQueue(order.getOrderNo(), ORDER_EXPIRE_MINUTES);
// 添加到ZSet
zSetService.addOrderToExpireSet(
order.getOrderNo(),
order.getExpireTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
);
log.info("创建订单成功: {}, 过期时间: {}", order.getOrderNo(), order.getExpireTime());
return order;
}
/**
* 设置Redis键过期
*/
private void setOrderExpire(String orderNo) {
String key = ORDER_EXPIRE_KEY_PREFIX + orderNo;
String value = String.valueOf(System.currentTimeMillis());
// 设置30分钟后过期
redisTemplate.opsForValue().set(
key,
value,
ORDER_EXPIRE_MINUTES,
TimeUnit.MINUTES
);
// 同时存储订单信息,用于过期时处理
Map<String, String> orderInfo = new HashMap<>();
orderInfo.put("orderNo", orderNo);
orderInfo.put("userId", "1"); // 实际从订单获取
orderInfo.put("amount", "100.00");
redisTemplate.opsForHash().putAll(ORDER_KEY_PREFIX + orderNo, orderInfo);
}
/**
* 支付成功处理
*/
public boolean processPayment(String orderNo) {
// 获取分布式锁
String lockKey = ORDER_LOCK_PREFIX + orderNo;
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (Boolean.FALSE.equals(locked)) {
throw new RuntimeException("订单处理中,请稍后");
}
try {
Order order = orderRepository.findByOrderNo(orderNo)
.orElseThrow(() -> new RuntimeException("订单不存在"));
// 检查订单状态
if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new RuntimeException("订单状态异常: " + order.getStatus());
}
// 检查是否过期
if (order.isExpired()) {
order.setStatus(OrderStatus.EXPIRED);
orderRepository.save(order);
throw new RuntimeException("订单已过期");
}
// 更新订单状态
order.setStatus(OrderStatus.PAID);
order.setUpdateTime(LocalDateTime.now());
orderRepository.save(order);
// 移除过期设置
removeOrderExpire(orderNo);
// 发布支付成功事件
eventPublisher.publishEvent(new OrderPaidEvent(this, order));
log.info("订单支付成功: {}", orderNo);
return true;
} finally {
// 释放锁
redisTemplate.delete(lockKey);
}
}
/**
* 处理过期订单
*/
public boolean cancelExpiredOrder(String orderNo) {
String lockKey = ORDER_LOCK_PREFIX + orderNo;
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (Boolean.FALSE.equals(locked)) {
return false;
}
try {
Order order = orderRepository.findByOrderNo(orderNo)
.orElseThrow(() -> new RuntimeException("订单不存在"));
// 双重检查:订单是否仍为待支付状态
if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
log.info("订单 {} 状态已变更为 {},跳过取消", orderNo, order.getStatus());
return false;
}
// 检查是否真的过期
if (!order.isExpired()) {
log.info("订单 {} 未过期,跳过取消", orderNo);
return false;
}
// 更新订单状态
order.setStatus(OrderStatus.EXPIRED);
order.setUpdateTime(LocalDateTime.now());
orderRepository.save(order);
// 释放库存等业务逻辑
releaseStock(order);
// 发送通知
sendExpireNotification(order);
log.info("订单 {} 已过期取消", orderNo);
return true;
} finally {
redisTemplate.delete(lockKey);
}
}
/**
* 移除订单过期设置
*/
private void removeOrderExpire(String orderNo) {
// 删除过期key
redisTemplate.delete(ORDER_EXPIRE_KEY_PREFIX + orderNo);
// 从延时队列移除
// 注意:Redisson延时队列不支持直接移除,需要其他方式
// 从ZSet移除
redisTemplate.opsForZSet().remove(ORDER_EXPIRE_ZSET, orderNo);
// 删除订单缓存
redisTemplate.delete(ORDER_KEY_PREFIX + orderNo);
}
/**
* 生成订单号
*/
private String generateOrderNo() {
// 时间戳 + 随机数
return "ORD" +
System.currentTimeMillis() +
String.format("%06d", ThreadLocalRandom.current().nextInt(1000000));
}
private void releaseStock(Order order) {
// 释放库存逻辑
log.info("释放订单 {} 的库存", order.getOrderNo());
}
private void sendExpireNotification(Order order) {
// 发送通知逻辑
log.info("发送订单 {} 过期通知", order.getOrderNo());
}
}
|