openxiangda-skill-kit 2.3.41 → 2.3.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda-skill-kit",
3
- "version": "2.3.41",
3
+ "version": "2.3.57",
4
4
  "description": "OpenXiangda 2.0 中文 AI 技能的校验、分发与安装。",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,7 +17,7 @@
17
17
  "README.md"
18
18
  ],
19
19
  "dependencies": {
20
- "openxiangda-devkit-core": "2.36.0"
20
+ "openxiangda-devkit-core": "2.41.6"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsx": "4.23.12",
@@ -26,6 +26,97 @@ MCP 对应 `workflow_node_configurations`。workflowCode 来自当前应用声
26
26
 
27
27
  成员和委托修改带 UUID operationId、原因,更新/撤销带最新 expectedRevision;冲突后重新读取。应用不另建授权表、选择 actor 或自行保存权限快照。SDK 和当前用户数据边界见[权限](data-authz.md)。
28
28
 
29
+ ### 批量核对与逐项回执
30
+
31
+ `listRoleMemberships` 支持 `dimensionCode` 与 `scopeValue` 成对精确筛选,和角色、人员、状态、关键词一起在服务端分页前过滤。管理授权仍按角色和动作委派;筛选某个学院不会额外授予该学院的维护权限。同步投影成员和系统认证成员继续只读。
32
+
33
+ 平台 `authz.native-membership-batch@1.0.0` 自动提供批量能力。`previewRoleMembershipBatch` 与 `executeRoleMembershipBatch` 使用当前用户、当前挂载环境;一次 1–50 项,最多 128 KiB,整批只支持同一种新增、更新或撤销。每项保留自己的 UUID operationId 和原因;更新/撤销带读取时的 expectedRevision。同一批不能重复人员+角色、成员 ID 或 operationId。
34
+
35
+ ```ts
36
+ import { listRoleMemberships, previewRoleMembershipBatch,
37
+ executeRoleMembershipBatch, type NativeRoleMembershipBatchItem } from 'openxiangda/core';
38
+
39
+ const page = await listRoleMemberships({ roleCode: 'college_reviewer',
40
+ dimensionCode: 'college', scopeValue: 'art', status: 'active', limit: 20, offset: 0 });
41
+ const items: NativeRoleMembershipBatchItem[] = page.items.filter(row => row.maintainable).map(row => ({
42
+ operation: 'update', operationId: crypto.randomUUID(), membershipId: row.id,
43
+ expectedRevision: row.revision, reason: '调整艺术学院审批职责有效期',
44
+ scopeGrants: row.scopeGrants, validFrom: row.validFrom, validTo: '2027-01-01T00:00:00.000Z',
45
+ }));
46
+ if (items.length) {
47
+ const proposal = await previewRoleMembershipBatch({ items });
48
+ // 页面向管理员展示 before/after;核对后显式提交完全相同的 items。
49
+ if (proposal.failed === 0 && proposal.unconfirmed === 0) {
50
+ const result = await executeRoleMembershipBatch({ items });
51
+ // 逐项读取 result.items,不把 HTTP 成功当成整批成功。
52
+ }
53
+ }
54
+ ```
55
+
56
+ 示例角色和范围必须已在本应用声明;页面将预览与执行放在两次明确操作中。更新是完整替换 scope 与有效期;省略 scope 会清空范围,省略时间会解除相应限制,因此只修改时间时也带回保留的 scope 与另一端时间。
57
+
58
+ 预览在原授权内核验证后回滚所有 SQL,不保存成员、版本或回执,不失效授权缓存;单项差异超过 16 KiB 被拒绝。预览不是预留,执行时会重新检查人员、权限和 CAS。预览 `ready/already_committed` 只返回 before/after 的业务投影,没有临时成员 ID 或虚构回执。
59
+
60
+ 执行每项独立事务、按顺序处理,允许部分成功。`committed/replayed` 返回原不可变回执;`failed` 是已确认拒绝;`unconfirmed` 表示结果未知,包括提交后缓存失效异常,不能认为没有提交。失败只返回脱敏的 code/status/pointer/retryable。保存原请求,使用 `loadAuthorizationMutationReceipt(operationId)` 核对,必要时显式重放相同 operationId 和 payload;修正已确认失败项的内容后使用新 operationId,不重发已成功项。网络中断也按未知结果处理。
61
+
62
+ 成员维护影响后续授权和分派;已有任务保持进入时参与快照,实际办理资格仍实时核验。批量维护不隐式改派当前待办。
63
+
64
+ ### 共享成员维护组件
65
+
66
+ 标准管理入口与应用自定义工具页可复用 `RoleMembershipManager`,保留所在页面的 Shell 和入口授权:
67
+
68
+ ```tsx
69
+ import { RoleMembershipManager } from 'openxiangda/react';
70
+
71
+ export function ResponsibilityMembers() {
72
+ return <RoleMembershipManager initialRoleCode="college_reviewer" />;
73
+ }
74
+ ```
75
+
76
+ `initialRoleCode` 只是初始筛选,必须来自本应用的角色声明,不授予管理权限。可选 `refreshKey` 变化时重新读取目录与成员。组件使用当前用户、当前挂载环境,按角色、人员关键词、状态和业务范围分页;同步投影及系统认证来源只读。
77
+
78
+ 选择最多 50 位成员后调整某个范围维度或有效期,组件显式保留每位成员未修改的范围、操作上限和时间。预览逐项展示修改前后;提交后区分成功、拒绝及未知结果。冲突后保留输入,显式载入最新基准并重新核对;未知操作只能核对原回执或重试相同编号及请求。已核对成功项不会再次提交。组件支持窄屏布局、表格内部横滚、未保存关闭提示及保存中离开保护。
79
+
80
+ 默认应用 Shell 在窄屏自动折叠侧边栏;宿主自定义 Shell 应给内容区保留可收缩宽度。成员读取失败显示重试状态,只有成功读取后的空结果才显示没有成员。核对或提交中禁用关闭、修改和再次提交按钮。
81
+
82
+ ### 角色关联的潜在流程节点
83
+
84
+ `loadWorkflowRoleReferences(roleCode, { keyword, limit, offset })` 查询当前激活及在途固定版本中可能使用此角色的节点;平台自动提供 `workflow.role-references@1.0.0`。返回定义/绑定版本与摘要、配置修订、激活/在途上下文,并区分代码默认角色、节点配置覆盖和代码许可路由来源。许可来源不表示规则已经命中;这里的条数不是受影响待办数,也不会改派旧任务。
85
+
86
+ 读取沿用原流程管理权限。仅获成员管理委托的用户可能可以维护角色,同时没有权限读取关联流程;组件单独呈现该拒绝。返回不含成员、实例标识或业务事实,每页最多 100 条;最多 1000 个版本组合、32 MiB 源 JSON、10000 节点/引用,超限明确拒绝。
87
+
88
+ 管理范围检索使用 `listRoleManagementScopeValues(dimensionCode, { keyword, limit, offset })`,返回 `NativeRoleManagementScopeValuePage` 的 `id/label` 与 `limit/offset`。它使用 `openxiangda.native-role-management-scope-value-page/v2`,与 Data 字段选择器的 `value/label/cursor` 协议分开。权限仍要求对成员的分配或更新能力;只读权限不因此扩张。
89
+
90
+ ## 限时审批代理 {#workflow-delegations}
91
+
92
+ 平台自动提供 `workflow.delegation-management@1.0.0`,没有额外的默认关闭开关。本人只能委托自己的有效审批职责;代理人必须具有同角色、覆盖原范围且有效期覆盖整个代理窗口的成员身份。采用平台数据库时间与 `[开始, 结束)` 区间,禁止自己代理、重叠链和环。管理员可全应用查看和撤销,创建由原审批人本人完成;成员管理委托不会扩大后台或代理管理权。
93
+
94
+ 标准管理入口、应用后台工具与门户个人页可复用 `WorkflowDelegationManager`。组件读取当前挂载应用和环境;`initialAll` 只是筛选意图,平台仍核验超管权限。宿主把草稿回调交给已有导航保护,避免切页时丢失输入或未知操作:
95
+
96
+ ```tsx
97
+ import { useState } from 'react';
98
+ import { WorkflowDelegationManager, useUnsavedChangesGuard,
99
+ type WorkflowDelegationDraftState } from 'openxiangda/react';
100
+
101
+ export function MyApprovalDelegations() {
102
+ const [draft, setDraft] = useState<WorkflowDelegationDraftState>({ dirty: false, busy: false, unknown: false });
103
+ useUnsavedChangesGuard({ when: draft.dirty || draft.busy || draft.unknown,
104
+ preventNavigation: draft.busy || draft.unknown,
105
+ message: draft.unknown ? '请先核对原操作回执。' : '代理维护尚未完成。' });
106
+ return <WorkflowDelegationManager onDraftStateChange={setDraft} />;
107
+ }
108
+ ```
109
+
110
+ 以上页面须位于原 `OpenXiangdaApplication` 路由内。独立平台Console使用自己的现有导航owner处理回调,不为组件新增Router。提交中或结果未知不能确认丢弃后离开;普通草稿可明确放弃。组件提供PC/窄屏列表、筛选、资格诊断、合法候选分页、核对/提交、撤销原因和回执恢复。
111
+
112
+ 自定义页面使用 `openxiangda/core` 的 `loadWorkflowDelegationCatalog`、`listWorkflowDelegations`、`loadWorkflowDelegation`、`listWorkflowDelegationCandidates`、`previewWorkflowDelegationMutation`、`executeWorkflowDelegationMutation` 和 `loadWorkflowDelegationMutationReceipt`。列表默认20/最大100,候选默认20/最大50,搜索80字;本人职责目录最多100、流程标题最多200。创建请求带双方预期成员修订,撤销带规则 `expectedRevision`,均有UUID `operationId`、原因和绑定环境,请求最多16KiB;额外actor、权限或字段配置被拒绝。
113
+
114
+ 预览完整回滚,不创建规则或回执;新规则提案id为null。核对后显式提交同一个请求,平台再检查当前权限和资格。规则与原回执同事务保存;同key同内容返回原回执,同key改内容或跨actor/应用/环境拒绝。首次明确4xx拒绝可保留输入、读新基准再核对;提交结果未知先查询原回执,404仍未知,允许用户显式重试原key及原请求,不能自动换key重发。`WORKFLOW_V2_DELEGATION_OPERATION_CONFLICT`、`SOURCE_REVISION_CONFLICT`和原owner的`REVISION_CONFLICT`等409都保持草稿;503不表示未提交。
115
+
116
+ 回执保存提交时结果,当前状态以刷新列表的 `effectiveState/evaluatedAt/issues` 为准。创建和撤销只影响后续分派,已有任务保留进入时的原人/代理/职责/时间快照,办理仍复核资格;需要处理既有待办时使用显式任务修复操作。
117
+
118
+ Nest后端注入请求作用域的 `OpenXiangdaWorkflowService`,调用 `delegationCatalog/delegationManagement/delegationCandidates/delegationAdministration` 与 `previewDelegationMutation/executeDelegationMutation/delegationMutationReceipt`;它复用已验证当前用户,环境来自模块绑定。业务处理器不自报actor、不替别人代建授权,审批后代建属于单独受限合同。
119
+
29
120
  ## 可读流程图与实例路径 {#workflow-graph}
30
121
 
31
122
  管理员在流程目录检索全部定义,查看指定版本的分支顺序、默认路径、变量类型/单位及来源。拓扑和条件由开发者发布;图和列表只用于查看。当前有效配置只叠加在匹配的激活定义上;历史实例使用固定定义和节点进入时的人员/配置,尚未执行的节点不计入执行路径。
@@ -67,7 +158,9 @@ export function ReadableWorkflow({ workflowCode, version }: {
67
158
 
68
159
  开发者在审批节点的 `administration` 声明可维护的模式、人员来源和任务按钮。未声明这些项的节点保留名称、说明及原人员来源维护;拓扑、分支、范围计算仍由代码控制。使用新声明的应用需要平台 `workflow.node-administration@1.0.0`,编译与激活均检查该能力。
69
160
 
70
- 模式为 `single/any/all/sequence` 的允许子集;来源限 `fixed_users/app_role/app_role_in_scope`,范围角色必须已有代码声明的 scope。按钮 code 保持同意、拒绝、退回、转交、委托、加签的稳定语义;同意和拒绝不能关闭。只有同意/拒绝支持意见规则,拒绝或代码已有必填意见不能放宽。字段显隐、填写与必填行为由应用页面和代码维护,不提供流程节点的字段管理覆盖。可编译声明见 `examples/workflow-administration/declaration.ts`。
161
+ 模式为 `single/any/all/sequence` 的允许子集;可维护来源限 `fixed_users/app_role/app_role_in_scope`,范围角色必须已有代码声明的 scope。按钮 code 保持同意、拒绝、退回、转交、委托、加签的稳定语义;同意和拒绝不能关闭。只有同意/拒绝支持意见规则,拒绝或代码已有必填意见不能放宽。字段显隐、填写与必填行为由应用页面和代码维护,不提供流程节点的字段管理覆盖。可编译声明见 `examples/workflow-administration/declaration.ts`。
162
+
163
+ 自动抄送节点仅开放名称、说明和显式声明的 `administration.assigneeProviders`;未开放时接收来源只读。共享编辑器显示“抄送人”,不显示审批方式或审批按钮,固定名单最多20人。`next`、空人策略和通知策略仍归代码;新配置只影响以后进入,过去抄送名单保持冻结。完整例子见 `examples/workflow-administration/automatic-cc.ts`,执行及读取边界见[自动抄送](workflow-events.md#automatic-cc)。
71
164
 
72
165
  应用自定义管理页可使用 `WorkflowNodeConfigurationEditor`(`openxiangda/react`),输入同一读面中的节点、principals 和 context.headRevision,放在平台 App/UI 作用域内。它复用下列当前用户 SDK:
73
166
 
@@ -88,3 +181,37 @@ await saveWorkflowNodeConfiguration('requests', node.nodeId, {
88
181
  只有已声明允许 all 的节点可保存此例。输入最多 32KiB,人员 200、按钮文字 40 字。用 `patch: null` 恢复默认,也产生审计修订。结果未知时保留相同 operationId 和完整请求,查询后重试相同操作;确认的修订/Head 冲突先重新读取,不能覆盖别人。共享编辑器按审批人、审批按钮、名称与说明分组,显示配置来源、修订与生效范围;切组保留草稿,校验失败定位到相应分组。在冲突时保留草稿,加载最新基准后再次显式保存。
89
182
 
90
183
  新配置影响以后进入的节点,当前待办保留进入时模式、人员、按钮和必填意见;实时人员资格仍复核。含新增配置或快照的环境不能直接回退到忽略策略的旧服务器。
184
+
185
+ ## 审批人通用与专项路由 {#assignment-routing}
186
+
187
+ 在 binding entry 的 `routing` 声明稳定策略代码、匹配维度及其事实路径,以及具名应用角色/范围角色来源。声明例子见 `examples/workflow-administration/routing.ts`。平台 `workflow.assignment-routing@1.0.0` 随 V2 内核提供,编译及目标平台检查自动校验;无路由声明时沿用原人员解析。成员继续在原生角色管理维护,无需再建审批人员表。字段行为、分支和范围计算由应用代码维护。
188
+
189
+ 打包和目标预检自动将含 `routing` 的 binding 纳入该能力的使用摘要,与平台编译结果严格对齐;无需应用手工填写能力清单或额外开启开关。
190
+
191
+ 标准管理页的“流程定义 / 审批人路由”提供策略检索、规则维护和分页历史。应用工具页可直接嵌入 `WorkflowAssignmentRoutingManager`(`openxiangda/react`);单策略编辑可使用 `WorkflowAssignmentRoutingEditor`。放在现有 App/UI 作用域内,保留后台路由和入口权限。读写都使用当前用户及当前挂载环境,首版与节点配置一样要求应用 superAdmin,角色维护委派不授予路由管理权。
192
+
193
+ 规则核对只计算内容变化,打开规则表单后原样加入草稿不会产生多余修改。发生修订冲突时载入最新基准并保留整组草稿,再逐项核对;这个操作不会自动合并其他管理员的修改。窄屏工具栏允许换行,表格在内部滚动,规则表单按单列显示。
194
+
195
+ ```tsx
196
+ import { WorkflowAssignmentRoutingManager } from 'openxiangda/react';
197
+ export function RoutingPage() { return <WorkflowAssignmentRoutingManager />; }
198
+ ```
199
+
200
+ ```ts
201
+ import { loadWorkflowAssignmentRoutingConfiguration, saveWorkflowAssignmentRoutingConfiguration,
202
+ loadWorkflowAssignmentRoutingCatalog, loadWorkflowAssignmentRoutingHistory } from 'openxiangda/core';
203
+
204
+ const catalog = await loadWorkflowAssignmentRoutingCatalog({ keyword: '学院', limit: 20, offset: 0 });
205
+ const current = await loadWorkflowAssignmentRoutingConfiguration('college-review');
206
+ await saveWorkflowAssignmentRoutingConfiguration(current.policy.policyCode, {
207
+ expectedHeadRevision: current.headRevision, expectedRevision: current.revision,
208
+ operationId: crypto.randomUUID(), reason: '增加学院补充审批职责',
209
+ rules: [{ ruleCode: 'art-extra', title: '艺术学院补充', enabled: true,
210
+ matches: { college: ['art'] }, sourceCode: 'extra', effect: 'append', priority: 0 }],
211
+ });
212
+ const history = await loadWorkflowAssignmentRoutingHistory('college-review', { limit: 10, offset: 0 });
213
+ ```
214
+
215
+ 保存的是整组规则,最多 256 条/256 KiB;最多 8 个维度、16 个来源,每维度 64 个匹配值。维度间同时满足、值内任意匹配,空 matches 为通用。可限定流程和审批节点,节点须带流程代码。时间为 `[validFrom,validTo)`。唯一最高优先级替换优先,`replace_then_append` 随后追加,`replace_only` 命中替换时忽略追加;没有替换时均采用默认来源再追加。追加按优先级降序、通用先专项、稳定代码排序,同来源同次只解析一次。
216
+
217
+ 64 条以上规则同时命中、最高替换同级冲突、缺少事实、替换无人或候选越界都会明确阻塞,不自动通过或隐式兜底。预览和执行复用同一解析器;任务冻结规则修订和分派,管理员调整只影响未来进入。修改与发布使用同一环境锁,CAS 冲突重新读取基准、保留草稿并核对前后差异;未知结果保留完整原请求及 operationId 后显式重试。清空 rules 也产生修订和审计。发布不得改变在途同名策略语义,即便尚无补充规则;非空规则引用的来源和节点必须保留。
@@ -245,7 +245,7 @@ ISO instant)与 `minuteStep`(1 到 60 且整除 60)。设置步长后只
245
245
  - 单选、复选直接展示选项;下拉单选/复选使用可搜索的底部弹层,多选展示已选数量。
246
246
  - 日期使用月历,日期时间可切换时间滚轮;区间依次选择开始和结束,可返回上一步修改。
247
247
  - 地址采用地区路径逐层选择,详细地址单独输入。定位沿用当前可用的钉钉或浏览器能力。
248
- - 子表单直接展开行内字段,支持折叠、添加和删除;父表提交校验所有行,包括折叠的行。
248
+ - 子表单使用每页十行的行内字段,手机支持折叠、添加和删除;父表提交校验所有行,包括折叠及未显示页的行。分页保留完整输入和草稿,任务完成校验会定位到错误行所在页;有待处理上传时先完成或移除文件,再翻页。
249
249
  权限、最大行数、原子事务和已存行的 revision 继续由原有资源契约约束。
250
250
  - 签名在底部画布手写并保存;图片、签名和附件仍通过托管文件接口上传与鉴权读取。
251
251
  - 手机富文本编辑降级为多行文本。未修改时保留已有 HTML;修改后将纯文本转为转义后的
@@ -65,6 +65,8 @@ capability、应用角色和数据策略。前端只根据当前登录用户完
65
65
  `roleManagement` 投影显示,但前端显示不能替代服务端复核。不要在应用中复制角色表、
66
66
  权限表或通过 NestJS 转发开发者凭据。
67
67
 
68
+ 通用职责成员维护可直接嵌入 `openxiangda/react` 的 `RoleMembershipManager`,复用角色/范围筛选、批量差异核对、逐项回执及原操作恢复。页面保持原路由和入口权限,组件按当前用户目录显示动作,服务端逐项复核;详见[共享成员维护](administration.md#共享成员维护组件)。
69
+
68
70
  列表查询必须使用服务端过滤、排序和分页;新增、读取、更新、删除和文件上传都经过
69
71
  可替换 Data API adapter。不要调用自定义 Nest CRUD、Function 或 Workflow 来绕过
70
72
  Data API。只有真正需要事务或外部系统的动作才使用同源 `/api`。
@@ -224,17 +224,22 @@ export function ClaimAction({ offerId }: { offerId: string }) {
224
224
 
225
225
  ```tsx
226
226
  const client = useMemo(() => createManagedConcurrencyClient(), []);
227
- const command = useDurableCommand({client,command:'registration-enroll',resourceKey:activityId,acceptanceRecoveryMs:30*60*1000});
227
+ const command = useDurableCommand({client,command:'registration-enroll',resourceKey:activityId,
228
+ acceptanceRecoveryMs:30*60*1000,discoveryRecoveryMs:30*60*1000});
228
229
  // 仅真实点击提交;不是 useEffect 或页面 mount 回调。
229
230
  const submit = () => command.submit({activity:activityId,channel:channelId,agreed:true,phone});
230
231
  ```
231
232
 
232
- hook 从 `openxiangda/react` 和 `openxiangda/mobile` 导出。state、initialized、isObserving、requestKey、receipt、errorCode 用于统一状态区;submit(input)、resume()、refresh()、stop() 分别为明确提交、用冻结 input 重试原请求、恢复查询和停止观察。挂载只查本地原 key 或服务端 mine,不自动 enqueue。未知应答保留原 key 与 input;用户恢复后先查原结果,不能换 key 重试。浏览器存储失败在提交前明确失败。未知应答后的恢复按钮调用 resume(),不传当前可能已经修改的表单。snapshot.input 可恢复本地冻结表单;跨设备只有回执时不展示推测的原输入。一次明确 submit/resume 期间,429、暂时5xx或网络错误会按原 key/input 自动退避恢复,先查原结果再重试 enqueue,默认最多6次、最长120秒;可用 acceptanceRecoveryMs 明确配置120000至1800000毫秒,更长预算最多120次,遵守更长的 Retry-After。预算从首次明确提交计时,刷新或 resume() 不重置;旧版意图没有时间时从首次明确恢复计时,无法证明更早的提交时间。尚无 receipt 时只能提示“正在确认受理”,不能承诺可关闭页面。用完预算保留原意图,停止自动入队,refresh() 仍可只读核对迟到结果;这不是报名失败。该预算只控制受理恢复,不是后端最终完成时限承诺。如果刷新时原请求尚未被平台受理,挂载只查询,明确点击 resume() 才重新启动有界受理重试。
233
+ hook 从 `openxiangda/react` 和 `openxiangda/mobile` 导出。state、initialized、isObserving、acceptanceConfirmed、requestKey、receipt、errorCode 用于统一状态区;submit(input)、resume()、refresh()、stop() 分别为明确提交、用冻结 input 重试原请求、恢复查询和停止观察。挂载只查本地原 key 或服务端 mine,不自动 enqueue。未知应答保留原 key 与 input;用户恢复后先查原结果,不能换 key 重试。浏览器存储失败在提交前明确失败。未知应答后的恢复按钮调用 resume(),不传当前可能已经修改的表单。snapshot.input 可恢复本地冻结表单;跨设备只有回执时不展示推测的原输入。一次明确 submit/resume 期间,429、暂时5xx或网络错误会按原 key/input 自动退避恢复,先查原结果再重试 enqueue,默认最多6次、最长120秒;可用 acceptanceRecoveryMs 明确配置120000至1800000毫秒,更长预算最多120次,遵守更长的 Retry-After。预算从首次明确提交计时,刷新或 resume() 不重置;旧版意图没有时间时从首次明确恢复计时,无法证明更早的提交时间。尚无已核对归属的受理回执时 acceptanceConfirmed 为 false,只能提示“正在确认受理”,不能承诺可关闭页面。为 true 仅证明此原申请曾持久受理,不代表已成功,也不能替代服务端证明。用完预算保留原意图,停止自动入队,refresh() 仍可只读核对迟到结果;这不是报名失败。该预算只控制受理恢复,不是后端最终完成时限承诺。如果刷新时原请求尚未被平台受理,挂载只查询,明确点击 resume() 才重新启动有界受理重试。
233
234
 
234
- 已受理申请的自动观察最多持续到首次明确提交后的 30 分钟,默认受理恢复仍为 120 秒,两者分别计算。每次结果读取同时受单次 120 秒和原提交剩余时间限制;刷新和 resume 不重新获得观察时间。跨设备没有本地首次时间时,用原回执 acceptedAt 计算观察窗口,不能以页面挂载时间重新计时。达到窗口后保留原意图,状态为 recovering、isObserving 为 false,提示稍后核对;明确 refresh 仍可查询迟到终态,但不重新启动已经到期的自动观察,也不自动提交。
235
+ 初查/refresh 的 discoveryRecoveryMs 默认仍是 120000 毫秒,应用可以显式配置 1 至 1800000 毫秒。上面的 30 分钟是热点应用的主动选择,不会扩大其他应用默认值。一轮发现的原键 result 和必要的 mine 共用固定、单调计时的读取截止;有尚未到期的原申请时还取原提交剩余窗口的较小值。每次底层只读调用在剩余预算大于 120 秒时最多 120 次请求,其他情况最多 12 次,遵守服务端退避与随机抖动。同一控制器的重复 refresh 共用当前任务,stop 后再次挂载可以开始新读取,旧代次的迟到应答不能覆盖新状态。这项预算不续期、不写入,也不为首个提交额外添加等待。
236
+
237
+ 已受理申请的自动观察最多持续到首次明确提交后的 30 分钟,默认受理恢复仍为 120 秒,两者分别计算。自动观察的每次结果读取同时受单次 120 秒和原提交剩余时间限制;刷新和 resume 不重新获得观察时间。跨设备没有本地首次时间时,用原回执 acceptedAt 计算观察窗口,不能以页面挂载时间重新计时。达到窗口后保留原意图,状态为 recovering、isObserving 为 false,提示稍后核对;明确 refresh 仍可用有界初查预算查询迟到终态,但不重新启动已经到期的自动观察,也不自动提交。
235
238
 
236
239
  正常待处理结果每次至少间隔 5 秒,遵守更长的服务端 retryAfterMs,再加随机抖动。单次只读恢复耗尽且仍是已知预算繁忙时,外层可在原观察窗口内指数退避继续;权限、依赖、网络故障或悬挂请求超过读取预算时立即停止自动观察,显示 error 并保留原回执和请求键。终态停止。受理恢复中核对原结果同样只允许已知预算繁忙继续,读取依赖失败不能被外层重试隐藏。一个业务区域只挂载一个观察者。离开或关闭页面不撤销已受理请求,平台自动继续;新设备通过 mine 找到本人的原请求。position 为空时显示「已受理,稍后可查看」,不要显示虚假的精确人数或预计秒数。
237
240
 
238
- refresh 会优先恢复 mine 返回的新进行中周期,避免本地历史 succeeded 遮蔽另一个设备的新申请。不要在 mount 发现历史 succeeded 时自动跳成功页或永久禁用提交。它可能已经被管理员取消,需结合当前业务记录展示。只有用户明确再次点击 submit,且原请求已有终态,SDK 才创建新的 requestKey;活跃请求或未知应答始终恢复原 key。平台明确返回未受理的参数错误(400 + CONCURRENCY_INPUT_INVALID 等约定错误)时,SDK 才清除被拒输入,允许修正后再提交;未知 400、409、429、5xx 和网络错误仍保留原意图。成功提示以 receipt.state==='succeeded' 和 receipt.result 为准,accepted/executing 只显示「已登记,处理中」。
241
+ refresh 有本地原键时先薄查询原 key;找到已受理非终态后不再 mine。没有本地意图或原申请已有终态时,mine 优先恢复新的进行中周期,避免本地历史 succeeded 遮蔽另一个设备的新申请。原键明确 404 后保留一次本人列表兜底;未确认的本地意图只能采用同原键回执。列表中只有别的活跃周期时显示 recovering / CONCURRENCY_ORIGINAL_REQUEST_REQUIRED,并保留原 key/input;没有匹配时显示 CONCURRENCY_ACCEPTANCE_UNCONFIRMED,提供「核对原申请」和「恢复原申请」动作。不能把无匹配解释为业务失败,也不丢弃可能迟到受理的原意图。已知读取繁忙耗尽显示 recovering,真实依赖、网络、权限或未知 400 显示 error;两种状态均保留原键、输入和已受理回执,读取失败不自动提交。
242
+
243
+ 不要在 mount 发现历史 succeeded 时自动跳成功页或永久禁用提交。它可能已经被管理员取消,需结合当前业务记录展示。只有用户明确再次点击 submit,且原请求已有终态,SDK 才创建新的 requestKey;活跃请求或未知应答始终恢复原 key。平台明确返回未受理的参数错误(400 + CONCURRENCY_INPUT_INVALID 等约定错误)时,SDK 才清除被拒输入,允许修正后再提交;未知 400、409、429、5xx 和网络错误仍保留原意图。成功提示以 receipt.state==='succeeded' 和 receipt.result 为准,accepted/executing 只显示「已登记,处理中」。
239
244
 
240
245
  permit 的 ManagedCommandGate 仍只用于 admitted 短确认,不用于 durable。durable 不能套任意前端 onSubmit 冒充后台事务,真正业务必须由已声明的 backend-plan handler 返回受管计划。
@@ -153,13 +153,55 @@ Workflow 发起只接受 `{ resourceCode, id }` 形式的 `dataRef`,并要求
153
153
 
154
154
  ## 标准工作流范围
155
155
 
156
- 首期只支持 `approval`、`condition`、`end`,以及 `single`、`any`、`all`、`sequence` 审批模式。标准操作为提交、同意、拒绝、退回、重新提交、转交、委托、前/后加签、撤回、管理员改派、管理员终止和不改变流程状态的催办。
156
+ 标准节点支持 `approval`、`condition`、`cc`、`end`,以及 `single`、`any`、`all`、`sequence` 审批模式。标准操作为提交、同意、拒绝、退回、重新提交、转交、委托、前/后加签、撤回、管理员改派、管理员终止和不改变流程状态的催办。
157
157
 
158
158
  除命令集之外,平台为实例管理员提供两个维护动作:`admin_jump`(把处于运行或退回状态的实例跳转到指定节点)与 `admin_delete`(删除实例,可选同时删除表单数据、是否触发自动化)。两者走平台管理端点的预览/执行两步流程并要求同源浏览器请求,不属于应用声明的工作流命令,也不占用 `commandToken` 命令合同。
159
159
 
160
160
  复杂业务状态机继续放在应用领域服务。不要把任意 JavaScript、Service Task、BPMN、通用长事务或业务记录复制进 Workflow。
161
161
 
162
162
  所有页面和消息动作必须来自后端 Workflow Surface 的 `operations[]`。前端、模板和渠道 Adapter 不自行推断操作权限。
163
+
164
+ ### 自动抄送 {#automatic-cc}
165
+
166
+ 在固定拓扑中声明 `cc` 节点,进入时解析接收人、保存抄送事实,然后继续 `next`。
167
+ 完整声明例子见 `examples/workflow-administration/automatic-cc.ts`:
168
+
169
+ ```ts
170
+ copy: {
171
+ id: 'copy', kind: 'cc', title: '抄送办理负责人',
172
+ binding: 'readers', next: 'approved', emptyPolicy: 'block',
173
+ administration: { assigneeProviders: ['app_role', 'fixed_users'] },
174
+ },
175
+ ```
176
+
177
+ 接收 binding 支持 `fixed_users`、`initiator`、`input_users`、`form_field_users`、
178
+ `app_role`、`app_role_in_scope`、`previous_node_actor`。仅使用事务内原生来源,
179
+ 不支持 `application_provider` 或需要申请人交互的 `initiator_select`。名单按用户去重,
180
+ 每次进入最多 20 人;`min/max` 默认 1/20,非空名单仍必须符合声明的下限。
181
+ 角色查询超过 200 条有效成员行时明确拒绝,不能使用截断后的名单。
182
+ 范围角色要求对应范围的 `cc` 授权(或未限制操作、`*`),仅 `approve` 不满足。
183
+ 抄送不套用审批代理。
184
+
185
+ `emptyPolicy: 'block'` 在真实空名单时回滚触发动作,补齐人员后可重试原申请或任务;
186
+ `'skip'` 记录无人跳过并继续。未知角色、失效账号、非法输入和超限均属于错误,
187
+ 不会被 skip 吞掉。通知默认开启;`notify: false` 仍保存抄送审计、事件和实例阅读关系,
188
+ Notification Hub 不生成该次抄送消息。渠道失败通过原 Hub 恢复,不回滚流程。
189
+
190
+ 进入记录、系统抄送日志及事件在同一事务提交。原命令重放不重复抄送;退回等导致实际
191
+ 再次进入时产生新轮次,可采用新的兼容节点配置,过去名单和配置保持冻结。时间线显示
192
+ “流程自动抄送”,不会把系统动作记为发起人的人工操作。
193
+
194
+ 接收人可以使用既有抄送列表及实例详情,不获得审批权、后台权限或通用业务数据读取权。
195
+ 标准流程详情由服务器按页面代码声明的固定详情字段投影读取;需要排除敏感字段时,
196
+ 在 `crud.detail` 和 `subject.factProjection` 中明确选择允许字段,不能仅在浏览器隐藏。
197
+ 通用 Data API 继续核验当前数据授权,流程节点不提供字段权限配置。
198
+
199
+ 含 cc 的包自动声明 `workflow.automatic-cc@1.0.0`,平台正式迁移后自动提供该能力,
200
+ 没有额外开关。管理员仅可修改名称、说明及代码显式开放的接收来源,
201
+ `next/emptyPolicy/notify` 保持代码所有;配置 SDK 见[应用管理](administration.md#node-administration)。
202
+
203
+ ### 工作流命令
204
+
163
205
  Surface 同时签发短期、一次性的 `commandToken`,绑定当前用户/会话、应用环境
164
206
  Head、实例/任务版本、允许的命令集合与 CSRF。命令请求只提交
165
207
  `commandToken + idempotencyKey + input`;旧的 caller-authored
@@ -242,8 +284,8 @@ Native Data 事件的 capture plan 由当前 Head 的 Event、Data、AuthZ revis
242
284
  不是增量 patch。definition 和 activation 必须一致声明
243
285
  `acceptedCommandDeactivationPolicy`:`finish-pinned` 让已接受的 durable process
244
286
  command 按固定版本完成,`cancel-on-deactivate` 在声明删除后取消尚未启动的命令;
245
- 既有 Workflow instance 始终按固定版本继续。所有审批人 Provider 的 `min/max`
246
- 默认 1/200,最大 200。
287
+ 既有 Workflow instance 始终按固定版本继续。审批人 Provider 的 `min/max`
288
+ 默认 1/200,最大 200;自动抄送默认 1/20,最大 20。
247
289
  需要按流程实例串行投递时只声明 `ordering: 'workflow-instance'`,不接受下划线别名。
248
290
 
249
291
  平台按 desired set 直接覆盖环境 Head,不做版本比较。因此 `openxiangda deploy`
@@ -401,4 +443,262 @@ readability: {
401
443
 
402
444
  说明的执行位置只能是 submission、某节点的 node_input 或 completion。它是对已有逻辑的注释,不会自动执行计算或产生新流程节点。没有实际计算步骤时,不应写成“系统已计算总金额”。条件的变量必须存在于输入 Schema;对旧未标注定义,读取投影会明确给出未知来源诊断。
403
445
 
446
+ 流程图的条件卡直接摘要参与判断的变量与分支数,业务步骤卡摘要声明的产出。管理详情在条件旁显示申请字段、流程输入或固定步骤来源;业务步骤优先展示输出说明,缺少说明时明确提示。来源节点只有与当前图的 handler 代码和版本一致才可跳转,历史实例使用自己的固定定义。界面不根据节点标题推断公式,也不加载或执行源码引用。
447
+
448
+ 长图首次打开会保留可读比例;可用“适应全图”查看全貌,再通过搜索、来源链接、缩略导航或方向键定位。手机使用同一结构的节点列表和详情。图的连线、条件和代码始终只读,审批/抄送配置沿已声明的管理员能力修改,字段行为仍由页面代码定义。
449
+
404
450
  可选 `source: { path, symbol?, digest }` 只返回源码位置和 SHA256,不返回源码内容。path 指向工作区 apps/packages/platform 下的代码文件,digest 为 `sha256:<原始文件字节摘要>`。工作区加载时核对文件存在、无符号链接、大小和摘要;源码变化后以 WORKFLOW_LOGIC_DESCRIPTION_STALE 阻止封装,开发者应核实说明并更新引用。32 个文件、单文件 2MiB、总量 8MiB 为上限。元数据不能验证任意 TypeScript 的业务含义,业务说明及验收仍由开发者维护。
451
+
452
+ ## 持久业务步骤
453
+
454
+ 必须收到业务计算或外部操作结果后才继续的流程,用固定 `action` 节点。平台自动
455
+ 提供 `workflow.durable-business-step@1.0.0`;应用包含动作时自动声明能力需求并
456
+ 按需引导标准 Nest 后端,普通无动作应用保持原合同。字段显隐/编辑/必填继续由
457
+ 页面代码负责;动作版本、Schema、条件和连线只由开发者发布。
458
+
459
+ ```ts
460
+ const amountSchema = { type: 'object', additionalProperties: false,
461
+ required: ['amountCents'], properties: { amountCents: { type: 'integer', minimum: 0 } } };
462
+ // 放入已声明流程的 nodes;amount-route 必须是同一固定定义中的真实目标。
463
+ const calculate = {
464
+ id: 'calculate', kind: 'action', title: '核算金额', next: 'amount-route',
465
+ handler: { code: 'calculate-v1', version: 1, mode: 'pure' },
466
+ inputSchema: amountSchema, outputSchema: amountSchema,
467
+ inputs: { amountCents: { source: 'fact', path: 'amountCents' } },
468
+ };
469
+ // 放入 events.subscriptions;应用 backend.enabled=true,消费 generated manifest。
470
+ const subscription = {
471
+ code: 'calculate-v1', eventTypes: ['openxiangda.workflow.step.requested.v2'],
472
+ filter: { workflowStep: { handlerCode: 'calculate-v1' } },
473
+ payload: { includeChanges: false, fields: [] },
474
+ };
475
+ ```
476
+
477
+ 成功输出写入 `steps.calculate.amountCents`。后续条件或动作只能读取每条路径上
478
+ 已经完成的生产者;前向/自引用、非法路径和输入类型不兼容会拒绝编译。步骤后
479
+ 需要 HTTP provider 或发起人选人的组合暂不支持,先在动作中产生有 Schema 的
480
+ 人员数据,再使用原生人员字段/角色分派。示例见
481
+ `examples/workflow-administration/business-step.ts`。
482
+
483
+ Nest 使用 `@OpenXiangdaEventHandler(generatedContract)` 注册实现,`handle` 返回
484
+ `{ output, receipt? }`;上下文 `workflowStep` 包含验证过的固定请求,
485
+ `idempotencyKey` 是稳定 executionId。不得通过返回值任意指定下一节点或调用
486
+ Workflow 跳转。使用标准 `PlatformOpenXiangdaEventReceiptStore`;内存 receipt
487
+ 不能完成业务步骤。
488
+
489
+ `pure` 用于无外部效果的计算;`reconciled-effect` 必须实现 `reconcile(event,
490
+ context)`:成功返回 `{ status:'succeeded', result:{output,receipt?} }`,确定未执行
491
+ 返回 `{status:'not-executed'}`,无法确定返回 `{status:'unknown'}`。每次尝试先
492
+ 核对原执行键,未知结果需要人工核对后重放原事件。增加v2时保留v1合同及代码;
493
+ 旧具名版本合同不可变,缺失实现明确失败,不自动改用最新版本。
494
+
495
+ 输入输出各16KiB、回执2KiB,最多32映射、200节点;Schema闭合且有界,无远端
496
+ 引用或正则执行。平台先持久保存合法结果,再尝试流转;下游缺人等失败保留
497
+ `result_ready`,恢复仅推进,不再次执行处理器。自动恢复最多5次,每批20;
498
+ outbox容量等事务故障会回滚,处理器按原键核对后重交,不承诺跨服务绝对一次。
499
+ 撤回后的未领取步骤拒绝执行,已开始的外部操作仍需原键核对。
500
+
501
+ 管理员在标准实例管理页或 `WorkflowBusinessStepRecoveryPanel` 查看并继续受阻
502
+ 步骤;公开客户端为 `loadWorkflowBusinessStepRecovery`、
503
+ `previewWorkflowBusinessStepRecovery`、`executeWorkflowBusinessStepRecovery`。
504
+ 复用原管理权限/token/CAS/CSRF/审计,提交失败保留相同token/input/idempotencyKey。
505
+ 组件通过 `onDraftStateChange` 报告 dirty/busy/unknown;宿主将其接入已有导航保护,
506
+ 提交中或结果未知时保留组件和实例。未知结果只允许显式重试原恢复请求;首次明确
507
+ 的4xx拒绝可保留原因并重新预览。宿主刷新失败不改变已经确认的提交结果。
508
+ 等待处理器的失败从原事件管理受控重放,保留原执行键。普通详情只显示安全状态
509
+ 摘要;原输入、输出和外部回执不放入普通时间线。
510
+
511
+ ## 代码任务页面与补填 {#task-page-submit}
512
+
513
+ 需要办理人补填时,在定义的 `taskPages` 声明命名页面,审批节点以
514
+ `taskPageCode` 引用。页面字段顺序、`readonly`、`required`、`visibleWhen` 和
515
+ `requiredWhen` 属于应用代码;流程管理员不能覆盖页面字段、条件或连线。
516
+ 字段类型和编码来自同一个 Native 模型。例子见
517
+ `examples/workflow-administration/task-page.ts`。
518
+
519
+ 页面表达式只读取本页 `values.*`,平台以合并后的业务值强制显隐和必填。
520
+ 最多16页、每页64字段、单次值1MiB,表达式8层/64节点。
521
+ 系统、流水号和隐藏字段不可作为补填入口。根附件、图片复用平台托管文件;
522
+ 签名和富文本同样保留 Native 字段协议;普通 owned 子行以固定页面白名单和行 CAS 提交。
523
+
524
+ ### 标准流程的主子表发起
525
+
526
+ 标准发起页复用 Field Kit 的 PC/手机子表,通过原 `standard-commands` 的
527
+ `mutation.data` 一次提交完整表单。子表字段值沿用表单行结构:
528
+ `{ key, state, data, id?, revision? }`;日期等子字段按字段 codec 编码,
529
+ 界面快照与原值不作为授权或并发依据。声明关系、行序和父记录引用由 Native 生成。
530
+ 创建主表、所有子行和持久流程发起命令在同一个事务中完成;失败不留下部分申请。
531
+
532
+ 发起仍执行当前用户的父、子资源及字段授权。拥有主表权限不会自动得到通用子表权限。
533
+ 已有记录提交核对父 CAS 和完整已读子行集合,包括未修改行;删除显式保留原 id/revision,
534
+ 跨单、遗漏、重复或陈旧行均拒绝。原命令已提交时先返回同一回执,再也不重新展开当前行。
535
+ 每表100/累计400、完整意图双倍、整请求2MiB及展开后1000操作与 Native 预算一致。
536
+ 普通显式 Named Action 的16个业务操作预算不变;其自定义子表提交仍按自身合同实现。
537
+
538
+ ### 任务 owned 子表
539
+
540
+ 在主模型的 subtable 字段上声明 `subtable: { fields, create, delete, reorder }`,例如:
541
+
542
+ ```ts
543
+ { code: 'items', required: true, subtable: {
544
+ create: true, delete: true, reorder: true,
545
+ fields: [{ code: 'name', required: true }, { code: 'quantity', required: true },
546
+ { code: 'originalNote', readonly: true }],
547
+ } }
548
+ ```
549
+
550
+ 关系沿用模型的 `resourceCode/foreignKey/orderField/maxRows`,不接受任务调用者自报。
551
+ 子字段可声明同样的只读、可见和条件必填规则;这些属于页面代码,管理员不能覆盖。
552
+ `create/delete/reorder` 省略时关闭。仅一层,每表 maxRows 最多100,父资源及任务页
553
+ 声明总量最多400;默认仍20。完整意图最多为每表 maxRows 的两倍,可在一次提交中
554
+ 删除满表旧行并新增同等数量,仍按有效行数校验上限。整个 form/任务私有草稿值限1MiB,
555
+ 页面定义仍64KiB/64根字段;系统、隐藏、关系键和顺序字段不进入子字段白名单。
556
+ 标准主子表提交复用同一个 Native 原子事务,最多1000操作/2MiB(400行全量替换加主表为801操作),
557
+ 任一行失败全单回滚,不静默截断或分批提交。普通表单草稿values最多2MiB,含原值与当前值;
558
+ 状态字段的独立配额不变。附件上传数量/字节与详情读取预算仍独立执行,行数容量不豁免文件配额。
559
+ 子行支持 file/image/signature/text.rich,使用同一个任务上传入口并绑定准确行;普通
560
+ 子行字段和已授权只读展示不降为 JSON 编辑框。例子见 `examples/workflow-administration/task-owned-subtable.ts`。
561
+
562
+ 标准 PC/手机控件自动消费 `surface.taskForm.subtables`,不会调用普通 child CRUD。
563
+ 自定义客户端提交 `values.items` 的完整行意图数组:
564
+ `{key,state,values,id?,revision?}`;state为created/persisted/deleted,key为小写UUID,
565
+ 持久行key等于id。删除显式列出原id/revision与空values,遗漏不代表删除。
566
+ 所有观察到的行版本都核对,包括未修改行;新行key作为其业务行ID。
567
+ values只能写白名单的当前可编辑字段,关系键与顺序由服务器维护。
568
+
569
+ 原save_form/approve/resubmit同时提交主子数据、事实与决定,失败全部回滚。
570
+ 子表提交推进主版本;普通child操作仍各自拥有行版本,不能仅用主版本判断子表并发。
571
+ 私有草稿保存相同行意图,不改业务。CAS拒绝保留输入,核对后明确保留编辑或采用最新资料;
572
+ 正在编辑的行被别人删除时不会静默丢弃或复活。未知命令沿原请求/原回执恢复。
573
+ 能力 `workflow.task-owned-subtables@1.0.0` 从声明自动推导,正式 SQL
574
+ `AuthorizeWorkflowTaskOwnedSubtablesV2` 随初始化迁移自动生效,无新增默认关闭开关。
575
+
576
+ 子行上传时在原 `WorkflowTaskFileUpload` 上增加
577
+ `row: { subtableFieldCode: 'items', rowKey }`,`fieldCode` 是该行中的材料字段。
578
+ rowKey 是新建或已持久化行的稳定小写 UUID;新行上传不会提前写入业务记录,排序也不会
579
+ 改变文件归属。不能自行指定子资源或父记录,也不能把任务私有文件交给普通 child CRUD。
580
+ 草稿递归保留有效行文件,删除意图不保留文件;原流程命令统一提交数据、引用和草稿消费。
581
+
582
+ 条件文件的上传资格依据已保存业务值。若本地编辑才使字段可见,标准页面提示先保存补填,
583
+ 再上传;私有草稿不会改变已经生效的业务事实。无条件材料可直接在尚未保存的新行上传。
584
+ 上传未知时保留原行地址、ID、规格和字节,核对 ready 回执只读恢复,不再 PUT;处理中
585
+ 禁用行修改、删除、排序和提交。标准 PC/手机控件自动处理四类值和准确行恢复。
586
+ 声明可写子行材料自动要求 `workflow.task-owned-files@1.0.0` 及适用的 managed/rich 能力;
587
+ 平台正式迁移 `AddWorkflowTaskOwnedFilesV2` 自动扩展绑定列,无需另开功能开关。
588
+
589
+ 标准 PC/手机任务页和 `WorkflowTaskOperationsPanel` 自动显示当前参与人的
590
+ `surface.taskForm`。`save_form` 表示“提交补填,继续办理”,不是私有草稿。
591
+ approve/resubmit 携带 `form: { expectedRevision, values }` 时,Native 业务更新、
592
+ 事实刷新、任务决定和下游流转同事务提交。reject/return/transfer 等操作不得
593
+ 夹带表单。审批人仅获得当前任务对应的单记录/页面字段入口,不获得通用 CRUD。
594
+
595
+ 相同幂等键不同输入拒绝;结果未知时保留原 token/input/key。共享组件锁定
596
+ 编辑,只重试原请求或调用 `loadWorkflowTaskCommandReceipt(taskId, key, tokenDigest)`
597
+ 查询本人原回执。重载只保存定位信息和 SHA256 token 摘要,不保存凭据或表单。
598
+ `not_observed` 仍然未知;只有平台锁住确切凭证、确认数据库时间已过期且未使用
599
+ 的 `expired_unconsumed` 才能解除等待,供用户核对资料后重新确认。
600
+ 组件复用宿主的 `OpenXiangdaApplication` 导航保护;成功后刷新失败不变成提交失败。
601
+
602
+ 嵌入自定义详情时,可用 `renderLayout={({ content, actions, surface }) => ...}` 将补填表单和
603
+ 恢复提示放在可滚动正文,将操作按钮放在底栏。两部分始终使用同一个任务面板,
604
+ 不要分别创建两个面板或复制其表单、请求和回执状态。`surface` 是面板当前实际展示的
605
+ 资料,自定义详情用它显示刷新后的字段;加载或无可用资料时为空。标准详情已采用该布局。
606
+
607
+ 多人补填与管理纠错使用 Native 记录 CAS,冲突保留输入。首次请求的明确拒绝会只读刷新,
608
+ 修订变化时对照最新已保存值和本人输入,核对前锁住字段与操作;可选择保留输入或采用最新值。
609
+ 拒绝后的读取失败明确提示本次未提交,锁住旧操作,恢复读取后再核对;未知结果仍只能恢复原请求。
610
+ 补填刷新固定事实投影;
611
+ 修改已计算步骤的输入时清除当前失效输出及依赖输出。仅退回 replay 且所有前向
612
+ 审批路径确定重经生产者时允许;resume_current 或后置补填跳过重算会返回
613
+ `WORKFLOW_TASK_FORM_RECOMPUTATION_REQUIRED` 并回滚,历史结果与签名保持。
614
+ 自动能力为 `workflow.task-page-submit@1.0.0`,平台初始化无需额外开关。
615
+
616
+ ### 任务私有草稿
617
+
618
+ 标准PC/手机任务页和嵌入面板提供“保存私有草稿”“我的草稿”。暂存仅本人可见,
619
+ 不会更新业务资料、事实、待办或推进任务;可以保存尚未完成必填的标量输入。
620
+ 页面的显隐/只读规则和Native类型仍强制,管理员没有字段覆盖或读取他人草稿的入口。
621
+
622
+ 自定义页面从 `openxiangda/core` 或 `openxiangda/react` 调用
623
+ `loadWorkflowTaskDrafts(taskId)`、`saveWorkflowTaskDraft(taskId, input)` 和
624
+ `removeWorkflowTaskDraft(taskId, { id, expectedRevision })`。
625
+ 保存输入为 `{ id, expectedRevision, recordRevision, values }`:首写前固定UUID及
626
+ `expectedRevision: 0`;recordRevision来自当前taskForm.expectedRevision。
627
+ 身份、环境、业务记录和固定页面由平台派生,不传actor/resource/page作为授权。
628
+ 可编译例子见 `examples/workflow-administration/task-draft.ts`。
629
+
630
+ 最多20份/90天与同用户、应用、环境、资源的其他表单草稿共享,单份最多64根字段/1MiB。
631
+ 字段支持根附件、图片、完整业务签名和富文本,暂不包含 owned 子表。读到不兼容草稿时只返回安全标识与诊断,
632
+ 不静默丢弃或泄露旧值。失去任务、角色或代理资格后不能继续读/用。
633
+
634
+ 读取不会自动覆盖输入;采用前确认,业务基线变化先核对并再次保存。相同输入暂存成功后
635
+ 可安全离开,后续新编辑恢复导航保护;浏览器持久存储不保存字段值。
636
+ 结果未知保留原id/CAS/values,仅核对同id或重试原保存;同一次最后保存确认不新增修订,
637
+ 其他CAS冲突保留输入。新id必须是用户明确另存,不能用自动另存掩盖未知结果。
638
+
639
+ 正式 `form: { expectedRevision, values, draft: { id, expectedRevision } }` 可以包含暂存之后的新编辑。
640
+ 平台锁住本人草稿及业务基线,再沿原save_form/approve/resubmit命令同事务更新业务并消费,
641
+ 失败全部回滚。原成功命令重试先返回唯一Workflow回执,不因草稿已消费重复写入。
642
+ 任务真正关闭才过期未消费草稿,all/sequence任务仍可办理时保留其他人的有效草稿。
643
+ 能力 `workflow.task-private-drafts@1.0.0` 自动提供,无额外配置开关。
644
+
645
+ ### 任务附件与图片
646
+
647
+ 当前任务的可编辑 `file/image` 字段自动接入标准 PC/手机上传、缩略图、预览和下载。
648
+ 当前处理资格不授通用资源 CRUD;平台从固定任务派生记录、页面、身份和环境,
649
+ 上传时还核验**已保存业务资料对应的页面状态**。局部修改才显示附件字段时,
650
+ 先“保存补填”使字段生效,再上传;组件说明此边界,不自动提交业务值。
651
+
652
+ 自定义页面可从 `openxiangda/core` 或 `openxiangda/react` 调用:
653
+
654
+ ```ts
655
+ const input = { id: crypto.randomUUID(), fieldCode: 'evidence',
656
+ fileName: file.name, fileSize: file.size, contentType: file.type };
657
+ const plan = await initiateWorkflowTaskFileUpload(taskId, input);
658
+ // pending 才按 plan.uploadMethod/uploadUrl/headers 上传原 File;ready 直接采用 plan.file。
659
+ const readyFile = await completeWorkflowTaskFileUpload(taskId, input.id);
660
+ // 结果未知时:loadWorkflowTaskFileUploadPlan(taskId, input.id),或重试同 ID/规格。
661
+ ```
662
+
663
+ 上传 ID 必须在第一次请求前固定。标准组件一次处理一个文件;上传中或结果未知时
664
+ 保留原 File、ID、规格和当前输入,锁住新任务动作与草稿写入,提供“核对原上传”
665
+ 和“重试原上传”。ready 结果不再 PUT;完成响应丢失后只恢复原完成结果。
666
+ 文件字节、签名地址与表单值不进入浏览器持久存储。离开未完成上传时需先确认结果。
667
+
668
+ 预览字段组件传入 `workflowFileBinding: { taskId, resourceCode, recordId, fieldCode }`;
669
+ 正式实例详情使用原 `instanceId` binding。同一 binding 只提供一种范围。
670
+ 仅本人当前任务可读私有暂存文件,其他处理人和管理员没有私有读取特权。
671
+ 当前根业务记录已经引用的文件可按该任务页面规则读取;隐藏、资格失效或任务关闭后拒绝。
672
+ 通用 Native 完成、删除和未绑定文件读取不能绕过此范围。
673
+
674
+ 附件完成后仍只是当前输入;私有草稿延长文件保留至该草稿到期,业务绑定再移除引用
675
+ 仍尊重该保留期。原 save_form/approve/resubmit 验证真实文件状态、字段、范围与元数据,
676
+ 同事务提交业务引用、事实、任务决定和草稿消费;失败全回滚。完成上传时平台复制为
677
+ 独占正式对象,旧上传地址不能再修改正式字节。
678
+
679
+ 单文件不超过字段限制与100MiB;每账号/应用/环境最多100个尚未业务绑定的任务文件,
680
+ 声明大小合计200MiB。此大小是上传容量预算,staging/正式对象与缩略图另有存储开销。
681
+ 复用既有 Native 文件引用 worker 和 GC;流式正式复制限时10秒。
682
+ 需要服务端正式 SQL `AddWorkflowTaskManagedFilesV2` 和自动能力
683
+ `workflow.task-managed-files@1.0.0`,初始化无需新增开关。
684
+
685
+ ### 任务业务签名与富文本
686
+
687
+ 任务页面声明可编辑 `signature/text.rich` 时,标准 PC/手机字段控件自动使用同一任务
688
+ 文件、私有草稿和原流程提交入口。编译器同时要求 `workflow.task-rich-fields@1.0.0`;
689
+ 平台初始化自动提供,无新增配置开关。只有只读展示时不要求上传能力。
690
+
691
+ 业务签名保留完整 Native 值:`file: { id, name, size, contentType }`、可选 `signer`、
692
+ `signedAt`、可选 `points` 和 `hash`,清空为 `null`。PNG、时间、笔迹与 SHA-256 在上传前
693
+ 固定,未知结果恢复只采用原文件,不重新签署。任务控件最多采样512个笔迹点并保留首尾;
694
+ 原 PNG 字节不改,仍受任务值1MiB限制。这些是业务采集信息,不代表平台认证的签署人、
695
+ 可信时间或法律电子签章。
696
+
697
+ 富文本保存清洗后的 HTML 和 Native 稳定托管图片地址,支持最多20图、单图10MiB、100MP。
698
+ PC/手机保留格式、图片和前后文字;插图未知时锁住编辑,恢复采用原插入位置并只插入一次。
699
+ `blob:` 仅用于授权预览,不进入保存值。新任务签名/富文本图片完成时核验实际图片解码与
700
+ 声明格式。完整值经 Native 校验后,草稿和正式审批共用排序文件锁、引用验证、保留期及事务。
701
+
702
+ 自定义 Field Kit 上传 renderer 的第四参数为可选 `onRecovered(file)`,仅在恢复原任务上传
703
+ 时采用完整字段值;正常上传仍返回 `DataFileRef`。非数组字段必须提供该回调;不能用文件数组
704
+ append 处理签名或 HTML。当前任务、实例、记录或字段改变时清理旧图片预览,重新按范围读取。