2026-01-06 22:41:48 +08:00

2133 lines
90 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.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.designhub.common.ResultCode;
import com.designhub.dto.TaskAuditRequest;
import com.designhub.dto.TaskAssignRequest;
import com.designhub.dto.TaskUpdateDTO;
import com.designhub.entity.*;
import com.designhub.exception.BusinessException;
import com.designhub.mapper.TaskAuditLogMapper;
import com.designhub.mapper.TaskAssignLogMapper;
import com.designhub.mapper.TaskMapper;
import com.designhub.mapper.BrandMapper;
import com.designhub.mapper.CategoryMapper;
import com.designhub.mapper.PlatformMapper;
import com.designhub.mapper.DemandTypeMapper;
import com.designhub.vo.DashboardStatsVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.util.StringUtils;
/**
* 任务服务类
*/
@Service
@RequiredArgsConstructor
public class TaskService {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TaskService.class);
private final TaskMapper taskMapper;
private final TaskAuditLogMapper taskAuditLogMapper;
private final TaskAssignLogMapper taskAssignLogMapper;
private final BrandMapper brandMapper;
private final CategoryMapper categoryMapper;
private final PlatformMapper platformMapper;
private final DemandTypeMapper demandTypeMapper;
private final TeamScorePoolService teamScorePoolService;
private final ConfigService configService;
private final DelayPenaltyService delayPenaltyService;
private final UserService userService;
private final DepartmentService departmentService;
private final RoleService roleService;
private final NotificationService notificationService;
private final com.designhub.mapper.TaskItemMapper taskItemMapper;
private final DeptMonthlyScoreService deptMonthlyScoreService;
private final SystemConfigService systemConfigService;
/**
* 创建任务
*/
@Transactional(rollbackFor = Exception.class)
public Long createTask(Task task) {
log.info("创建任务: {}", task.getTitle());
// 验证用户权限:只有需求部门可以发起任务(管理员除外)
if (task.getPublisherId() != null) {
var user = userService.getUserById(task.getPublisherId());
if (user == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户不存在");
}
// 检查是否是管理员(管理员可以同时拥有需求部门和接单部门权限)
boolean isAdmin = isAdminUser(user.getId());
if (!isAdmin) {
if (user.getDeptId() == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门信息不完整");
}
var department = departmentService.getDepartmentById(user.getDeptId());
if (department == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门不存在");
}
if (!"demand".equals(department.getDeptType())) {
throw new BusinessException(ResultCode.FORBIDDEN, "只有需求部门可以发起任务,您所在的部门是接单部门");
}
}
// 设置发布人部门ID
if (task.getPublisherDeptId() == null && user.getDeptId() != null) {
task.setPublisherDeptId(user.getDeptId());
}
}
// 验证任务类型并计算积分(支持多类型)
if (task.getQuantity() != null) {
// 优先处理多类型
if (task.getTaskTypeIds() != null && !task.getTaskTypeIds().isEmpty()) {
String[] typeIds = task.getTaskTypeIds().split(",");
// 验证所有类型ID有效
for (String typeIdStr : typeIds) {
Long typeId = Long.parseLong(typeIdStr.trim());
var demandType = configService.getDemandTypeById(typeId);
if (demandType == null) {
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型ID无效: " + typeId);
}
}
// 计算总积分和工时:取第一个类型的单价
Long firstTypeId = Long.parseLong(typeIds[0].trim());
var firstType = configService.getDemandTypeById(firstTypeId);
int totalScore = firstType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).intValue();
BigDecimal totalHours = firstType.getEstimatedHours() != null ? firstType.getEstimatedHours().multiply(BigDecimal.valueOf(task.getQuantity())) : BigDecimal.ZERO;
task.setScore(totalScore);
task.setEstimatedHours(totalHours);
// 兼容性:同时保存第一个类型到task_type_id
task.setTaskTypeId(firstTypeId);
log.info("创建多类型任务: typeIds={}, score={}", task.getTaskTypeIds(), totalScore);
}
// 兼容单类型模式
else if (task.getTaskTypeId() != null) {
var demandType = configService.getDemandTypeById(task.getTaskTypeId());
if (demandType == null) {
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型不存在");
}
// 计算总积分和工时
int totalScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).intValue();
BigDecimal totalHours = demandType.getEstimatedHours() != null ? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(task.getQuantity())) : BigDecimal.ZERO;
task.setScore(totalScore);
task.setEstimatedHours(totalHours);
// 自动填充taskTypeIds字段
task.setTaskTypeIds(String.valueOf(task.getTaskTypeId()));
log.info("创建单类型任务: typeId={}, score={}", task.getTaskTypeId(), totalScore);
} else {
throw new BusinessException(ResultCode.PARAM_ERROR, "请选择任务类型");
}
}
// 检查部门积分是否充足(管理员跳过积分检查)
boolean isAdminPublisher = task.getPublisherId() != null && isAdminUser(task.getPublisherId());
if (!isAdminPublisher && task.getPublisherDeptId() != null && task.getScore() != null) {
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), task.getScore())) {
throw new BusinessException(ResultCode.INSUFFICIENT_SCORE, "部门积分余额不足");
}
// 检查周期消耗限制
if (!teamScorePoolService.checkPeriodLimit(task.getPublisherDeptId(), task.getScore())) {
throw new BusinessException(ResultCode.SCORE_LIMIT_EXCEEDED, "超过本周期积分消耗限制");
}
}
if (isAdminPublisher) {
log.info("管理员发布任务,跳过积分检查: publisherId={}", task.getPublisherId());
}
// 生成任务编号
task.setTaskNo(generateTaskNo());
// 生成第三方平台任务编号
task.setThirdPartyTaskNo(generateThirdPartyTaskNo());
// 设置任务状态:默认为草稿,需要审核
task.setStatus("draft");
// 设置审核状态:初始为草稿状态
task.setAuditStatus("draft");
// 设置进度为0
task.setProgress(0);
// 设置紧急标识默认值
if (task.getIsUrgent() == null) {
task.setIsUrgent(0);
}
// 设置延误标识默认值
if (task.getIsDelayed() == null) {
task.setIsDelayed(0);
}
// 【两级审核流程】初始化审核状态
initApprovalStatus(task);
// 保存任务(草稿状态不扣除积分)
taskMapper.insert(task);
// 注意:草稿状态不扣除积分,只有审核通过后才扣除
// 积分扣除将在审核通过时进行
// 【两级审核流程】发送审核通知
sendApprovalNotification(task);
return task.getId();
}
/**
* 创建流水线任务
*/
@Transactional(rollbackFor = Exception.class)
public Long createPipelineTask(com.designhub.dto.PipelineTaskDTO dto, Long publisherId) {
log.info("创建流水线任务: {}, 模式: {}", dto.getTitle(), dto.getPipelineMode());
// 验证用户权限
var user = userService.getUserById(publisherId);
if (user == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户不存在");
}
// 检查是否是管理员
boolean isAdmin = isAdminUser(user.getId());
if (!isAdmin) {
if (user.getDeptId() == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门信息不完整");
}
var department = departmentService.getDepartmentById(user.getDeptId());
if (department == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门不存在");
}
if (!"demand".equals(department.getDeptType())) {
throw new BusinessException(ResultCode.FORBIDDEN, "只有需求部门可以发起任务");
}
}
// 验证流水线模式
if (!"sequential".equals(dto.getPipelineMode()) && !"parallel".equals(dto.getPipelineMode())) {
throw new BusinessException(ResultCode.PARAM_ERROR, "流水线模式必须是 sequential 或 parallel");
}
// 验证子任务列表
if (dto.getItems() == null || dto.getItems().isEmpty()) {
throw new BusinessException(ResultCode.PARAM_ERROR, "流水线任务至少需要一个子任务");
}
// 计算总积分和工时
int totalScore = 0;
double totalHours = 0;
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : dto.getItems()) {
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
if (demandType == null) {
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型ID无效: " + itemDto.getTaskTypeId());
}
int itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue();
double itemHours = demandType.getEstimatedHours() != null ? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity())).doubleValue() : 0;
totalScore += itemScore;
totalHours += itemHours;
}
// 检查部门积分是否充足(管理员跳过积分检查)
if (!isAdmin && user.getDeptId() != null) {
if (!teamScorePoolService.checkBalance(user.getDeptId(), totalScore)) {
throw new BusinessException(ResultCode.INSUFFICIENT_SCORE, "部门积分余额不足");
}
if (!teamScorePoolService.checkPeriodLimit(user.getDeptId(), totalScore)) {
throw new BusinessException(ResultCode.SCORE_LIMIT_EXCEEDED, "超过本周期积分消耗限制");
}
}
if (isAdmin) {
log.info("管理员创建流水线任务,跳过积分检查: publisherId={}", publisherId);
}
// 创建主任务
Task task = new Task();
task.setTaskNo(generateTaskNo());
task.setThirdPartyTaskNo(generateThirdPartyTaskNo());
task.setTitle(dto.getTitle());
task.setPublisherId(publisherId);
task.setPublisherDeptId(user.getDeptId());
task.setIsPipeline(1);
task.setPipelineMode(dto.getPipelineMode());
task.setScore(totalScore);
task.setEstimatedHours(BigDecimal.valueOf(totalHours));
task.setBrandId(dto.getBrandId());
task.setCategoryId(dto.getCategoryId());
task.setPlatformId(dto.getPlatformId());
task.setSpuInfo(dto.getSpuInfo());
task.setTaskSubType(dto.getTaskSubType());
task.setAttachmentUrl(dto.getAttachmentUrl());
task.setPreviewImage(dto.getPreviewImage());
task.setRemark(dto.getRemark());
task.setPriority(dto.getPriority() != null ? dto.getPriority() : 1); // 默认优先级:1-普通
// 设置商品条码列表(如果有)
if (dto.getProductBarcodes() != null && !dto.getProductBarcodes().isEmpty()) {
task.setProductBarcodes(String.join(",", dto.getProductBarcodes()));
}
// 设置时间字段
task.setCreateTime(LocalDateTime.now());
// 如果前端没有传递截止时间,默认为创建时间+7天
if (dto.getDeadline() != null) {
task.setDeadline(dto.getDeadline());
} else {
task.setDeadline(LocalDateTime.now().plusDays(7));
}
// 设置任务类型ID(使用第一个任务类型作为主任务类型)
if (dto.getItems() != null && !dto.getItems().isEmpty()) {
task.setTaskTypeId(dto.getItems().get(0).getTaskTypeId());
// 设置所有任务类型ID(逗号分隔)
String taskTypeIds = dto.getItems().stream()
.map(item -> String.valueOf(item.getTaskTypeId()))
.collect(Collectors.joining(","));
task.setTaskTypeIds(taskTypeIds);
}
task.setStatus("draft");
task.setAuditStatus("draft");
task.setProgress(0);
task.setIsUrgent(0);
task.setIsDelayed(0);
// 设置发布类型(从前端传递,默认为recruitment)
task.setReleaseType(dto.getReleaseType() != null ? dto.getReleaseType() : "recruitment");
// 如果是assigned模式,设置接收人
if ("assigned".equals(dto.getReleaseType()) && dto.getReceiverId() != null) {
task.setReceiverId(dto.getReceiverId());
// 获取接收人的部门ID
User receiver = userService.getUserById(dto.getReceiverId());
if (receiver != null && receiver.getDeptId() != null) {
task.setReceiverDeptId(receiver.getDeptId());
}
}
// 【两级审核流程】初始化审核状态
initApprovalStatus(task);
taskMapper.insert(task);
log.info("主任务创建成功,ID: {}, 任务编号: {}", task.getId(), task.getTaskNo());
// **步骤1:按接单部门自动分组**
// 遍历任务类型列表,根据接单部门分组
int currentGroupNo = 1;
String lastAllowedDeptIds = null;
for (int i = 0; i < dto.getItems().size(); i++) {
com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto = dto.getItems().get(i);
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
// 获取当前任务类型的接单部门配置(支持三级分类继承二级分类的配置)
String currentAllowedDeptIds = demandType.getAllowedDeptIds();
if (currentAllowedDeptIds == null && demandType.getParentId() != null) {
// 如果当前类型没有配置,尝试获取父级的配置
var parentType = configService.getDemandTypeById(demandType.getParentId());
if (parentType != null) {
currentAllowedDeptIds = parentType.getAllowedDeptIds();
log.info("三级分类 {} 继承父级 {} 的接单部门配置: {}",
demandType.getName(), parentType.getName(), currentAllowedDeptIds);
}
}
// 判断是否需要创建新分组
boolean needNewGroup = false;
if (i > 0) {
// 如果当前或上一个的 allowed_dept_ids 为 null,则必须分组
// 因为 null 表示所有部门可接单,无法确定具体接单部门
if (currentAllowedDeptIds == null || lastAllowedDeptIds == null) {
needNewGroup = true;
}
// 如果两者都不为 null,则比较是否相同
else if (!currentAllowedDeptIds.equals(lastAllowedDeptIds)) {
needNewGroup = true;
}
}
if (needNewGroup) {
currentGroupNo++;
log.info("检测到接单部门变化,创建新分组: groupNo={}, 前一部门={}, 当前部门={}",
currentGroupNo, lastAllowedDeptIds, currentAllowedDeptIds);
}
// 记录当前分组号(临时存储在sortOrder字段,稍后会恢复)
itemDto.setSortOrder(currentGroupNo * 1000 + i); // 高位存group_no,低位存原始顺序
lastAllowedDeptIds = currentAllowedDeptIds;
}
log.info("分组完成,共 {} 个子任务组", currentGroupNo);
// **步骤2:创建子任务,按分组设置状态**
for (int i = 0; i < dto.getItems().size(); i++) {
com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto = dto.getItems().get(i);
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
// 解析分组号
int itemGroupNo = itemDto.getSortOrder() / 1000;
int itemIndex = itemDto.getSortOrder() % 1000;
com.designhub.entity.TaskItem item = new com.designhub.entity.TaskItem();
item.setTaskId(task.getId());
item.setItemNo(generateTaskItemNo(task.getTaskNo(), i + 1));
item.setGroupNo(itemGroupNo);
item.setTaskTypeId(itemDto.getTaskTypeId());
item.setQuantity(itemDto.getQuantity());
item.setUnitScore(demandType.getUnitScore().intValue());
item.setTotalScore(demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue());
item.setEstimatedHours(demandType.getEstimatedHours() != null ? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity())) : BigDecimal.ZERO);
item.setSortOrder(i + 1); // 恢复真实的排序号
item.setProgress(0);
// 设置子任务指定的接收人
if (itemDto.getReceiverId() != null) {
item.setReceiverId(itemDto.getReceiverId());
// 获取接收人名字
User receiver = userService.getUserById(itemDto.getReceiverId());
if (receiver != null) {
item.setReceiverName(receiver.getRealName());
}
}
// 关键:根据流水线模式和分组设置初始状态
if ("sequential".equals(dto.getPipelineMode())) {
if (itemGroupNo == 1) {
// 顺序流水线:第一组
if (item.getReceiverId() != null) {
item.setStatus("in_progress"); // 指定了人,直接进行中
} else {
item.setStatus("pending"); // 没指定人,待接单
}
item.setIsLocked(0);
item.setPrevItemId(null);
} else {
// 其他组的子任务都是 locked(锁定)
item.setStatus("locked");
item.setIsLocked(1);
}
} else {
// 并行流水线
if (item.getReceiverId() != null) {
item.setStatus("in_progress");
} else {
item.setStatus("pending");
}
item.setIsLocked(0);
}
taskItemMapper.insert(item);
log.info("子任务创建成功,ID: {}, 编号: {}, 组号: {}, 状态: {}",
item.getId(), item.getItemNo(), item.getGroupNo(), item.getStatus());
}
log.info("流水线任务创建完成,主任务ID: {}, 子任务数: {}, 分组数: {}",
task.getId(), dto.getItems().size(), currentGroupNo);
return task.getId();
}
/**
* 生成子任务编号
*/
private String generateTaskItemNo(String taskNo, int itemIndex) {
return taskNo + "-" + String.format("%02d", itemIndex);
}
/**
* 生成任务编号
*/
private String generateTaskNo() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String date = LocalDateTime.now().format(formatter);
String random = String.format("%04d", new Random().nextInt(10000));
return "TASK" + date + random;
}
/**
* 生成第三方平台任务编号
* 格式:THIRD-yyyyMMddHHmmss-随机数
*/
private String generateThirdPartyTaskNo() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String datetime = LocalDateTime.now().format(formatter);
String random = String.format("%06d", new Random().nextInt(1000000));
return "THIRD-" + datetime + "-" + random;
}
/**
* 获取任务列表
*/
public IPage<Task> getTaskList(Integer page, Integer pageSize, String status, Long taskTypeId) {
Page<Task> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
// 根据状态筛选
if (StringUtils.hasText(status)) {
wrapper.eq(Task::getStatus, status);
}
// 根据任务类型筛选
if (taskTypeId != null) {
wrapper.eq(Task::getTaskTypeId, taskTypeId);
}
// 按更新时间倒序
wrapper.orderByDesc(Task::getUpdateTime);
return taskMapper.selectPage(pageParam, wrapper);
}
/**
* 获取所有待审核任务(处于审批流程中的任务)
* 包括:pending_demand_approval 和 pending_receiving_approval 状态的任务
*/
public IPage<Task> getAllPendingApprovalTasks(Integer page, Integer pageSize, Long taskTypeId) {
Page<Task> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
// 查询处于审批流程中的任务
wrapper.in(Task::getApprovalStatus, "pending_demand_approval", "pending_receiving_approval");
// 根据任务类型筛选
if (taskTypeId != null) {
wrapper.eq(Task::getTaskTypeId, taskTypeId);
}
// 未删除的任务
wrapper.eq(Task::getDeleted, 0);
// 按创建时间倒序
wrapper.orderByDesc(Task::getCreateTime);
IPage<Task> result = taskMapper.selectPage(pageParam, wrapper);
// 填充关联信息
for (Task task : result.getRecords()) {
// 填充发布人名字
if (task.getPublisherId() != null) {
User publisher = userService.getUserById(task.getPublisherId());
if (publisher != null) {
task.setPublisherName(publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername());
}
}
// 填充接收人名字
if (task.getReceiverId() != null) {
User receiver = userService.getUserById(task.getReceiverId());
if (receiver != null) {
task.setReceiverName(receiver.getRealName() != null ? receiver.getRealName() : receiver.getUsername());
}
}
// 填充当前审核人姓名
if (task.getCurrentApproverId() != null) {
User approver = userService.getUserById(task.getCurrentApproverId());
if (approver != null) {
task.setCurrentApproverName(approver.getRealName() != null ? approver.getRealName() : approver.getUsername());
}
}
}
return result;
}
/**
* 获取已完成任务列表(根据角色权限)
* - 个人用户:只看自己接的已完成任务
* - 部门负责人:看本部门所有已完成任务
* - 管理员:看所有已完成任务
*/
public IPage<Task> getCompletedTasks(Integer page, Integer pageSize,
Long userId, String keyword, String sortBy) {
// 1. 获取用户信息和角色
User user = userService.getUserById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
boolean isAdmin = isAdminUser(userId);
boolean isDeptHead = isDeptHeadUser(userId);
// 2. 构建查询条件
Page<Task> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
// 状态必须是已完成
wrapper.eq(Task::getStatus, "completed");
// 3. 根据角色筛选数据范围
if (isAdmin) {
// 管理员:查看所有
log.info("管理员查看所有已完成任务");
} else if (isDeptHead) {
// 部门负责人:查看本部门的
wrapper.and(w -> w
.eq(Task::getPublisherDeptId, user.getDeptId()) // 本部门发起的
.or()
.eq(Task::getReceiverDeptId, user.getDeptId()) // 或本部门接的
);
log.info("部门负责人查看部门已完成任务: deptId={}", user.getDeptId());
} else {
// 普通用户:只看自己接的
wrapper.eq(Task::getReceiverId, userId);
log.info("个人用户查看个人已完成任务: userId={}", userId);
}
// 4. 关键词搜索
if (org.springframework.util.StringUtils.hasText(keyword)) {
wrapper.and(w -> w
.like(Task::getTitle, keyword)
.or()
.like(Task::getTaskNo, keyword)
);
}
// 5. 排序
if ("score".equals(sortBy)) {
wrapper.orderByDesc(Task::getScore);
} else {
wrapper.orderByDesc(Task::getCompleteTime); // 默认按完成时间降序
}
// 6. 查询并填充用户名
IPage<Task> result = taskMapper.selectPage(pageParam, wrapper);
for (Task task : result.getRecords()) {
// 填充发布人名字
if (task.getPublisherId() != null) {
User publisher = userService.getUserById(task.getPublisherId());
if (publisher != null) {
task.setPublisherName(publisher.getRealName());
}
}
// 填充接收人名字
if (task.getReceiverId() != null) {
User receiver = userService.getUserById(task.getReceiverId());
if (receiver != null) {
task.setReceiverName(receiver.getRealName());
}
}
// 填充当前审核人姓名
if (task.getCurrentApproverId() != null) {
User approver = userService.getUserById(task.getCurrentApproverId());
if (approver != null) {
task.setCurrentApproverName(approver.getRealName() != null ? approver.getRealName() : approver.getUsername());
}
}
}
return result;
}
/**
* 获取任务大厅(根据用户部门筛选可接单的任务)
*/
public IPage<Task> getTaskHall(Integer page, Integer pageSize, Long taskTypeId, Long demandTypeId,
Long brandId, Long categoryId, Long platformId, String taskSubType,
Integer minScore, Integer maxScore, String keyword, String publisherName,
String timeRange, String sortBy, Long userDeptId) {
Page<Task> pageParam = new Page<>(page, pageSize);
// 获取当前用户部门可以接单的任务类型ID列表(支持三级分类继承二级分类的配置)
List<Long> allowedTaskTypeIds = null;
if (userDeptId != null) {
List<DemandType> allDemandTypes = demandTypeMapper.selectList(null);
// 构建ID到DemandType的映射,用于快速查找父级
Map<Long, DemandType> demandTypeMap = allDemandTypes.stream()
.collect(Collectors.toMap(DemandType::getId, dt -> dt));
allowedTaskTypeIds = allDemandTypes.stream()
.filter(dt -> {
// 获取允许的部门ID(支持继承)
String allowedDepts = dt.getAllowedDeptIds();
if ((allowedDepts == null || allowedDepts.isEmpty()) && dt.getParentId() != null) {
// 三级分类没有配置,尝试获取父级(二级分类)的配置
DemandType parentType = demandTypeMap.get(dt.getParentId());
if (parentType != null) {
allowedDepts = parentType.getAllowedDeptIds();
}
}
// 如果allowedDeptIds为空,表示所有部门都可接单
if (allowedDepts == null || allowedDepts.isEmpty()) {
return true;
}
// 检查用户部门是否在允许列表中
String[] deptIds = allowedDepts.split(",");
for (String deptId : deptIds) {
if (deptId.trim().equals(String.valueOf(userDeptId))) {
return true;
}
}
return false;
})
.map(DemandType::getId)
.collect(Collectors.toList());
}
return taskMapper.selectTaskHallByDept(pageParam, taskTypeId, demandTypeId, brandId, categoryId,
platformId, taskSubType, minScore, maxScore, keyword,
publisherName, timeRange, sortBy, allowedTaskTypeIds);
}
/**
* 获取用户所属部门ID
*/
public Long getUserDeptId(Long userId) {
User user = userService.getUserById(userId);
return user != null ? user.getDeptId() : null;
}
/**
* 获取我发布的任务
*/
public IPage<Task> getMyPublishedTasks(Integer page, Integer pageSize, Long userId, String status) {
Page<Task> pageParam = new Page<>(page, pageSize);
IPage<Task> taskPage = taskMapper.selectMyPublishedTasks(pageParam, userId, status);
// 填充子任务统计信息
for (Task task : taskPage.getRecords()) {
fillSubTaskStats(task);
}
return taskPage;
}
/**
* 获取我接收的任务
*/
public IPage<Task> getMyReceivedTasks(Integer page, Integer pageSize, Long userId, String status) {
Page<Task> pageParam = new Page<>(page, pageSize);
IPage<Task> taskPage = taskMapper.selectMyReceivedTasks(pageParam, userId, status);
// 填充子任务统计信息
for (Task task : taskPage.getRecords()) {
fillSubTaskStats(task);
}
return taskPage;
}
/**
* 填充流水线任务的子任务统计信息
*/
private void fillSubTaskStats(Task task) {
// 只有流水线任务才需要填充子任务信息
if (task.getIsPipeline() == null || task.getIsPipeline() != 1) {
return;
}
try {
// 查询所有子任务
List<TaskItem> subTasks = taskItemMapper.selectList(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getTaskId, task.getId())
.orderByAsc(TaskItem::getSortOrder)
);
// 汇总所有子任务的交付路径
if (subTasks != null && !subTasks.isEmpty()) {
List<String> allDeliveryPaths = subTasks.stream()
.filter(item -> item.getDeliveryPath() != null && !item.getDeliveryPath().isEmpty())
.map(item -> "【" + item.getItemNo() + "】" + item.getDeliveryPath())
.collect(Collectors.toList());
task.setAllDeliveryPaths(allDeliveryPaths);
}
if (subTasks != null && !subTasks.isEmpty()) {
task.setSubTaskTotal(subTasks.size());
// 统计各状态子任务数量
long completedCount = subTasks.stream().filter(item -> "completed".equals(item.getStatus())).count();
long inProgressCount = subTasks.stream().filter(item -> "in_progress".equals(item.getStatus())).count();
long pendingCount = subTasks.stream().filter(item -> "pending".equals(item.getStatus())).count();
task.setSubTaskCompleted((int) completedCount);
task.setSubTaskInProgress((int) inProgressCount);
task.setSubTaskPending((int) pendingCount);
// 设置子任务列表(包含处理人信息)
task.setSubTasks(subTasks);
// 如果主任务没有receiverName,从子任务中获取第一个有接单人的
if (task.getReceiverName() == null || task.getReceiverName().isEmpty()) {
subTasks.stream()
.filter(item -> item.getReceiverName() != null && !item.getReceiverName().isEmpty())
.findFirst()
.ifPresent(item -> task.setReceiverName(item.getReceiverName() + "等"));
}
}
} catch (Exception e) {
log.error("填充子任务统计信息失败:taskId={}", task.getId(), e);
}
}
/**
* 获取任务详情
*/
public Task getTaskById(Long id) {
Task task = taskMapper.selectById(id);
if (task == null) {
throw new BusinessException(ResultCode.TASK_NOT_EXIST);
}
// 填充关联信息
fillTaskRelatedInfo(task);
return task;
}
/**
* 填充任务关联信息(品牌、类目、平台、发布人等)
*/
private void fillTaskRelatedInfo(Task task) {
// 填充品牌名称
if (task.getBrandId() != null) {
Brand brand = brandMapper.selectById(task.getBrandId());
if (brand != null) {
task.setBrandName(brand.getName());
}
}
// 填充类目名称
if (task.getCategoryId() != null) {
Category category = categoryMapper.selectById(task.getCategoryId());
if (category != null) {
task.setCategoryName(category.getName());
}
}
// 填充平台名称
if (task.getPlatformId() != null) {
Platform platform = platformMapper.selectById(task.getPlatformId());
if (platform != null) {
task.setPlatformName(platform.getName());
}
}
// 填充发布人名称
if (task.getPublisherId() != null) {
User publisher = userService.getUserById(task.getPublisherId());
if (publisher != null) {
task.setPublisherName(publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername());
}
}
// 填充发布人部门名称
if (task.getPublisherDeptId() != null) {
Department dept = departmentService.getDepartmentById(task.getPublisherDeptId());
if (dept != null) {
task.setPublisherDeptName(dept.getDeptName());
}
}
// 填充接收人名称
if (task.getReceiverId() != null) {
User receiver = userService.getUserById(task.getReceiverId());
if (receiver != null) {
task.setReceiverName(receiver.getRealName() != null ? receiver.getRealName() : receiver.getUsername());
}
}
// 填充当前审核人名称
if (task.getCurrentApproverId() != null) {
User approver = userService.getUserById(task.getCurrentApproverId());
if (approver != null) {
task.setCurrentApproverName(approver.getRealName() != null ? approver.getRealName() : approver.getUsername());
}
}
// 填充子任务统计信息
fillSubTaskStats(task);
}
/**
* 更新任务
*/
@Transactional(rollbackFor = Exception.class)
public void updateTask(Long id, Task task) {
Task existTask = getTaskById(id);
// TODO: 验证权限
task.setId(id);
taskMapper.updateById(task);
}
/**
* 抢单
*/
@Transactional(rollbackFor = Exception.class)
public void grabTask(Long taskId, Long userId) {
log.info("用户 {} 抢单任务 {}", userId, taskId);
Task task = getTaskById(taskId);
if (!"pending".equals(task.getStatus())) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "任务不是待接单状态");
}
if (task.getReceiverId() != null) {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN);
}
if (!"recruitment".equalsIgnoreCase(task.getReleaseType())) {
throw new BusinessException(ResultCode.FORBIDDEN, "该任务不支持抢单");
}
// 获取用户信息
var user = userService.getUserById(userId);
if (user == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户不存在");
}
// 检查是否是管理员(管理员可以抢单,绕过所有限制)
boolean isAdmin = isAdminUser(userId);
Long deptId = user.getDeptId();
// 非管理员需要进行权限验证
if (!isAdmin) {
if (deptId == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "您未分配部门,无法接单");
}
// 获取用户所属部门
Department department = departmentService.getDepartmentById(deptId);
if (department == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "未找到用户所属部门,请联系管理员");
}
// 检查1:需求部门不能抢单
if ("demand".equals(department.getDeptType())) {
throw new BusinessException(ResultCode.FORBIDDEN, "需求部门成员不能接单,只能发起需求");
}
// 检查2:接单部门类型验证
if (!"receiving".equals(department.getDeptType())) {
throw new BusinessException(ResultCode.FORBIDDEN, "只有接单部门成员可以接收任务");
}
// 检查3:部门负责人不能抢单
if (department.getLeaderId() != null && department.getLeaderId().equals(userId)) {
throw new BusinessException(ResultCode.FORBIDDEN, "部门负责人不能接单,请分配给设计师");
}
// 检查4:验证部门是否允许接该需求类型
if (task.getDemandTypeId() != null) {
validateAllowedDemandTypes(department, task.getDemandTypeId());
}
} else {
log.info("管理员 {} 抢单任务 {}", user.getRealName(), taskId);
}
LocalDateTime now = LocalDateTime.now();
// 重新计算deadline:从接单时间开始计算,无论之前是否有deadline
LocalDateTime deadline = null;
if (task.getEstimatedHours() != null) {
int hours = task.getEstimatedHours().intValue();
if (hours > 0) {
deadline = now.plusHours(hours);
log.info("任务deadline已重新计算:从接单时间 {} 开始,预估 {} 小时,截止时间 {}",
now, hours, deadline);
}
}
int updated = taskMapper.grabTask(taskId, userId, deptId, now, deadline);
if (updated == 0) {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "任务已被其他设计师抢走");
}
log.info("用户 {} 成功抢单任务 {}", userId, taskId);
// 发送通知给任务发起人
if (task.getPublisherId() != null) {
try {
notificationService.sendNotification(
task.getPublisherId(),
"任务已被接单",
String.format("您的任务【%s】已被 %s 接单", task.getTitle(), user.getRealName() != null ? user.getRealName() : user.getUsername()),
"task_grabbed",
taskId
);
} catch (Exception e) {
log.error("发送抢单通知失败", e);
}
}
}
/**
* 提交任务
*/
@Transactional(rollbackFor = Exception.class)
public void submitTask(Long taskId, Long userId, String deliveryPath) {
log.info("用户 {} 提交任务 {}", userId, taskId);
Task task = getTaskById(taskId);
// 验证权限(必须是任务接收人)
if (!userId.equals(task.getReceiverId())) {
throw new BusinessException(ResultCode.FORBIDDEN, "只有任务接收人可以提交");
}
// 验证任务状态:允许 in_progress 或 rejected(驳回后重新提交)
if (!"in_progress".equals(task.getStatus()) && !"rejected".equals(task.getStatus())) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "任务状态不允许提交,当前状态:" + task.getStatus());
}
// 更新任务
task.setDeliveryPath(deliveryPath);
task.setStatus("review");
task.setCompleteTime(LocalDateTime.now());
task.setProgress(100);
taskMapper.updateById(task);
// 发送通知给发布人
if (task.getPublisherId() != null) {
try {
User receiver = userService.getUserById(userId);
String receiverName = receiver != null ?
(receiver.getRealName() != null ? receiver.getRealName() : receiver.getUsername()) : "设计师";
notificationService.sendTaskNotification(
task.getPublisherId(),
taskId,
"任务已提交待审核",
String.format("您发布的任务【%s】已由 %s 提交,请及时审核", task.getTitle(), receiverName)
);
} catch (Exception e) {
log.error("发送任务提交通知失败", e);
}
}
}
/**
* 审核任务
*/
@Transactional(rollbackFor = Exception.class)
public void reviewTask(Long taskId, Long userId, Boolean approved, String comment, Integer rating) {
log.info("用户 {} 审核任务 {}, 结果: {}", userId, taskId, approved);
Task task = getTaskById(taskId);
// 验证权限(必须是任务发布人)
if (!userId.equals(task.getPublisherId())) {
throw new BusinessException(ResultCode.FORBIDDEN, "只有任务发布人可以审核");
}
// 验证任务状态
if (!"review".equals(task.getStatus())) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "任务不是待审核状态");
}
if (approved) {
// 审核通过
task.setStatus("completed");
task.setReviewComment(comment);
task.setRating(rating);
// 设置完成时间(如果还没有设置)
if (task.getCompleteTime() == null) {
task.setCompleteTime(LocalDateTime.now());
}
// 检查是否延误并计算扣分
if (task.getDeadline() != null && task.getCompleteTime() != null
&& task.getCompleteTime().isAfter(task.getDeadline())) {
task.setIsDelayed(1);
// 计算延误天数
long delayDays = java.time.temporal.ChronoUnit.DAYS.between(
task.getDeadline(), task.getCompleteTime());
task.setDelayDays((int) delayDays);
}
// 计算延误扣分(移到外层作用域)
DelayPenaltyRecord delayRecord = null;
if (task.getCompleteTime() != null && task.getDeadline() != null
&& task.getCompleteTime().isAfter(task.getDeadline())
&& task.getReceiverId() != null) {
delayRecord = delayPenaltyService.calculateDelayPenalty(
taskId,
task.getReceiverId(),
task.getDeadline(),
task.getCompleteTime()
);
if (delayRecord != null) {
// 保存延误扣分记录
delayPenaltyService.saveDelayPenaltyRecord(delayRecord);
log.info("任务延误扣分: taskId={}, designerId={}, penaltyScore={}",
taskId, task.getReceiverId(), delayRecord.getPenaltyScore());
}
}
// 奖励设计师所在部门积分(任务奖励)
if (task.getReceiverDeptId() != null && task.getScore() != null) {
teamScorePoolService.addScore(task.getReceiverDeptId(), task.getScore());
// 如果有延误扣分,从积分池中扣除
if (delayRecord != null && delayRecord.getPenaltyScore() > 0
&& "confirmed".equals(delayRecord.getStatus())) {
try {
teamScorePoolService.deductScore(task.getReceiverDeptId(), delayRecord.getPenaltyScore());
log.info("已扣除延误扣分: deptId={}, penaltyScore={}",
task.getReceiverDeptId(), delayRecord.getPenaltyScore());
} catch (Exception e) {
log.error("扣除延误扣分失败: {}", e.getMessage());
}
}
}
} else {
// 审核驳回
task.setStatus("rejected");
task.setRejectReason(comment);
// 驳回后设计师可以重新提交,无需更改积分
}
taskMapper.updateById(task);
// 发送通知给设计师
if (task.getReceiverId() != null) {
try {
if (approved) {
// 审核通过通知
notificationService.sendTaskNotification(
task.getReceiverId(),
taskId,
"任务审核通过",
String.format("恭喜!您完成的任务【%s】已审核通过,积分已发放", task.getTitle())
);
} else {
// 驳回通知
notificationService.sendTaskNotification(
task.getReceiverId(),
taskId,
"任务被驳回",
String.format("您提交的任务【%s】被驳回,原因:%s,请修改后重新提交", task.getTitle(), comment)
);
}
} catch (Exception e) {
log.error("发送任务审核通知失败", e);
}
}
}
/**
* 取消任务
*/
@Transactional(rollbackFor = Exception.class)
public void cancelTask(Long taskId, Long userId, String reason) {
log.info("用户 {} 取消任务 {}", userId, taskId);
Task task = getTaskById(taskId);
// 验证权限(必须是任务发布人)
if (!userId.equals(task.getPublisherId())) {
throw new BusinessException(ResultCode.FORBIDDEN, "只有任务发布人可以取消");
}
// 验证任务状态(不能是已完成)
if ("completed".equals(task.getStatus())) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "已完成的任务不能取消");
}
// 更新任务状态
task.setStatus("cancelled");
task.setRejectReason(reason);
taskMapper.updateById(task);
// 返还积分(任务取消时返还给发布部门)
if (task.getPublisherDeptId() != null && task.getScore() != null) {
teamScorePoolService.addScore(task.getPublisherDeptId(), task.getScore());
}
// 发送通知给接单设计师
if (task.getReceiverId() != null) {
try {
notificationService.sendTaskNotification(
task.getReceiverId(),
taskId,
"任务已取消",
String.format("任务【%s】已被发起人取消,原因:%s", task.getTitle(), reason != null ? reason : "无")
);
} catch (Exception e) {
log.error("发送任务取消通知失败", e);
}
}
}
/**
* 检查用户是否是管理员(ADMIN或SUPER_ADMIN)
*/
private boolean isAdminUser(Long userId) {
try {
// 首先检查用户的 isAdmin 字段
User user = userService.getUserById(userId);
if (user != null && Integer.valueOf(1).equals(user.getIsAdmin())) {
return true;
}
// 其次检查角色
var roles = roleService.getRolesByUserId(userId);
return roles.stream().anyMatch(role ->
"ADMIN".equals(role.getRoleCode()) ||
"SUPER_ADMIN".equals(role.getRoleCode())
);
} catch (Exception e) {
log.warn("检查用户角色失败: userId={}, error={}", userId, e.getMessage());
return false;
}
}
/**
* 检查用户是否是部门负责人
*/
private boolean isDeptHeadUser(Long userId) {
try {
User user = userService.getUserById(userId);
if (user == null || user.getDeptId() == null) {
return false;
}
Department dept = departmentService.getDepartmentById(user.getDeptId());
if (dept == null) {
return false;
}
// 检查是否是部门负责人
return userId.equals(dept.getLeaderId());
} catch (Exception e) {
log.warn("检查部门负责人失败: userId={}, error={}", userId, e.getMessage());
return false;
}
}
private void validateAllowedDemandTypes(Department department, Long demandTypeId) {
if (department == null || !StringUtils.hasText(department.getAllowedDemandTypes()) || demandTypeId == null) {
return;
}
Set<Long> allowed = Arrays.stream(department.getAllowedDemandTypes().split(","))
.map(String::trim)
.filter(str -> !str.isEmpty())
.map(Long::valueOf)
.collect(Collectors.toSet());
if (!allowed.isEmpty() && !allowed.contains(demandTypeId)) {
throw new BusinessException(ResultCode.FORBIDDEN, "您所在部门不支持该需求类型的任务");
}
}
/**
* 提交任务审核
*/
@Transactional(rollbackFor = Exception.class)
public boolean submitTaskForAudit(Long taskId, Long userId) {
log.info("提交任务审核: taskId={}, userId={}", taskId, userId);
Task task = taskMapper.selectById(taskId);
if (task == null) {
throw new BusinessException("任务不存在");
}
// 检查是否是任务发布人
if (!task.getPublisherId().equals(userId)) {
throw new BusinessException("只有任务发布人才能提交审核");
}
// 检查任务状态
if (!"draft".equals(task.getAuditStatus())) {
throw new BusinessException("只有草稿状态的任务才能提交审核");
}
// 更新审核状态
task.setAuditStatus("pending_audit");
taskMapper.updateById(task);
// 记录审核日志
TaskAuditLog auditLog = new TaskAuditLog();
auditLog.setTaskId(taskId);
auditLog.setAuditorId(userId);
auditLog.setAction("submit");
auditLog.setComment("提交审核");
taskAuditLogMapper.insert(auditLog);
// 发送通知给部门负责人
Department department = departmentService.getDepartmentById(task.getPublisherDeptId());
if (department != null && department.getLeaderId() != null) {
User publisher = userService.getUserById(userId);
String publisherName = publisher != null ?
(publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername())
: "未知用户";
notificationService.sendTaskNotification(
department.getLeaderId(),
taskId,
"【待审核】新任务提交审核",
String.format("%s 提交了任务《%s》,请及时审核", publisherName, task.getTitle())
);
log.info("已发送审核通知给负责人: leaderId={}, taskId={}", department.getLeaderId(), taskId);
}
return true;
}
/**
* 审核任务
*/
@Transactional(rollbackFor = Exception.class)
public boolean auditTask(TaskAuditRequest request, Long auditorId) {
log.info("审核任务: taskId={}, result={}, auditorId={}",
request.getTaskId(), request.getResult(), auditorId);
Task task = taskMapper.selectById(request.getTaskId());
if (task == null) {
throw new BusinessException("任务不存在");
}
// 检查是否是部门负责人
User auditor = userService.getUserById(auditorId);
if (auditor == null) {
throw new BusinessException("用户不存在");
}
Department publisherDept = departmentService.getDepartmentById(task.getPublisherDeptId());
if (publisherDept == null || !auditorId.equals(publisherDept.getLeaderId())) {
throw new BusinessException("只有发起部门的负责人才能审核任务");
}
// 检查任务状态
if (!"pending_audit".equals(task.getAuditStatus())) {
throw new BusinessException("只有待审核状态的任务才能审核");
}
// 更新审核状态
if ("pass".equals(request.getResult())) {
task.setAuditStatus("audit_pass");
task.setStatus("pending"); // 审核通过后变为待接单状态
// 审核通过后扣除部门积分
if (task.getPublisherDeptId() != null && task.getScore() != null) {
// 检查积分是否充足
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), task.getScore())) {
throw new BusinessException("部门积分余额不足,无法通过审核");
}
// 扣除积分
teamScorePoolService.consumeScore(task.getPublisherDeptId(), task.getScore());
log.info("审核通过,扣除部门积分: deptId={}, score={}", task.getPublisherDeptId(), task.getScore());
}
} else if ("reject".equals(request.getResult())) {
task.setAuditStatus("audit_reject");
task.setStatus("draft"); // 审核驳回后回到草稿状态
} else {
throw new BusinessException("审核结果参数错误");
}
task.setAuditorId(auditorId);
task.setAuditTime(LocalDateTime.now());
task.setAuditComment(request.getComment());
taskMapper.updateById(task);
// 记录审核日志
TaskAuditLog auditLog = new TaskAuditLog();
auditLog.setTaskId(task.getId());
auditLog.setAuditorId(auditorId);
auditLog.setAuditorName(auditor.getRealName() != null ? auditor.getRealName() : auditor.getUsername());
auditLog.setAction(request.getResult());
auditLog.setComment(request.getComment());
taskAuditLogMapper.insert(auditLog);
// 发送审核结果通知给任务发起人
if (task.getPublisherId() != null) {
String auditorName = auditor.getRealName() != null ? auditor.getRealName() : auditor.getUsername();
String resultText = "pass".equals(request.getResult()) ? "通过" : "驳回";
String title = String.format("【审核%s】任务《%s》", resultText, task.getTitle());
String content;
if ("pass".equals(request.getResult())) {
content = String.format("%s 审核通过了您的任务《%s》,任务已发布到任务大厅",
auditorName, task.getTitle());
if (request.getComment() != null && !request.getComment().isEmpty()) {
content += "。审核意见:" + request.getComment();
}
} else {
content = String.format("%s 驳回了您的任务《%s》", auditorName, task.getTitle());
if (request.getComment() != null && !request.getComment().isEmpty()) {
content += "。驳回原因:" + request.getComment();
}
}
notificationService.sendTaskNotification(
task.getPublisherId(),
task.getId(),
title,
content
);
log.info("已发送审核结果通知: publisherId={}, result={}, taskId={}",
task.getPublisherId(), request.getResult(), task.getId());
}
return true;
}
/**
* 强制指派任务给部门成员
*/
@Transactional(rollbackFor = Exception.class)
public boolean assignTask(TaskAssignRequest request, Long assignerId) {
log.info("强制指派任务: taskId={}, assigneeId={}, assignerId={}",
request.getTaskId(), request.getAssigneeId(), assignerId);
Task task = taskMapper.selectById(request.getTaskId());
if (task == null) {
throw new BusinessException("任务不存在");
}
// 检查任务状态(只有待接单或招募中的任务可以指派)
if (!"pending".equals(task.getStatus())) {
throw new BusinessException("只有待接单状态的任务才能指派");
}
// 检查是否已有人接单
if (task.getReceiverId() != null) {
throw new BusinessException("该任务已被接单");
}
// 检查指派人是否是部门负责人
User assigner = userService.getUserById(assignerId);
if (assigner == null || assigner.getDeptId() == null) {
throw new BusinessException("用户不存在或未分配部门");
}
Department assignerDept = departmentService.getDepartmentById(assigner.getDeptId());
if (assignerDept == null || !assignerId.equals(assignerDept.getLeaderId())) {
throw new BusinessException("只有部门负责人才能指派任务");
}
// 检查被指派人是否在该部门
User assignee = userService.getUserById(request.getAssigneeId());
if (assignee == null || !assigner.getDeptId().equals(assignee.getDeptId())) {
throw new BusinessException("只能指派给本部门成员");
}
// 更新任务
task.setReceiverId(request.getAssigneeId());
task.setReceiverDeptId(assignee.getDeptId());
task.setStatus("in_progress");
task.setStartTime(LocalDateTime.now());
taskMapper.updateById(task);
// 记录指派日志
TaskAssignLog log = new TaskAssignLog();
log.setTaskId(task.getId());
log.setAssignerId(assignerId);
log.setAssigneeId(request.getAssigneeId());
log.setAssignType(request.getAssignType());
log.setComment(request.getComment());
taskAssignLogMapper.insert(log);
return true;
}
/**
* 获取部门负责人的任务列表(需求部门负责人视图)
*/
public IPage<Task> getLeaderTaskList(Long userId, Integer page, Integer pageSize, String auditStatus) {
log.info("查询负责人任务列表: userId={}, auditStatus={}", userId, auditStatus);
User user = userService.getUserById(userId);
if (user == null || user.getDeptId() == null) {
throw new BusinessException("用户不存在或未分配部门");
}
Department dept = departmentService.getDepartmentById(user.getDeptId());
if (dept == null) {
throw new BusinessException("部门不存在");
}
// 检查是否是部门负责人
if (!userId.equals(dept.getLeaderId())) {
throw new BusinessException("只有部门负责人才能查看该视图");
}
// 查询部门内所有任务
Page<Task> pageParam = new Page<>(page, pageSize);
return taskMapper.selectLeaderTaskPage(pageParam, user.getDeptId(), auditStatus);
}
/**
* 获取接单部门负责人的接单明细(接单部门负责人视图)
*/
public IPage<Task> getLeaderReceivingTaskList(Long userId, Integer page, Integer pageSize, String status) {
log.info("查询接单部门负责人视图: userId={}, status={}", userId, status);
User user = userService.getUserById(userId);
if (user == null || user.getDeptId() == null) {
throw new BusinessException("用户不存在或未分配部门");
}
Department dept = departmentService.getDepartmentById(user.getDeptId());
if (dept == null) {
throw new BusinessException("部门不存在");
}
// 检查是否是部门负责人
if (!userId.equals(dept.getLeaderId())) {
throw new BusinessException("只有部门负责人才能查看该视图");
}
// 查询部门成员接单的所有任务
Page<Task> pageParam = new Page<>(page, pageSize);
return taskMapper.selectDeptReceivingTaskPage(pageParam, user.getDeptId(), status);
}
/**
* 获取工作台统计数据
*/
public DashboardStatsVO getDashboardStats(Long userId, String month) {
try {
// 获取用户角色,判断是否为管理员
List<Role> roles = roleService.getRolesByUserId(userId);
boolean isAdmin = roles.stream()
.anyMatch(role -> "ADMIN".equals(role.getRoleCode()) || "SUPER_ADMIN".equals(role.getRoleCode()));
log.info("获取工作台统计数据: userId={}, isAdmin={}, month={}", userId, isAdmin, month);
// 解析月份,如果不传则使用当前月份
LocalDate targetMonth;
if (month != null && !month.isEmpty()) {
try {
targetMonth = LocalDate.parse(month + "-01", java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
} catch (Exception e) {
log.warn("月份格式错误: {}, 使用当前月份", month);
targetMonth = LocalDate.now().withDayOfMonth(1);
}
} else {
targetMonth = LocalDate.now().withDayOfMonth(1);
}
// 计算月份的开始和结束时间
LocalDateTime monthStart = LocalDateTime.of(targetMonth, LocalTime.MIN);
LocalDateTime monthEnd = monthStart.plusMonths(1).minusSeconds(1);
// 当前日期范围
LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
// 本周范围
LocalDate now = LocalDate.now();
LocalDate weekStart = now.minusDays(now.getDayOfWeek().getValue() - 1);
LocalDate weekEnd = weekStart.plusDays(6);
LocalDateTime weekStartTime = LocalDateTime.of(weekStart, LocalTime.MIN);
LocalDateTime weekEndTime = LocalDateTime.of(weekEnd, LocalTime.MAX);
// 上周范围
LocalDate lastWeekStart = weekStart.minusDays(7);
LocalDate lastWeekEnd = lastWeekStart.plusDays(6);
LocalDateTime lastWeekStartTime = LocalDateTime.of(lastWeekStart, LocalTime.MIN);
LocalDateTime lastWeekEndTime = LocalDateTime.of(lastWeekEnd, LocalTime.MAX);
// 进行中的任务数
LambdaQueryWrapper<Task> inProgressQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "in_progress");
if (!isAdmin) {
inProgressQuery.eq(Task::getReceiverId, userId);
}
Long inProgressCount = taskMapper.selectCount(inProgressQuery);
// 已完成的任务数
LambdaQueryWrapper<Task> completedQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "completed");
if (!isAdmin) {
completedQuery.eq(Task::getReceiverId, userId);
}
Long completedCount = taskMapper.selectCount(completedQuery);
// 待审核的任务数
LambdaQueryWrapper<Task> reviewQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "review");
if (!isAdmin) {
reviewQuery.eq(Task::getReceiverId, userId);
}
Long reviewCount = taskMapper.selectCount(reviewQuery);
// 今日开始的任务数
LambdaQueryWrapper<Task> todayStartedQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "in_progress")
.ge(Task::getStartTime, todayStart)
.le(Task::getStartTime, todayEnd);
if (!isAdmin) {
todayStartedQuery.eq(Task::getReceiverId, userId);
}
Long todayStartedCount = taskMapper.selectCount(todayStartedQuery);
// 今日完成的任务数
LambdaQueryWrapper<Task> todayCompletedQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, todayStart)
.le(Task::getCompleteTime, todayEnd);
if (!isAdmin) {
todayCompletedQuery.eq(Task::getReceiverId, userId);
}
Long todayCompletedCount = taskMapper.selectCount(todayCompletedQuery);
// 本周完成的任务数
LambdaQueryWrapper<Task> weekCompletedQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, weekStartTime)
.le(Task::getCompleteTime, weekEndTime);
if (!isAdmin) {
weekCompletedQuery.eq(Task::getReceiverId, userId);
}
Long weekCompletedCount = taskMapper.selectCount(weekCompletedQuery);
// 上周完成的任务数
LambdaQueryWrapper<Task> lastWeekCompletedQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, lastWeekStartTime)
.le(Task::getCompleteTime, lastWeekEndTime);
if (!isAdmin) {
lastWeekCompletedQuery.eq(Task::getReceiverId, userId);
}
Long lastWeekCompletedCount = taskMapper.selectCount(lastWeekCompletedQuery);
// 可接任务数(状态为pending的任务)
LambdaQueryWrapper<Task> availableQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "pending");
// 可接任务对所有人都是全局的,不需要过滤用户
Long availableCount = taskMapper.selectCount(availableQuery);
// 当日新增任务数
LambdaQueryWrapper<Task> todayNewQuery = new LambdaQueryWrapper<Task>()
.ge(Task::getCreateTime, todayStart)
.le(Task::getCreateTime, todayEnd);
if (!isAdmin) {
// 普通用户看自己相关的任务(作为发起人或接单人)
todayNewQuery.and(wrapper -> wrapper
.eq(Task::getPublisherId, userId)
.or()
.eq(Task::getReceiverId, userId));
}
Long todayNewCount = taskMapper.selectCount(todayNewQuery);
// 获取总积分
LambdaQueryWrapper<Task> scoreQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "completed")
.select(Task::getScoreReward);
if (!isAdmin) {
scoreQuery.eq(Task::getReceiverId, userId);
}
List<Task> completedTasks = taskMapper.selectList(scoreQuery);
int totalScore = completedTasks.stream()
.filter(t -> t.getScoreReward() != null)
.mapToInt(t -> t.getScoreReward().intValue())
.sum();
// 获取当前用户当月发布的任务数
LambdaQueryWrapper<Task> publishedQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getPublisherId, userId)
.ge(Task::getCreateTime, monthStart)
.le(Task::getCreateTime, monthEnd)
.eq(Task::getDeleted, 0);
Long publishedCount = taskMapper.selectCount(publishedQuery);
DashboardStatsVO result = DashboardStatsVO.builder()
.availableCount(availableCount.intValue())
.publishedCount(publishedCount.intValue())
.inProgressCount(inProgressCount.intValue())
.completedCount(completedCount.intValue())
.reviewCount(reviewCount.intValue())
.todayNewCount(todayNewCount.intValue())
.totalScore(totalScore)
.todayStartedCount(todayStartedCount.intValue())
.todayCompletedCount(todayCompletedCount.intValue())
.weekCompletedCount(weekCompletedCount.intValue())
.lastWeekCompletedCount(lastWeekCompletedCount.intValue())
.build();
log.info("工作台统计数据查询完成: userId={}, isAdmin={}, result={}", userId, isAdmin, result);
return result;
} catch (Exception e) {
log.error("获取工作台统计数据失败: userId={}", userId, e);
// 返回空数据而不是抛异常
return DashboardStatsVO.builder()
.availableCount(0)
.publishedCount(0)
.inProgressCount(0)
.completedCount(0)
.reviewCount(0)
.todayNewCount(0)
.totalScore(0)
.todayStartedCount(0)
.todayCompletedCount(0)
.weekCompletedCount(0)
.lastWeekCompletedCount(0)
.build();
}
}
/**
* 获取我的进行中任务列表
*/
public List<Task> getMyInProgressTasks(Long userId) {
return taskMapper.selectList(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "in_progress")
.orderByDesc(Task::getStartTime)
);
}
/**
* 批量更新任务进度
*/
@Transactional(rollbackFor = Exception.class)
public void batchUpdateProgress(Long userId, List<java.util.Map<String, Object>> updates) {
for (java.util.Map<String, Object> update : updates) {
Long taskId = ((Number) update.get("taskId")).longValue();
Integer progress = ((Number) update.get("progress")).intValue();
String remark = (String) update.get("remark");
// 验证任务归属
Task task = taskMapper.selectById(taskId);
if (task == null) {
throw new BusinessException("任务不存在: taskId=" + taskId);
}
if (!task.getReceiverId().equals(userId)) {
throw new BusinessException("无权更新此任务进度: taskId=" + taskId);
}
if (!"in_progress".equals(task.getStatus())) {
throw new BusinessException("只能更新进行中的任务进度: taskId=" + taskId);
}
// 验证进度值
if (progress < 0 || progress > 100) {
throw new BusinessException("进度值必须在0-100之间: taskId=" + taskId);
}
Integer oldProgress = task.getProgress() != null ? task.getProgress() : 0;
if (progress < oldProgress) {
throw new BusinessException("新进度不能小于当前进度: taskId=" + taskId);
}
// 更新进度
Task updateTask = new Task();
updateTask.setId(taskId);
updateTask.setProgress(progress);
updateTask.setUpdateTime(LocalDateTime.now());
// 如果进度达到100%,自动标记为待审核
if (progress == 100) {
updateTask.setStatus("pending_review");
}
taskMapper.updateById(updateTask);
log.info("任务进度更新成功: taskId={}, userId={}, oldProgress={}, newProgress={}, remark={}",
taskId, userId, oldProgress, progress, remark);
}
}
/**
* 更新任务进度
*/
@Transactional
public void updateTaskProgress(Long taskId, Long userId, Integer progress) {
log.info("更新任务进度: taskId={}, userId={}, progress={}", taskId, userId, progress);
// 验证进度值
if (progress < 0 || progress > 100) {
throw new BusinessException("进度值必须在0-100之间");
}
Task task = taskMapper.selectById(taskId);
if (task == null) {
throw new BusinessException("任务不存在");
}
// 验证是接单人
if (!userId.equals(task.getReceiverId())) {
throw new BusinessException("只有接单人才能更新任务进度");
}
// 验证任务状态(只有进行中的任务才能更新进度)
if (!"in_progress".equals(task.getStatus())) {
throw new BusinessException("只有进行中的任务才能更新进度");
}
// 更新进度
task.setProgress(progress);
task.setUpdateTime(LocalDateTime.now());
taskMapper.updateById(task);
log.info("任务进度更新成功: taskId={}, progress={}", taskId, progress);
}
/**
* 完结任务(提交交付路径)
*/
@Transactional
public void completeTask(Long taskId, Long userId, String deliveryPath) {
log.info("完结任务: taskId={}, userId={}, deliveryPath={}", taskId, userId, deliveryPath);
Task task = taskMapper.selectById(taskId);
if (task == null) {
throw new BusinessException("任务不存在");
}
// 验证是接单人
if (!userId.equals(task.getReceiverId())) {
throw new BusinessException("只有接单人才能完结任务");
}
// 验证任务状态(只有进行中的任务才能完结)
if (!"in_progress".equals(task.getStatus())) {
throw new BusinessException("只有进行中的任务才能完结");
}
// 更新任务状态为待审核
task.setStatus("review");
task.setDeliveryPath(deliveryPath);
task.setProgress(100); // 完结时进度设为100%
task.setSubmitTime(LocalDateTime.now());
task.setUpdateTime(LocalDateTime.now());
taskMapper.updateById(task);
// 发送通知给发起人
try {
Notification notification = new Notification();
notification.setReceiverId(task.getPublisherId());
notification.setTitle("任务提交审核");
notification.setContent("任务【" + task.getTitle() + "】已提交审核,请查看");
notification.setNotificationType("TASK_REVIEW");
notification.setRelatedType("task");
notification.setRelatedId(taskId);
notification.setPushDingtalk(1); // 推送到钉钉
notificationService.sendNotification(notification);
} catch (Exception e) {
log.error("发送审核通知失败", e);
}
log.info("任务已提交审核: taskId={}", taskId);
}
/**
* 审核通过任务
*/
@Transactional
public void approveTask(Long taskId, Long userId) {
log.info("审核通过任务: taskId={}, userId={}", taskId, userId);
Task task = taskMapper.selectById(taskId);
if (task == null) {
throw new BusinessException("任务不存在");
}
// 验证权限(发起人或部门负责人)
User user = userService.getUserById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
boolean isPublisher = userId.equals(task.getPublisherId());
boolean isDeptLeader = false;
if (user.getDeptId() != null) {
Department dept = departmentService.getDepartmentById(user.getDeptId());
if (dept != null && userId.equals(dept.getLeaderId())) {
isDeptLeader = true;
}
}
if (!isPublisher && !isDeptLeader) {
throw new BusinessException("只有任务发起人或部门负责人才能审核任务");
}
// 验证任务状态
if (!"review".equals(task.getStatus())) {
throw new BusinessException("只有待审核的任务才能审核");
}
LocalDateTime completeTime = LocalDateTime.now();
// 更新任务状态为已完成
task.setStatus("completed");
task.setCompleteTime(completeTime);
task.setUpdateTime(completeTime);
task.setRating(5); // 默认5分
// 计算是否延误以及延误扣分
boolean isDelayed = false;
BigDecimal delayHours = BigDecimal.ZERO;
BigDecimal deductedScore = BigDecimal.ZERO;
if (task.getStartTime() != null && task.getEstimatedHours() != null && task.getEstimatedHours().compareTo(BigDecimal.ZERO) > 0) {
// 计算预计完成时间(接单时间 + 预估工时)
long estimatedMinutes = task.getEstimatedHours().multiply(BigDecimal.valueOf(60)).longValue();
LocalDateTime expectedCompleteTime = task.getStartTime().plusMinutes(estimatedMinutes);
// 判断是否延误
if (completeTime.isAfter(expectedCompleteTime)) {
isDelayed = true;
task.setIsDelayed(1);
// 计算延误小时数
long delayMinutes = java.time.Duration.between(expectedCompleteTime, completeTime).toMinutes();
delayHours = BigDecimal.valueOf(delayMinutes).divide(BigDecimal.valueOf(60), 2, java.math.RoundingMode.HALF_UP);
task.setDelayDays(delayHours.divide(BigDecimal.valueOf(24), 0, java.math.RoundingMode.CEILING).intValue());
// 从系统配置获取延误每小时扣分数
String penaltyPerHourStr = systemConfigService.getConfigValue("score.delay_penalty_per_hour");
BigDecimal penaltyPerHour = penaltyPerHourStr != null ? new BigDecimal(penaltyPerHourStr) : BigDecimal.valueOf(0.5);
// 计算扣分
deductedScore = delayHours.multiply(penaltyPerHour).setScale(2, java.math.RoundingMode.HALF_UP);
log.info("任务延误: taskId={}, 延误小时数={}, 扣分={}", taskId, delayHours, deductedScore);
} else {
task.setIsDelayed(0);
task.setDelayDays(0);
}
}
// 计算实际积分奖励(原积分 - 延误扣分)
BigDecimal earnedScore = BigDecimal.ZERO;
if (task.getScore() != null) {
earnedScore = BigDecimal.valueOf(task.getScore());
BigDecimal actualReward = earnedScore.subtract(deductedScore);
if (actualReward.compareTo(BigDecimal.ZERO) < 0) {
actualReward = BigDecimal.ZERO; // 最少为0,不会变成负数
}
task.setScoreReward(actualReward);
}
taskMapper.updateById(task);
// 更新接单部门的月度积分统计
if (task.getReceiverDeptId() != null) {
try {
deptMonthlyScoreService.addEarnedScore(
task.getReceiverDeptId(),
task.getScoreReward() != null ? task.getScoreReward() : BigDecimal.ZERO,
isDelayed,
delayHours
);
// 如果有延误扣分,单独记录扣除
if (deductedScore.compareTo(BigDecimal.ZERO) > 0) {
deptMonthlyScoreService.addDeductedScore(task.getReceiverDeptId(), deductedScore);
}
log.info("更新部门月度积分: deptId={}, earned={}, deducted={}",
task.getReceiverDeptId(), task.getScoreReward(), deductedScore);
} catch (Exception e) {
log.error("更新部门月度积分失败", e);
}
}
// 发送通知给接单人
try {
Notification notification = new Notification();
notification.setReceiverId(task.getReceiverId());
notification.setTitle("任务审核通过");
String content = "任务【" + task.getTitle() + "】审核通过";
if (isDelayed && deductedScore.compareTo(BigDecimal.ZERO) > 0) {
content += ",但因延误" + delayHours + "小时,扣除" + deductedScore + "分,实得" + task.getScoreReward() + "分";
} else {
content += ",积分已到账";
}
notification.setContent(content);
notification.setNotificationType("TASK_COMPLETED");
notification.setRelatedType("task");
notification.setRelatedId(taskId);
notification.setPushDingtalk(1); // 推送到钉钉
notificationService.sendNotification(notification);
} catch (Exception e) {
log.error("发送完成通知失败", e);
}
log.info("任务审核通过: taskId={}, isDelayed={}, deductedScore={}", taskId, isDelayed, deductedScore);
}
/**
* 驳回任务
*/
@Transactional
public void rejectTask(Long taskId, Long userId, String reason) {
log.info("驳回任务: taskId={}, userId={}, reason={}", taskId, userId, reason);
Task task = taskMapper.selectById(taskId);
if (task == null) {
throw new BusinessException("任务不存在");
}
// 验证权限(发起人或部门负责人)
User user = userService.getUserById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
boolean isPublisher = userId.equals(task.getPublisherId());
boolean isDeptLeader = false;
if (user.getDeptId() != null) {
Department dept = departmentService.getDepartmentById(user.getDeptId());
if (dept != null && userId.equals(dept.getLeaderId())) {
isDeptLeader = true;
}
}
if (!isPublisher && !isDeptLeader) {
throw new BusinessException("只有任务发起人或部门负责人才能驳回任务");
}
// 验证任务状态
if (!"review".equals(task.getStatus())) {
throw new BusinessException("只有待审核的任务才能驳回");
}
// 更新任务状态为进行中(驳回后重新修改)
task.setStatus("in_progress");
task.setRejectReason(reason);
task.setUpdateTime(LocalDateTime.now());
taskMapper.updateById(task);
// 发送通知给接单人
try {
Notification notification = new Notification();
notification.setReceiverId(task.getReceiverId());
notification.setTitle("任务被驳回");
notification.setContent("任务【" + task.getTitle() + "】被驳回,原因:" + reason);
notification.setNotificationType("TASK_REJECTED");
notification.setRelatedType("task");
notification.setRelatedId(taskId);
notification.setPriority(2); // 高优先级
notification.setPushDingtalk(1); // 推送到钉钉
notificationService.sendNotification(notification);
} catch (Exception e) {
log.error("发送驳回通知失败", e);
}
log.info("任务已驳回: taskId={}, reason={}", taskId, reason);
}
/**
* 初始化审核状态(两级审核流程)
*/
private void initApprovalStatus(Task task) {
try {
// 设置审核状态为待需求部门审核
task.setApprovalStatus("pending_demand_approval");
// 确定需求部门负责人
if (task.getPublisherDeptId() != null) {
Department demandDept = departmentService.getDepartmentById(task.getPublisherDeptId());
if (demandDept != null && demandDept.getLeaderId() != null) {
task.setCurrentApproverId(demandDept.getLeaderId());
task.setCurrentApproverRole("demand_leader");
log.info("任务已设置待审核状态,等待需求部门负责人审核:leaderId={}", demandDept.getLeaderId());
} else {
log.warn("需求部门未配置负责人,任务将直接发布:deptId={}", task.getPublisherDeptId());
// 如果没有配置负责人,跳过审核直接发布
task.setApprovalStatus("approved");
task.setStatus("pending");
}
}
} catch (Exception e) {
log.error("初始化审核状态失败", e);
// 失败时跳过审核直接发布
task.setApprovalStatus("approved");
task.setStatus("pending");
}
}
/**
* 发送审核通知(两级审核流程)
*/
private void sendApprovalNotification(Task task) {
try {
if (task.getCurrentApproverId() != null) {
User approver = userService.getUserById(task.getCurrentApproverId());
if (approver != null) {
Notification notification = new Notification();
notification.setReceiverId(task.getCurrentApproverId());
notification.setTitle("新任务待审核");
notification.setContent(String.format("任务【%s】需要您审核", task.getTitle()));
notification.setNotificationType("TASK_APPROVAL");
notification.setRelatedType("task");
notification.setRelatedId(task.getId());
notification.setPushDingtalk(1); // 推送到钉钉
notificationService.sendNotification(notification);
log.info("已发送审核通知给用户:userId={}, taskId={}", task.getCurrentApproverId(), task.getId());
}
}
} catch (Exception e) {
log.error("发送审核通知失败", e);
}
}
/**
* 计算任务总积分(用于审核时修改)
*/
public int calculateTotalScore(List<TaskUpdateDTO.TaskTypeUpdateDTO> taskTypes) {
int totalScore = 0;
for (TaskUpdateDTO.TaskTypeUpdateDTO taskType : taskTypes) {
DemandType demandType = configService.getDemandTypeById(taskType.getTaskTypeId());
if (demandType != null && demandType.getUnitScore() != null) {
totalScore += demandType.getUnitScore().multiply(BigDecimal.valueOf(taskType.getQuantity())).intValue();
}
}
return totalScore;
}
/**
* 创建流水线任务的子任务项(用于审核通过后拆分)
*/
public void createPipelineTaskItems(Task mainTask, com.designhub.dto.PipelineTaskDTO dto) {
// 委托给已有的createPipelineTask逻辑中的子任务创建部分
// 这里简化处理,实际应该提取createPipelineTask中的子任务创建逻辑
log.info("开始拆分流水线子任务:taskId={}, mode={}", mainTask.getId(), dto.getPipelineMode());
// TODO: 实现流水线子任务拆分逻辑
// 可以参考createPipelineTask方法中的实现
}
}