|
|
@@ -0,0 +1,144 @@
|
|
|
+package shop.alien.store.service.impl;
|
|
|
+
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.boot.CommandLineRunner;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import shop.alien.entity.store.LawyerConsultationOrder;
|
|
|
+import shop.alien.store.config.BaseRedisService;
|
|
|
+import shop.alien.store.listener.RedisKeyExpirationHandler;
|
|
|
+import shop.alien.store.service.OrderExpirationService;
|
|
|
+
|
|
|
+import javax.annotation.PostConstruct;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 訂單過期處理服務實現類
|
|
|
+ *
|
|
|
+ * @author system
|
|
|
+ * @since 2025-01-XX
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class OrderExpirationServiceImpl implements OrderExpirationService, CommandLineRunner {
|
|
|
+
|
|
|
+ private final BaseRedisService redisService;
|
|
|
+ private final RedisKeyExpirationHandler expirationHandler;
|
|
|
+ private final LawyerConsultationOrderServiceImpl orderService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Redis key前綴:訂單支付超時
|
|
|
+ */
|
|
|
+ private static final String ORDER_PAYMENT_TIMEOUT_PREFIX = "lawyer:order:payment:timeout:";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默認超時時間:30分鐘
|
|
|
+ */
|
|
|
+ private static final long DEFAULT_TIMEOUT_SECONDS = 30 * 60;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化時註冊訂單支付超時處理器
|
|
|
+ */
|
|
|
+ @PostConstruct
|
|
|
+ public void init() {
|
|
|
+ // 註冊訂單支付超時處理器
|
|
|
+ expirationHandler.registerHandler(ORDER_PAYMENT_TIMEOUT_PREFIX, this::handleExpiredOrderKey);
|
|
|
+ log.info("訂單支付超時處理器註冊完成,前綴: {}", ORDER_PAYMENT_TIMEOUT_PREFIX);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void run(String... args) {
|
|
|
+ log.info("OrderExpirationService 初始化完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 處理過期的訂單key
|
|
|
+ *
|
|
|
+ * @param expiredKey 過期的key,格式:order:payment:timeout:{orderId}
|
|
|
+ */
|
|
|
+ private void handleExpiredOrderKey(String expiredKey) {
|
|
|
+ try {
|
|
|
+ // 從key中提取訂單ID
|
|
|
+ String orderIdStr = expiredKey.replace(ORDER_PAYMENT_TIMEOUT_PREFIX, "");
|
|
|
+ Integer orderId = Integer.parseInt(orderIdStr);
|
|
|
+
|
|
|
+ log.info("檢測到訂單支付超時,訂單ID: {}", orderId);
|
|
|
+
|
|
|
+ // 處理訂單支付超時
|
|
|
+ handleOrderPaymentTimeout(orderId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("處理過期訂單key失敗,key: {}", expiredKey, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void handleOrderPaymentTimeout(Integer orderId) {
|
|
|
+ log.info("開始處理訂單支付超時,訂單ID: {}", orderId);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 查詢訂單
|
|
|
+ LawyerConsultationOrder order = orderService.getById(orderId);
|
|
|
+ if (order == null) {
|
|
|
+ log.warn("訂單不存在,訂單ID: {}", orderId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只處理待支付狀態的訂單
|
|
|
+ if (order.getOrderStatus() != null && order.getOrderStatus() == 0) {
|
|
|
+ log.info("訂單處於待支付狀態,開始取消訂單,訂單ID: {}", orderId);
|
|
|
+
|
|
|
+ // 更新訂單狀態為已取消
|
|
|
+ order.setOrderStatus(4); // 4:已取消
|
|
|
+ order.setUpdatedTime(new java.util.Date());
|
|
|
+
|
|
|
+ boolean updated = orderService.updateById(order);
|
|
|
+ if (updated) {
|
|
|
+ log.info("訂單支付超時,已自動取消訂單,訂單ID: {}", orderId);
|
|
|
+ } else {
|
|
|
+ log.error("取消訂單失敗,訂單ID: {}", orderId);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.info("訂單狀態不是待支付,無需處理,訂單ID: {}, 當前狀態: {}", orderId, order.getOrderStatus());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("處理訂單支付超時失敗,訂單ID: {}", orderId, e);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setOrderPaymentTimeout(Integer orderId, long timeoutSeconds) {
|
|
|
+ if (orderId == null) {
|
|
|
+ log.warn("訂單ID為null,無法設置支付超時監聽");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String key = ORDER_PAYMENT_TIMEOUT_PREFIX + orderId;
|
|
|
+ long timeout = timeoutSeconds > 0 ? timeoutSeconds : DEFAULT_TIMEOUT_SECONDS;
|
|
|
+
|
|
|
+ // 設置Redis key,帶過期時間
|
|
|
+ // 當key過期時,會觸發RedisKeyExpirationListener
|
|
|
+ redisService.setString(key, String.valueOf(orderId), timeout);
|
|
|
+
|
|
|
+ log.info("設置訂單支付超時監聽,訂單ID: {}, 超時時間: {}秒, key: {}", orderId, timeout, key);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取消訂單支付超時監聽(當訂單已支付時調用)
|
|
|
+ *
|
|
|
+ * @param orderId 訂單ID
|
|
|
+ */
|
|
|
+ public void cancelOrderPaymentTimeout(Integer orderId) {
|
|
|
+ if (orderId == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String key = ORDER_PAYMENT_TIMEOUT_PREFIX + orderId;
|
|
|
+ redisService.delete(key);
|
|
|
+
|
|
|
+ log.info("取消訂單支付超時監聽,訂單ID: {}, key: {}", orderId, key);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|