From b553787cfd8a9c28e067d92447b9bdd228de37bf Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 19 Mar 2026 08:39:53 +0800 Subject: [PATCH] docs(flow): add withdraw resubmit design --- ...026-03-19-flow-withdraw-resubmit-design.md | 377 ++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 docs/plans/2026-03-19-flow-withdraw-resubmit-design.md diff --git a/docs/plans/2026-03-19-flow-withdraw-resubmit-design.md b/docs/plans/2026-03-19-flow-withdraw-resubmit-design.md new file mode 100644 index 000000000..014067af5 --- /dev/null +++ b/docs/plans/2026-03-19-flow-withdraw-resubmit-design.md @@ -0,0 +1,377 @@ +# Flow Withdraw Resubmit Design + +## Scope + +This design covers starter-side "resubmit after withdraw" behavior for: + +- `pigx-ui-pro/src/views/flow/task/started.vue` +- dynamic flow forms +- custom business forms + +The capability is limited to processes already withdrawn by the starter. It does not reopen the old process instance. + +## Problem Statement + +The current flow center supports: + +- withdraw for starter-side in-progress instances +- resubmit for `rejectToStarter` + +But it does not support the common case where the starter withdraws because they found mistakes in the form and wants to: + +1. edit the old submitted data +2. keep the withdrawn record as history +3. start a brand new process instance from the corrected data + +This must work for both: + +- dynamic flow forms +- custom business forms + +## Confirmed Product Decisions + +1. After withdraw, resubmission must create a new process instance +2. The old withdrawn record must remain as a separate historical record +3. The entry point should be a single user-facing capability: `重新编辑提交` +4. The implementation path should split by form type: + - dynamic flow form: edit in flow center drawer + - business form: edit in a page/container path +5. Previous manually selected approvers / copy recipients should be carried as defaults and remain editable + +## Goals + +1. Add `重新编辑提交` for withdrawn starter records +2. Let users correct form mistakes before submitting again +3. Preserve withdrawn process history +4. Reuse existing form loading and start-process capabilities where possible +5. Support both dynamic and business forms in the same product capability + +## Non-Goals + +1. Do not reopen the old withdrawn Flowable instance +2. Do not merge new submission into the old withdrawn record +3. Do not copy approval history from the old instance to the new one +4. Do not introduce a dedicated backend "withdraw-resubmit" API in V1 + +## Recommended Approach + +### Option 1: Unified Entry, Split by Form Type + +Description: + +- show one action named `重新编辑提交` +- branch internally by form type +- dynamic flow forms open an editable drawer in flow center +- business forms go through a common restart container page + +Pros: + +- unified user mental model +- minimal duplication +- works for both form types +- avoids forcing every business form page to implement workflow-side participant editing on its own + +Cons: + +- needs a common restart context loader +- needs one new common business-form restart container page + +Conclusion: + +- recommend + +### Option 2: Always Jump to a Full Page + +Description: + +- both dynamic forms and business forms always jump to a dedicated page + +Pros: + +- interaction form is consistent + +Cons: + +- duplicates current dynamic-form drawer flow +- larger change surface + +Conclusion: + +- not recommended for V1 + +### Option 3: Always Stay in started.vue Drawer + +Description: + +- both dynamic forms and business forms are handled inside `started.vue` + +Pros: + +- entry is centralized + +Cons: + +- business forms often depend on route/page context +- harder to keep complex custom forms maintainable + +Conclusion: + +- reject + +## Product Behavior + +### Starter List + +On [started.vue](/Users/lengleng/work/dev1227/pigx-ui-pro/src/views/flow/task/started.vue): + +- records with process status `已撤回` should display `重新编辑提交` +- records with `rejectToStarter` should keep the current `重新提交` +- these are different capabilities and must remain separate + +### Old Record Handling + +After successful resubmission: + +- the old record remains `已撤回` +- a new record appears as `进行中` + +### Button Naming + +Use a single starter-facing action name: + +- `重新编辑提交` + +Reason: + +- it makes the user intent explicit +- it distinguishes this flow from `驳回待提交 -> 重新提交` + +## Data Flow Design + +### Shared Source Data + +When the user clicks `重新编辑提交`, frontend should load: + +1. old instance snapshot via `detail(processInstanceId)` +2. current flow definition via `getFlowDetail(flowId)` + +The old instance snapshot provides: + +- previous form data +- previous manually selected participant values stored in submitted process params + +The current flow definition provides: + +- current form type +- current form component path / dynamic form schema +- current selectable participant nodes + +### Participant Default Value Strategy + +Only carry forward values the user explicitly selected before. + +Use old submitted process params such as: + +- `nodeId_assignee_select` + +Do not carry forward: + +- historical approval results +- historical comments +- runtime-derived approval outcomes + +If a previously selected node no longer exists in the current flow definition: + +- ignore the old value + +If a new required selectable node is present in the current definition: + +- require the user to select it again before submit + +## Form-Type Split + +### Dynamic Flow Form + +When `formType=10`: + +- stay in [started.vue](/Users/lengleng/work/dev1227/pigx-ui-pro/src/views/flow/task/started.vue) +- open an editable restart drawer +- left side reuses dynamic form rendering +- right side reuses [FlowNodeFormatData.vue](/Users/lengleng/work/dev1227/pigx-ui-pro/src/views/flow/form/tools/FlowNodeFormatData.vue) +- old participant selections are injected into `nodeUser` +- submit starts a new process instance + +### Business Form + +When `formType=20`: + +- do not jump directly to each existing business create page as the primary architecture +- add a common restart container page under flow task module +- the container page is responsible for: + - loading the business form create component + - loading the current flow definition + - rendering the right-side participant panel + - orchestrating final submit as a new process start + +Reason: + +- existing business pages usually do not own a common workflow participant panel +- a common restart container keeps workflow behavior centralized and reusable + +## Frontend Component Boundary + +### started.vue + +Responsibilities: + +- expose `重新编辑提交` entry for withdrawn records +- decide route vs drawer by form type +- continue handling current starter list concerns + +### New Restart Context Loader + +Add a shared loader/composable responsible for: + +- fetching old instance detail +- fetching current flow detail +- building initial form values +- building initial participant defaults +- exposing the resolved restart mode + +### FlowNodeFormatData.vue + +Needs a small enhancement: + +- allow external default `nodeUser` injection for restart scenarios + +### New Business Restart Container Page + +Responsibilities: + +- render the business create component dynamically +- render `FlowNodeFormatData` +- merge edited business form data and participant selections +- start a new process instance + +## Existing Capabilities Reused + +### Frontend + +- [dynamicComponent.ts](/Users/lengleng/work/dev1227/pigx-ui-pro/src/views/flow/workflow/utils/dynamicComponent.ts) +- [useTaskForm.ts](/Users/lengleng/work/dev1227/pigx-ui-pro/src/views/flow/task/composables/useTaskForm.ts) +- [FlowNodeFormatData.vue](/Users/lengleng/work/dev1227/pigx-ui-pro/src/views/flow/form/tools/FlowNodeFormatData.vue) +- [startFlow](/Users/lengleng/work/dev1227/pigx-ui-pro/src/api/flow/flow/index.ts) + +### Backend + +V1 should reuse existing APIs: + +- `detail(processInstanceId)` +- `getFlowDetail(flowId)` +- `startProcessInstance / startFlow` + +No dedicated backend restart API is required in V1. + +## Business Form Compatibility + +For business forms to participate in V1: + +- their create component must support loading old data by `processInstanceId` + +Example: + +- [create.vue](/Users/lengleng/work/dev1227/pigx-ui-pro/src/views/flow/oaLeave/create.vue) already supports querying old data with `processInstanceId` + +If some business form cannot load old data this way: + +- it is out of V1 support unless adapted + +## Submit Semantics + +### Dynamic Form Submit + +Payload: + +- edited dynamic form data +- participant selections from `FlowNodeFormatData` + +Behavior: + +- call process start API +- create a new process instance + +### Business Form Submit + +Payload: + +- edited business form data from the business create component +- participant selections from `FlowNodeFormatData` + +Behavior: + +- call business-form submit path that starts a new process instance +- create a new process instance + +## Success and Failure UX + +### Success + +Message: + +- `已重新发起流程` + +Behavior: + +- close restart drawer or leave restart page +- return to starter list +- refresh list + +### Failure + +If current flow definition is unavailable: + +- `当前流程已不可发起` + +If business form component path is invalid: + +- `业务表单不可用,无法重新发起` + +If old participant values are no longer valid: + +- do not block opening +- require reselection at validation time + +## Acceptance Criteria + +1. Withdrawn starter records show `重新编辑提交` +2. Dynamic flow forms open editable restart drawer with old form values +3. Business forms open common restart container with old form values +4. Previous manually selected approvers/copy users are prefilled when possible +5. User can change both form data and participant selections before submit +6. Submit creates a new process instance +7. Old withdrawn record remains unchanged +8. New record appears independently in starter list + +## Risks + +1. Business forms vary in how much they depend on route context +2. Some business create components may not yet support restart-container integration cleanly +3. Participant default values rely on old submitted explicit selections, not on all runtime assignment types + +## Recommended V1 Rollout + +1. Dynamic flow forms first +2. Common restart container for business forms +3. Verify `oaLeave` as the first business-form example +4. Expand to additional business forms that already support `processInstanceId`-based reload + +## Implementation Outline + +The `writing-plans` skill referenced by brainstorming is unavailable in this workspace. As a fallback, implement in this order: + +1. Add withdrawn-record `重新编辑提交` action to starter list +2. Add restart context loader and split logic by form type +3. Add dynamic-form restart drawer path +4. Add business-form restart container page +5. Enhance participant panel to accept default values +6. Verify `oaLeave` and one dynamic form end to end