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 095bc5f..f176802 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 @@ -33,6 +33,9 @@ public class SafeOrderExpirationListener { // Redis键常量 private static final String ORDER_KEY_PREFIX = "bike:dispatch:lock:"; + // Redis键常量 + private static final String BIKE_KEY_PREFIX = "bike:inspection:lock:"; + // 获取锁等待时间(秒) private static final long LOCK_WAIT_TIME = 5L; @@ -42,7 +45,7 @@ public class SafeOrderExpirationListener { * 处理Redis键过期事件 */ public void handleOrderExpiration(String expiredKey) { - // 验证是否为工单键 + // 验证是否为调度工单键 if (expiredKey.startsWith(RedisUtil.BIKE_DISPATCH_ORDER_PREFIX)) { // 提取订单ID String orderId = extractOrderIdFromKey(expiredKey); @@ -59,6 +62,24 @@ public class SafeOrderExpirationListener { return null; }); } + // 验证是否为巡检工单键 + if (expiredKey.startsWith(RedisUtil.BIKE_INSPECTION_ORDER_PREFIX)) { + + // 提取车辆编号 + String bikeCode = extractBikeCodeFromKey(expiredKey); + if (bikeCode == null) { + return; + } + + log.info("实例 {} 接收到巡检生成事件: bikeCode={}", + instanceManager.getInstanceId(), bikeCode); + + // 异步处理,避免阻塞监听线程 + CompletableFuture.runAsync(() -> createOrderSafely(bikeCode)).exceptionally(e -> { + log.error("车辆生成巡检工单失败: bikeCode={}", bikeCode, e); + return null; + }); + } // 判断是否为车辆多长时间无单生成调度工单,过期则创建调度工单 if (expiredKey.startsWith(RedisUtil.BIKE_NO_DOCUMENT_PREFIX)) { // 提取车辆编号 @@ -110,6 +131,38 @@ public class SafeOrderExpirationListener { } } + /** + * 安全的生成工单(使用RedisUtil分布式锁) + */ + private void createOrderSafely(String bikeCode) { + String instanceId = instanceManager.getInstanceId(); + String lockKey = BIKE_KEY_PREFIX + bikeCode; + + // 使用RedisUtil的分布式锁功能 + boolean locked = redisUtil.tryInspectionLock(lockKey, instanceId, LOCK_WAIT_TIME, TimeUnit.SECONDS); + + if (!locked) { + log.debug("实例 {} 获取锁失败,其他实例正在处理巡检工单生成时间: bikeCode={}", instanceId, bikeCode); + return; + } + + log.info("实例 {} 开始处理工单生成: bikeCode={}", instanceId, bikeCode); + + try { + + // 执行实际的过期处理逻辑 + processCreateInspectionOrderBusiness(bikeCode); + + log.info("实例 {} 完成工单过期处理: bikeCode={}", instanceId, bikeCode); + + } catch (Exception e) { + log.error("处理过期工单业务逻辑异常: bikeCode={}", bikeCode, e); + } finally { + // 安全释放锁:先检查锁是否还是自己的 + releaseLockSafely(lockKey, instanceId); + } + } + private void processExpiredNoDocumentSafely(String bikeCode) { String instanceId = instanceManager.getInstanceId(); String lockKey = NO_DOCUMENT_LOCK_PREFIX + bikeCode; @@ -153,7 +206,7 @@ public class SafeOrderExpirationListener { } /** - * 实际的业务处理逻辑 + * 实际的有效调度业务处理逻辑 */ private void processExpiredOrderBusiness(String orderId) { try { @@ -177,7 +230,21 @@ public class SafeOrderExpirationListener { } /** - * 实际的业务处理逻辑 + * 实际的调度业务处理逻辑 + */ + private void processCreateInspectionOrderBusiness(String bikeCode) { + try { + //TODO: 生成巡检工单 +// orderService.createInspectionSwapOrder(bikeCode); + System.out.println("生成巡检工单"); + } catch (Exception e) { + log.error("生成巡检工单业务异常: orderId={}", bikeCode, e); + throw e; + } + } + + /** + * 实际的调度业务处理逻辑 */ private void processCreateDispatchOrderBusiness(String bikeCode) { try { @@ -207,6 +274,9 @@ public class SafeOrderExpirationListener { if (redisKey.startsWith(RedisUtil.BIKE_NO_DOCUMENT_PREFIX)) { return redisKey.substring(RedisUtil.BIKE_NO_DOCUMENT_PREFIX.length()); } + if (redisKey.startsWith(RedisUtil.BIKE_INSPECTION_ORDER_PREFIX)) { + return redisKey.substring(RedisUtil.BIKE_INSPECTION_ORDER_PREFIX.length()); + } return null; } } \ No newline at end of file diff --git a/ebike-operations/src/main/java/com/cdzy/operations/utils/RedisUtil.java b/ebike-operations/src/main/java/com/cdzy/operations/utils/RedisUtil.java index 5521f73..109521d 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/utils/RedisUtil.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/utils/RedisUtil.java @@ -38,6 +38,9 @@ public class RedisUtil { // 调度工单相关 public static final String BIKE_DISPATCH_ORDER_PREFIX = "bike:dispatch:order:"; + //巡检工单 + public static final String BIKE_INSPECTION_ORDER_PREFIX = "bike:inspection:order:"; + // 车辆信息相关 public static final String BIKE_ECU_PREFIX = "bike:ecu:"; @@ -56,14 +59,11 @@ public class RedisUtil { * 获取指定数据库的RedisTemplate */ private RedisTemplate getRedisTemplate(int database) { - switch (database) { - case Database.DB1: - return redisTemplate; - case Database.DB2: - return redisTemplateDb2; - default: - throw new IllegalArgumentException(ErrorMessages.UNSUPPORTED_DATABASE + database); - } + return switch (database) { + case Database.DB1 -> redisTemplate; + case Database.DB2 -> redisTemplateDb2; + default -> throw new IllegalArgumentException(ErrorMessages.UNSUPPORTED_DATABASE + database); + }; } /* ==================== 原有功能保持不变 ==================== */ @@ -459,12 +459,40 @@ public class RedisUtil { } /** - * 数据库2专用:存储调度工单并设置过期时间 + * 数据库2专用:移除调度工单并设置过期时间 */ public void deleteDispatchOrder(Long orderId) { delete(Database.DB2, BIKE_DISPATCH_ORDER_PREFIX + orderId); } + /** + * 数据库2专用:存储需要定期巡检车辆并设置过期时间 + */ + public void saveInspectionOrder(String bikeCode, Object bikeInfo, long timeout, TimeUnit unit) { + set(Database.DB2, BIKE_INSPECTION_ORDER_PREFIX + bikeCode, bikeInfo, timeout, unit); + } + + /** + * 数据库2专用:移除调度工单并设置过期时间 + */ + public void deleteInspectionOrder(String bikeCode) { + delete(Database.DB2, BIKE_INSPECTION_ORDER_PREFIX + bikeCode); + } + + /** + * 数据库2专用:分布式锁(针对巡检任务) + */ + public Boolean tryInspectionLock(String lockKey, Object value, long timeout, TimeUnit unit) { + return getRedisTemplate(Database.DB2).opsForValue().setIfAbsent(lockKey, value, timeout, unit); + } + + /** + * 数据库2专用:释放调度锁 + */ + public Boolean releaseInspectionLock(String lockKey) { + return delete(Database.DB2, lockKey); + } + /** * 数据库2专用:分布式锁(针对调度任务) */