2026-06-12-qc-task-assignee-my-tasks-design.md 12 KB

质检任务负责人与「我的质检任务」— 设计文档

日期: 2026-06-12
状态: 已实现(2026-06-12)
范围: web-admin + backend(质检工单三 Tab、配置/调整检测、我的质检任务)

1. 背景

质检工单页(入库 / 出库 / 临时三个 Tab)通过「配置检测」「调整检测」生成质检任务,但任务无负责人指派。质检专员无法在独立入口查看「分给自己的任务」并填写结果。

本变更引入任务负责人(单人或多人的集合),并在「质检管理」下新增我的质检任务菜单页。

2. 已确认的产品决策

问题 决策
多人负责时,完成语义? A — 任一负责人可独立完成(开始、填写结果、完成);completed_by 记录实际操作人
工单页 vs 我的任务页操作权限? A — 管理覆盖:我的任务页仅 assignee 可操作;工单展开行持有 biz:qcTask:list 可督办全部任务
负责人数据存储方式? 关联表 biz_qc_task_assignee(非逗号分隔字段)
负责人指派粒度? 每个检测项目(对应一条 biz_qc_task)独立指派
配置时负责人是否必填? 是 — 每个已勾选的 lab/third 项目至少 1 人

3. 现状摘要

项 现状
配置 API POST /web/admin/qc-work-orders/{id}/configure
任务生成 每个 lab/third 明细行 → create_task_for_line;保留项不重建
任务操作 QcWorkOrderTaskSubList.vue:开始 / 填写结果 / 查看 / 删除
我的视角参考 my-qc-material-usage(mine-only + 独立 menuCode)
工作流参考 wf_task.assignee_user_id + task-pending 过滤模式

当前缺失:biz_qc_task 无 assignee;list_tasks 无「我的任务」过滤。

4. 功能清单

4.1 配置检测 / 调整检测弹窗

  • 在 qc-work-order/index.vue 配置弹窗中,每个已勾选的实验室 / 第三方检测项目旁增加负责人多选(el-select multiple filterable)。
  • 用户选项:本租户有效 sys_user(展示 nickName(userNo),与流程设计器一致)。
  • 首次配置:每个勾选项目必选 ≥1 负责人,否则前端 + 后端校验拒绝。
  • 调整检测:
    • 回显已配置项目的负责人;
    • 新增项目:必选负责人;
    • 保留项目:可更新负责人;
    • 移除项目:连带软删任务及 assignee(沿用现有 _sync_* 逻辑);
    • 已完成任务(ywc):若检测项目仍保留,允许改负责人(仅影响「我的任务」可见性,不改 completed_by);不建议移除已完成项对应的检测项目(现有逻辑已限制删除进行中/已完成任务的场景,保持后端既有校验)。
  • 提交 body 扩展见 §5.2。

4.2 工单展开行任务卡片

  • QcWorkOrderTaskSubList.vue(或抽取的共用组件)任务卡片 meta 区展示负责人(el-tag 或 muted 文本,多人用顿号连接)。
  • 操作权限:不收紧 — 持有 biz:qcTask:list 的用户仍可按现有状态机操作(管理督办)。

4.3 我的质检任务(新页面)

  • 路由:/#/quality-manage/my-qc-task
  • 组件:web-admin/src/views/quality-inspection/my-qc-task/index.vue
  • 菜单:质检管理 → 我的质检任务,menuCode: biz:myQcTask:list
  • UI 模式:P02(状态驱动操作列)+ P21 任务卡片;列表结构参考 order/index.vue,任务卡片与结果弹窗复用 QcWorkOrderTaskSubList 抽取逻辑。

筛选区(filter-block):

字段 类型
关键词 任务编号 / 项目名称 / 工单号 / 样本编码
任务状态 dzx / jxz / ywc
任务类型 sysz / dsfjc
工单类型 inbound / outbound / temporary

列表列 / 卡片信息:

  • 任务编号、检测项目名称、任务类型 Tag、任务状态 Tag、结论 Tag(若有)
  • 关联工单号、样本编码(若有)、工单类型
  • 负责人(当前用户高亮可选)
  • 操作:开始 / 填写结果 / 查看(规则同 QcWorkOrderTaskSubList)

查看详情:

  • 任务:SystemViewDialog 或现有任务查看逻辑
  • 关联工单:入库/临时 → 工单详情;出库 → OutboundQcViewDialog

