Files
zkxw/docs/superpowers/specs/2026-06-25-production-step-dispatch-design.md
T
2026-06-25 12:41:07 +08:00

325 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 生产任务 · 工艺步骤级派发 — 需求分析与设计
> 日期: 2026-06-25
> 状态: **已评审,可实施**
> 范围: web-admin + backend(生产工单派发、任务执行、我的生产任务)
---
## 1. 背景与目标
### 1.1 现状
| 项 | 现状 |
|----|------|
| 派发粒度 | `DispatchTaskDialog.vue` 创建 **整条生产任务** 时指定 **单一任务负责人**(`biz_production_task.current_assignee_user_id`) |
| 步骤模型 | `biz_production_task_step` 有工序与执行状态(`dzx/jxz/ywc`),**无负责人、无计划开始时间** |
| 我的生产任务 | 按 `current_assignee_user_id = 当前用户` 过滤任务 |
| 任务执行 | `TaskExecutionDialog.vue`:当前负责人对 **全部步骤** 可操作 |
| 类似实现 | 质检任务 **`biz_qc_task_assignee`**:每检测项多人负责,**任一可完成** |
### 1.2 客户需求(原文摘要)
1. **派发按工艺步骤**,而非整条生产任务整体派发。
2. **列表仍按工单 + 生产任务展示**;展开/进入执行后,**仅派给自己的步骤可操作**,其他步骤 **只读可见**。
3. 一个工艺步骤可 **同时派给多人**;**任一负责人** 可开始/完成该步骤。
4. 每个步骤派发时可设置 **计划开始时间**(展示/排序,**不限制**提前执行)。
5. 同一生产任务内,**多个步骤可派给同一人**。
6. 支持 **创建时一并派发(B)** 与 **工艺配置后单独派发(A)** 两种入口。
7. 已派给 A 的步骤,**可改派** 给其他人(更新负责人列表)。
8. **未指定负责人的步骤**:仅管理员在 **生产工单页** 可见可操作;**不出现在** 任何人的「我的生产任务」。
---
## 2. 产品决策
| # | 问题 | 决策 | 状态 |
|---|------|------|------|
| D1 | 「我的生产任务」列表粒度? | **工单 + 任务卡片**(非步骤级平铺列表) | ✅ |
| D2 | 未派给自己的步骤如何展示? | **只读可见**(可见完整工艺链路与状态,不可操作) | ✅ |
| D3 | 一步多人时的完成语义? | **任一负责人可独立完成**(与质检任务一致;`completed_by` 记实际操作人) | ✅ |
| D4 | 计划开始时间含义? | **仅展示/排序**,不阻断执行 | ✅ |
| D5 | 派发入口? | **A + B 均支持**,共用同一套步骤派发能力 | ✅ |
| D6 | 未派步骤可见性? | **仅管理员**(工单页 `operateScope=manage`);mine 不可见 | ✅ |
| D7 | 数据模型方案? | **方案 1**:`biz_production_task_step_assignee` + 步骤 `planned_start_time` | ✅ |
| D8 | 任务级负责人字段? | 创建时 **不再必填**;「我的生产任务」改按 **步骤负责人** 过滤 | ✅ |
| D9 | 任务级「转发」? | **保留代码但标记 deprecated**;协作改由 **步骤改派** 完成 | ✅ |
| D10 | 原材料/产出/纸质文档? | **任一步骤负责人** 在 mine 视角均可编辑(与步骤协作一致) | ✅ |
| D11 | mine 端能否自行改派? | **否**;改派仅 **工单管理页**(`operateScope=manage`) | ✅ |
---
## 3. 业务流程(目标态)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 生产工单 · 派发生产任务 │
│ 阶段一:任务名称、工艺路线(可选)、描述 — 【移除任务级负责人】 │
│ 阶段二:编辑工艺步骤(现有三栏编辑器) │
└─────────────────────────────────────────────────────────────────────────┘
│ 若选了工艺路线(B) │ 未选路线 / 后补步骤(A)
▼ ▼
┌────────────────────────────┐ ┌────────────────────────────┐
│ 创建后立即打开「步骤派发」 │ │ 任务卡片 · 「步骤派发」 │
│ 每步:多人 + 计划开始时间 │ │ 同上 │
└────────────────────────────┘ └────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 我的生产任务 · 工单 + 任务卡片 │
│ 过滤:当前用户是 ≥1 个步骤的负责人 │
│ 展开 → 进入执行:自己的步骤可操作;其他步骤只读;未派步骤 mine 不可见 │
└─────────────────────────────────────────────────────────────────────────┘
│
管理员工单页 · 步骤改派
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ sync 步骤负责人列表 + 更新 planned_start_time │
│ dzx/jxz:改派后新负责人可操作;ywc:仅影响可见性,不 reopen 步骤 │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 4. 数据模型
### 4.1 新增 / 变更
**步骤表扩展** `biz_production_task_step`:
```sql
ALTER TABLE biz_production_task_step
ADD COLUMN planned_start_time datetime DEFAULT NULL COMMENT '计划开始时间' AFTER step_status;
ALTER TABLE biz_production_task_step
ADD COLUMN completed_by varchar(64) DEFAULT NULL COMMENT '完成步骤的用户ID' AFTER completed_time;
```
**步骤负责人关联表** `biz_production_task_step_assignee`(对齐 `biz_qc_task_assignee`):
```sql
CREATE TABLE biz_production_task_step_assignee (
id varchar(64) NOT NULL COMMENT '主键',
step_id varchar(64) NOT NULL COMMENT '生产任务步骤ID',
user_id varchar(64) NOT NULL COMMENT '负责人用户ID',
tenant_no varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
order_num int DEFAULT 0 COMMENT '展示顺序',
remark varchar(512) DEFAULT NULL COMMENT '备注',
create_by varchar(64) DEFAULT NULL COMMENT '创建人id',
create_dept_id varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
create_time datetime NOT NULL COMMENT '创建时间',
update_by varchar(64) DEFAULT NULL COMMENT '更新人id',
update_time datetime NOT NULL COMMENT '更新时间',
del_flag tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (id),
KEY idx_step_assignee_step (step_id),
KEY idx_step_assignee_user (user_id, tenant_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产任务步骤负责人';
```
### 4.2 任务级字段(兼容)
| 字段 | 处理 |
|------|------|
| `biz_production_task.current_assignee_user_id` | **保留列**;新创建任务可为 NULL;API 创建 body **移除 assigneeUserId 必填** |
| `biz_production_task_handover` | **保留**;UI 不再主推「任务转发」,文档标注 deprecated |
### 4.3 历史数据迁移(可选脚本)
- 若旧任务 `current_assignee_user_id` 非空且无步骤负责人:可将该用户写入 **全部现有步骤** 的 assignee(一次性迁移),保证旧任务仍在「我的生产任务」可见。
- 无 assignee 且无步骤负责人的旧任务:行为与「未派步骤」一致,仅 manage 可见。
---
## 5. API 设计
### 5.1 步骤派发(批量)
```
PUT /api/web/admin/production-tasks/{taskId}/steps/dispatch
```
**Body**(camelCase 与现有约定一致):
```json
{
"steps": [
{
"stepId": "uuid",
"assigneeUserIds": ["u1", "u2"],
"plannedStartTime": "2026-06-25T09:00:00"
}
]
}
```
**规则:**
- 至少 **1 个 step** 在请求中且 `assigneeUserIds.length >= 1`(允许部分步骤暂不派)。
- 未出现在请求中的步骤:**不修改** 其负责人与时间。
- 出现的步骤:**全量 sync** 负责人(软删旧 + 插入新,复用 `qc_task_assignee.sync_assignees` 模式)。
- `plannedStartTime` 可选;传 null 表示清空。
- 权限:`production:work-order:list`(manage);mine 端 **403**。
**响应:** 任务详情(含 steps、assignees、plannedStartTime、assigneeNames)。
### 5.2 创建生产任务
```
POST /api/web/admin/production-tasks
```
- **移除** `assigneeUserId` 必填校验。
- 可选扩展:同 body 内嵌 `stepDispatch` 数组(B 入口一次提交);或创建成功后前端再调 §5.1。
### 5.3 我的生产任务列表
```
GET /api/web/admin/production-tasks/mine
```
**过滤逻辑变更:**
- **current**:存在 `biz_production_task_step_assignee.user_id = 当前用户` 且 `del_flag=0` 的 **至少一个步骤** 的任务。
- **history**(若保留):曾出现在步骤 assignee 但当前已不在任一步 assignee 列表的任务(可选首版简化:与 current 合并或暂不实现 history 变更)。
> 首版:**替换** 原 `current_assignee_user_id = 我` 为主过滤条件;history 沿用 handover 逻辑仅作兼容,不作为步骤协作主路径。
### 5.4 步骤写操作鉴权
| 操作 | mine | manage |
|------|------|--------|
| 开始/完成/保存 **已派给自己** 的步骤 | ✅ | ✅ |
| 操作 **未派给自己** 的步骤 | ❌ 只读 | ✅ |
| 操作 **未派任何人** 的步骤 | ❌ 不可见 | ✅ |
| 编辑任务输入/产出/附件 | ✅ 若本人是任一步 assignee | ✅ |
| 步骤派发/改派 | ❌ | ✅ |
实现:在 `production_task` CRUD 增加 `assert_step_assignee(step_id, user_id)`;manage 路径跳过。
### 5.5 任务详情 / 列表 enrichment
- `_task_to_dict` / 步骤 `_step_to_dict` 增加:
- `assigneeUserIds: string[]`
- `assigneeNames: string[]`(或 `assignees: {userId, nickName, userNo}[]`)
- `plannedStartTime`
- `completedBy` / `completedByName`
- 列表接口批量 `_enrich_step_assignees`(对齐 QC `_enrich_assignee_names`)。
---
## 6. 前端设计
### 6.1 新增组件
| 组件 | 职责 |
|------|------|
| `DispatchStepDialog.vue` | 步骤派发表格:工序名、负责人多选、计划开始时间;提交 §5.1 |
### 6.2 改造组件
| 组件 | 变更 |
|------|------|
| `DispatchTaskDialog.vue` | **移除** 任务级「负责人」表单项与校验;选工艺路线且创建成功后 **可选打开** `DispatchStepDialog`(B) |
| `WorkOrderTaskSubList.vue` | 管理视角:任务卡片 meta 展示各步负责人 Tag + 计划开始时间;按钮「步骤派发 / 调整派发」 |
| `TaskExecutionDialog.vue` | mine:`canEditStep(step)` = 当前用户在 step.assigneeUserIds;否则只读;未派步骤不渲染或 manage 才显示 |
| `StepDetailPanel.vue` | 禁用开始/完成/保存按钮当 `!canEditStep` |
| `useTaskStepEditor.ts` | 传入 `operateScope`;步骤操作前校验 assignee |
| `production-task.ts` | 新增 `fetchDispatchProductionTaskSteps`;创建 body 去掉 assigneeUserId |
| `production-mine-task-groups.ts` | mine 列表依赖后端新过滤(前端无需按 task assignee 过滤) |
### 6.3 步骤派发表格 UI
| 列 | 控件 |
|----|------|
| 序号 / 工序名称 | 只读 |
| 负责人 | `el-select multiple filterable remote`,展示 `nickName(userNo)`,≥1 |
| 计划开始时间 | `el-date-picker type="datetime"`,可选 |
- 弹窗:`dialog-form-layout`,`label-position="top"`。
- 底栏:取消 / 确定(Close / Check 图标)。
### 6.4 执行弹窗 · 步骤列表视觉
- **可操作步骤**:现有高亮样式(`is-active`)。
- **只读步骤**:降低 opacity 或 `readonly` class;隐藏开始/完成/保存;保留状态 Tag。
- **未派步骤(mine)**:不展示(manage 正常展示并可操作)。
### 6.5 Deprecated UI
- 任务级「转发」「改派」按钮:管理页 **隐藏或替换** 为「步骤派发」;API 保留不删。
---
## 7. 改派规则
| 步骤状态 | 改负责人 | 改计划开始时间 | 步骤是否 reopen |
|----------|----------|----------------|-----------------|
| `dzx` | ✅ | ✅ | — |
| `jxz` | ✅ | ✅ | 否,新负责人接手进行中步骤 |
| `ywc` | ✅(仅影响 mine 可见性) | ✅ | **否** |
---
## 8. 任务 / 工单状态联动
- **任务状态**(`dpz/jxz/ywc`)逻辑 **不变**:仍由步骤聚合 + 现有 `sync_production_work_order_status_from_tasks` 驱动。
- **未派步骤** 不参与 mine 过滤;manage 仍可执行未派步骤(管理员兜底)。
---
## 9. 权限与菜单
| 能力 | menuCode |
|------|----------|
| 生产工单(manage、步骤派发) | `production:work-order:list` |
| 我的生产任务 | `production:my-task:list` |
无需新增菜单;步骤派发挂在工单页任务操作区。
---
## 10. 测试要点
1. B 入口:选路线 → 创建 → 步骤派发 → 各 assignee 在 mine 见任务,仅自己步骤可操作。
2. A 入口:无路线创建 → 手工加步骤 → 步骤派发 → 同上。
3. 一步多人:A、B 均可见任务;A 完成后 B 见只读已完成。
4. 同人多步:mine 展开见多个可操作步骤。
5. 计划开始时间:列表/卡片展示;未到时间仍可执行。
6. 未派步骤:mine 不可见;manage 可见可操作。
7. 改派:`dzx/jxz/ywc` 按 §7;mine 不可改派。
8. 旧任务兼容(若跑迁移脚本):原 task assignee 仍可见。
---
## 11. 文件清单(实施参考)
| 层 | 文件 |
|----|------|
| SQL | `docs/sql/migrations/2026-06-25-production-step-dispatch.sql`,同步 `docs/sql/zkxw.sql` |
| Backend Model | `backend/app/models/models.py` — `BizProductionTaskStepAssignee` + 步骤新字段 |
| Backend CRUD | `backend/app/crud/production_task_step_assignee.py`(新建,对齐 qc_task_assignee) |
| Backend CRUD | `backend/app/crud/production_task.py` — mine 过滤、step assert、complete 写 completed_by |
| Backend Schema | `backend/app/schemas/production_task.py` — DispatchStepsBody |
| Backend API | `backend/app/api/production.py` — PUT steps/dispatch;调整 create/mine |
| Frontend API | `web-admin/src/api/production-task.ts` |
| Frontend | `DispatchStepDialog.vue`(新建) |
| Frontend | `DispatchTaskDialog.vue`, `WorkOrderTaskSubList.vue`, `TaskExecutionDialog.vue`, `StepDetailPanel.vue`, `useTaskStepEditor.ts` |
---
## 12. 与 OpenSpec / 既有 spec 关系
- supersede 部分:`2026-06-13-production-task-assignee-my-tasks-design.md` 中 **任务级单一负责人 + 任务转发** 产品路径;本 spec 以 **步骤级多人 + 步骤改派** 为准。
- 对齐 `api-conventions`:租户隔离、软删除、`operateScope=mine|manage`。
- 对齐 `ui-conventions`:Fresh Mint 弹窗、按钮图标、Tag 展示负责人。
---
## 13. 版本记录
| 日期 | 说明 |
|------|------|
| 2026-06-25 | 初稿:步骤级派发方案 1,产品决策 D1–D11 已确认 |