feat(ops): 新增通用人员调度状态服务 UserDispatchStatusService

基于 Redis Hash 维护人员维度的调度状态,供安保/工程/客服业务线共用。
与保洁的 BadgeDeviceStatusService(设备维度)并行。

核心设计:
- Redis Key: ops:user:dispatch:{userId},存储 status/activeOrderCount/waitingTaskCount 等
- 所有写操作使用 Lua 脚本原子执行,保证多业务线并发安全
- 事件监听器 @EventListener(事务内同步)自动排除 CLEAN 类型
- Redis 丢数据时降级为 IDLE,下一次事件自动重建(自愈)

新增文件:
- UserDispatchStatusDTO (ops-api)
- UserDispatchStatusService 接口 (ops-biz)
- UserDispatchStatusServiceImpl - Lua 脚本实现 (ops-biz)
- UserDispatchStatusEventListener - 通用事件监听 (ops-biz)
- UserAssigneeStatusAdapter - AssigneeStatus 适配器 (ops-biz)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-03-25 15:44:42 +08:00
parent 53f51e7336
commit 9115e03878
6 changed files with 958 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
package com.viewsh.module.ops.api.dispatch;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 人员调度状态 DTO
* <p>
* 对应 Redis Hash: ops:user:dispatch:{userId}
* <p>
* 适用于安保、工程、客服等人员维度的业务线。
* 保洁使用设备维度的 {@code BadgeDeviceStatusDTO}。
*
* @author lzh
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserDispatchStatusDTO {
/**
* 人员ID
*/
private Long userId;
/**
* 调度状态IDLE / BUSY / PAUSED
*/
private String status;
/**
* 当前执行中的工单IDDISPATCHED/CONFIRMED/ARRIVED 状态)
*/
private Long currentOrderId;
/**
* 当前工单类型SECURITY / REPAIR / SERVICE
*/
private String currentOrderType;
/**
* 当前工单状态DISPATCHED / CONFIRMED / ARRIVED
*/
private String currentOrderStatus;
/**
* 活跃工单总数(含直接派单 + 排队中,不依赖队列记录)
*/
private Integer activeOrderCount;
/**
* 队列中 WAITING 状态的任务数
*/
private Integer waitingTaskCount;
/**
* 最后更新时间戳(毫秒)
*/
private Long lastUpdateTime;
public boolean isIdle() {
return "IDLE".equals(status) || status == null;
}
public boolean isBusy() {
return "BUSY".equals(status);
}
public boolean isPaused() {
return "PAUSED".equals(status);
}
/**
* 获取活跃工单数null 安全)
*/
public int getActiveOrderCountSafe() {
return activeOrderCount != null ? activeOrderCount : 0;
}
/**
* 获取等待任务数null 安全)
*/
public int getWaitingTaskCountSafe() {
return waitingTaskCount != null ? waitingTaskCount : 0;
}
}