操作权限(严格):

  • 仅当 current_user_id ∈ assignee_user_ids 时展示「开始」「填写结果」
  • 非 assignee 或已完成:主按钮为「查看」
  • 后端写接口同步校验 assignee(见 §6)

4.4 菜单与权限注册

三处对齐(参照 2026-06-11-my-qc-material-usage-menu.sql):

  1. web-admin/src/router/modules/sqxp.ts
  2. backend/app/api/menu.py
  3. docs/sql/migrations/2026-06-12-my-qc-task-menu.sql + docs/sql/zkxw.sql
菜单 path perms order_num(建议)
我的质检任务 my-qc-task biz:myQcTask:list 5(在「我的物料领用记录」之后)

角色授权:默认给质检相关角色;超管租户管理员在菜单管理中分配。

5. 数据模型

5.1 新表 biz_qc_task_assignee

CREATE TABLE `biz_qc_task_assignee` (
  `id` varchar(64) NOT NULL COMMENT '主键',
  `task_id` varchar(64) NOT NULL COMMENT '质检任务ID',
  `user_id` varchar(64) NOT NULL COMMENT '负责人用户ID',
  `tenant_no` varchar(64) NOT NULL COMMENT '租户编号',
  `order_num` int DEFAULT 0 COMMENT '展示顺序',
  `remark` varchar(512) DEFAULT NULL COMMENT '备注',
  `create_by` varchar(64) DEFAULT NULL,
  `create_dept_id` varchar(64) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_by` varchar(64) DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `del_flag` tinyint DEFAULT 0 COMMENT '删除标志',
  PRIMARY KEY (`id`),
  KEY `idx_qc_task_assignee_task` (`task_id`, `del_flag`),
  KEY `idx_qc_task_assignee_user` (`user_id`, `tenant_no`, `del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='质检任务负责人';

唯一性在应用层保证:同一 task_id + user_id 未删除记录不重复插入。

5.2 配置请求体扩展

WorkOrderConfigureBody 增加:

lab_item_assignees: Dict[str, List[str]] = Field(default_factory=dict, alias="labItemAssignees")
third_party_item_assignees: Dict[str, List[str]] = Field(default_factory=dict, alias="thirdPartyItemAssignees")
  • Key:主数据项目 ID(lab_test_item_id / third_party_item_id)
  • Value:用户 ID 数组(≥1)
  • 校验:lab_item_ids 中每个 id 必须在 lab_item_assignees 中有非空列表;third 同理

5.3 任务响应字段扩展

_task_to_dict / _task_row_to_dict 增加:

字段 类型 说明
assigneeUserIds string[] 负责人 ID 列表
assigneeUserNames string 负责人姓名,顿号分隔

5.4 历史数据

  • 已存在、配置时未指派负责人的任务:无 assignee 行。
  • 不出现在「我的质检任务」列表。
  • 工单展开行仍可通过 biz:qcTask:list 操作(管理覆盖)。
  • 不做批量回填脚本(v1);若需补录,通过「调整检测」重新指定负责人。

6. API 设计

6.1 扩展 configure

POST /web/admin/qc-work-orders/{work_order_id}/configure

  • 权限:biz:qcWorkOrder:list(不变)
  • 在 _sync_work_order_lab_items / _sync_work_order_third_party_items 任务创建或保留后,调用 _sync_task_assignees(task_id, user_ids)。
  • 用户校验:所有 user_id 须属于当前 tenant_no 且用户有效。

6.2 我的任务列表(新)

GET /web/admin/qc-tasks/mine

  • 权限:biz:myQcTask:list
  • Query:page, pageSize, keyword, taskType, taskStatus, qcBizType(工单类型)
  • 过滤:JOIN biz_qc_task_assignee ON task_id AND user_id = 当前用户 AND del_flag=0
  • 排序:create_time DESC(任务)
  • 响应:与 GET /qc-tasks 列表项结构一致,附带工单摘要字段(workOrderNo, workOrderType, qcBizType, sampleCode)

6.3 写操作鉴权(我的任务入口)

对以下端点增加 可选严格模式 参数或根据请求来源/menuCode 区分;推荐实现:新增内部函数 _require_task_assignee_or_qc_admin(task, user, strict=False):

端点 我的任务页调用 工单页调用
POST /qc-tasks/{id}/start strict=True:须 assignee strict=False:仅需 biz:qcTask:list
POST /qc-tasks/{id}/complete-with-result 同上 同上
PUT /qc-tasks/{id}/attachments 同上 同上

实现方式(推荐):请求头或 query operateScope=mine|manage:

  • mine:biz:myQcTask:list + assignee 校验
  • manage(默认):biz:qcTask:list,不校验 assignee

前端:

  • my-qc-task 所有写请求带 operateScope=mine
  • QcWorkOrderTaskSubList 不带或 operateScope=manage

6.4 既有列表 API

GET /web/admin/qc-tasks:不变;可选后续增加 assigneeUserId 筛选(v1 不必须)。

7. 前端实现要点

7.1 组件抽取(建议)

从 QcWorkOrderTaskSubList.vue 抽取:

组件 职责
QcTaskCardList.vue 任务卡片列表 + 操作按钮显隐
QcTaskResultDialog.vue 填写检测结果弹窗(参数 / 物料 / 结论)

Props:

  • tasks: QcTask[]
  • operateScope: 'mine' | 'manage'
  • loading, onRefresh

避免 1500+ 行逻辑双份维护。

7.2 配置弹窗 UI

  • 实验室项目:checkbox 与 assignee el-select 同一行或下一行缩进
  • 第三方项目:按公司分组内同样结构
  • 弹窗 width 保持 900px 或略增至 960px 以容纳选人控件
  • 校验失败:ElMessageBox.alert + scroll-to-error(与规范一致)

7.3 状态 Tag

沿用 status-tag-registry.ts 既有 qcTaskStatus、qcTaskType、qcTaskConclusion 映射。

8. 后端实现要点

8.1 CRUD 新增

backend/app/crud/qc_task_assignee.py(或合入 qc_task.py):

  • sync_assignees_for_task(db, task_id, user_ids, tenant_no, user_id)
  • soft_delete_assignees_by_task_ids(db, task_ids, tenant_no)
  • list_assignees_by_task_ids(db, task_ids) -> Dict[task_id, List[user]]
  • is_user_assignee(db, task_id, user_id) -> bool

8.2 configure 改造

qc_work_order.py → _sync_work_order_lab_items / _sync_work_order_third_party_items:

  • 创建/保留任务后,从 body 的 assignees map 取对应 item id 的 user_ids 并 sync。
  • 保留项:每次 configure 全量替换该任务 assignee 列表(软删旧 + 插新)。

8.3 字段一致性

  • _row_to_dict(任务)须含 assigneeUserIds(只读)
  • Create/Update schema:configure body 含 labItemAssignees / thirdPartyItemAssignees
  • 运行 python scripts/check_field_consistency.py

9. 测试要点

场景 预期
配置检测,每项选 1 人 生成任务 + assignee;我的任务可见
配置检测,某项未选人 400 校验错误
调整检测改负责人 assignee 更新;新负责人可见,旧负责人不可见(未完成时)
调整检测新增项目 新任务 + 新 assignee
调整检测移除项目 任务与 assignee 软删
我的任务:assignee 开始/填结果 成功
我的任务:非 assignee 写操作 403
工单展开行:非 assignee 有 qcTask:list 仍可操作
多人 assignee,一人完成 任务 ywc;他人列表变已完成
历史无 assignee 任务 不出现在我的任务;工单页可操作

10. 改动文件清单(预估)

层级 文件
SQL docs/sql/migrations/2026-06-12-qc-task-assignee.sql, 2026-06-12-my-qc-task-menu.sql, docs/sql/zkxw.sql
Backend models.py, qc_task.py, qc_work_order.py, qc.py, menu.py
Frontend sqxp.ts, qc-work-order/index.vue, my-qc-task/index.vue, 抽取组件, qc-task.ts, qc-work-order.ts, api.d.ts
图谱 ./scripts/update-graph.sh backend frontend

11. 不在 v1 范围

  • 消息通知(指派时短信/站内信)
  • 负责人按角色/部门自动解析
  • 任务转派独立 API(通过调整检测改人即可)
  • 小程序端质检任务

12. OpenSpec 跟进(实现阶段)

在 openspec/changes/ 或归档后 openspec/specs/ 补充:

  • api-conventions:质检任务 assignee 与 operateScope 鉴权
  • ui-conventions:配置检测负责人选择、我的质检任务 参考页登记

请评审本文档。 确认无修改后,进入 writing-plans 生成实现任务分解。