chore: 【ops】保洁员Dispatch初步搭建
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
package com.viewsh.module.ops.environment.service.dispatch;
|
||||
|
||||
import com.viewsh.module.ops.api.queue.OrderQueueDTO;
|
||||
import com.viewsh.module.ops.dal.dataobject.cleaner.OpsCleanerStatusDO;
|
||||
import com.viewsh.module.ops.enums.CleanerStatusEnum;
|
||||
import com.viewsh.module.ops.enums.PriorityEnum;
|
||||
import com.viewsh.module.ops.environment.service.cleaner.CleanerStatusService;
|
||||
import com.viewsh.module.ops.service.dispatch.DispatchStrategy;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 保洁区域优先派单策略
|
||||
* 优先分配给该区域的空闲保洁员,按以下规则排序:
|
||||
* 1. 状态必须是IDLE(空闲)
|
||||
* 2. 优先选择同区域的保洁员
|
||||
* 3. 考虑保洁员的工作量平衡
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CleanerAreaPriorityStrategy implements DispatchStrategy {
|
||||
|
||||
@Resource
|
||||
private CleanerStatusService cleanerStatusService;
|
||||
|
||||
@Resource
|
||||
private com.viewsh.module.ops.service.dispatch.DispatchEngineServiceImpl dispatchEngineService;
|
||||
|
||||
/**
|
||||
* 策略名称
|
||||
*/
|
||||
private static final String STRATEGY_NAME = "cleaner_area_priority";
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// 注册策略到派单引擎
|
||||
dispatchEngineService.registerStrategy(this);
|
||||
// 注册执行人员类型与策略的映射
|
||||
dispatchEngineService.registerAssigneeTypeStrategy("CLEANER", STRATEGY_NAME);
|
||||
log.info("保洁区域优先派单策略已注册: strategyName={}", STRATEGY_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return STRATEGY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long recommendAssignee(OrderQueueDTO queueDTO) {
|
||||
log.info("执行保洁区域优先派单策略: opsOrderId={}", queueDTO.getOpsOrderId());
|
||||
|
||||
// 在新的队列模型中,推荐逻辑应该在工单创建时完成
|
||||
// 这里主要用于手动派单或重新派单的场景
|
||||
// TODO: 从 eventPayload 中解析区域ID,然后查询可用保洁员
|
||||
log.warn("在新队列模型中,推荐保洁员应该在工单创建时完成: opsOrderId={}",
|
||||
queueDTO.getOpsOrderId());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInterrupt(Long currentAssigneeId, Long currentOrderId, Long urgentOrderId) {
|
||||
log.warn("判断是否可打断保洁员任务: assigneeId={}, currentOrderId={}, urgentOrderId={}",
|
||||
currentAssigneeId, currentOrderId, urgentOrderId);
|
||||
|
||||
// P0任务可以打断P1/P2/P3任务
|
||||
// 简化处理:默认允许打断
|
||||
log.info("允许打断:紧急任务优先级更高: urgentOrderId={}, currentOrderId={}",
|
||||
urgentOrderId, currentOrderId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择最佳保洁员
|
||||
* 综合考虑以下因素:
|
||||
* 1. 是否在该区域(已过滤)
|
||||
* 2. 当前工作量(已完成的工单数少优先)
|
||||
*/
|
||||
private OpsCleanerStatusDO selectBestCleaner(List<OpsCleanerStatusDO> cleaners,
|
||||
OrderQueueDTO queueDTO) {
|
||||
// 使用Comparator进行多条件排序
|
||||
return cleaners.stream()
|
||||
.max(Comparator
|
||||
// 1. 电量充足优先(电量>20%)
|
||||
.comparing((OpsCleanerStatusDO cleaner) ->
|
||||
cleaner.getBatteryLevel() != null && cleaner.getBatteryLevel() > 20 ? 1 : 0)
|
||||
// 2. 心跳时间最新的优先(在线状态好)
|
||||
.thenComparing(OpsCleanerStatusDO::getLastHeartbeatTime, Comparator.nullsLast(Comparator.naturalOrder()))
|
||||
// 3. 没有当前工单的<E58D95><E79A84><EFBFBD>先(完全空闲)
|
||||
.thenComparing((OpsCleanerStatusDO cleaner) ->
|
||||
cleaner.getCurrentOpsOrderId() == null ? 1 : 0)
|
||||
// 4. 工作量少的优先(状态变更时间早的说明刚完成)
|
||||
.thenComparing(OpsCleanerStatusDO::getUpdateTime, Comparator.nullsLast(Comparator.naturalOrder()))
|
||||
)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为新工单推荐保洁员(工单创建时调用)
|
||||
* 这是主要的推荐方法,用于在工单创建时选择合适的保洁员
|
||||
*
|
||||
* @param areaId 区域ID
|
||||
* @param priority 工单优先级
|
||||
* @return 推荐的保洁员ID,如果没有合适的返回null
|
||||
*/
|
||||
public Long recommendCleanerForNewOrder(Long areaId, PriorityEnum priority) {
|
||||
log.info("为新工单推荐保洁员: areaId={}, priority={}", areaId, priority);
|
||||
|
||||
// TODO-lzh:目前场景是单个区域绑定单个保洁员(所以只需查出对应保洁返回即可)
|
||||
|
||||
// 查询该区域的可用保洁员(IDLE状态)
|
||||
List<OpsCleanerStatusDO> availableCleaners =
|
||||
cleanerStatusService.listAvailableCleaners(areaId);
|
||||
|
||||
if (availableCleaners.isEmpty()) {
|
||||
log.warn("该区域没有可用保洁员: areaId={}", areaId);
|
||||
return null;
|
||||
}
|
||||
|
||||
log.info("找到 {} 个可用保洁员: areaId={}", availableCleaners.size(), areaId);
|
||||
|
||||
// 对于P0紧急任务,优先选择电量充足、心跳最新的
|
||||
if (priority.isUrgent()) {
|
||||
log.warn("为P0紧急任务推荐保洁员,优先选择最佳保洁员");
|
||||
}
|
||||
|
||||
// 选择最佳保洁员
|
||||
OpsCleanerStatusDO selectedCleaner = selectBestCleanerById(availableCleaners);
|
||||
|
||||
if (selectedCleaner != null) {
|
||||
log.info("为新工单推荐保洁员: areaId={}, priority={}, cleanerId={}, cleanerName={}",
|
||||
areaId, priority, selectedCleaner.getUserId(), selectedCleaner.getUserName());
|
||||
return selectedCleaner.getUserId();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从保洁员列表中选择最佳保洁员(简化版本,不需要 queueDTO)
|
||||
*/
|
||||
private OpsCleanerStatusDO selectBestCleanerById(List<OpsCleanerStatusDO> cleaners) {
|
||||
return cleaners.stream()
|
||||
.max(Comparator
|
||||
// 1. 电量充足优先(电量>20%)
|
||||
.comparing((OpsCleanerStatusDO cleaner) ->
|
||||
cleaner.getBatteryLevel() != null && cleaner.getBatteryLevel() > 20 ? 1 : 0)
|
||||
// 2. 心跳时间最新的优先(在线状态好)
|
||||
.thenComparing(OpsCleanerStatusDO::getLastHeartbeatTime, Comparator.nullsLast(Comparator.naturalOrder()))
|
||||
// 3. 没有当前工单的优先(完全空闲)
|
||||
.thenComparing((OpsCleanerStatusDO cleaner) ->
|
||||
cleaner.getCurrentOpsOrderId() == null ? 1 : 0)
|
||||
)
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user