# 执行人员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)