From c34d9e69cc531b45a1221ec380a3ede923fd6f16fbcb3eb34867d358b66468e2 Mon Sep 17 00:00:00 2001 From: yanglei Date: Thu, 4 Dec 2025 16:55:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BD=A6=E8=BE=86=E5=A4=9A=E9=95=BF=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=97=A0=E5=8D=95=E7=94=9F=E6=88=90=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E5=B7=A5=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SafeOrderExpirationListener.java | 86 +++++++++++++++++-- .../entity/EbikeDispatchConfiguration.java | 2 +- .../impl/EbikeBikeInfoServiceImpl.java | 2 +- .../com/cdzy/operations/utils/RedisUtil.java | 32 +++++++ 4 files changed, 112 insertions(+), 10 deletions(-) diff --git a/ebike-operations/src/main/java/com/cdzy/operations/component/SafeOrderExpirationListener.java b/ebike-operations/src/main/java/com/cdzy/operations/component/SafeOrderExpirationListener.java index 61a3fdf..c3d7383 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/component/SafeOrderExpirationListener.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/component/SafeOrderExpirationListener.java @@ -10,6 +10,7 @@ import com.mybatisflex.core.query.QueryWrapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -18,22 +19,24 @@ import static com.cdzy.operations.model.entity.table.EbikeBikeOrderTableDef.EBIK @Component @Slf4j public class SafeOrderExpirationListener { - + @Resource private RedisUtil redisUtil; - + @Resource private ServiceInstanceManager instanceManager; @Resource private EbikeBikeOrderService orderService; - + // Redis键常量 private static final String ORDER_KEY_PREFIX = "bike:dispatch:lock:"; private static final long LOCK_WAIT_TIME = 5L; // 获取锁等待时间(秒) - + + private static final String CODE_KEY_PREFIX = "bike:no:lock:"; + /** * 处理Redis键过期事件 */ @@ -55,6 +58,23 @@ public class SafeOrderExpirationListener { return null; }); } + // 判断是否为车辆多长时间无单生成调度工单,过期则创建调度工单 + if (expiredKey.startsWith(RedisUtil.BIKE_NO_DOCUMENT_PREFIX)) { + // 提取车辆编号 + String bikeCode = extractBikeCodeFromKey(expiredKey); + if (bikeCode == null) { + return; + } + + log.info("实例 {} 接收到工单过期事件: bikeCode={}", + instanceManager.getInstanceId(), bikeCode); + + // 异步处理,避免阻塞监听线程 + CompletableFuture.runAsync(() -> processExpiredNoDocumentSafely(bikeCode)).exceptionally(e -> { + log.error("车辆多长时间无单生成调度工单异常: bikeCode={}", bikeCode, e); + return null; + }); + } } /** @@ -89,6 +109,33 @@ public class SafeOrderExpirationListener { } } + private void processExpiredNoDocumentSafely(String bikeCode) { + String instanceId = instanceManager.getInstanceId(); + String lockKey = CODE_KEY_PREFIX + bikeCode; + + // 使用RedisUtil的分布式锁功能 + boolean locked = redisUtil.tryNoDocumentLock(lockKey, instanceId, LOCK_WAIT_TIME, TimeUnit.SECONDS); + + if (!locked) { + log.debug("实例 {} 获取锁失败,其他实例正在处理: bikeCode={}", instanceId, bikeCode); + return; + } + log.info("实例 {} 开始生成调度工单: bikeCode={}", instanceId, bikeCode); + try { + + // 执行实际的生成调度工单逻辑 + processCreateDispatchOrderBusiness(bikeCode); + + log.info("实例 {} 完成生成调度工单处理: bikeCode={}", instanceId, bikeCode); + + } catch (Exception e) { + log.error("生成调度工单业务逻辑异常: bikeCode={}", bikeCode, e); + } finally { + // 安全释放锁:先检查锁是否还是自己的 + releaseLockSafely(lockKey, instanceId); + } + } + /** * 安全释放锁 */ @@ -103,13 +150,13 @@ public class SafeOrderExpirationListener { log.error("释放锁异常: lockKey={}", lockKey, e); } } - + /** * 实际的业务处理逻辑 */ private void processExpiredOrderBusiness(String orderId) { try { - //处理订单,该订单为到期未有用户骑行的调度订单 + //处理订单,该订单为到期未有用户骑行的调度订单 QueryWrapper query = QueryWrapper.create() .where(EBIKE_BIKE_ORDER.ORDER_ID.eq(orderId)) .where(EBIKE_BIKE_ORDER.ORDER_TYPE.eq(BikeOrderType.DISPATCH)) @@ -124,13 +171,36 @@ public class SafeOrderExpirationListener { } } - + /** + * 实际的业务处理逻辑 + */ + private void processCreateDispatchOrderBusiness(String bikeCode) { + try { + // 生成调度工单 + orderService.createDispatchSwapOrder(bikeCode); + } catch (Exception e) { + log.error("生成调度工单业务异常: orderId={}", bikeCode, e); + throw e; + } + } + + /** * 从Redis键中提取订单ID */ private String extractOrderIdFromKey(String redisKey) { if (redisKey.startsWith(RedisUtil.BIKE_DISPATCH_ORDER_PREFIX)) { - return redisKey.substring(ORDER_KEY_PREFIX.length()); + return redisKey.substring(RedisUtil.BIKE_DISPATCH_ORDER_PREFIX.length()); + } + return null; + } + + /** + * 从Redis键中提取车辆编号 + */ + private String extractBikeCodeFromKey(String redisKey) { + if (redisKey.startsWith(RedisUtil.BIKE_NO_DOCUMENT_PREFIX)) { + return redisKey.substring(RedisUtil.BIKE_NO_DOCUMENT_PREFIX.length()); } return null; } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/model/entity/EbikeDispatchConfiguration.java b/ebike-operations/src/main/java/com/cdzy/operations/model/entity/EbikeDispatchConfiguration.java index fb0ecdb..c20e6a2 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/model/entity/EbikeDispatchConfiguration.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/model/entity/EbikeDispatchConfiguration.java @@ -13,7 +13,7 @@ import java.io.Serializable; import java.time.LocalDateTime; /** - * 车辆相关配置 实体类 + * 车辆调度配置 实体类 * * @author yanglei * @since 2025-12-03 16:18 diff --git a/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeBikeInfoServiceImpl.java b/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeBikeInfoServiceImpl.java index 19ceeec..6675d49 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeBikeInfoServiceImpl.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeBikeInfoServiceImpl.java @@ -469,7 +469,7 @@ public class EbikeBikeInfoServiceImpl extends ServiceImpl