docs: add production cascade complete design for task and work order

Document cascading status completion when finishing a production task or work order, removing incomplete-step gates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-31 14:30:21 +08:00
1 parent 0d52442902
commit 5367abf7f2
1 file changed
+167
@@ -0,0 +1,167 @@
# 生产「完成任务 / 完成工单」状态级联 — 设计文档
> 日期: 2026-08-31
> 状态: 待实现
> 范围: 生产工单、生产任务、工艺步骤(web-admin + backend)
> 页面: 【生产工单】`/#/production-group/production/production-work-order`、【我的生产任务】`/#/production-group/production/my-production-task`(共用任务执行弹窗)
## 1. 背景
当前行为:
| 入口 | 现状 |
|------|------|
| 任务执行弹窗 ·「完成任务」 | 前后端均校验「全部工艺步骤已完成」;未完成则拦截。步骤上仍有「执行此步骤」「完成步骤」。 |
| 卡片 ·「完成工单」 | 仅将工单标为 `ywc`,**不**同步完成下属任务与步骤。 |
| 我的生产任务 | 不展示「完成工单」;与生产工单共用 `TaskExecutionDialog`。 |
用户诉求:完成上级时**自动级联完成下级状态**,不再因下级未完成而阻止操作;业务明细(投入产出、步骤填报等)不强制补全。
## 2. 已确认的产品决策
| 问题 | 决策 |
|------|------|
| 实现路径 | **方案 B**:在现有「完成任务」「完成工单」接口内做级联(同一事务) |
| 完成工单时未完成任务的结论 | **弹窗统一选择**成功(`cg`)/失败(`sb`),再级联写入所有本次新完成的任务 |
| 级联完成步骤的完成人/时间 | **当前操作人 + 当前时间**(与手动「完成步骤」一致) |
| 完成任务时的 I/O、储物格、步骤完整性校验 | **全部去掉**;只改状态,不因明细未填拦截 |
| 完成工单级联任务范围 | 工单下所有未删除且未完成任务(含 `dpz` / `dzx` / `jxz`) |
| 已完成任务 | **不动**(结论与步骤保持原样) |
| 单步「执行此步骤 / 完成步骤」 | **保留**,可继续逐步操作 |
| 我的生产任务 · 完成工单入口 | **不新增**(卡片仍不展示) |
## 3. 行为与范围
### 3.1 完成任务(任务执行弹窗顶栏)
1. 仍弹出结论弹窗(成功/失败 + 可选备注)。
2. 不再因「尚有未完成工艺步骤」拦截(前端 + 后端同步去掉)。
3. 不再因投入/产出未填、储物格未分配而拦截。
4. 提交时后端在置任务 `ywc` 前,将该任务下所有未删除且 `step_status != ywc` 的步骤一次性置为 `ywc`:
- `completed_by` = 当前操作人
- `completed_time` = 当前时间
- 已有 `started_time` 的步骤保留;未开始的步骤可不补 `started_time`(与手动完成步骤路径对齐即可)
5. 结论必填、任务 `completed_time`、投入物消耗 `_consume_task_input_cell_products`、以及 `sync_production_work_order_status_from_tasks` **保持现状**(完成任务本身不把工单直接标成 `ywc`)。
### 3.2 完成工单(生产工单卡片)
1. 点击后**先弹统一结论弹窗**(成功/失败,可选备注),不再只是简单 `confirm`。
2. 弹窗可提示:将同步完成该工单下所有未完成任务及其工艺步骤。
3. 确认后同一事务内级联:
- 未完成步骤 → `ywc`(完成人/时间同上)
- 未完成任务 → `ywc`,写入统一 `conclusion`;弹窗有备注时写入任务 `remark`
- 工单 → `ywc`;弹窗备注写入工单 `remark`(若有)
4. 已是 `ywc` 的任务:结论、步骤不动。
### 3.3 明确不做
- 不自动补全步骤/任务业务明细(试剂、产出物、数据采集等)
- 不改派发、步骤负责人权限、单步执行流程
- 不改工单 Tab / 列表卡片布局(仅完成工单交互从 confirm → 结论弹窗)
- 不在「我的生产任务」开放完成工单
## 4. 后端设计
### 4.1 共享内部方法
`_force_complete_task_steps(db, task_id, tenant_no, user_id, now)`:
- 条件:`BizProductionTaskStep.del_flag=0` 且 `step_status != 'ywc'`
- 写入:`step_status='ywc'`,`completed_by=user_id`,`completed_time=now`,`update_by` / `update_time`
- 不单独 `commit`
### 4.2 完成任务
入口不变:现有更新任务状态为 `ywc` 的路径(`update_task_status`)。
变更:
1. 删除「尚有未完成的工艺步骤,请完成全部步骤后再完成任务」校验。
2. 置任务 `ywc` 之前调用 `_force_complete_task_steps`。
3. 其余逻辑(结论校验、消耗、工单状态同步)不变。
### 4.3 完成工单
入口不变:`PUT /production-work-orders/{wo_id}/complete`。
`ProductionWorkOrderComplete` 扩展:
| 字段 | 必填 | 说明 |
|------|------|------|
| `conclusion` | 是 | `cg` / `sb`,写入所有**本次新完成**的任务 |
| `remark` | 否 | 写入工单备注;若有值则同时写入本次新完成任务的 `remark` |
级联顺序(**同一事务、一次 commit**):
```
1. 校验工单存在且非 ywc;conclusion 合法
2. 查工单下 del_flag=0 且 task_status != ywc 的任务
3. 对每个上述任务:
a. _force_complete_task_steps
b. task_status=ywc,conclusion=统一结论,completed_time=now(若空)
c. 走与「完成任务」一致的侧效(含投入物消耗)
4. 工单 work_order_status=ywc,写 remark(若有)
5. sync_schedule_from_production_work_order
6. commit
```
实现约束:
- 级联过程中**不要**对每个任务单独 `commit`。
- 级联过程中**跳过** `sync_production_work_order_status_from_tasks`(避免中途把工单写成 `jxz`),最后一步直接置工单 `ywc`。
- 工单下无未完成任务:仍要求选结论;仅改工单状态(结论不落任务)。
### 4.4 错误边界
| 条件 | 行为 |
|------|------|
| 工单已 `ywc` | 400「工单已完成」 |
| `conclusion` 缺失或非法 | 400 |
| 权限 | 沿用现有 complete 接口,不新增 |
## 5. 前端设计
### 5.1 完成任务
- 文件:`useTaskStepEditor.ts`(`handleCompleteTask`)
- 去掉:`validateAllStepsComplete()`、投入/产出完整校验、储物格确认
- 保留:`CompleteProductionTaskDialog` → 现有完成任务 API
- 成功后刷新任务详情;步骤应全部为 `ywc`
### 5.2 完成工单
- 文件:`production-work-order/index.vue` 的 `handleCompleteSingle`
- 用结论弹窗替换简单 `confirm`(可复用/轻抽 `CompleteProductionTaskDialog`,标题「完成工单」,增加级联说明文案)
- 提交:`fetchCompleteProductionWorkOrder(id, { conclusion, remark })`
- **API 传参**:当前实现用 query `params`;后端期望 Body。实现时改为请求体 `data` 传 `conclusion`/`remark`,并核对后端能收到
- 「我的生产任务」:不展示完成工单;调用形态可顺带对齐,不新增入口
### 5.3 文案
| 场景 | 文案 |
|------|------|
| 完成任务成功 | 保持现有「任务已完成」类提示 |
| 完成工单弹窗标题 | 完成工单 |
| 完成工单成功 | 工单已完成 |
## 6. 主要改动文件
| 层 | 文件 |
|----|------|
| Backend | `app/crud/production_task.py`、`app/crud/production_work_order.py`、`app/schemas/production_work_order.py` |
| Frontend | `useTaskStepEditor.ts`、`production-work-order/index.vue`、结论弹窗复用/抽取、`api/production-work-order.ts` |
## 7. 测试要点
1. 任务有未完成步骤 → 完成任务并选结论 → 任务与全部步骤变 `ywc`,完成人/时间为当前用户。
2. 任务步骤已全部完成 → 完成任务行为与改前一致(含结论写入)。
3. 工单含 `dpz`/`dzx`/`jxz` 任务与未完成步骤 → 完成工单选「失败」→ 工单 `ywc`;上述任务均为 `ywc`+`sb`;步骤均为 `ywc`;已完成任务结论不变。
4. 工单已完成 → 仍拒绝。
5. 完成工单不传结论 → 400。
6. 回归:单步执行/完成步骤、派发、我的生产任务执行弹窗仍可用。
## 8. 非目标回顾
- 不做前端循环调多接口的半成功方案
- 不新建独立 force-complete API(方案 C)
- 不强制补全业务明细数据