# OA 打回钉钉化:终态 + 复制开新单 Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 将打回改为本单终态;进度不再出现独立「打回」节点与多轮轴;已处理含打回;已打回/已撤销/已拒绝均可复制表单开新实例(无旧单关联)。 **Architecture:** 引擎侧修正 `list_tasks`/`build_progress_items`,废弃同实例 `resubmit` 与 `source_instance_id` 业务写入;前端统一「复制并重新发起」走 `copyFromInstanceId` 预填后 `start`。 **Tech Stack:** FastAPI / SQLAlchemy / Vue 3 / Element Plus;单测 `backend/tests/test_oa_engine.py` **Spec:** `docs/superpowers/specs/2026-07-15-oa-return-copy-new-instance-design.md` --- ### Task 1: 后端 — done 含 returned;progress 去重复打回节点;returned 终态收尾 **Files:** - Modify: `backend/app/services/oa_engine.py` (`list_tasks`, `build_progress_items`, `return_task` 注释) - Modify: `backend/tests/test_oa_engine.py` - [ ] **Step 1:** `list_tasks` 的 `kind=="done"` 改为 `status.in_(("approved", "rejected", "returned"))` - [ ] **Step 2:** `build_progress_items`:删除对 `returns_by_round` / `kind=return` 的追加;在终态分支增加 `returned` → `_terminal_item("returned", "已打回", inst)`;轮次循环可保留字段但前端不再标「第 N 轮」(引擎仍可只投当前已有任务) - [ ] **Step 3:** 单测: - `test_done_list_includes_returned`:任务 status=returned 出现在 done - `test_progress_return_no_duplicate_return_kind`:task returned + return event 时 progress 无 `kind==return`,仅有 task.status=returned,且有 terminal returned - 删除或改写 `test_resubmit_starts_new_round` 为 `test_resubmit_raises_deprecated` - [ ] **Step 4:** `pytest backend/tests/test_oa_engine.py -q` 通过后 commit ### Task 2: 后端 — 废弃 resubmit;start 不再写关联;详情不回显关联 **Files:** - Modify: `backend/app/services/oa_engine.py` (`start_instance`, `get_instance_detail`, `resubmit_instance`) - Modify: `backend/app/api/oa_approval.py`(resubmit 路由返回 410/业务错误或保留但引擎抛错) - [ ] **Step 1:** `resubmit_instance` 改为直接 `raise OaEngineError("同实例再次提交已废弃,请复制表单重新发起")` - [ ] **Step 2:** `start_instance` 不再将 `source_instance_id` 写入实例(参数可忽略) - [ ] **Step 3:** `get_instance_detail` 不再查/返回有意义的 `resubmittedInstanceId` 关联跳转(置空);`sourceInstanceId` 恒返回空字符串(兼容前端字段) - [ ] **Step 4:** 更新测试 + commit ### Task 3: 前端 — 详情/列表/发起页 **Files:** - Modify: `web-admin/src/views/oa-approval/instances/detail.vue` - Modify: `web-admin/src/views/oa-approval/_placeholder/RuntimeList.vue` - Modify: `web-admin/src/views/oa-approval/start/index.vue` - Modify: `web-admin/src/api/oaApproval.ts`(可选保留 fetchResubmit 不再使用) - [ ] **Step 1:** 详情:`returned` 文案「已打回」;三种终态显示「复制并重新发起」→ `start?appId=©FromInstanceId=`;去掉同单 resubmit、原单/重提单提示、轮次标题、`kind===return` 标签 - [ ] **Step 2:** RuntimeList:mine 在 `returned|cancelled|rejected` 显示同一按钮;文案「已打回」;query 用 `copyFromInstanceId` - [ ] **Step 3:** start:读 `copyFromInstanceId` 预填 formData;submit **不传** `sourceInstanceId` - [ ] **Step 4:** commit ### Task 4: 验收勾选 + 图谱 - [ ] 勾选 spec §7 验收项(实现完成项) - [ ] `./scripts/update-graph.sh backend`(若改了 backend) - [ ] commit docs if needed --- **Self-review:** Spec §3–§7 均有对应 Task;无 TBD;`copyFromInstanceId` 命名前后一致。