833 lines
30 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.designhub.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.designhub.entity.*;
import com.designhub.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* 任务排队解锁服务
* 实现任务排队机制,确保先发布的任务优先被处理
*/
@Service
@Slf4j
public class TaskUnlockService {
@Autowired
private TaskUnlockStatusMapper unlockStatusMapper;
@Autowired
private TaskItemMapper taskItemMapper;
@Autowired
private TaskMapper taskMapper;
@Autowired
private DepartmentMapper departmentMapper;
@Autowired
private SystemConfigService systemConfigService;
@Autowired
private DemandTypeMapper demandTypeMapper;
@Autowired
private DepartmentService departmentService;
@Autowired
private TaskStatusNotificationService taskStatusNotificationService;
private static final int DEFAULT_DAILY_UNLOCK_MIN = 5;
private static final int DEFAULT_BATCH_SIZE = 5;
/**
* 获取 TaskUnlockStatusMapper(供其他 Service 使用)
*/
public TaskUnlockStatusMapper getUnlockStatusMapper() {
return unlockStatusMapper;
}
// ==================== 配置相关 ====================
/**
* 检查排队解锁功能是否启用
*/
public boolean isUnlockEnabled() {
String enabled = systemConfigService.getConfigValue("task_unlock_enabled", "false");
return "true".equalsIgnoreCase(enabled);
}
/**
* 获取每日最少解锁数
*/
public int getDailyUnlockMin() {
String value = systemConfigService.getConfigValue("task_unlock_daily_min", String.valueOf(DEFAULT_DAILY_UNLOCK_MIN));
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return DEFAULT_DAILY_UNLOCK_MIN;
}
}
/**
* 获取解锁批次大小
*/
public int getBatchSize() {
String value = systemConfigService.getConfigValue("task_unlock_batch_size", String.valueOf(DEFAULT_BATCH_SIZE));
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return DEFAULT_BATCH_SIZE;
}
}
// ==================== 定时任务 ====================
/**
* 每日解锁任务(每天凌晨1点执行)
*/
@Scheduled(cron = "0 0 1 * * ?")
public void dailyUnlockTask() {
if (!isUnlockEnabled()) {
log.info("任务排队解锁功能已关闭,跳过每日解锁任务");
return;
}
log.info("========== 开始执行每日任务解锁 ==========");
// 获取所有接单部门
List<Department> receivingDepts = departmentMapper.selectList(
new LambdaQueryWrapper<Department>()
.eq(Department::getDeptType, "receiving")
.eq(Department::getStatus, 1)
);
int totalUnlocked = 0;
int dailyMin = getDailyUnlockMin();
for (Department dept : receivingDepts) {
try {
int count = unlockTasksForDept(dept.getId(), dailyMin);
totalUnlocked += count;
if (count > 0) {
log.info("部门 {} ({}) 解锁了 {} 个任务", dept.getDeptName(), dept.getId(), count);
}
} catch (Exception e) {
log.error("部门 {} 解锁任务失败: {}", dept.getId(), e.getMessage(), e);
}
}
log.info("========== 每日任务解锁完成,共解锁 {} 个任务 ==========", totalUnlocked);
}
// ==================== 核心解锁逻辑 ====================
/**
* 为指定部门解锁任务
* @param deptId 部门ID
* @param minCount 最少解锁数量
* @return 实际解锁数量
*/
@Transactional(rollbackFor = Exception.class)
public int unlockTasksForDept(Long deptId, int minCount) {
// 1. 检查当前已解锁但未接单的任务数
int currentUnlockedPending = unlockStatusMapper.countUnlockedPendingTasks(deptId);
// 如果已有足够的可接任务,不需要解锁更多
if (currentUnlockedPending >= minCount) {
log.debug("部门 {} 已有 {} 个可接任务,无需解锁更多", deptId, currentUnlockedPending);
return 0;
}
// 2. 计算需要解锁的数量
int needToUnlock = minCount - currentUnlockedPending;
// 3. 获取待解锁的任务ID列表(按发布日期排序)
List<Long> tasksToUnlock = unlockStatusMapper.selectTasksToUnlock(deptId, needToUnlock);
if (tasksToUnlock.isEmpty()) {
log.debug("部门 {} 没有待解锁的任务", deptId);
return 0;
}
// 4. 批量解锁
int unlocked = unlockStatusMapper.batchUnlock(deptId, tasksToUnlock);
log.info("部门 {} 成功解锁 {} 个任务", deptId, unlocked);
// 5. 发送解锁通知给需求人
try {
notifyUnlockedTasks(tasksToUnlock);
} catch (Exception e) {
log.error("发送解锁通知失败: {}", e.getMessage());
// 不影响解锁流程
}
return unlocked;
}
// ==================== 应急解锁(管理员/机器人调用) ====================
/**
* 应急解锁:将指定任务(主任务编号/子任务编号)从排队中直接解锁到大厅
* <p>
* - 如果传入的是子任务编号(itemNo),仅解锁该子任务(仅限 pending)
* - 如果传入的是主任务编号(taskNo),解锁该主任务下所有 pending 子任务(仅解锁排队中的)
* </p>
*/
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> emergencyUnlockToHall(String taskNoOrItemNo) {
String key = taskNoOrItemNo == null ? "" : taskNoOrItemNo.trim();
if (key.isEmpty()) {
throw new IllegalArgumentException("任务编号不能为空");
}
TaskItem item = taskItemMapper.selectOne(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getItemNo, key)
.last("LIMIT 1")
);
if (item != null) {
return emergencyUnlockTaskItemToHall(item);
}
Task task = taskMapper.selectOne(
new LambdaQueryWrapper<Task>()
.eq(Task::getTaskNo, key)
.last("LIMIT 1")
);
if (task != null) {
return emergencyUnlockTaskToHall(task);
}
throw new IllegalArgumentException("任务不存在,请检查编号是否正确");
}
/**
* 应急解锁:按主任务编号解锁
*/
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> emergencyUnlockToHallByTaskNo(String taskNo) {
String key = taskNo == null ? "" : taskNo.trim();
if (key.isEmpty()) {
throw new IllegalArgumentException("任务编号不能为空");
}
Task task = taskMapper.selectOne(
new LambdaQueryWrapper<Task>()
.eq(Task::getTaskNo, key)
.last("LIMIT 1")
);
if (task == null) {
throw new IllegalArgumentException("任务不存在,请检查任务编号是否正确");
}
return emergencyUnlockTaskToHall(task);
}
private Map<String, Object> emergencyUnlockTaskItemToHall(TaskItem item) {
if (item.getTaskId() == null) {
throw new IllegalArgumentException("子任务缺少主任务ID,无法解锁");
}
Task task = taskMapper.selectById(item.getTaskId());
if (task == null) {
throw new IllegalArgumentException("主任务不存在,无法解锁");
}
if ("assigned".equals(task.getReleaseType())) {
Map<String, Object> result = new HashMap<>();
result.put("type", "item");
result.put("taskNo", task.getTaskNo());
result.put("itemNo", item.getItemNo());
result.put("taskItemId", item.getId());
result.put("unlockedItemCount", 0);
result.put("message", "指定任务不受排队限制,无需解锁");
return result;
}
if (!"pending".equals(item.getStatus())) {
throw new IllegalArgumentException("子任务状态不是待接单,无法解锁到大厅");
}
List<TaskUnlockStatus> queuedRows = unlockStatusMapper.selectList(
new LambdaQueryWrapper<TaskUnlockStatus>()
.eq(TaskUnlockStatus::getTaskItemId, item.getId())
.eq(TaskUnlockStatus::getIsUnlocked, 0)
);
if (queuedRows == null || queuedRows.isEmpty()) {
Map<String, Object> result = new HashMap<>();
result.put("type", "item");
result.put("taskNo", task.getTaskNo());
result.put("itemNo", item.getItemNo());
result.put("taskItemId", item.getId());
result.put("unlockedItemCount", 0);
result.put("message", "该子任务不在排队中或已解锁");
return result;
}
LocalDateTime now = LocalDateTime.now();
int affectedRows = unlockStatusMapper.update(
null,
new LambdaUpdateWrapper<TaskUnlockStatus>()
.eq(TaskUnlockStatus::getTaskItemId, item.getId())
.eq(TaskUnlockStatus::getIsUnlocked, 0)
.set(TaskUnlockStatus::getIsUnlocked, 1)
.set(TaskUnlockStatus::getUnlockTime, now)
.set(TaskUnlockStatus::getUpdateTime, now)
);
try {
notifyUnlockedTasks(Collections.singletonList(item.getId()));
} catch (Exception e) {
log.error("发送应急解锁通知失败: itemId={}, error={}", item.getId(), e.getMessage());
}
Map<String, Object> result = new HashMap<>();
result.put("type", "item");
result.put("taskNo", task.getTaskNo());
result.put("itemNo", item.getItemNo());
result.put("taskItemId", item.getId());
result.put("deptId", queuedRows.get(0).getDeptId());
result.put("unlockedItemCount", affectedRows > 0 ? 1 : 0);
result.put("affectedRows", affectedRows);
result.put("message", affectedRows > 0 ? "已解锁到大厅" : "解锁失败或已被解锁");
return result;
}
private Map<String, Object> emergencyUnlockTaskToHall(Task task) {
if ("assigned".equals(task.getReleaseType())) {
Map<String, Object> result = new HashMap<>();
result.put("type", "task");
result.put("taskNo", task.getTaskNo());
result.put("taskId", task.getId());
result.put("unlockedItemCount", 0);
result.put("message", "指定任务不受排队限制,无需解锁");
return result;
}
List<TaskItem> pendingItems = taskItemMapper.selectList(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getTaskId, task.getId())
.eq(TaskItem::getStatus, "pending")
);
Map<String, Object> result = new HashMap<>();
result.put("type", "task");
result.put("taskNo", task.getTaskNo());
result.put("taskId", task.getId());
result.put("pendingItemCount", pendingItems == null ? 0 : pendingItems.size());
if (pendingItems == null || pendingItems.isEmpty()) {
result.put("unlockedItemCount", 0);
result.put("message", "主任务下没有待接单子任务,无需解锁");
return result;
}
List<Long> itemIds = pendingItems.stream()
.map(TaskItem::getId)
.filter(Objects::nonNull)
.toList();
if (itemIds.isEmpty()) {
result.put("unlockedItemCount", 0);
result.put("message", "子任务ID为空,无法解锁");
return result;
}
List<TaskUnlockStatus> queuedRows = unlockStatusMapper.selectList(
new LambdaQueryWrapper<TaskUnlockStatus>()
.in(TaskUnlockStatus::getTaskItemId, itemIds)
.eq(TaskUnlockStatus::getIsUnlocked, 0)
);
if (queuedRows == null || queuedRows.isEmpty()) {
result.put("unlockedItemCount", 0);
result.put("message", "该主任务下没有排队中的待接单子任务");
return result;
}
List<Long> queuedItemIds = queuedRows.stream()
.map(TaskUnlockStatus::getTaskItemId)
.filter(Objects::nonNull)
.distinct()
.toList();
LocalDateTime now = LocalDateTime.now();
int affectedRows = unlockStatusMapper.update(
null,
new LambdaUpdateWrapper<TaskUnlockStatus>()
.in(TaskUnlockStatus::getTaskItemId, queuedItemIds)
.eq(TaskUnlockStatus::getIsUnlocked, 0)
.set(TaskUnlockStatus::getIsUnlocked, 1)
.set(TaskUnlockStatus::getUnlockTime, now)
.set(TaskUnlockStatus::getUpdateTime, now)
);
try {
notifyUnlockedTasks(queuedItemIds);
} catch (Exception e) {
log.error("发送应急解锁通知失败: taskNo={}, error={}", task.getTaskNo(), e.getMessage());
}
result.put("unlockedItemCount", queuedItemIds.size());
result.put("affectedRows", affectedRows);
result.put("message", queuedItemIds.isEmpty() ? "无可解锁子任务" : "已解锁到大厅");
return result;
}
/**
* 任务被抢单后,检查并补充解锁
*/
@Transactional(rollbackFor = Exception.class)
public void onTaskGrabbed(Long deptId) {
if (!isUnlockEnabled()) {
return;
}
// 检查剩余可接任务数
int remainingUnlocked = unlockStatusMapper.countUnlockedPendingTasks(deptId);
int batchSize = getBatchSize();
// 如果少于批次大小,补充解锁
if (remainingUnlocked < batchSize) {
int unlocked = unlockTasksForDept(deptId, batchSize);
if (unlocked > 0) {
log.info("任务被接单后,为部门 {} 补充解锁 {} 个任务", deptId, unlocked);
}
}
}
// ==================== 任务入队逻辑 ====================
/**
* 任务审核通过后,加入排队池
* @param task 主任务
* @param items 子任务列表
*/
@Transactional(rollbackFor = Exception.class)
public void onTaskApproved(Task task, List<TaskItem> items) {
if (!isUnlockEnabled()) {
log.debug("排队解锁功能未启用,跳过入队处理");
return;
}
// 指定任务不进入排队池
if ("assigned".equals(task.getReleaseType())) {
log.info("指定任务 {} 不进入排队池,直接可见", task.getTaskNo());
return;
}
LocalDate publishDate = task.getCreateTime() != null ? task.getCreateTime().toLocalDate() : LocalDate.now();
int addedRows = 0;
Set<Long> affectedDeptIds = new LinkedHashSet<>();
for (TaskItem item : items) {
if (item.getId() == null) {
continue;
}
List<Long> deptIds = resolveTargetDeptIds(item, task);
if (deptIds.isEmpty()) {
log.warn("子任务 {} 无法确定目标部门,跳过入队", item.getItemNo());
continue;
}
for (Long deptId : deptIds) {
if (deptId == null) {
continue;
}
int inserted = unlockStatusMapper.insertIfNotExists(item.getId(), deptId, publishDate, 0);
addedRows += inserted;
if (inserted > 0) {
affectedDeptIds.add(deptId);
}
}
}
if (addedRows > 0) {
log.info("任务 {} 已写入 {} 条排队记录", task.getTaskNo(), addedRows);
int minUnlockedCount = getDailyUnlockMin();
for (Long deptId : affectedDeptIds) {
try {
int unlocked = unlockTasksForDept(deptId, minUnlockedCount);
if (unlocked > 0) {
log.info("任务 {} 入队后,为部门 {} 立即补充解锁 {} 个任务", task.getTaskNo(), deptId, unlocked);
}
} catch (Exception e) {
log.error("任务 {} 入队后补充部门 {} 可接任务失败: {}", task.getTaskNo(), deptId, e.getMessage(), e);
}
}
}
}
/**
* 流水线解锁时,自动标记为已解锁(不走排队)
*/
@Transactional(rollbackFor = Exception.class)
public void onPipelineUnlock(TaskItem item, Long deptId) {
if (!isUnlockEnabled()) {
return;
}
// 检查是否已有记录
TaskUnlockStatus existing = unlockStatusMapper.selectOne(
new LambdaQueryWrapper<TaskUnlockStatus>()
.eq(TaskUnlockStatus::getTaskItemId, item.getId())
.eq(TaskUnlockStatus::getDeptId, deptId)
);
if (existing != null) {
// 更新为已解锁
existing.setIsUnlocked(1);
existing.setUnlockTime(LocalDateTime.now());
unlockStatusMapper.updateById(existing);
} else {
// 创建已解锁记录
TaskUnlockStatus unlockStatus = new TaskUnlockStatus();
unlockStatus.setTaskItemId(item.getId());
unlockStatus.setDeptId(deptId);
unlockStatus.setPublishDate(LocalDate.now());
unlockStatus.setIsUnlocked(1); // 直接解锁
unlockStatus.setUnlockTime(LocalDateTime.now());
unlockStatus.setCreateTime(LocalDateTime.now());
unlockStatusMapper.insert(unlockStatus);
}
log.info("流水线子任务 {} 已自动标记为已解锁", item.getItemNo());
}
// ==================== 查询方法 ====================
/**
* 获取已解锁的任务ID列表
*/
public List<Long> getUnlockedTaskItemIds(Long deptId) {
return unlockStatusMapper.selectUnlockedTaskItemIds(deptId);
}
/**
* 获取排队中的任务ID列表
*/
public List<Long> getQueuedTaskItemIds(Long deptId) {
return unlockStatusMapper.selectQueuedTaskItemIds(deptId);
}
/**
* 填充任务的排队状态信息
*/
public void fillUnlockStatus(TaskItem item, Long deptId, String releaseType) {
// 指定任务不显示排队状态
if ("assigned".equals(releaseType)) {
item.setUnlockStatus(null);
item.setCanGrab(true);
return;
}
// 非 pending 状态不需要排队信息
if (!"pending".equals(item.getStatus())) {
item.setUnlockStatus(null);
item.setCanGrab(false);
return;
}
// 功能未启用时,所有任务都可抢
if (!isUnlockEnabled()) {
item.setUnlockStatus("unlocked");
item.setCanGrab(true);
return;
}
// 查询解锁状态
Integer isUnlocked = unlockStatusMapper.isUnlocked(item.getId(), deptId);
if (isUnlocked == null) {
// 没有记录:补全排队记录,避免“新任务绕过排队”
ensureUnlockStatusExists(taskItemId(item), deptId);
tryTopUpDeptQueue(deptId);
isUnlocked = unlockStatusMapper.isUnlocked(item.getId(), deptId);
// 兜底:依然查不到则按排队中处理
if (isUnlocked == null) {
item.setUnlockStatus("queuing");
item.setCanGrab(false);
return;
}
}
if (isUnlocked == 1) {
// 已解锁 = 抢单中
item.setUnlockStatus("unlocked");
item.setCanGrab(true);
} else {
// 未解锁 = 排队中
item.setUnlockStatus("queuing");
item.setCanGrab(false);
// 计算排队位置
int position = unlockStatusMapper.calculateQueuePosition(item.getId(), deptId);
item.setQueuePosition(position);
// 计算总排队数
int totalQueue = unlockStatusMapper.countQueuedTasks(deptId);
item.setTotalQueueSize(totalQueue);
// 预估解锁日期(每天解锁 dailyMin 个)
int dailyMin = getDailyUnlockMin();
int daysToUnlock = (int) Math.ceil((double) position / dailyMin);
LocalDate unlockDate = LocalDate.now().plusDays(daysToUnlock);
item.setEstimatedUnlockDate(unlockDate.format(DateTimeFormatter.ofPattern("MM月dd日")));
// 计算进度百分比
int progress = totalQueue > 0 ?
(int) Math.round((totalQueue - position + 1.0) / totalQueue * 100) : 0;
item.setQueueProgress(Math.min(progress, 99)); // 最高99%,100%表示已解锁
}
}
/**
* 批量填充任务的排队状态
*/
public void batchFillUnlockStatus(List<TaskItem> items, Long deptId) {
if (items == null || items.isEmpty()) {
return;
}
for (TaskItem item : items) {
String releaseType = item.getReleaseType();
fillUnlockStatus(item, deptId, releaseType);
}
}
/**
* 检查任务是否可以抢单
*/
public boolean canGrab(Long taskItemId, Long deptId, String releaseType) {
// 指定任务始终可抢(由其他逻辑控制权限)
if ("assigned".equals(releaseType)) {
return true;
}
// 功能未启用时,都可抢
if (!isUnlockEnabled()) {
return true;
}
// 检查解锁状态
Integer isUnlocked = unlockStatusMapper.isUnlocked(taskItemId, deptId);
if (isUnlocked == null) {
// 没有记录:补全排队记录,并尝试按部门阈值补位
ensureUnlockStatusExists(taskItemId, deptId);
tryTopUpDeptQueue(deptId);
isUnlocked = unlockStatusMapper.isUnlocked(taskItemId, deptId);
return isUnlocked != null && isUnlocked == 1;
}
return isUnlocked == 1;
}
// ==================== 辅助方法 ====================
/**
* 补全排队记录(仅补全当前部门维度)
* <p>用于修复历史数据缺失导致的绕过排队与不可解锁问题</p>
*/
private void ensureUnlockStatusExists(Long taskItemId, Long deptId) {
if (taskItemId == null || deptId == null) {
return;
}
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());
}
}
private void tryTopUpDeptQueue(Long deptId) {
if (deptId == null || !isUnlockEnabled()) {
return;
}
try {
unlockTasksForDept(deptId, getDailyUnlockMin());
} catch (Exception e) {
log.warn("补齐部门可接任务失败: deptId={}, error={}", deptId, e.getMessage());
}
}
private Long taskItemId(TaskItem item) {
return item == null ? null : item.getId();
}
/**
* 获取任务的目标接单部门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();
}
}
}
}
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();
}
return departmentService.expandDeptIdsWithChildren(allowedDeptIds);
}
/**
* 删除任务的解锁状态(任务取消/删除时调用)
*/
@Transactional(rollbackFor = Exception.class)
public void deleteUnlockStatus(Long taskItemId) {
unlockStatusMapper.deleteByTaskItemId(taskItemId);
}
/**
* 通知任务解锁
*/
private void notifyUnlockedTasks(List<Long> taskItemIds) {
if (taskItemIds == null || taskItemIds.isEmpty()) {
return;
}
// 查询子任务和主任务信息
List<TaskItem> items = taskItemMapper.selectBatchIds(taskItemIds);
if (items.isEmpty()) {
return;
}
// 获取主任务信息
Set<Long> taskIds = new HashSet<>();
for (TaskItem item : items) {
if (item.getTaskId() != null) {
taskIds.add(item.getTaskId());
}
}
Map<Long, Task> taskMap = new HashMap<>();
if (!taskIds.isEmpty()) {
List<Task> tasks = taskMapper.selectBatchIds(taskIds);
for (Task task : tasks) {
taskMap.put(task.getId(), task);
}
}
// 批量通知
taskStatusNotificationService.batchNotifyTasksUnlocked(items, taskMap);
}
// ==================== 统计方法 ====================
/**
* 获取解锁状态统计
*/
public Map<String, Object> getUnlockStats() {
Map<String, Object> stats = new HashMap<>();
// 按部门统计
List<Department> depts = departmentMapper.selectList(
new LambdaQueryWrapper<Department>()
.eq(Department::getDeptType, "receiving")
.eq(Department::getStatus, 1)
);
List<Map<String, Object>> deptStats = new ArrayList<>();
long totalQueued = 0;
long totalUnlocked = 0;
for (Department dept : depts) {
Map<String, Object> deptStat = new HashMap<>();
int queuedCount = unlockStatusMapper.countQueuedPendingTasks(dept.getId());
int unlockedPendingCount = unlockStatusMapper.countUnlockedPendingTasks(dept.getId());
deptStat.put("deptId", dept.getId());
deptStat.put("deptName", dept.getDeptName());
deptStat.put("queuedCount", queuedCount);
deptStat.put("unlockedPendingCount", unlockedPendingCount);
deptStats.add(deptStat);
totalQueued += queuedCount;
totalUnlocked += unlockedPendingCount;
}
stats.put("totalQueued", totalQueued);
stats.put("totalUnlocked", totalUnlocked);
stats.put("deptStats", deptStats);
return stats;
}
}