diff --git a/backend/src/main/java/com/designhub/controller/TaskApprovalController.java b/backend/src/main/java/com/designhub/controller/TaskApprovalController.java index 7832732..ffae590 100644 --- a/backend/src/main/java/com/designhub/controller/TaskApprovalController.java +++ b/backend/src/main/java/com/designhub/controller/TaskApprovalController.java @@ -62,7 +62,8 @@ public class TaskApprovalController { ) { String token = jwtUtil.extractTokenFromHeader(authHeader); Long approverId = jwtUtil.getUserIdFromToken(token); - taskApprovalService.approveReceivingLevel(id, approverId, dto); + // 从dto中获取指定的接收人ID + taskApprovalService.approveReceivingLevel(id, approverId, dto, dto.getAssignedReceiverId()); return Result.success(); } diff --git a/backend/src/main/java/com/designhub/controller/UserController.java b/backend/src/main/java/com/designhub/controller/UserController.java index 1440f72..e4909c4 100644 --- a/backend/src/main/java/com/designhub/controller/UserController.java +++ b/backend/src/main/java/com/designhub/controller/UserController.java @@ -125,5 +125,14 @@ public class UserController { log.info("调整用户部门结果: userId={}, deptId={}, result={}", id, deptId, result); return Result.success(result); } + + @Operation(summary = "获取设计师列表及工作量", description = "获取部门内的设计师列表,包含每个设计师当前进行中的任务数量") + @GetMapping("/designers-with-workload") + public Result>> getDesignersWithWorkload( + @RequestParam(required = false) Long deptId + ) { + log.info("获取设计师列表及工作量: deptId={}", deptId); + return Result.success(userService.getDesignersWithWorkload(deptId)); + } } diff --git a/backend/src/main/java/com/designhub/dto/TaskApprovalDTO.java b/backend/src/main/java/com/designhub/dto/TaskApprovalDTO.java index 74943c3..5399ccb 100644 --- a/backend/src/main/java/com/designhub/dto/TaskApprovalDTO.java +++ b/backend/src/main/java/com/designhub/dto/TaskApprovalDTO.java @@ -17,6 +17,11 @@ public class TaskApprovalDTO { */ private TaskUpdateDTO taskUpdates; + /** + * 指定的接收人ID(可选,用于接单部门审核时指定设计师) + */ + private Long assignedReceiverId; + // Getters and Setters public String getComment() { return comment; @@ -33,4 +38,12 @@ public class TaskApprovalDTO { public void setTaskUpdates(TaskUpdateDTO taskUpdates) { this.taskUpdates = taskUpdates; } + + public Long getAssignedReceiverId() { + return assignedReceiverId; + } + + public void setAssignedReceiverId(Long assignedReceiverId) { + this.assignedReceiverId = assignedReceiverId; + } } diff --git a/backend/src/main/java/com/designhub/mapper/TaskMapper.java b/backend/src/main/java/com/designhub/mapper/TaskMapper.java index de44a7e..dccf54e 100644 --- a/backend/src/main/java/com/designhub/mapper/TaskMapper.java +++ b/backend/src/main/java/com/designhub/mapper/TaskMapper.java @@ -202,5 +202,12 @@ public interface TaskMapper extends BaseMapper { IPage selectDeptReceivingTaskPage(Page page, @Param("deptId") Long deptId, @Param("status") String status); + + /** + * 查询设计师进行中的子任务数量 + */ + @Select("SELECT COUNT(*) FROM task_item WHERE receiver_id = #{receiverId} " + + "AND status = 'in_progress' AND is_locked = 0") + Long countSubTasksByReceiverId(@Param("receiverId") Long receiverId); } diff --git a/backend/src/main/java/com/designhub/service/TaskApprovalService.java b/backend/src/main/java/com/designhub/service/TaskApprovalService.java index dd2336a..290dc1b 100644 --- a/backend/src/main/java/com/designhub/service/TaskApprovalService.java +++ b/backend/src/main/java/com/designhub/service/TaskApprovalService.java @@ -189,9 +189,13 @@ public class TaskApprovalService { /** * 接单部门负责人审核通过 + * @param taskId 任务ID + * @param approverId 审核人ID + * @param dto 审核数据(包含备注和任务修改) + * @param assignedReceiverId 指定的接收人ID(可选) */ @Transactional(rollbackFor = Exception.class) - public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto) { + public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto, Long assignedReceiverId) { Task task = getTaskForApproval(taskId, "pending_receiving_approval"); validateApprover(task, approverId, "receiving_leader"); @@ -201,13 +205,31 @@ public class TaskApprovalService { 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 { - // 普通任务:直接发布到任务大厅 - task.setStatus("pending"); + // 普通任务:根据是否指定接收人设置状态 + if (assignedReceiverId != null) { + task.setStatus("in_progress"); // 指定接单直接进入进行中状态 + task.setStartTime(LocalDateTime.now()); + task.setActualStartTime(LocalDateTime.now()); + } else { + task.setStatus("pending"); // 未指定则发布到大厅 + } } // 更新审核状态 @@ -226,13 +248,33 @@ public class TaskApprovalService { } // 记录审核日志 - logApproval(taskId, approverId, 2, "approve", dto.getComment()); + 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); - // TODO: 通知任务大厅(所有接单部门成员) - // 暂时不实现群发通知功能,后续可以通过WebSocket或轮询方式实现 - log.info("任务【{}】已发布到任务大厅", task.getTitle()); + // 如果指定了接收人,发送通知 + if (assignedReceiverId != null) { + // TODO: 通知指定的设计师 + log.info("任务【{}】已指定给设计师", task.getTitle()); + } else { + // TODO: 通知任务大厅(所有接单部门成员) + log.info("任务【{}】已发布到任务大厅", task.getTitle()); + } - log.info("接单部门负责人审核通过,任务ID:{},审核人:{}", taskId, approverId); + log.info("接单部门负责人审核通过,任务ID:{},审核人:{},指定接收人:{}", + taskId, approverId, assignedReceiverId); + } + + /** + * 接单部门负责人审核通过(重载方法,保持向后兼容) + */ + @Transactional(rollbackFor = Exception.class) + public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto) { + approveReceivingLevel(taskId, approverId, dto, null); } /** diff --git a/backend/src/main/java/com/designhub/service/UserService.java b/backend/src/main/java/com/designhub/service/UserService.java index cfbfe6a..33e2eda 100644 --- a/backend/src/main/java/com/designhub/service/UserService.java +++ b/backend/src/main/java/com/designhub/service/UserService.java @@ -19,6 +19,7 @@ import com.designhub.vo.UserInfoVO; import com.designhub.vo.UserDepartmentVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -48,6 +49,9 @@ public class UserService { private final DepartmentService departmentService; private final JwtUtil jwtUtil; private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + @Autowired(required = false) + private com.designhub.mapper.TaskMapper taskMapper; private Department getDepartmentOrThrow(Long deptId) { if (deptId == null) { @@ -1195,4 +1199,67 @@ public class UserService { return matchedDeptIds; } + + /** + * 获取设计师列表及工作量 + * @param deptId 部门ID(可选,如果不传则返回所有设计师) + * @return 设计师列表,包含每个设计师的进行中任务数量 + */ + public List> getDesignersWithWorkload(Long deptId) { + log.info("获取设计师列表及工作量: deptId={}", deptId); + + // 查询用户列表 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(User::getStatus, 1); // 只查询启用的用户 + queryWrapper.eq(User::getDeleted, 0); // 未删除 + if (deptId != null) { + queryWrapper.eq(User::getDeptId, deptId); + } + queryWrapper.orderByAsc(User::getRealName); + + List users = userMapper.selectList(queryWrapper); + + // 构建返回结果 + List> result = new ArrayList<>(); + for (User user : users) { + Map designerInfo = new HashMap<>(); + designerInfo.put("id", user.getId()); + designerInfo.put("username", user.getUsername()); + designerInfo.put("realName", user.getRealName()); + designerInfo.put("deptId", user.getDeptId()); + + // 查询进行中的任务数量(包括普通任务和子任务) + int inProgressCount = 0; + if (taskMapper != null) { + // 查询普通任务 + Long normalTaskCount = taskMapper.selectCount( + new LambdaQueryWrapper() + .eq(com.designhub.entity.Task::getReceiverId, user.getId()) + .eq(com.designhub.entity.Task::getStatus, "in_progress") + .eq(com.designhub.entity.Task::getDeleted, 0) + ); + + // 查询子任务(TaskItem) + Long subTaskCount = taskMapper.countSubTasksByReceiverId(user.getId()); + + inProgressCount = (normalTaskCount != null ? normalTaskCount.intValue() : 0) + + (subTaskCount != null ? subTaskCount.intValue() : 0); + } + + designerInfo.put("inProgressCount", inProgressCount); + + // 获取部门名称 + if (user.getDeptId() != null) { + Department dept = departmentMapper.selectById(user.getDeptId()); + if (dept != null) { + designerInfo.put("deptName", dept.getDeptName()); + } + } + + result.add(designerInfo); + } + + log.info("查询到{}个设计师", result.size()); + return result; + } } diff --git a/frontend/src/api/task.ts b/frontend/src/api/task.ts index 0a3e088..85e6375 100644 --- a/frontend/src/api/task.ts +++ b/frontend/src/api/task.ts @@ -452,6 +452,7 @@ export const getTaskItemsProgress = (taskId: number) => { export interface TaskApprovalDTO { comment?: string // 审核意见 taskUpdates?: TaskUpdateDTO // 任务修改(可选) + assignedReceiverId?: number // 指定的接收人ID(用于接单部门审核时指定设计师) } /** diff --git a/frontend/src/api/user.ts b/frontend/src/api/user.ts index 23cda73..1e63c06 100644 --- a/frontend/src/api/user.ts +++ b/frontend/src/api/user.ts @@ -139,3 +139,25 @@ export const updateUserDepartment = async (id: number, deptId: number | null): P return response.data; }; +/** + * 设计师及工作量信息 + */ +export interface DesignerWithWorkload { + id: number; + username: string; + realName: string; + deptId?: number; + deptName?: string; + inProgressCount: number; // 进行中的任务数量 +} + +/** + * 获取设计师列表及工作量 + */ +export const getDesignersWithWorkload = async (deptId?: number): Promise => { + const params: any = {}; + if (deptId) params.deptId = deptId; + const response = await request.get('/users/designers-with-workload', { params }); + return response.data || []; +}; + diff --git a/frontend/src/views/TaskHall.vue b/frontend/src/views/TaskHall.vue index ec4f277..2c27397 100644 --- a/frontend/src/views/TaskHall.vue +++ b/frontend/src/views/TaskHall.vue @@ -1875,11 +1875,13 @@