diff --git a/backend/src/main/java/com/designhub/controller/TaskController.java b/backend/src/main/java/com/designhub/controller/TaskController.java index b5b0247..38a466a 100644 --- a/backend/src/main/java/com/designhub/controller/TaskController.java +++ b/backend/src/main/java/com/designhub/controller/TaskController.java @@ -618,5 +618,16 @@ public class TaskController { taskApprovalService.completeTaskDetail(id, userId, taskItems); return Result.success(); } + + // ==================== 任务流程查询接口 ==================== + + @Operation(summary = "查询任务流程状态", description = "根据任务编号或关键字查询任务当前所在流程节点") + @GetMapping("/flow-status") + public Result getTaskFlowStatus( + @Parameter(description = "任务编号") @RequestParam(required = false) String taskNo, + @Parameter(description = "任务标题关键字") @RequestParam(required = false) String keyword) { + log.info("查询任务流程状态: taskNo={}, keyword={}", taskNo, keyword); + return Result.success(taskService.getTaskFlowStatus(taskNo, keyword)); + } } diff --git a/backend/src/main/java/com/designhub/service/TaskApprovalService.java b/backend/src/main/java/com/designhub/service/TaskApprovalService.java index f6d823b..913ee22 100644 --- a/backend/src/main/java/com/designhub/service/TaskApprovalService.java +++ b/backend/src/main/java/com/designhub/service/TaskApprovalService.java @@ -1082,6 +1082,19 @@ public class TaskApprovalService { } } + /** + * 获取任务的审批日志列表 + * @param taskId 任务ID + * @return 审批日志列表 + */ + public List getApprovalLogs(Long taskId) { + return approvalLogMapper.selectList( + new LambdaQueryWrapper() + .eq(TaskApprovalLog::getTaskId, taskId) + .orderByAsc(TaskApprovalLog::getCreateTime) + ); + } + /** * 记录审核日志 */ diff --git a/backend/src/main/java/com/designhub/service/TaskService.java b/backend/src/main/java/com/designhub/service/TaskService.java index 34fabea..c37166b 100644 --- a/backend/src/main/java/com/designhub/service/TaskService.java +++ b/backend/src/main/java/com/designhub/service/TaskService.java @@ -4466,5 +4466,483 @@ public class TaskService { taskMapper.updateById(task); log.info("任务 {} 子任务创建完成,共 {} 个子任务组,总积分 {}", taskId, groupedItems.size(), totalScore); } + + // ==================== 任务流程状态查询 ==================== + + /** + * 获取任务流程状态(用于任务节点查询功能) + * @param taskNo 任务编号 + * @param keyword 任务标题关键字 + * @return 任务流程状态信息 + */ + public com.designhub.vo.TaskFlowStatusVO getTaskFlowStatus(String taskNo, String keyword) { + // 1. 查询任务 + Task task = null; + if (taskNo != null && !taskNo.trim().isEmpty()) { + task = taskMapper.selectOne( + new LambdaQueryWrapper() + .eq(Task::getTaskNo, taskNo.trim()) + .eq(Task::getDeleted, 0) + ); + } else if (keyword != null && !keyword.trim().isEmpty()) { + task = taskMapper.selectOne( + new LambdaQueryWrapper() + .like(Task::getTitle, keyword.trim()) + .eq(Task::getDeleted, 0) + .orderByDesc(Task::getId) + .last("LIMIT 1") + ); + } + + if (task == null) { + throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在"); + } + + // 2. 构建返回结果 + com.designhub.vo.TaskFlowStatusVO vo = new com.designhub.vo.TaskFlowStatusVO(); + vo.setTask(buildTaskBasicInfo(task)); + vo.setCurrentNode(buildCurrentNodeInfo(task)); + vo.setFlowTimeline(buildFlowTimeline(task)); + vo.setRelatedUsers(buildRelatedUsers(task)); + + return vo; + } + + /** + * 构建任务基本信息 + */ + private com.designhub.vo.TaskFlowStatusVO.TaskBasicInfo buildTaskBasicInfo(Task task) { + com.designhub.vo.TaskFlowStatusVO.TaskBasicInfo info = new com.designhub.vo.TaskFlowStatusVO.TaskBasicInfo(); + info.setId(task.getId()); + info.setTaskNo(task.getTaskNo()); + info.setTitle(task.getTitle()); + info.setScore(task.getScore()); + info.setIsPrimaryTask(task.getIsPrimaryTask() != null && task.getIsPrimaryTask() == 1); + info.setIsPipeline(task.getIsPipeline() != null && task.getIsPipeline() == 1); + info.setStatus(task.getStatus()); + info.setStatusName(getStatusName(task.getStatus())); + info.setCreateTime(task.getCreateTime()); + return info; + } + + /** + * 构建当前节点信息 + */ + private com.designhub.vo.TaskFlowStatusVO.CurrentNodeInfo buildCurrentNodeInfo(Task task) { + com.designhub.vo.TaskFlowStatusVO.CurrentNodeInfo info = new com.designhub.vo.TaskFlowStatusVO.CurrentNodeInfo(); + + String approvalStatus = task.getApprovalStatus(); + String status = task.getStatus(); + boolean isPrimaryTask = task.getIsPrimaryTask() != null && task.getIsPrimaryTask() == 1; + + info.setStatus(approvalStatus != null ? approvalStatus : status); + info.setStatusName(getApprovalStatusName(approvalStatus, status, isPrimaryTask)); + info.setDescription(getApprovalStatusDescription(approvalStatus, status, isPrimaryTask)); + + // 设置等待操作人 + if (task.getCurrentApproverId() != null) { + com.designhub.vo.TaskFlowStatusVO.WaitingUser waitingUser = new com.designhub.vo.TaskFlowStatusVO.WaitingUser(); + waitingUser.setUserId(task.getCurrentApproverId()); + + User approver = userMapper.selectById(task.getCurrentApproverId()); + if (approver != null) { + waitingUser.setUserName(approver.getRealName()); + waitingUser.setDeptName(approver.getDeptName()); + } + + String role = task.getCurrentApproverRole(); + waitingUser.setRole(role); + waitingUser.setRoleName(getApproverRoleName(role)); + + info.setWaitingFor(waitingUser); + } else if ("pending".equals(status) && task.getReceiverId() == null) { + // 待接单状态 + com.designhub.vo.TaskFlowStatusVO.WaitingUser waitingUser = new com.designhub.vo.TaskFlowStatusVO.WaitingUser(); + waitingUser.setRoleName("等待设计师接单"); + info.setWaitingFor(waitingUser); + } else if ("in_progress".equals(status) && task.getReceiverId() != null) { + // 进行中状态 + com.designhub.vo.TaskFlowStatusVO.WaitingUser waitingUser = new com.designhub.vo.TaskFlowStatusVO.WaitingUser(); + waitingUser.setUserId(task.getReceiverId()); + + User receiver = userMapper.selectById(task.getReceiverId()); + if (receiver != null) { + waitingUser.setUserName(receiver.getRealName()); + waitingUser.setDeptName(receiver.getDeptName()); + } + waitingUser.setRoleName("设计师执行中"); + info.setWaitingFor(waitingUser); + } + + return info; + } + + /** + * 构建流程时间线 + */ + private java.util.List buildFlowTimeline(Task task) { + java.util.List timeline = new java.util.ArrayList<>(); + boolean isPrimaryTask = task.getIsPrimaryTask() != null && task.getIsPrimaryTask() == 1; + String approvalStatus = task.getApprovalStatus(); + String status = task.getStatus(); + + // 查询审批日志 + java.util.List logs = taskApprovalService.getApprovalLogs(task.getId()); + java.util.Map logMap = new java.util.HashMap<>(); + for (TaskApprovalLog log : logs) { + logMap.put(log.getAction(), log); + } + + if (isPrimaryTask) { + // 一级需求任务流程 + + // 1. 一级需求发起 + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node1 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node1.setNodeName("一级需求发起"); + node1.setStatus("completed"); + node1.setTime(task.getCreateTime()); + if (task.getPrimaryPublisherId() != null) { + User publisher = userMapper.selectById(task.getPrimaryPublisherId()); + if (publisher != null) { + node1.setOperatorName(publisher.getRealName()); + node1.setOperatorDept(publisher.getDeptName()); + } + } + timeline.add(node1); + + // 2. 一级需求审批 + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node2 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node2.setNodeName("一级需求审批"); + TaskApprovalLog primaryLog = logMap.get("approve_primary"); + if (primaryLog != null) { + node2.setStatus("completed"); + node2.setOperatorName(primaryLog.getApproverName()); + node2.setTime(primaryLog.getCreateTime()); + node2.setComment(primaryLog.getComment()); + // 获取审批人部门 + User approver = userMapper.selectById(primaryLog.getApproverId()); + if (approver != null) { + node2.setOperatorDept(approver.getDeptName()); + } + } else if ("pending_primary_approval".equals(approvalStatus)) { + node2.setStatus("pending"); + if (task.getCurrentApproverId() != null) { + User approver = userMapper.selectById(task.getCurrentApproverId()); + if (approver != null) { + node2.setOperatorName(approver.getRealName()); + node2.setOperatorDept(approver.getDeptName()); + } + } + } else { + node2.setStatus("waiting"); + } + timeline.add(node2); + + // 3. 明细完善 + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node3 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node3.setNodeName("明细完善"); + TaskApprovalLog detailLog = logMap.get("complete_detail"); + if (detailLog != null) { + node3.setStatus("completed"); + node3.setOperatorName(detailLog.getApproverName()); + node3.setTime(detailLog.getCreateTime()); + node3.setComment(detailLog.getComment()); + User completer = userMapper.selectById(detailLog.getApproverId()); + if (completer != null) { + node3.setOperatorDept(completer.getDeptName()); + } + } else if ("pending_detail_completion".equals(approvalStatus)) { + node3.setStatus("pending"); + if (task.getDetailCompleterId() != null) { + User completer = userMapper.selectById(task.getDetailCompleterId()); + if (completer != null) { + node3.setOperatorName(completer.getRealName()); + node3.setOperatorDept(completer.getDeptName()); + } + } + } else if (primaryLog != null) { + node3.setStatus("waiting"); + } else { + node3.setStatus("waiting"); + } + timeline.add(node3); + + // 4. 接单部门审批(如果是流水线任务) + boolean isPipeline = task.getIsPipeline() != null && task.getIsPipeline() == 1; + if (isPipeline || "pending_receiving_approval".equals(approvalStatus) || + logMap.containsKey("approve_receiving") || logMap.containsKey("reject_receiving")) { + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node4 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node4.setNodeName("接单部门审批"); + TaskApprovalLog receivingLog = logMap.get("approve_receiving"); + if (receivingLog != null) { + node4.setStatus("completed"); + node4.setOperatorName(receivingLog.getApproverName()); + node4.setTime(receivingLog.getCreateTime()); + node4.setComment(receivingLog.getComment()); + User approver = userMapper.selectById(receivingLog.getApproverId()); + if (approver != null) { + node4.setOperatorDept(approver.getDeptName()); + } + } else if ("pending_receiving_approval".equals(approvalStatus)) { + node4.setStatus("pending"); + if (task.getCurrentApproverId() != null) { + User approver = userMapper.selectById(task.getCurrentApproverId()); + if (approver != null) { + node4.setOperatorName(approver.getRealName()); + node4.setOperatorDept(approver.getDeptName()); + } + } + } else { + node4.setStatus("waiting"); + } + timeline.add(node4); + } + + // 5. 任务发布/执行 + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node5 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node5.setNodeName("任务执行"); + if ("approved".equals(approvalStatus) && ("pending".equals(status) || "in_progress".equals(status) || "completed".equals(status))) { + if ("completed".equals(status)) { + node5.setStatus("completed"); + node5.setTime(task.getCompleteTime()); + } else if ("in_progress".equals(status)) { + node5.setStatus("pending"); + } else { + node5.setStatus("pending"); + } + if (task.getReceiverId() != null) { + User receiver = userMapper.selectById(task.getReceiverId()); + if (receiver != null) { + node5.setOperatorName(receiver.getRealName()); + node5.setOperatorDept(receiver.getDeptName()); + } + } + } else { + node5.setStatus("waiting"); + } + timeline.add(node5); + + } else { + // 普通任务流程 + + // 1. 任务发起 + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node1 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node1.setNodeName("任务发起"); + node1.setStatus("completed"); + node1.setTime(task.getCreateTime()); + if (task.getPublisherId() != null) { + User publisher = userMapper.selectById(task.getPublisherId()); + if (publisher != null) { + node1.setOperatorName(publisher.getRealName()); + node1.setOperatorDept(publisher.getDeptName()); + } + } + timeline.add(node1); + + // 2. 任务审核 + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node2 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node2.setNodeName("任务审核"); + if ("approved".equals(approvalStatus) || "pending".equals(status) || "in_progress".equals(status) || "completed".equals(status)) { + node2.setStatus("completed"); + // 查找审批日志 + TaskApprovalLog approveLog = logMap.get("approve"); + if (approveLog != null) { + node2.setOperatorName(approveLog.getApproverName()); + node2.setTime(approveLog.getCreateTime()); + node2.setComment(approveLog.getComment()); + User approver = userMapper.selectById(approveLog.getApproverId()); + if (approver != null) { + node2.setOperatorDept(approver.getDeptName()); + } + } + } else if ("pending_approval".equals(approvalStatus)) { + node2.setStatus("pending"); + if (task.getCurrentApproverId() != null) { + User approver = userMapper.selectById(task.getCurrentApproverId()); + if (approver != null) { + node2.setOperatorName(approver.getRealName()); + node2.setOperatorDept(approver.getDeptName()); + } + } + } else { + node2.setStatus("waiting"); + } + timeline.add(node2); + + // 3. 任务执行 + com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode node3 = new com.designhub.vo.TaskFlowStatusVO.FlowTimelineNode(); + node3.setNodeName("任务执行"); + if ("completed".equals(status)) { + node3.setStatus("completed"); + node3.setTime(task.getCompleteTime()); + if (task.getReceiverId() != null) { + User receiver = userMapper.selectById(task.getReceiverId()); + if (receiver != null) { + node3.setOperatorName(receiver.getRealName()); + node3.setOperatorDept(receiver.getDeptName()); + } + } + } else if ("in_progress".equals(status)) { + node3.setStatus("pending"); + if (task.getReceiverId() != null) { + User receiver = userMapper.selectById(task.getReceiverId()); + if (receiver != null) { + node3.setOperatorName(receiver.getRealName()); + node3.setOperatorDept(receiver.getDeptName()); + } + } + } else if ("pending".equals(status)) { + node3.setStatus("waiting"); + } else { + node3.setStatus("waiting"); + } + timeline.add(node3); + } + + return timeline; + } + + /** + * 构建相关人员信息 + */ + private com.designhub.vo.TaskFlowStatusVO.RelatedUsers buildRelatedUsers(Task task) { + com.designhub.vo.TaskFlowStatusVO.RelatedUsers users = new com.designhub.vo.TaskFlowStatusVO.RelatedUsers(); + + // 发起人 + Long publisherId = task.getPrimaryPublisherId() != null ? task.getPrimaryPublisherId() : task.getPublisherId(); + if (publisherId != null) { + User publisher = userMapper.selectById(publisherId); + if (publisher != null) { + com.designhub.vo.TaskFlowStatusVO.UserInfo info = new com.designhub.vo.TaskFlowStatusVO.UserInfo(); + info.setUserId(publisherId); + info.setName(publisher.getRealName()); + info.setDept(publisher.getDeptName()); + users.setPublisher(info); + } + } + + // 明细完善人 + if (task.getDetailCompleterId() != null) { + User completer = userMapper.selectById(task.getDetailCompleterId()); + if (completer != null) { + com.designhub.vo.TaskFlowStatusVO.UserInfo info = new com.designhub.vo.TaskFlowStatusVO.UserInfo(); + info.setUserId(task.getDetailCompleterId()); + info.setName(completer.getRealName()); + info.setDept(completer.getDeptName()); + users.setDetailCompleter(info); + } + } + + // 接单人 + if (task.getReceiverId() != null) { + User receiver = userMapper.selectById(task.getReceiverId()); + if (receiver != null) { + com.designhub.vo.TaskFlowStatusVO.UserInfo info = new com.designhub.vo.TaskFlowStatusVO.UserInfo(); + info.setUserId(task.getReceiverId()); + info.setName(receiver.getRealName()); + info.setDept(receiver.getDeptName()); + users.setReceiver(info); + } + } + + // 当前审批人 + if (task.getCurrentApproverId() != null) { + User approver = userMapper.selectById(task.getCurrentApproverId()); + if (approver != null) { + com.designhub.vo.TaskFlowStatusVO.UserInfo info = new com.designhub.vo.TaskFlowStatusVO.UserInfo(); + info.setUserId(task.getCurrentApproverId()); + info.setName(approver.getRealName()); + info.setDept(approver.getDeptName()); + users.setCurrentApprover(info); + } + } + + return users; + } + + /** + * 获取任务执行状态中文名称 + */ + private String getStatusName(String status) { + if (status == null) return "未知"; + switch (status) { + case "draft": return "草稿"; + case "pending_audit": return "待审核"; + case "pending": return "待接单"; + case "in_progress": return "进行中"; + case "completed": return "已完成"; + case "cancelled": return "已取消"; + default: return status; + } + } + + /** + * 获取审批状态中文名称 + */ + private String getApprovalStatusName(String approvalStatus, String status, boolean isPrimaryTask) { + if (approvalStatus == null) { + return getStatusName(status); + } + + switch (approvalStatus) { + case "draft": return "草稿"; + case "pending_primary_approval": return "待一级需求审批"; + case "pending_detail_completion": return "待完善明细"; + case "pending_demand_approval": return "待需求部门审批"; + case "pending_receiving_approval": return "待接单部门审批"; + case "pending_approval": return "待审核"; + case "approved": + // 已审核通过,显示执行状态 + return getStatusName(status); + case "rejected": return "已驳回"; + case "rejected_by_demand": return "需求部门驳回"; + case "rejected_by_receiving": return "接单部门驳回"; + default: return approvalStatus; + } + } + + /** + * 获取审批状态描述 + */ + private String getApprovalStatusDescription(String approvalStatus, String status, boolean isPrimaryTask) { + if (approvalStatus == null) { + return ""; + } + + switch (approvalStatus) { + case "draft": return "任务已创建但未提交"; + case "pending_primary_approval": return "等待需求部门负责人审核并指定明细完善人"; + case "pending_detail_completion": return "等待指定的明细完善人填写任务明细"; + case "pending_demand_approval": return "明细完善后等待需求部门负责人审核"; + case "pending_receiving_approval": return "等待接单部门负责人审核"; + case "pending_approval": return "等待审核通过后发布"; + case "approved": + if ("pending".equals(status)) { + return "任务已发布,等待设计师接单"; + } else if ("in_progress".equals(status)) { + return "设计师正在执行任务"; + } else if ("completed".equals(status)) { + return "任务已完成"; + } + return "任务已审核通过"; + case "rejected": return "任务被驳回,请修改后重新提交"; + case "rejected_by_demand": return "被需求部门负责人驳回"; + case "rejected_by_receiving": return "被接单部门负责人驳回"; + default: return ""; + } + } + + /** + * 获取审批人角色中文名称 + */ + private String getApproverRoleName(String role) { + if (role == null) return ""; + switch (role) { + case "demand_leader": return "需求部门负责人"; + case "detail_completer": return "明细完善人"; + case "receiving_leader": return "接单部门负责人"; + default: return role; + } + } } diff --git a/backend/src/main/java/com/designhub/vo/TaskFlowStatusVO.java b/backend/src/main/java/com/designhub/vo/TaskFlowStatusVO.java new file mode 100644 index 0000000..f9be2b5 --- /dev/null +++ b/backend/src/main/java/com/designhub/vo/TaskFlowStatusVO.java @@ -0,0 +1,144 @@ +package com.designhub.vo; + +import lombok.Data; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 任务流程状态VO - 用于任务节点查询功能 + */ +@Data +public class TaskFlowStatusVO { + + /** + * 任务基本信息 + */ + private TaskBasicInfo task; + + /** + * 当前节点信息 + */ + private CurrentNodeInfo currentNode; + + /** + * 流程时间线 + */ + private List flowTimeline; + + /** + * 相关人员信息 + */ + private RelatedUsers relatedUsers; + + /** + * 任务基本信息 + */ + @Data + public static class TaskBasicInfo { + private Long id; + private String taskNo; + private String title; + private BigDecimal score; + private Boolean isPrimaryTask; + private Boolean isPipeline; + private String status; + private String statusName; + private LocalDateTime createTime; + } + + /** + * 当前节点信息 + */ + @Data + public static class CurrentNodeInfo { + private String status; + private String statusName; + private String description; + private WaitingUser waitingFor; + } + + /** + * 等待操作的用户 + */ + @Data + public static class WaitingUser { + private Long userId; + private String userName; + private String deptName; + private String role; + private String roleName; + } + + /** + * 流程时间线节点 + */ + @Data + public static class FlowTimelineNode { + /** + * 节点名称 + */ + private String nodeName; + + /** + * 节点状态:completed-已完成, pending-进行中, waiting-等待中 + */ + private String status; + + /** + * 操作人姓名 + */ + private String operatorName; + + /** + * 操作人部门 + */ + private String operatorDept; + + /** + * 操作时间 + */ + private LocalDateTime time; + + /** + * 操作备注 + */ + private String comment; + } + + /** + * 相关人员信息 + */ + @Data + public static class RelatedUsers { + /** + * 发起人(普通任务)或一级需求发起人 + */ + private UserInfo publisher; + + /** + * 明细完善人(一级需求任务) + */ + private UserInfo detailCompleter; + + /** + * 接单人/执行人 + */ + private UserInfo receiver; + + /** + * 当前审批人 + */ + private UserInfo currentApprover; + } + + /** + * 用户信息 + */ + @Data + public static class UserInfo { + private Long userId; + private String name; + private String dept; + } +} diff --git a/docs/任务节点查询功能开发文档.md b/docs/任务节点查询功能开发文档.md new file mode 100644 index 0000000..f3aa52d --- /dev/null +++ b/docs/任务节点查询功能开发文档.md @@ -0,0 +1,499 @@ +# 任务节点查询功能开发文档 + +## 1. 需求背景 + +用户反馈经常找不到任务当前处于什么节点,不清楚: +- 任务当前状态是什么 +- 下一步需要谁操作 +- 任务经历了哪些审批流程 + +因此需要开发一个**任务节点查询功能**,让用户可以通过任务编号快速查询任务的流程状态。 + +--- + +## 2. 功能设计 + +### 2.1 功能入口 + +在页面顶部导航栏或任务大厅页面增加「任务查询」按钮,点击后弹出查询对话框。 + +### 2.2 查询方式 + +- **任务编号查询**:输入完整的任务编号(如 `TASK202601221029343146`) +- **模糊搜索**:支持输入任务标题关键字搜索 + +### 2.3 展示内容 + +#### 基本信息卡片 +| 字段 | 说明 | +|------|------| +| 任务编号 | TASK202601221029343146 | +| 任务标题 | AD0259-欧意朗-天猫 | +| 任务类型 | 一级需求任务 / 普通任务 | +| 任务积分 | 5.20 分 | +| 创建时间 | 2026-01-22 10:29:34 | + +#### 当前节点信息(重点展示) +``` +┌─────────────────────────────────────────────┐ +│ 📍 当前节点:待接单部门审核 │ +│ │ +│ 等待操作人:清云(营销策划中心) │ +│ 操作说明:接单部门负责人需审核此任务 │ +└─────────────────────────────────────────────┘ +``` + +#### 流程时间线(可视化展示) +``` +● 一级需求发起 2026-01-22 10:29:34 瑶池(亿家达组) + ↓ +● 一级需求审批通过 2026-01-22 13:46:56 步惊云(策划三部) + ↓ +● 明细完善完成 2026-01-23 10:44:24 殷云(策划四部) + ↓ +○ 接单部门审核 等待中... 清云(营销策划中心) ← 当前 + ↓ +○ 任务发布 - - +``` + +#### 相关人员信息 +| 角色 | 姓名 | 部门 | +|------|------|------| +| 发起人 | 瑶池 | 亿家达组 | +| 明细完善人 | 殷云 | 策划四部 | +| 接单人 | 乐游 | 平面视觉五组 | +| 当前审批人 | 清云 | 营销策划中心 | + +--- + +## 3. 任务状态映射表 + +### 3.1 普通任务状态 + +| approval_status | 中文名称 | 说明 | +|-----------------|----------|------| +| draft | 草稿 | 任务已创建但未提交 | +| pending_approval | 待审核 | 等待需求部门负责人审核 | +| approved | 已审核 | 审核通过,任务已发布 | +| rejected | 已驳回 | 审核被驳回 | + +### 3.2 一级需求任务状态 + +| approval_status | 中文名称 | 当前操作人角色 | 说明 | +|-----------------|----------|----------------|------| +| draft | 草稿 | 发起人 | 任务已创建但未提交 | +| pending_primary_approval | 待一级需求审批 | 需求部门负责人 | 等待需求部门负责人审核并指定明细完善人 | +| pending_detail_completion | 待完善明细 | 明细完善人 | 等待指定的明细完善人填写任务明细 | +| pending_demand_approval | 待需求部门审批 | 需求部门负责人 | 明细完善后等待需求部门负责人审核 | +| pending_receiving_approval | 待接单部门审批 | 接单部门负责人 | 等待接单部门负责人审核 | +| approved | 已审核 | - | 所有审批通过,任务已发布 | +| rejected_by_demand | 需求部门驳回 | 明细完善人 | 被需求部门负责人驳回 | +| rejected_by_receiving | 接单部门驳回 | 需求部门负责人 | 被接单部门负责人驳回 | + +### 3.3 任务执行状态 + +| status | 中文名称 | 说明 | +|--------|----------|------| +| draft | 草稿 | 未提交 | +| pending_audit | 待审核 | 审批流程中 | +| pending | 待接单/进行中 | 已发布等待接单或执行中 | +| in_progress | 进行中 | 设计师正在执行 | +| completed | 已完成 | 任务完成 | +| cancelled | 已取消 | 任务被取消 | + +--- + +## 4. 技术实现 + +### 4.1 后端接口 + +#### 4.1.1 任务节点查询接口 + +**接口地址**:`GET /api/task/flow-status` + +**请求参数**: +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| taskNo | String | 否 | 任务编号 | +| keyword | String | 否 | 任务标题关键字 | + +**响应示例**: +```json +{ + "code": 200, + "message": "success", + "data": { + "task": { + "id": 432, + "taskNo": "TASK202601221029343146", + "title": "AD0259-欧意朗-天猫", + "score": 5.20, + "isPrimaryTask": true, + "createTime": "2026-01-22 10:29:34" + }, + "currentNode": { + "status": "pending_receiving_approval", + "statusName": "待接单部门审批", + "description": "等待接单部门负责人审核此任务", + "waitingFor": { + "userId": 17, + "userName": "清云", + "deptName": "营销策划中心", + "role": "接单部门负责人" + } + }, + "flowTimeline": [ + { + "nodeName": "一级需求发起", + "status": "completed", + "operatorName": "瑶池", + "operatorDept": "亿家达组", + "time": "2026-01-22 10:29:34" + }, + { + "nodeName": "一级需求审批", + "status": "completed", + "operatorName": "步惊云", + "operatorDept": "策划三部", + "time": "2026-01-22 13:46:56", + "comment": "审核通过,请完善任务明细" + }, + { + "nodeName": "明细完善", + "status": "completed", + "operatorName": "殷云", + "operatorDept": "策划四部", + "time": "2026-01-23 10:44:24" + }, + { + "nodeName": "接单部门审批", + "status": "pending", + "operatorName": "清云", + "operatorDept": "营销策划中心", + "time": null + }, + { + "nodeName": "任务发布", + "status": "waiting", + "operatorName": null, + "operatorDept": null, + "time": null + } + ], + "relatedUsers": { + "publisher": { "name": "瑶池", "dept": "亿家达组" }, + "detailCompleter": { "name": "殷云", "dept": "策划四部" }, + "receiver": { "name": "乐游", "dept": "平面视觉五组" }, + "currentApprover": { "name": "清云", "dept": "营销策划中心" } + } + } +} +``` + +### 4.2 后端实现 + +#### 4.2.1 Controller 层 + +```java +// TaskController.java + +/** + * 查询任务流程状态 + */ +@GetMapping("/flow-status") +public Result getTaskFlowStatus( + @RequestParam(required = false) String taskNo, + @RequestParam(required = false) String keyword) { + return Result.success(taskService.getTaskFlowStatus(taskNo, keyword)); +} +``` + +#### 4.2.2 Service 层 + +```java +// TaskService.java + +/** + * 获取任务流程状态 + */ +public TaskFlowStatusVO getTaskFlowStatus(String taskNo, String keyword) { + // 1. 查询任务 + Task task = findTaskByNoOrKeyword(taskNo, keyword); + if (task == null) { + throw new BusinessException("任务不存在"); + } + + // 2. 构建基本信息 + TaskFlowStatusVO vo = new TaskFlowStatusVO(); + vo.setTask(buildTaskInfo(task)); + + // 3. 构建当前节点信息 + vo.setCurrentNode(buildCurrentNode(task)); + + // 4. 构建流程时间线 + vo.setFlowTimeline(buildFlowTimeline(task)); + + // 5. 构建相关人员信息 + vo.setRelatedUsers(buildRelatedUsers(task)); + + return vo; +} +``` + +#### 4.2.3 VO 类 + +```java +// TaskFlowStatusVO.java + +@Data +public class TaskFlowStatusVO { + private TaskBasicInfo task; + private CurrentNodeInfo currentNode; + private List flowTimeline; + private RelatedUsers relatedUsers; + + @Data + public static class TaskBasicInfo { + private Long id; + private String taskNo; + private String title; + private BigDecimal score; + private Boolean isPrimaryTask; + private LocalDateTime createTime; + } + + @Data + public static class CurrentNodeInfo { + private String status; + private String statusName; + private String description; + private WaitingUser waitingFor; + } + + @Data + public static class WaitingUser { + private Long userId; + private String userName; + private String deptName; + private String role; + } + + @Data + public static class FlowTimelineNode { + private String nodeName; + private String status; // completed, pending, waiting + private String operatorName; + private String operatorDept; + private LocalDateTime time; + private String comment; + } + + @Data + public static class RelatedUsers { + private UserInfo publisher; + private UserInfo detailCompleter; + private UserInfo receiver; + private UserInfo currentApprover; + } + + @Data + public static class UserInfo { + private String name; + private String dept; + } +} +``` + +### 4.3 前端实现 + +#### 4.3.1 查询对话框组件 + +```vue + + +``` + +#### 4.3.2 样式设计 + +```scss +.current-node-card { + margin: 16px 0; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + + .current-node { + display: flex; + align-items: center; + color: #fff; + + .node-icon { + font-size: 48px; + margin-right: 20px; + } + + .node-status { + font-size: 20px; + font-weight: bold; + margin-bottom: 8px; + } + + .node-waiting { + font-size: 16px; + margin-bottom: 4px; + } + + .node-desc { + font-size: 14px; + opacity: 0.9; + } + } +} + +.timeline-card { + .timeline-node { + .node-name { + font-weight: bold; + font-size: 15px; + } + + .node-operator { + color: #409eff; + margin-top: 4px; + } + + .node-time { + color: #909399; + font-size: 12px; + margin-top: 4px; + } + + .node-comment { + color: #67c23a; + font-size: 12px; + margin-top: 4px; + } + } +} +``` + +--- + +## 5. 功能入口位置 + +### 5.1 方案A:顶部导航栏 + +在导航栏右侧增加「🔍 任务查询」按钮,全局可用。 + +### 5.2 方案B:任务大厅页面 + +在任务大厅页面顶部筛选区域增加「任务节点查询」按钮。 + +### 5.3 方案C:快捷搜索框 + +在页面顶部增加全局搜索框,支持直接输入任务编号快速查询。 + +**推荐方案**:方案A + 方案C 组合,既有明显入口,又支持快捷查询。 + +--- + +## 6. 权限控制 + +| 用户角色 | 可查询范围 | +|----------|-----------| +| 管理员 | 所有任务 | +| 需求部门成员 | 本部门发起的任务 + 自己参与的任务 | +| 接单部门成员 | 自己接单的任务 + 本部门的任务 | +| 普通用户 | 自己发起或参与的任务 | + +--- + +## 7. 开发工作量估算 + +| 模块 | 工作内容 | 预估时间 | +|------|----------|----------| +| 后端 | 接口开发、VO类、Service逻辑 | 4小时 | +| 前端 | 对话框组件、时间线、样式 | 4小时 | +| 测试 | 功能测试、边界情况 | 2小时 | +| **合计** | | **10小时** | + +--- + +## 8. 后续扩展 + +1. **消息通知集成**:在通知消息中增加「查看任务进度」链接 +2. **批量查询**:支持一次查询多个任务的状态 +3. **导出功能**:支持导出任务流程记录 +4. **订阅提醒**:用户可订阅任务状态变更通知 diff --git a/frontend/src/api/task.ts b/frontend/src/api/task.ts index 11924c9..6ee247e 100644 --- a/frontend/src/api/task.ts +++ b/frontend/src/api/task.ts @@ -792,3 +792,51 @@ export interface TaskItemDTO { export const completeTaskDetail = (taskId: number, taskItems: TaskItemDTO[]) => { return request.post(`/tasks/${taskId}/complete-detail`, taskItems) } + +// ==================== 任务流程查询接口 ==================== + +// 任务流程状态响应类型 +export interface TaskFlowStatusVO { + task: { + id: number + taskNo: string + title: string + score: number + isPrimaryTask: boolean + isPipeline: boolean + status: string + statusName: string + createTime: string + } + currentNode: { + status: string + statusName: string + description: string + waitingFor?: { + userId: number + userName: string + deptName: string + role: string + roleName: string + } + } + flowTimeline: Array<{ + nodeName: string + status: 'completed' | 'pending' | 'waiting' + operatorName?: string + operatorDept?: string + time?: string + comment?: string + }> + relatedUsers: { + publisher?: { userId: number; name: string; dept: string } + detailCompleter?: { userId: number; name: string; dept: string } + receiver?: { userId: number; name: string; dept: string } + currentApprover?: { userId: number; name: string; dept: string } + } +} + +// 查询任务流程状态 +export const getTaskFlowStatus = (params: { taskNo?: string; keyword?: string }) => { + return request.get('/tasks/flow-status', { params }) +} diff --git a/frontend/src/components/Navbar.vue b/frontend/src/components/Navbar.vue index 3750760..9291c28 100644 --- a/frontend/src/components/Navbar.vue +++ b/frontend/src/components/Navbar.vue @@ -84,19 +84,35 @@ + + + + + + + +