package com.designhub.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.designhub.dto.PipelineTaskDTO; import com.designhub.dto.TaskApprovalDTO; import com.designhub.dto.TaskUpdateDTO; import com.designhub.entity.*; import com.designhub.exception.BusinessException; import com.designhub.mapper.*; import com.designhub.common.ResultCode; import com.designhub.entity.Brand; import com.designhub.entity.Category; import com.designhub.entity.Platform; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; /** * 任务审核服务 */ @Service @RequiredArgsConstructor @Slf4j public class TaskApprovalService { private final TaskMapper taskMapper; private final TaskApprovalLogMapper approvalLogMapper; private final DepartmentMapper departmentMapper; private final UserMapper userMapper; private final BrandMapper brandMapper; private final CategoryMapper categoryMapper; private final PlatformMapper platformMapper; private final NotificationService notificationService; private final TeamScorePoolService teamScorePoolService; private final TaskService taskService; private final ConfigService configService; private final ObjectMapper objectMapper; /** * 需求部门负责人审核通过 */ @Transactional(rollbackFor = Exception.class) public void approveDemandLevel(Long taskId, Long approverId, TaskApprovalDTO dto) { // 1. 验证任务和权限 Task task = getTaskForApproval(taskId, "pending_demand_approval"); validateApprover(task, approverId, "demand_leader"); // 2. 应用任务修改(如果有) if (dto.getTaskUpdates() != null) { applyTaskUpdates(task, dto.getTaskUpdates()); logApproval(taskId, approverId, 1, "modify", dto.getComment()); } // 3. 根据发布类型决定是否需要接单部门负责人审核 String releaseType = task.getReleaseType(); boolean needReceivingApproval = "default".equals(releaseType); if (needReceivingApproval) { // default模式:需要接单部门负责人审核 Long receivingLeaderId = determineReceivingLeader(task); if (receivingLeaderId == null) { throw new BusinessException(ResultCode.PARAM_ERROR, "无法确定接单部门负责人,请在系统设置中为接单部门配置负责人"); } // 更新任务状态为待接单部门审核 task.setApprovalStatus("pending_receiving_approval"); task.setDemandApprovalTime(LocalDateTime.now()); task.setCurrentApproverId(receivingLeaderId); task.setCurrentApproverRole("receiving_leader"); taskMapper.updateById(task); // 记录审核日志 logApproval(taskId, approverId, 1, "approve", dto.getComment()); // 发送通知给接单部门负责人 User receivingLeader = userMapper.selectById(receivingLeaderId); if (receivingLeader != null) { notificationService.sendNotification( receivingLeaderId, "新任务待审核", String.format("任务【%s】需要您审核并分配设计师", task.getTitle()), "task_approval", taskId ); } log.info("需求部门负责人审核通过,任务ID:{},审核人:{},等待接单部门负责人审核", taskId, approverId); } else { // recruitment或assigned模式:不需要接单部门负责人审核,直接发布 task.setDemandApprovalTime(LocalDateTime.now()); // 判断是否为流水线任务 if (task.getIsPipeline() != null && task.getIsPipeline() == 1) { // 流水线任务:拆分子任务 splitIntoPipelineSubTasks(task); // splitIntoPipelineSubTasks方法会根据releaseType设置任务状态 } else { // 普通任务处理 if ("assigned".equals(releaseType) && task.getReceiverId() != null) { // assigned模式:如果指定了接收人,直接分配给该设计师 User receiver = userMapper.selectById(task.getReceiverId()); if (receiver != null && receiver.getDeptId() != null) { task.setReceiverDeptId(receiver.getDeptId()); task.setStatus("in_progress"); task.setStartTime(LocalDateTime.now()); task.setActualStartTime(LocalDateTime.now()); // 重新计算截止时间 if (task.getEstimatedHours() != null) { int hours = task.getEstimatedHours().intValue(); if (hours > 0) { task.setDeadline(LocalDateTime.now().plusHours(hours)); } } // 发送通知给被指定的设计师 notificationService.sendNotification( task.getReceiverId(), "新任务分配", String.format("任务【%s】已分配给您", task.getTitle()), "task_assigned", taskId ); log.info("assigned模式任务已直接分配给设计师:taskId={}, receiverId={}", taskId, task.getReceiverId()); } else { throw new BusinessException(ResultCode.PARAM_ERROR, "指定的接收人不存在或部门信息不完整"); } } else { // recruitment模式:发布到任务大厅 task.setStatus("pending"); } } // 更新审核状态为已通过 task.setApprovalStatus("approved"); task.setCurrentApproverId(null); task.setCurrentApproverRole(null); taskMapper.updateById(task); // 扣除部门积分 try { teamScorePoolService.deductScore(task.getPublisherDeptId(), task.getScore()); } catch (Exception e) { log.error("扣除部门积分失败", e); throw new BusinessException(ResultCode.ERROR, "扣除部门积分失败:" + e.getMessage()); } // 记录审核日志 logApproval(taskId, approverId, 1, "approve", dto.getComment()); log.info("需求部门负责人审核通过,任务ID:{},审核人:{},发布类型:{},已直接发布", taskId, approverId, releaseType); } } /** * 需求部门负责人驳回 */ @Transactional(rollbackFor = Exception.class) public void rejectByDemandLeader(Long taskId, Long approverId, String reason) { Task task = getTaskForApproval(taskId, "pending_demand_approval"); validateApprover(task, approverId, "demand_leader"); task.setApprovalStatus("rejected_by_demand"); task.setStatus("draft"); task.setCurrentApproverId(null); task.setCurrentApproverRole(null); taskMapper.updateById(task); logApproval(taskId, approverId, 1, "reject", reason); // 通知发起人 notificationService.sendNotification( task.getPublisherId(), "任务已被驳回", String.format("您的任务【%s】被需求部门负责人驳回,原因:%s", task.getTitle(), reason), "task_reject", taskId ); log.info("需求部门负责人驳回任务,任务ID:{},审核人:{}", taskId, approverId); } /** * 接单部门负责人审核通过 * @param taskId 任务ID * @param approverId 审核人ID * @param dto 审核数据(包含备注和任务修改) * @param assignedReceiverId 指定的接收人ID(可选) */ @Transactional(rollbackFor = Exception.class) public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto, Long assignedReceiverId) { Task task = getTaskForApproval(taskId, "pending_receiving_approval"); validateApprover(task, approverId, "receiving_leader"); // 应用任务修改(如果有) if (dto.getTaskUpdates() != null) { applyTaskUpdates(task, dto.getTaskUpdates()); logApproval(taskId, approverId, 2, "modify", dto.getComment()); } // 如果指定了接收人,验证接收人是否存在并设置 if (assignedReceiverId != null) { com.designhub.entity.User receiver = userMapper.selectById(assignedReceiverId); if (receiver == null) { throw new BusinessException(ResultCode.PARAM_ERROR, "指定的接收人不存在"); } task.setReceiverId(assignedReceiverId); task.setReceiverDeptId(receiver.getDeptId()); task.setReleaseType("assigned"); // 设置为指定模式 log.info("任务【{}】已指定给设计师【{}】", task.getTitle(), receiver.getRealName()); } // 判断是否为流水线任务 if (task.getIsPipeline() != null && task.getIsPipeline() == 1) { // 流水线任务:拆分子任务 splitIntoPipelineSubTasks(task); } else { // 普通任务:根据是否指定接收人设置状态 if (assignedReceiverId != null) { task.setStatus("in_progress"); // 指定接单直接进入进行中状态 task.setStartTime(LocalDateTime.now()); task.setActualStartTime(LocalDateTime.now()); } else { task.setStatus("pending"); // 未指定则发布到大厅 } } // 更新审核状态 task.setApprovalStatus("approved"); task.setReceivingApprovalTime(LocalDateTime.now()); task.setCurrentApproverId(null); task.setCurrentApproverRole(null); taskMapper.updateById(task); // 扣除部门积分 try { teamScorePoolService.deductScore(task.getPublisherDeptId(), task.getScore()); } catch (Exception e) { log.error("扣除部门积分失败", e); throw new BusinessException(ResultCode.ERROR, "扣除部门积分失败:" + e.getMessage()); } // 记录审核日志 String logComment = dto.getComment(); if (assignedReceiverId != null) { com.designhub.entity.User receiver = userMapper.selectById(assignedReceiverId); logComment = (logComment != null ? logComment + ";" : "") + "已指定给设计师:" + (receiver != null ? receiver.getRealName() : assignedReceiverId); } logApproval(taskId, approverId, 2, "approve", logComment); // 如果指定了接收人,发送通知 if (assignedReceiverId != null) { // TODO: 通知指定的设计师 log.info("任务【{}】已指定给设计师", task.getTitle()); } else { // TODO: 通知任务大厅(所有接单部门成员) log.info("任务【{}】已发布到任务大厅", task.getTitle()); } log.info("接单部门负责人审核通过,任务ID:{},审核人:{},指定接收人:{}", taskId, approverId, assignedReceiverId); } /** * 接单部门负责人审核通过(重载方法,保持向后兼容) */ @Transactional(rollbackFor = Exception.class) public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto) { approveReceivingLevel(taskId, approverId, dto, null); } /** * 接单部门负责人驳回 */ @Transactional(rollbackFor = Exception.class) public void rejectByReceivingLeader(Long taskId, Long approverId, String reason) { Task task = getTaskForApproval(taskId, "pending_receiving_approval"); validateApprover(task, approverId, "receiving_leader"); // 退回给需求部门负责人 Department demandDept = departmentMapper.selectById(task.getPublisherDeptId()); if (demandDept == null || demandDept.getLeaderId() == null) { throw new BusinessException(ResultCode.PARAM_ERROR, "需求部门负责人不存在"); } task.setApprovalStatus("rejected_by_receiving"); task.setStatus("draft"); task.setCurrentApproverId(demandDept.getLeaderId()); task.setCurrentApproverRole("demand_leader"); taskMapper.updateById(task); logApproval(taskId, approverId, 2, "reject", reason); // 通知需求部门负责人 notificationService.sendNotification( demandDept.getLeaderId(), "任务已被驳回", String.format("任务【%s】被接单部门负责人驳回,原因:%s", task.getTitle(), reason), "task_reject", taskId ); log.info("接单部门负责人驳回任务,任务ID:{},审核人:{}", taskId, approverId); } /** * 获取待审核任务列表 */ public List getPendingApprovalTasks(Long userId) { List tasks = taskMapper.selectList( new LambdaQueryWrapper() .eq(Task::getCurrentApproverId, userId) .in(Task::getApprovalStatus, "pending_demand_approval", "pending_receiving_approval") .orderByDesc(Task::getCreateTime) ); // 填充关联信息 for (Task task : tasks) { // 填充发起人姓名 if (task.getPublisherId() != null) { User publisher = userMapper.selectById(task.getPublisherId()); if (publisher != null) { task.setPublisherName(publisher.getUsername()); } } // 填充发起部门名称 if (task.getPublisherDeptId() != null) { Department dept = departmentMapper.selectById(task.getPublisherDeptId()); if (dept != null) { task.setPublisherDeptName(dept.getDeptName()); } } // 填充品牌名称 if (task.getBrandId() != null) { Brand brand = brandMapper.selectById(task.getBrandId()); if (brand != null) { // Brand实体类中name字段映射到brand_name,使用getName() task.setBrandName(brand.getName()); } } // 填充类目名称 if (task.getCategoryId() != null) { Category category = categoryMapper.selectById(task.getCategoryId()); if (category != null) { // Category实体类中name字段映射到category_name,使用getName() task.setCategoryName(category.getName()); } } // 填充平台名称 if (task.getPlatformId() != null) { Platform platform = platformMapper.selectById(task.getPlatformId()); if (platform != null) { // Platform实体类中name字段映射到platform_name,使用getName() task.setPlatformName(platform.getName()); } } // 填充当前审核人姓名 if (task.getCurrentApproverId() != null) { User approver = userMapper.selectById(task.getCurrentApproverId()); if (approver != null) { task.setCurrentApproverName(approver.getRealName() != null ? approver.getRealName() : approver.getUsername()); } } } return tasks; } /** * 获取任务并验证审核状态 */ private Task getTaskForApproval(Long taskId, String expectedStatus) { Task task = taskMapper.selectById(taskId); if (task == null) { throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在"); } if (!expectedStatus.equals(task.getApprovalStatus())) { throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "任务当前状态不允许此操作,当前状态:" + task.getApprovalStatus()); } return task; } /** * 验证审核人权限 */ private void validateApprover(Task task, Long approverId, String expectedRole) { if (!approverId.equals(task.getCurrentApproverId())) { throw new BusinessException(ResultCode.FORBIDDEN, "您无权审核此任务"); } if (!expectedRole.equals(task.getCurrentApproverRole())) { throw new BusinessException(ResultCode.FORBIDDEN, "审核角色不匹配"); } } /** * 确定接单部门负责人 */ private Long determineReceivingLeader(Task task) { // 根据任务类型的allowedDeptIds确定接单部门 // 简化版本:取第一个任务类型的第一个允许部门 String taskTypeIds = task.getTaskTypeIds(); if (taskTypeIds == null || taskTypeIds.isEmpty()) { return null; } String[] typeIdArray = taskTypeIds.split(","); for (String typeIdStr : typeIdArray) { try { Long typeId = Long.parseLong(typeIdStr.trim()); DemandType demandType = configService.getDemandTypeById(typeId); if (demandType != null && demandType.getAllowedDeptIds() != null) { String[] deptIds = demandType.getAllowedDeptIds().split(","); if (deptIds.length > 0) { Long deptId = Long.parseLong(deptIds[0].trim()); Department dept = departmentMapper.selectById(deptId); if (dept != null && dept.getLeaderId() != null) { return dept.getLeaderId(); } } } } catch (Exception e) { log.error("解析任务类型或部门ID失败", e); } } return null; } /** * 应用任务更新 */ private void applyTaskUpdates(Task task, TaskUpdateDTO updates) { if (updates.getTitle() != null) { task.setTitle(updates.getTitle()); } if (updates.getTaskSubType() != null) { task.setTaskSubType(updates.getTaskSubType()); } if (updates.getBrandId() != null) { task.setBrandId(updates.getBrandId()); } if (updates.getPlatformId() != null) { task.setPlatformId(updates.getPlatformId()); } if (updates.getCategoryId() != null) { task.setCategoryId(updates.getCategoryId()); } if (updates.getSpuInfo() != null) { task.setSpuInfo(updates.getSpuInfo()); } if (updates.getRemark() != null) { task.setRemark(updates.getRemark()); } if (updates.getAttachmentUrl() != null) { task.setAttachmentUrl(updates.getAttachmentUrl()); } // 如果修改了任务类型和数量,需要重新计算积分 if (updates.getTaskTypes() != null && !updates.getTaskTypes().isEmpty()) { String taskTypeIds = updates.getTaskTypes().stream() .map(t -> String.valueOf(t.getTaskTypeId())) .collect(Collectors.joining(",")); task.setTaskTypeIds(taskTypeIds); // 如果只有一个任务类型,更新quantity字段 if (updates.getTaskTypes().size() == 1) { task.setQuantity(updates.getTaskTypes().get(0).getQuantity()); } // 重新计算积分 int totalScore = taskService.calculateTotalScore(updates.getTaskTypes()); task.setScore(totalScore); } } /** * 拆分为流水线子任务 */ private void splitIntoPipelineSubTasks(Task task) { try { // 构建流水线任务DTO PipelineTaskDTO dto = new PipelineTaskDTO(); dto.setTitle(task.getTitle()); dto.setTaskSubType(task.getTaskSubType()); dto.setBrandId(task.getBrandId()); dto.setPlatformId(task.getPlatformId()); dto.setCategoryId(task.getCategoryId()); dto.setSpuInfo(task.getSpuInfo()); dto.setRemark(task.getRemark()); dto.setAttachmentUrl(task.getAttachmentUrl()); dto.setPipelineMode(task.getPipelineMode()); dto.setReleaseType(task.getReleaseType()); // 传递发布类型 // 解析任务类型 String[] taskTypeIds = task.getTaskTypeIds().split(","); List items = new java.util.ArrayList<>(); // 简化版本:所有任务类型使用相同的数量(task.quantity) int quantity = task.getQuantity() != null ? task.getQuantity() : 1; for (String taskTypeIdStr : taskTypeIds) { PipelineTaskDTO.TaskItemDTO item = new PipelineTaskDTO.TaskItemDTO(); item.setTaskTypeId(Long.parseLong(taskTypeIdStr.trim())); item.setQuantity(quantity); items.add(item); } dto.setItems(items); // 调用TaskService创建流水线子任务 taskService.createPipelineTaskItems(task, dto); // 根据发布类型设置主任务状态 if ("recruitment".equals(task.getReleaseType())) { // recruitment模式:发布到任务大厅 task.setStatus("pending"); } else if ("assigned".equals(task.getReleaseType()) && task.getReceiverId() != null) { // assigned模式:如果指定了接收人,状态已经是in_progress(在approveDemandLevel中已设置) // 这里不需要再次设置 } else { // default模式或其他模式:流水线任务的主任务状态设置为pending // 子任务会根据is_locked字段控制可见性 task.setStatus("pending"); } } catch (Exception e) { log.error("拆分流水线任务失败", e); throw new BusinessException(ResultCode.ERROR, "拆分流水线任务失败:" + e.getMessage()); } } /** * 记录审核日志 */ private void logApproval(Long taskId, Long approverId, Integer level, String action, String comment) { try { User approver = userMapper.selectById(approverId); TaskApprovalLog log = new TaskApprovalLog(); log.setTaskId(taskId); log.setApprovalLevel(level); log.setApproverId(approverId); log.setApproverName(approver != null ? approver.getRealName() : null); log.setAction(action); log.setComment(comment); log.setCreateTime(LocalDateTime.now()); approvalLogMapper.insert(log); } catch (Exception e) { this.log.error("记录审核日志失败", e); } } }