fix: prevent queue bypass when unlock_status missing
This commit is contained in:
parent
4f9e17a1fc
commit
2331aec8c0
@ -16,21 +16,38 @@ public interface TaskUnlockStatusMapper extends BaseMapper<TaskUnlockStatus> {
|
||||
/**
|
||||
* 获取指定部门已解锁的任务ID列表
|
||||
*/
|
||||
@Select("SELECT task_item_id FROM task_unlock_status WHERE dept_id = #{deptId} AND is_unlocked = 1")
|
||||
@Select("SELECT tus.task_item_id FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 1 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned')")
|
||||
List<Long> selectUnlockedTaskItemIds(@Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
* 获取指定部门未解锁的任务ID列表(按发布日期排序)
|
||||
*/
|
||||
@Select("SELECT task_item_id FROM task_unlock_status " +
|
||||
"WHERE dept_id = #{deptId} AND is_unlocked = 0 " +
|
||||
"ORDER BY publish_date ASC, id ASC")
|
||||
@Select("SELECT tus.task_item_id FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 0 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned') " +
|
||||
"ORDER BY tus.publish_date ASC, tus.id ASC")
|
||||
List<Long> selectQueuedTaskItemIds(@Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
* 获取指定部门未解锁任务总数
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM task_unlock_status WHERE dept_id = #{deptId} AND is_unlocked = 0")
|
||||
@Select("SELECT COUNT(*) FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 0 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned')")
|
||||
int countQueuedTasks(@Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
@ -38,7 +55,11 @@ public interface TaskUnlockStatusMapper extends BaseMapper<TaskUnlockStatus> {
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 1 AND ti.status = 'pending'")
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 1 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned')")
|
||||
int countUnlockedPendingTasks(@Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
@ -46,7 +67,11 @@ public interface TaskUnlockStatusMapper extends BaseMapper<TaskUnlockStatus> {
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 0 AND ti.status = 'pending'")
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 0 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned')")
|
||||
int countQueuedPendingTasks(@Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
@ -65,9 +90,14 @@ public interface TaskUnlockStatusMapper extends BaseMapper<TaskUnlockStatus> {
|
||||
/**
|
||||
* 获取指定部门按发布日期分组的未解锁任务统计
|
||||
*/
|
||||
@Select("SELECT publish_date, COUNT(*) as count FROM task_unlock_status " +
|
||||
"WHERE dept_id = #{deptId} AND is_unlocked = 0 " +
|
||||
"GROUP BY publish_date ORDER BY publish_date ASC")
|
||||
@Select("SELECT tus.publish_date, COUNT(*) as count FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 0 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned') " +
|
||||
"GROUP BY tus.publish_date ORDER BY tus.publish_date ASC")
|
||||
@Results({
|
||||
@Result(column = "publish_date", property = "publishDate"),
|
||||
@Result(column = "count", property = "count")
|
||||
@ -77,20 +107,30 @@ public interface TaskUnlockStatusMapper extends BaseMapper<TaskUnlockStatus> {
|
||||
/**
|
||||
* 获取需要解锁的任务ID列表(按日期顺序,限制数量)
|
||||
*/
|
||||
@Select("SELECT task_item_id FROM task_unlock_status " +
|
||||
"WHERE dept_id = #{deptId} AND is_unlocked = 0 " +
|
||||
"ORDER BY publish_date ASC, id ASC " +
|
||||
@Select("SELECT tus.task_item_id FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 0 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned') " +
|
||||
"ORDER BY tus.publish_date ASC, tus.id ASC " +
|
||||
"LIMIT #{limit}")
|
||||
List<Long> selectTasksToUnlock(@Param("deptId") Long deptId, @Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* 计算任务在队列中的位置
|
||||
*/
|
||||
@Select("SELECT COUNT(*) + 1 FROM task_unlock_status " +
|
||||
"WHERE dept_id = #{deptId} AND is_unlocked = 0 " +
|
||||
"AND (publish_date < (SELECT publish_date FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId}) " +
|
||||
" OR (publish_date = (SELECT publish_date FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId}) " +
|
||||
" AND id < (SELECT id FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId})))")
|
||||
@Select("SELECT COUNT(*) + 1 FROM task_unlock_status tus " +
|
||||
"INNER JOIN task_item ti ON tus.task_item_id = ti.id " +
|
||||
"INNER JOIN task t ON ti.task_id = t.id " +
|
||||
"WHERE tus.dept_id = #{deptId} AND tus.is_unlocked = 0 " +
|
||||
" AND ti.status = 'pending' AND ti.is_locked = 0 " +
|
||||
" AND t.deleted = 0 AND t.approval_status = 'approved' " +
|
||||
" AND (t.release_type IS NULL OR t.release_type != 'assigned') " +
|
||||
" AND (tus.publish_date < (SELECT publish_date FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId} LIMIT 1) " +
|
||||
" OR (tus.publish_date = (SELECT publish_date FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId} LIMIT 1) " +
|
||||
" AND tus.id < (SELECT id FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId} LIMIT 1)))")
|
||||
int calculateQueuePosition(@Param("taskItemId") Long taskItemId, @Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
@ -99,6 +139,21 @@ public interface TaskUnlockStatusMapper extends BaseMapper<TaskUnlockStatus> {
|
||||
@Select("SELECT is_unlocked FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId}")
|
||||
Integer isUnlocked(@Param("taskItemId") Long taskItemId, @Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
* 写入排队记录(若不存在则插入)
|
||||
* <p>用于修复历史数据缺失导致的“新任务绕过排队”问题</p>
|
||||
*/
|
||||
@Insert("INSERT INTO task_unlock_status (task_item_id, dept_id, publish_date, is_unlocked, unlock_time, create_time, update_time) " +
|
||||
"SELECT #{taskItemId}, #{deptId}, #{publishDate}, #{isUnlocked}, " +
|
||||
" CASE WHEN #{isUnlocked} = 1 THEN NOW() ELSE NULL END, " +
|
||||
" NOW(), NOW() " +
|
||||
"FROM DUAL " +
|
||||
"WHERE NOT EXISTS (SELECT 1 FROM task_unlock_status WHERE task_item_id = #{taskItemId} AND dept_id = #{deptId})")
|
||||
int insertIfNotExists(@Param("taskItemId") Long taskItemId,
|
||||
@Param("deptId") Long deptId,
|
||||
@Param("publishDate") LocalDate publishDate,
|
||||
@Param("isUnlocked") Integer isUnlocked);
|
||||
|
||||
/**
|
||||
* 删除任务的解锁状态(任务取消/删除时调用)
|
||||
*/
|
||||
|
||||
@ -423,42 +423,30 @@ public class TaskUnlockService {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDate publishDate = task.getCreateTime().toLocalDate();
|
||||
int addedCount = 0;
|
||||
LocalDate publishDate = task.getCreateTime() != null ? task.getCreateTime().toLocalDate() : LocalDate.now();
|
||||
int addedRows = 0;
|
||||
|
||||
for (TaskItem item : items) {
|
||||
// 获取目标接单部门
|
||||
Long deptId = getTargetDeptId(item, task);
|
||||
if (deptId == null) {
|
||||
if (item.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Long> deptIds = resolveTargetDeptIds(item, task);
|
||||
if (deptIds.isEmpty()) {
|
||||
log.warn("子任务 {} 无法确定目标部门,跳过入队", item.getItemNo());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
TaskUnlockStatus existing = unlockStatusMapper.selectOne(
|
||||
new LambdaQueryWrapper<TaskUnlockStatus>()
|
||||
.eq(TaskUnlockStatus::getTaskItemId, item.getId())
|
||||
.eq(TaskUnlockStatus::getDeptId, deptId)
|
||||
);
|
||||
|
||||
if (existing != null) {
|
||||
continue; // 已存在,跳过
|
||||
for (Long deptId : deptIds) {
|
||||
if (deptId == null) {
|
||||
continue;
|
||||
}
|
||||
addedRows += unlockStatusMapper.insertIfNotExists(item.getId(), deptId, publishDate, 0);
|
||||
}
|
||||
|
||||
// 创建排队记录
|
||||
TaskUnlockStatus unlockStatus = new TaskUnlockStatus();
|
||||
unlockStatus.setTaskItemId(item.getId());
|
||||
unlockStatus.setDeptId(deptId);
|
||||
unlockStatus.setPublishDate(publishDate);
|
||||
unlockStatus.setIsUnlocked(0); // 未解锁
|
||||
unlockStatus.setCreateTime(LocalDateTime.now());
|
||||
|
||||
unlockStatusMapper.insert(unlockStatus);
|
||||
addedCount++;
|
||||
}
|
||||
|
||||
if (addedCount > 0) {
|
||||
log.info("任务 {} 的 {} 个子任务已加入排队池", task.getTaskNo(), addedCount);
|
||||
if (addedRows > 0) {
|
||||
log.info("任务 {} 已写入 {} 条排队记录", task.getTaskNo(), addedRows);
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,10 +532,17 @@ public class TaskUnlockService {
|
||||
Integer isUnlocked = unlockStatusMapper.isUnlocked(item.getId(), deptId);
|
||||
|
||||
if (isUnlocked == null) {
|
||||
// 没有记录,视为已解锁(兼容旧数据)
|
||||
item.setUnlockStatus("unlocked");
|
||||
item.setCanGrab(true);
|
||||
return;
|
||||
// 没有记录:补全排队记录,避免“新任务绕过排队”
|
||||
LocalDate publishDate = item.getCreateTime() != null ? item.getCreateTime().toLocalDate() : LocalDate.now();
|
||||
unlockStatusMapper.insertIfNotExists(item.getId(), deptId, publishDate, 0);
|
||||
isUnlocked = unlockStatusMapper.isUnlocked(item.getId(), deptId);
|
||||
|
||||
// 兜底:依然查不到则按排队中处理
|
||||
if (isUnlocked == null) {
|
||||
item.setUnlockStatus("queuing");
|
||||
item.setCanGrab(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isUnlocked == 1) {
|
||||
@ -611,37 +606,131 @@ public class TaskUnlockService {
|
||||
// 检查解锁状态
|
||||
Integer isUnlocked = unlockStatusMapper.isUnlocked(taskItemId, deptId);
|
||||
|
||||
// 没有记录或已解锁 = 可抢
|
||||
return isUnlocked == null || isUnlocked == 1;
|
||||
if (isUnlocked == null) {
|
||||
// 没有记录:补全排队记录,默认排队中(不可抢)
|
||||
ensureUnlockStatusExists(taskItemId, deptId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return isUnlocked == 1;
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
|
||||
/**
|
||||
* 获取任务的目标接单部门ID
|
||||
* 补全排队记录(仅补全当前部门维度)
|
||||
* <p>用于修复历史数据缺失导致的绕过排队与不可解锁问题</p>
|
||||
*/
|
||||
private Long getTargetDeptId(TaskItem item, Task task) {
|
||||
// 优先使用子任务的接单部门
|
||||
if (item.getReceiverDeptId() != null) {
|
||||
return item.getReceiverDeptId();
|
||||
private void ensureUnlockStatusExists(Long taskItemId, Long deptId) {
|
||||
if (taskItemId == null || deptId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从任务类型获取
|
||||
if (task.getDemandTypeId() != null) {
|
||||
DemandType demandType = demandTypeMapper.selectById(task.getDemandTypeId());
|
||||
if (demandType != null && demandType.getAllowedDeptIds() != null) {
|
||||
String[] deptIds = demandType.getAllowedDeptIds().split(",");
|
||||
if (deptIds.length > 0) {
|
||||
try {
|
||||
return Long.parseLong(deptIds[0].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略
|
||||
LocalDate publishDate = LocalDate.now();
|
||||
try {
|
||||
TaskItem item = taskItemMapper.selectById(taskItemId);
|
||||
if (item != null && item.getCreateTime() != null) {
|
||||
publishDate = item.getCreateTime().toLocalDate();
|
||||
}
|
||||
if (item != null && item.getTaskId() != null) {
|
||||
Task task = taskMapper.selectById(item.getTaskId());
|
||||
if (task != null && task.getCreateTime() != null) {
|
||||
publishDate = task.getCreateTime().toLocalDate();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("补全排队记录时读取任务信息失败: taskItemId={}, deptId={}, error={}", taskItemId, deptId, e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
unlockStatusMapper.insertIfNotExists(taskItemId, deptId, publishDate, 0);
|
||||
} catch (Exception e) {
|
||||
log.warn("补全排队记录失败: taskItemId={}, deptId={}, error={}", taskItemId, deptId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务的目标接单部门ID列表(支持多部门 & 三级分类继承)
|
||||
*/
|
||||
private List<Long> resolveTargetDeptIds(TaskItem item, Task task) {
|
||||
if (item == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 优先使用子任务的接单部门(明确指定)
|
||||
if (item.getReceiverDeptId() != null) {
|
||||
return Collections.singletonList(item.getReceiverDeptId());
|
||||
}
|
||||
|
||||
// 优先使用子任务类型配置(流水线任务主任务 demandTypeId 常为空)
|
||||
Long taskTypeId = item.getTaskTypeId();
|
||||
if (taskTypeId == null && task != null) {
|
||||
taskTypeId = task.getDemandTypeId() != null ? task.getDemandTypeId() : task.getTaskTypeId();
|
||||
}
|
||||
|
||||
String allowedDeptIds = null;
|
||||
if (taskTypeId != null) {
|
||||
DemandType demandType = demandTypeMapper.selectById(taskTypeId);
|
||||
if (demandType != null) {
|
||||
allowedDeptIds = demandType.getAllowedDeptIds();
|
||||
if ((allowedDeptIds == null || allowedDeptIds.trim().isEmpty()) && demandType.getParentId() != null) {
|
||||
DemandType parentType = demandTypeMapper.selectById(demandType.getParentId());
|
||||
if (parentType != null) {
|
||||
allowedDeptIds = parentType.getAllowedDeptIds();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
List<Long> parsedDeptIds = parseDeptIds(allowedDeptIds);
|
||||
if (!parsedDeptIds.isEmpty()) {
|
||||
return parsedDeptIds;
|
||||
}
|
||||
|
||||
// 未配置则视为所有接单部门可接:写入所有接单部门队列
|
||||
List<Department> receivingDepts = departmentMapper.selectList(
|
||||
new LambdaQueryWrapper<Department>()
|
||||
.eq(Department::getDeptType, "receiving")
|
||||
.eq(Department::getStatus, 1)
|
||||
);
|
||||
if (receivingDepts == null || receivingDepts.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Long> deptIds = new ArrayList<>();
|
||||
for (Department dept : receivingDepts) {
|
||||
if (dept != null && dept.getId() != null) {
|
||||
deptIds.add(dept.getId());
|
||||
}
|
||||
}
|
||||
return deptIds;
|
||||
}
|
||||
|
||||
private List<Long> parseDeptIds(String allowedDeptIds) {
|
||||
if (allowedDeptIds == null || allowedDeptIds.trim().isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String[] parts = allowedDeptIds.split(",");
|
||||
List<Long> result = new ArrayList<>();
|
||||
Set<Long> seen = new HashSet<>();
|
||||
for (String part : parts) {
|
||||
if (part == null) {
|
||||
continue;
|
||||
}
|
||||
String s = part.trim();
|
||||
if (s.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Long id = Long.parseLong(s);
|
||||
if (seen.add(id)) {
|
||||
result.add(id);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user