500 lines
14 KiB
Markdown
500 lines
14 KiB
Markdown
# 任务节点查询功能开发文档
|
||
|
||
## 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<TaskFlowStatusVO> 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<FlowTimelineNode> 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
|
||
<!-- TaskFlowQueryDialog.vue -->
|
||
<template>
|
||
<el-dialog title="任务节点查询" v-model="visible" width="700px">
|
||
<!-- 搜索框 -->
|
||
<div class="search-box">
|
||
<el-input
|
||
v-model="searchInput"
|
||
placeholder="请输入任务编号或任务标题"
|
||
@keyup.enter="handleSearch"
|
||
clearable>
|
||
<template #append>
|
||
<el-button @click="handleSearch" :loading="loading">
|
||
<el-icon><Search /></el-icon> 查询
|
||
</el-button>
|
||
</template>
|
||
</el-input>
|
||
</div>
|
||
|
||
<!-- 查询结果 -->
|
||
<div v-if="result" class="result-container">
|
||
<!-- 基本信息 -->
|
||
<el-card class="info-card">
|
||
<template #header>任务信息</template>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="任务编号">{{ result.task.taskNo }}</el-descriptions-item>
|
||
<el-descriptions-item label="任务标题">{{ result.task.title }}</el-descriptions-item>
|
||
<el-descriptions-item label="任务类型">
|
||
<el-tag :type="result.task.isPrimaryTask ? 'warning' : ''">
|
||
{{ result.task.isPrimaryTask ? '一级需求任务' : '普通任务' }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="任务积分">{{ result.task.score }} 分</el-descriptions-item>
|
||
</el-descriptions>
|
||
</el-card>
|
||
|
||
<!-- 当前节点(重点展示) -->
|
||
<el-card class="current-node-card">
|
||
<div class="current-node">
|
||
<div class="node-icon">📍</div>
|
||
<div class="node-info">
|
||
<div class="node-status">当前节点:{{ result.currentNode.statusName }}</div>
|
||
<div class="node-waiting">
|
||
等待操作人:{{ result.currentNode.waitingFor?.userName }}
|
||
({{ result.currentNode.waitingFor?.deptName }})
|
||
</div>
|
||
<div class="node-desc">{{ result.currentNode.description }}</div>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 流程时间线 -->
|
||
<el-card class="timeline-card">
|
||
<template #header>流程进度</template>
|
||
<el-timeline>
|
||
<el-timeline-item
|
||
v-for="node in result.flowTimeline"
|
||
:key="node.nodeName"
|
||
:type="getTimelineType(node.status)"
|
||
:hollow="node.status !== 'completed'">
|
||
<div class="timeline-node">
|
||
<div class="node-name">{{ node.nodeName }}</div>
|
||
<div class="node-operator" v-if="node.operatorName">
|
||
{{ node.operatorName }}({{ node.operatorDept }})
|
||
</div>
|
||
<div class="node-time" v-if="node.time">{{ formatTime(node.time) }}</div>
|
||
<div class="node-comment" v-if="node.comment">备注:{{ node.comment }}</div>
|
||
</div>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
</el-card>
|
||
</div>
|
||
|
||
<!-- 空状态 -->
|
||
<el-empty v-else-if="searched && !result" description="未找到相关任务" />
|
||
</el-dialog>
|
||
</template>
|
||
```
|
||
|
||
#### 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. **订阅提醒**:用户可订阅任务状态变更通知
|