Compare commits
No commits in common. "c34d9e69cc531b45a1221ec380a3ede923fd6f16fbcb3eb34867d358b66468e2" and "08d6256fd5e5f3163d6e62c909bb8ca0076e1314273224cae32ddd9695ba4108" have entirely different histories.
c34d9e69cc
...
08d6256fd5
@ -10,7 +10,6 @@ import com.mybatisflex.core.query.QueryWrapper;
|
|||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -19,24 +18,22 @@ import static com.cdzy.operations.model.entity.table.EbikeBikeOrderTableDef.EBIK
|
|||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SafeOrderExpirationListener {
|
public class SafeOrderExpirationListener {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ServiceInstanceManager instanceManager;
|
private ServiceInstanceManager instanceManager;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private EbikeBikeOrderService orderService;
|
private EbikeBikeOrderService orderService;
|
||||||
|
|
||||||
|
|
||||||
// Redis键常量
|
// Redis键常量
|
||||||
private static final String ORDER_KEY_PREFIX = "bike:dispatch:lock:";
|
private static final String ORDER_KEY_PREFIX = "bike:dispatch:lock:";
|
||||||
|
|
||||||
private static final long LOCK_WAIT_TIME = 5L; // 获取锁等待时间(秒)
|
private static final long LOCK_WAIT_TIME = 5L; // 获取锁等待时间(秒)
|
||||||
|
|
||||||
private static final String CODE_KEY_PREFIX = "bike:no:lock:";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理Redis键过期事件
|
* 处理Redis键过期事件
|
||||||
*/
|
*/
|
||||||
@ -58,23 +55,6 @@ public class SafeOrderExpirationListener {
|
|||||||
return null;
|
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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,33 +89,6 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安全释放锁
|
* 安全释放锁
|
||||||
*/
|
*/
|
||||||
@ -150,13 +103,13 @@ public class SafeOrderExpirationListener {
|
|||||||
log.error("释放锁异常: lockKey={}", lockKey, e);
|
log.error("释放锁异常: lockKey={}", lockKey, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实际的业务处理逻辑
|
* 实际的业务处理逻辑
|
||||||
*/
|
*/
|
||||||
private void processExpiredOrderBusiness(String orderId) {
|
private void processExpiredOrderBusiness(String orderId) {
|
||||||
try {
|
try {
|
||||||
//处理订单,该订单为到期未有用户骑行的调度订单
|
//处理订单,该订单为到期未有用户骑行的调度订单
|
||||||
QueryWrapper query = QueryWrapper.create()
|
QueryWrapper query = QueryWrapper.create()
|
||||||
.where(EBIKE_BIKE_ORDER.ORDER_ID.eq(orderId))
|
.where(EBIKE_BIKE_ORDER.ORDER_ID.eq(orderId))
|
||||||
.where(EBIKE_BIKE_ORDER.ORDER_TYPE.eq(BikeOrderType.DISPATCH))
|
.where(EBIKE_BIKE_ORDER.ORDER_TYPE.eq(BikeOrderType.DISPATCH))
|
||||||
@ -171,36 +124,13 @@ public class SafeOrderExpirationListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 实际的业务处理逻辑
|
|
||||||
*/
|
|
||||||
private void processCreateDispatchOrderBusiness(String bikeCode) {
|
|
||||||
try {
|
|
||||||
// 生成调度工单
|
|
||||||
orderService.createDispatchSwapOrder(bikeCode);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("生成调度工单业务异常: orderId={}", bikeCode, e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从Redis键中提取订单ID
|
* 从Redis键中提取订单ID
|
||||||
*/
|
*/
|
||||||
private String extractOrderIdFromKey(String redisKey) {
|
private String extractOrderIdFromKey(String redisKey) {
|
||||||
if (redisKey.startsWith(RedisUtil.BIKE_DISPATCH_ORDER_PREFIX)) {
|
if (redisKey.startsWith(RedisUtil.BIKE_DISPATCH_ORDER_PREFIX)) {
|
||||||
return redisKey.substring(RedisUtil.BIKE_DISPATCH_ORDER_PREFIX.length());
|
return redisKey.substring(ORDER_KEY_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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import java.io.Serializable;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 车辆调度配置 实体类
|
* 车辆相关配置 实体类
|
||||||
*
|
*
|
||||||
* @author yanglei
|
* @author yanglei
|
||||||
* @since 2025-12-03 16:18
|
* @since 2025-12-03 16:18
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public interface EbikeDispatchConfigurationService extends IService<EbikeDispatc
|
|||||||
*
|
*
|
||||||
* @return 配置项
|
* @return 配置项
|
||||||
*/
|
*/
|
||||||
EbikeDispatchConfiguration getConfigurationByOperationId(Long operationId);
|
EbikeDispatchConfiguration getConfigurationByOperationId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置项保存
|
* 配置项保存
|
||||||
|
|||||||
@ -12,7 +12,10 @@ import com.cdzy.operations.model.dto.EbikeUserBikeInfo;
|
|||||||
import com.cdzy.operations.model.dto.EbikeUserLockDto;
|
import com.cdzy.operations.model.dto.EbikeUserLockDto;
|
||||||
import com.cdzy.operations.model.entity.*;
|
import com.cdzy.operations.model.entity.*;
|
||||||
import com.cdzy.operations.model.vo.*;
|
import com.cdzy.operations.model.vo.*;
|
||||||
import com.cdzy.operations.service.*;
|
import com.cdzy.operations.service.EbikeBikeInfoService;
|
||||||
|
import com.cdzy.operations.service.EbikeEcuInfoService;
|
||||||
|
import com.cdzy.operations.service.EbikeInventoryRecordService;
|
||||||
|
import com.cdzy.operations.service.EbikeInventoryService;
|
||||||
import com.cdzy.operations.utils.RedisUtil;
|
import com.cdzy.operations.utils.RedisUtil;
|
||||||
import com.mybatisflex.core.paginate.Page;
|
import com.mybatisflex.core.paginate.Page;
|
||||||
import com.mybatisflex.core.query.QueryWrapper;
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
@ -32,7 +35,6 @@ import java.time.LocalTime;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static com.cdzy.operations.model.entity.table.EbikeBatteryInfoTableDef.EBIKE_BATTERY_INFO;
|
import static com.cdzy.operations.model.entity.table.EbikeBatteryInfoTableDef.EBIKE_BATTERY_INFO;
|
||||||
import static com.cdzy.operations.model.entity.table.EbikeBikeInfoTableDef.EBIKE_BIKE_INFO;
|
import static com.cdzy.operations.model.entity.table.EbikeBikeInfoTableDef.EBIKE_BIKE_INFO;
|
||||||
@ -94,9 +96,6 @@ public class EbikeBikeInfoServiceImpl extends ServiceImpl<EbikeBikeInfoMapper, E
|
|||||||
@Resource
|
@Resource
|
||||||
private EbikeBikeOrderMapper orderMapper;
|
private EbikeBikeOrderMapper orderMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private EbikeDispatchConfigurationService dispatchConfigurationService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void bind(EbikeBikeBindVo bindVo) {
|
public void bind(EbikeBikeBindVo bindVo) {
|
||||||
@ -467,10 +466,6 @@ public class EbikeBikeInfoServiceImpl extends ServiceImpl<EbikeBikeInfoMapper, E
|
|||||||
|
|
||||||
ebikeEcuInfoService.lock(ebikeEcuInfo);
|
ebikeEcuInfoService.lock(ebikeEcuInfo);
|
||||||
|
|
||||||
EbikeDispatchConfiguration configuration = dispatchConfigurationService.getConfigurationByOperationId(info.getOperatorId());
|
|
||||||
|
|
||||||
redisUtil.saveNoDocument(bikeCode, "2", configuration.getDispatchDuration(), TimeUnit.HOURS);
|
|
||||||
|
|
||||||
this.mapper.update(info);
|
this.mapper.update(info);
|
||||||
|
|
||||||
return EbikeUserLockDto.builder()
|
return EbikeUserLockDto.builder()
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import com.mybatisflex.spring.service.impl.ServiceImpl;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static com.cdzy.operations.model.entity.table.EbikeDispatchConfigurationTableDef.EBIKE_DISPATCH_CONFIGURATION;
|
import static com.cdzy.operations.model.entity.table.EbikeDispatchConfigurationTableDef.EBIKE_DISPATCH_CONFIGURATION;
|
||||||
|
|
||||||
@ -25,10 +24,9 @@ import static com.cdzy.operations.model.entity.table.EbikeDispatchConfigurationT
|
|||||||
public class EbikeDispatchConfigurationServiceImpl extends ServiceImpl<EbikeDispatchConfigurationMapper, EbikeDispatchConfiguration> implements EbikeDispatchConfigurationService {
|
public class EbikeDispatchConfigurationServiceImpl extends ServiceImpl<EbikeDispatchConfigurationMapper, EbikeDispatchConfiguration> implements EbikeDispatchConfigurationService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EbikeDispatchConfiguration getConfigurationByOperationId(Long operationId) {
|
public EbikeDispatchConfiguration getConfigurationByOperationId() {
|
||||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
.select(EBIKE_DISPATCH_CONFIGURATION.ALL_COLUMNS)
|
.select(EBIKE_DISPATCH_CONFIGURATION.ALL_COLUMNS);
|
||||||
.and(EBIKE_DISPATCH_CONFIGURATION.OPERATOR_ID.eq(operationId, Objects.nonNull(operationId)));
|
|
||||||
return this.mapper.selectOneByQuery(queryWrapper);
|
return this.mapper.selectOneByQuery(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,9 +41,6 @@ public class RedisUtil {
|
|||||||
// 车辆信息相关
|
// 车辆信息相关
|
||||||
public static final String BIKE_ECU_PREFIX = "bike:ecu:";
|
public static final String BIKE_ECU_PREFIX = "bike:ecu:";
|
||||||
|
|
||||||
// 车辆多长时间无单生成调度工单
|
|
||||||
public static final String BIKE_NO_DOCUMENT_PREFIX = "bike:no:document:";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误消息常量
|
* 错误消息常量
|
||||||
@ -479,35 +476,6 @@ public class RedisUtil {
|
|||||||
return delete(Database.DB2, lockKey);
|
return delete(Database.DB2, lockKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据库2专用:存储 车辆多长时间无单生成调度工单 并设置过期时间
|
|
||||||
*/
|
|
||||||
public void saveNoDocument(String bikeCode, Object orderData, long timeout, TimeUnit unit) {
|
|
||||||
set(Database.DB2, BIKE_NO_DOCUMENT_PREFIX + bikeCode, orderData, timeout, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据库2专用:存储 辆多长时间无单生成调度工单 并设置过期时间
|
|
||||||
*/
|
|
||||||
public void deleteNoDocument(String bikeCode) {
|
|
||||||
delete(Database.DB2, BIKE_NO_DOCUMENT_PREFIX + bikeCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据库2专用:分布式锁(针对调度任务)
|
|
||||||
*/
|
|
||||||
public Boolean tryNoDocumentLock(String lockKey, Object value, long timeout, TimeUnit unit) {
|
|
||||||
return getRedisTemplate(Database.DB2).opsForValue().setIfAbsent(lockKey, value, timeout, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据库2专用:释放调度锁
|
|
||||||
*/
|
|
||||||
public Boolean releaseNoDocumentLock(String lockKey) {
|
|
||||||
return delete(Database.DB2, lockKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据库2专用:存储中控信息
|
* 数据库2专用:存储中控信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user