# 执行人员assignee_id字段填充功能说明
## 功能概述
在创建急救转运任务时,将第一个执行人员的ID写入主任务表的 `assignee_id` 字段,作为主要执行人(负责人)。
## 业务背景
急救转运任务通常由多个人员协同执行(司机、护士、医生等),但系统需要明确一个主要负责人:
- **assignee_id**:主要执行人(通常是第一个选择的人员)
- **assigneeIds**:所有执行人员ID列表(关联表 sys_task_assignee)
## 技术实现
### 前端数据提交
在 `buildSubmitData()` 方法中添加 `assigneeId` 字段:
```javascript
const submitData = {
taskType: 'EMERGENCY_TRANSFER',
vehicleIds: this.selectedVehicleId ? [this.selectedVehicleId] : [],
assigneeIds: this.selectedStaff.map(staff => staff.userId), // 所有执行人员ID列表
assigneeId: this.selectedStaff.length > 0 ? this.selectedStaff[0].userId : null, // 主要执行人(第一个人员)
// ... 其他字段
}
```
### 数据库字段
**主任务表(sys_task)**:
```sql
-- 主要执行人ID(单个)
assignee_id BIGINT
-- 主要执行人姓名(冗余字段,便于查询显示)
assignee_name VARCHAR(100)
```
**关联表(sys_task_assignee)**:
```sql
-- 任务与执行人员的多对多关联表
CREATE TABLE sys_task_assignee (
id BIGINT PRIMARY KEY,
task_id BIGINT NOT NULL, -- 任务ID
user_id BIGINT NOT NULL, -- 执行人员ID
user_name VARCHAR(100), -- 执行人员姓名
is_primary CHAR(1) DEFAULT '0' -- 是否主要执行人:0-否,1-是
)
```
## 数据结构
### 前端执行人员数据
```javascript
selectedStaff: [
{
userId: 1,
nickName: "张三",
phonenumber: "13800138000",
deptName: "广州分公司",
postName: "司机",
roleName: "普通员工"
},
{
userId: 2,
nickName: "李四",
phonenumber: "13800138001",
deptName: "广州分公司",
postName: "护士",
roleName: "普通员工"
},
{
userId: 3,
nickName: "王五",
phonenumber: "13800138002",
deptName: "广州分公司",
postName: "医生",
roleName: "普通员工"
}
]
```
### 提交数据
```javascript
{
taskType: "EMERGENCY_TRANSFER",
assigneeId: 1, // 主要执行人(张三)
assigneeIds: [1, 2, 3], // 所有执行人员
// ... 其他字段
}
```
### 数据库存储
**主任务表(sys_task)**:
| 字段 | 值 | 说明 |
|------|-----|------|
| task_id | 10001 | 任务ID |
| assignee_id | 1 | 主要执行人ID(张三) |
| assignee_name | 张三 | 主要执行人姓名 |
**关联表(sys_task_assignee)**:
| id | task_id | user_id | user_name | is_primary |
|----|---------|---------|-----------|------------|
| 1 | 10001 | 1 | 张三 | 1 |
| 2 | 10001 | 2 | 李四 | 0 |
| 3 | 10001 | 3 | 王五 | 0 |
## 业务逻辑
### 主要执行人选择规则
1. **默认第一个人员**:`selectedStaff[0]` 自动成为主要执行人
2. **用户可调整顺序**:用户可以通过拖动或删除重新排序,第一个始终是主要执行人
3. **无执行人员时**:`assigneeId = null`
### 用户界面展示
```vue
{{ staff.nickName }}
({{ staff.postName || staff.roleName }})
负责人
```
## 应用场景
### 1. 任务分配通知
系统发送任务通知时,主要执行人收到详细信息,其他人员收到协助通知:
```javascript
// 通知主要执行人
sendNotification({
userId: task.assigneeId,
title: '您有新的转运任务',
content: `任务编号:${task.taskCode},您是本次任务的负责人`,
type: 'PRIMARY_ASSIGNEE'
})
// 通知其他执行人员
task.assigneeIds.filter(id => id !== task.assigneeId).forEach(userId => {
sendNotification({
userId,
title: '您有新的协助任务',
content: `任务编号:${task.taskCode},请协助完成`,
type: 'ASSIST_ASSIGNEE'
})
})
```
### 2. 任务列表显示
任务列表主要显示负责人信息:
```vue
{{ task.taskCode }}
负责人:{{ task.assigneeName }}
+{{ task.assigneeIds.length - 1 }}人协助
```
### 3. 权限控制
主要执行人拥有更多操作权限:
```javascript
// 判断当前用户是否是主要执行人
const isPrimaryAssignee = currentUser.userId === task.assigneeId
// 主要执行人可以操作的功能
if (isPrimaryAssignee) {
// 可以修改任务状态
// 可以添加/移除其他执行人员
// 可以填写任务报告
}
```
### 4. 统计报表
按主要执行人统计任务完成情况:
```sql
SELECT
u.user_name,
COUNT(*) AS total_tasks,
SUM(CASE WHEN t.task_status = 'COMPLETED' THEN 1 ELSE 0 END) AS completed_tasks,
AVG(TIMESTAMPDIFF(MINUTE, t.actual_start_time, t.actual_end_time)) AS avg_duration
FROM sys_task t
JOIN sys_user u ON t.assignee_id = u.user_id
WHERE t.task_type = 'EMERGENCY_TRANSFER'
AND t.create_time >= '2025-01-01'
GROUP BY u.user_name
ORDER BY completed_tasks DESC
```
## 异常处理
### 1. 无执行人员
**场景**:用户未选择任何执行人员
**处理**:
```javascript
assigneeId: this.selectedStaff.length > 0 ? this.selectedStaff[0].userId : null
```
**结果**:assignee_id 为 NULL,允许后续补充
### 2. 执行人员变更
**场景**:用户先选择了A,后来删除A并选择了B
**处理**:
- 始终以 `selectedStaff[0]` 为准
- 删除第一个人员后,原来的第二个人员自动成为负责人
### 3. 后端验证
建议后端进行验证:
```java
// Service层验证
if (taskCreateVO.getAssigneeIds() != null && !taskCreateVO.getAssigneeIds().isEmpty()) {
// assigneeId 必须在 assigneeIds 列表中
if (taskCreateVO.getAssigneeId() != null) {
if (!taskCreateVO.getAssigneeIds().contains(taskCreateVO.getAssigneeId())) {
throw new ServiceException("主要执行人必须在执行人员列表中");
}
} else {
// 如果没有指定 assigneeId,默认使用第一个
taskCreateVO.setAssigneeId(taskCreateVO.getAssigneeIds().get(0));
}
}
```
## 数据一致性
### 保证规则
1. **assigneeId 必须在 assigneeIds 中**:
- 前端:`assigneeId = selectedStaff[0].userId`
- 后端:验证并自动纠正
2. **关联表标记**:
- sys_task_assignee 表中,主要执行人的 is_primary = '1'
- 其他人员的 is_primary = '0'
3. **姓名冗余同步**:
- 保存时同时更新 assignee_name 字段
- 避免每次查询都要 JOIN user 表
## 测试建议
### 测试场景1:正常选择多个人员
**操作**:
1. 选择执行人员:张三、李四、王五
2. 保存任务
**验证**:
```sql
-- 检查主任务表
SELECT assignee_id, assignee_name FROM sys_task WHERE task_id = ?
-- 预期:assignee_id = 1(张三的ID),assignee_name = '张三'
-- 检查关联表
SELECT * FROM sys_task_assignee WHERE task_id = ? ORDER BY user_id
-- 预期:3条记录,张三的 is_primary = '1',其他为 '0'
```
### 测试场景2:只选择一个人员
**操作**:
1. 只选择张三
2. 保存任务
**验证**:
- assignee_id = 1
- assigneeIds = [1]
- 关联表只有1条记录
### 测试场景3:未选择人员
**操作**:
1. 不选择任何执行人员
2. 保存任务
**验证**:
- assignee_id = NULL
- assigneeIds = []
- 关联表无记录
### 测试场景4:人员顺序调整
**操作**:
1. 选择张三、李四
2. 删除张三
3. 现在李四是第一个
4. 保存任务
**验证**:
- assignee_id = 2(李四的ID)
- assignee_name = '李四'
## 版本历史
- **2025-01-25 v1**: 初始版本
- ✅ 添加 assigneeId 字段
- ✅ 自动取第一个执行人员作为主要执行人
- ✅ 兼容无执行人员的情况(assigneeId = null)