编辑 | blame | 历史 | 原始文档

执行人员assignee_id字段填充功能说明

功能概述

在创建急救转运任务时,将第一个执行人员的ID写入主任务表的 assignee_id 字段,作为主要执行人(负责人)。

业务背景

急救转运任务通常由多个人员协同执行(司机、护士、医生等),但系统需要明确一个主要负责人:
- assignee_id:主要执行人(通常是第一个选择的人员)
- assigneeIds:所有执行人员ID列表(关联表 sys_task_assignee)

技术实现

前端数据提交

buildSubmitData() 方法中添加 assigneeId 字段:

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)

-- 主要执行人ID(单个)
assignee_id BIGINT

-- 主要执行人姓名(冗余字段,便于查询显示)
assignee_name VARCHAR(100)

关联表(sys_task_assignee)

-- 任务与执行人员的多对多关联表
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-是
)

数据结构

前端执行人员数据

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: "普通员工"
  }
]

提交数据

{
  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

用户界面展示

<view class="staff-list">
  <view class="staff-item" v-for="(staff, index) in selectedStaff" :key="staff.userId">
    <view class="staff-info">
      <text class="staff-name">{{ staff.nickName }}</text>
      <text class="staff-role">({{ staff.postName || staff.roleName }})</text>
      <!-- 第一个人员显示"负责人"标识 -->
      <text v-if="index === 0" class="primary-badge">负责人</text>
    </view>
    <!-- 第一个人员不能删除 -->
    <uni-icons 
      v-if="index > 0" 
      type="closeempty" 
      @click="removeStaff(index)"
    ></uni-icons>
    <uni-icons 
      v-else
      type="checkmarkempty" 
      color="#007AFF"
    ></uni-icons>
  </view>
</view>

应用场景

1. 任务分配通知

系统发送任务通知时,主要执行人收到详细信息,其他人员收到协助通知:

// 通知主要执行人
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. 任务列表显示

任务列表主要显示负责人信息:

<view class="task-item">
  <text class="task-code">{{ task.taskCode }}</text>
  <text class="assignee">负责人:{{ task.assigneeName }}</text>
  <text class="staff-count" v-if="task.assigneeIds.length > 1">
    +{{ task.assigneeIds.length - 1 }}人协助
  </text>
</view>

3. 权限控制

主要执行人拥有更多操作权限:

// 判断当前用户是否是主要执行人
const isPrimaryAssignee = currentUser.userId === task.assigneeId

// 主要执行人可以操作的功能
if (isPrimaryAssignee) {
  // 可以修改任务状态
  // 可以添加/移除其他执行人员
  // 可以填写任务报告
}

4. 统计报表

按主要执行人统计任务完成情况:

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. 后端验证

建议后端进行验证:

// 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
  • 后端:验证并自动纠正
  1. 关联表标记
  • sys_task_assignee 表中,主要执行人的 is_primary = '1'
  • 其他人员的 is_primary = '0'
  1. 姓名冗余同步
  • 保存时同时更新 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)