Compare commits

...

2 Commits

Author SHA256 Message Date
PC
f872f77afe Merge remote-tracking branch 'origin/main' 2026-02-26 16:18:11 +08:00
PC
aa213a78d1 定期巡检工单:基础支持 2026-02-26 16:18:04 +08:00
2 changed files with 110 additions and 12 deletions

View File

@ -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;
}
}

View File

@ -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<String, Object> 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专用分布式锁针对调度任务
*/