openxiangda 2.0.0-alpha.99 → 2.0.0
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/LICENSE +21 -0
- package/README.md +23 -20
- package/bin/distribution/commands.js +55 -0
- package/bin/distribution/launcher.js +49 -0
- package/bin/distribution/migrate.js +60 -0
- package/bin/distribution/releases.js +52 -0
- package/bin/distribution/skills.js +80 -0
- package/bin/distribution/update.js +68 -0
- package/bin/distribution/workspace.js +85 -0
- package/bin/run.js +9 -11
- package/dist/browser/AuthoritativeSelector.d.ts +3 -2
- package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
- package/dist/browser/AuthoritativeSelector.js +39 -24
- package/dist/browser/AuthoritativeSelector.js.map +1 -1
- package/dist/browser/components/platform-fields/MobileFieldControls.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/MobileFieldControls.js +2 -2
- package/dist/browser/components/platform-fields/MobileFieldControls.js.map +1 -1
- package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
- package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/ResourceReferenceField.js +2 -2
- package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
- package/dist/browser/components/platform-fields/rich-text-value.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/rich-text-value.js +11 -1
- package/dist/browser/components/platform-fields/rich-text-value.js.map +1 -1
- package/dist/browser/components/resource/RecordDetailFrame.js +1 -1
- package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -1
- package/dist/browser/components/resource/SurfaceFields.d.ts +3 -2
- package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
- package/dist/browser/components/resource/SurfaceFields.js +14 -7
- package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
- package/dist/browser/components/resource/resource-import.d.ts +16 -1
- package/dist/browser/components/resource/resource-import.d.ts.map +1 -1
- package/dist/browser/components/resource/resource-import.js +58 -34
- package/dist/browser/components/resource/resource-import.js.map +1 -1
- package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
- package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts.map +1 -1
- package/dist/browser/components/todo/ApplicationTodoCenterPage.js +1 -2
- package/dist/browser/components/todo/ApplicationTodoCenterPage.js.map +1 -1
- package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
- package/dist/browser/components/workflow/StandardWorkflowPages.js +54 -52
- package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
- package/dist/browser/platform-client.d.ts +3 -2
- package/dist/browser/platform-client.d.ts.map +1 -1
- package/dist/browser/platform-client.js +69 -18
- package/dist/browser/platform-client.js.map +1 -1
- package/dist/browser/record-detail.css +3 -2
- package/dist/browser/runtime.d.ts.map +1 -1
- package/dist/browser/runtime.js +26 -2
- package/dist/browser/runtime.js.map +1 -1
- package/dist/browser/workflow-launch.d.ts +4 -1
- package/dist/browser/workflow-launch.d.ts.map +1 -1
- package/dist/browser/workflow-launch.js +32 -0
- package/dist/browser/workflow-launch.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js.map +1 -1
- package/documentation/AGENTS.md +26 -0
- package/documentation/administration.md +27 -0
- package/documentation/application-foundation.md +162 -0
- package/documentation/appspec.md +152 -0
- package/documentation/backend.md +132 -0
- package/documentation/concepts.md +61 -0
- package/documentation/data-authz.md +62 -0
- package/documentation/delivery.md +110 -0
- package/documentation/development.md +32 -0
- package/documentation/field-components.md +236 -0
- package/documentation/frontend.md +269 -0
- package/documentation/getting-started.md +66 -0
- package/documentation/interaction-patterns.md +56 -0
- package/documentation/manifest.json +120 -0
- package/documentation/product-design.md +142 -0
- package/documentation/public-access.md +167 -0
- package/documentation/reference/cli.md +27 -0
- package/documentation/reference/mcp.md +649 -0
- package/documentation/testing.md +63 -0
- package/documentation/upgrading.md +39 -0
- package/documentation/workflow-events.md +181 -0
- package/launcher-skill/openxiangda/SKILL.md +24 -0
- package/package.json +72 -9
- package/releases/2.0.0.json +50 -0
- package/skills/manifest.json +2 -2
- package/skills/openxiangda-v2/SKILL.md +64 -51
- package/skills/openxiangda-v2/agents/openai.yaml +2 -2
- package/skills/openxiangda-v2/references/administration.md +27 -0
- package/skills/openxiangda-v2/references/application-foundation.md +162 -0
- package/skills/openxiangda-v2/references/appspec.md +132 -47
- package/skills/openxiangda-v2/references/backend.md +101 -248
- package/skills/openxiangda-v2/references/cli.md +27 -0
- package/skills/openxiangda-v2/references/concepts.md +61 -0
- package/skills/openxiangda-v2/references/data-authz.md +36 -388
- package/skills/openxiangda-v2/references/delivery.md +110 -49
- package/skills/openxiangda-v2/references/development.md +32 -0
- package/skills/openxiangda-v2/references/field-components.md +236 -0
- package/skills/openxiangda-v2/references/frontend.md +254 -280
- package/skills/openxiangda-v2/references/getting-started.md +66 -0
- package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
- package/skills/openxiangda-v2/references/mcp.md +649 -0
- package/skills/openxiangda-v2/references/product-design.md +142 -0
- package/skills/openxiangda-v2/references/public-access.md +92 -84
- package/skills/openxiangda-v2/references/testing.md +45 -56
- package/skills/openxiangda-v2/references/upgrading.md +39 -0
- package/skills/openxiangda-v2/references/workflow-events.md +143 -285
- package/skills/openxiangda-v2/references/architecture.md +0 -9
- package/skills/openxiangda-v2/references/commands.md +0 -21
- package/skills/openxiangda-v2/references/discovery.md +0 -15
- package/skills/openxiangda-v2/references/workspace.md +0 -62
|
@@ -1,151 +1,81 @@
|
|
|
1
|
-
# Workflow
|
|
2
|
-
|
|
3
|
-
Read [Architecture](architecture.md), [Data and authorization](data-authz.md)
|
|
4
|
-
and [Testing](testing.md) before enabling this optional layer.
|
|
5
|
-
|
|
6
|
-
Use these optional modules only when the application has a real standard approval or cross-channel notification requirement. Ordinary CRUD and application-specific state machines stay on Data API/App API and must not pull Workflow or Notification code into the default template.
|
|
7
|
-
|
|
8
|
-
## Boundaries
|
|
9
|
-
|
|
10
|
-
- The application owns business records and domain invariants.
|
|
11
|
-
- Workflow Kernel v2 owns definitions, instances, tasks, participants, commands, transitions and audit facts.
|
|
12
|
-
- Application Events v2 owns durable facts and delivery.
|
|
13
|
-
- Notification Hub v2 owns templates, rules, logical messages, channel delivery, callbacks and message operations.
|
|
14
|
-
- Named Action business notifications use `OpenXiangdaBusinessNotificationService` and the platform-owned `application.informational.standard` template. App Gateway authorizes the current user once; Notification Hub revalidates the action proof, sends as the application principal, and retains initiating user/action audit. Ordinary users never need `app:notification2:send`.
|
|
15
|
-
- Native Identity/AuthZ v2 owns the current user and the union of that user's current application roles. Perspective is read-only and never changes workflow participation or action authorization.
|
|
16
|
-
- Never import, query, migrate or call 1.x workflow/message code, tables, APIs, queues, templates or callbacks.
|
|
17
|
-
|
|
18
|
-
## Workflow declaration
|
|
19
|
-
|
|
20
|
-
Declare workflow topology and bindings once in `openxiangda.config.ts`. Use only approval, condition and end nodes for the standard module. Every definition `inputSchema` is a closed root object (`type: 'object'`, `additionalProperties: false`). Keep business data in Data API/App API and pass the exact `{ resourceCode, id }` `dataRef`, a separate positive `dataRevision`, and bounded schema-valid facts into prepare/start. Undeclared facts and answers fail closed.
|
|
21
|
-
|
|
22
|
-
`workflows.activations` is the complete desired set for the deployed version,
|
|
23
|
-
not a patch list. Removing a declaration deactivates that workflow for new
|
|
24
|
-
starts. Every assignee provider binding uses candidate `min`/`max` defaults of
|
|
25
|
-
1/200 and may not exceed 200. Application Provider callbacks accept only the
|
|
26
|
-
signed `openxiangda.workflow-assignee-request/v2.1` envelope, including the
|
|
27
|
-
business/data/version tuple and `factDigest`.
|
|
28
|
-
|
|
29
|
-
Every definition declares a launch mode: `standalone` for a self-contained
|
|
30
|
-
generated durable-process page, `custom-page` for an application operation page,
|
|
31
|
-
`hidden-handoff` for a non-menu handoff route, or `work-center-only` when users
|
|
32
|
-
only act on existing tasks. `standalone` exposes the standard user launch
|
|
33
|
-
route, while `hidden-handoff` keeps the same route out of portal navigation;
|
|
34
|
-
the generated browser runtime uses the workflow's localized definition title,
|
|
35
|
-
so applications do not maintain code-to-title maps. `standalone` and
|
|
36
|
-
`hidden-handoff` default to a compiler-owned
|
|
37
|
-
`processOperationCode` and standard process commit. When the subject mutation is
|
|
38
|
-
owned by an App Operation, declare `launch.submission.kind: 'named-operation'`
|
|
39
|
-
instead. The compiler seals its POST path, capability, schemas, input sources,
|
|
40
|
-
result paths and optional context prefill; the standard page invokes that exact
|
|
41
|
-
operation and persists only a returned `commandId`. Missing/null
|
|
42
|
-
`processCommand` is an explicit successful no-approval result. The browser never
|
|
43
|
-
owns a save callback, facts/dataRef, preparation token, or direct start. Custom
|
|
44
|
-
pages launch only inside a verified Named Action through
|
|
45
|
-
`OpenXiangdaBusinessProcessService`.
|
|
1
|
+
# Workflow 与 Notification Hub 2.0 边界
|
|
46
2
|
|
|
47
|
-
|
|
48
|
-
launch: {
|
|
49
|
-
mode: 'hidden-handoff',
|
|
50
|
-
submission: {
|
|
51
|
-
kind: 'named-operation',
|
|
52
|
-
create: {
|
|
53
|
-
operationCode: 'reservation.submit',
|
|
54
|
-
inputs: {
|
|
55
|
-
idempotencyKey: { source: 'idempotency-key' },
|
|
56
|
-
applicant: { source: 'current-user-reference' },
|
|
57
|
-
venue: { source: 'field', fieldCode: 'venue' },
|
|
58
|
-
startAt: { source: 'field', fieldCode: 'startAt' },
|
|
59
|
-
purpose: { source: 'field', fieldCode: 'purpose' },
|
|
60
|
-
},
|
|
61
|
-
output: {
|
|
62
|
-
subjectId: 'id',
|
|
63
|
-
subjectRevision: 'revision',
|
|
64
|
-
processCommand: 'processCommand',
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
context: [{ queryParameter: 'venueId', fieldCode: 'venue' }],
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
```
|
|
3
|
+
标准 Workflow 与 Notification Hub 已作为 OpenXiangda 2.0 可选平台模块重新开放。它们不属于默认 CRUD 模板,也不兼容或复用 1.x 工作流、消息中心、模板、卡片、回调、表和 API。
|
|
71
4
|
|
|
72
|
-
|
|
73
|
-
`field`, `idempotency-key`, `current-user-reference`, `requested-at`, and—for an
|
|
74
|
-
`existing` intent—both `subject-id` and `subject-revision`. Context parameters
|
|
75
|
-
only prefill declared fields; the Named Action still derives identity, validates
|
|
76
|
-
authorization/business invariants and owns idempotency. Its
|
|
77
|
-
`platformAccess.workflow.codes` must include this workflow.
|
|
5
|
+
## 标准详情与当前用户入口
|
|
78
6
|
|
|
79
|
-
|
|
7
|
+
普通记录、流程记录、任务和实例复用同一详情框架。流程详情提供申请内容、审批历史和变更记录三个标签页;管理员在当前抽屉或页面中切换到普通表单编辑,直接保存并自动留下变更记录,审批结果保持不变。PC 子表在表格内编辑,父表提交时统一校验。
|
|
80
8
|
|
|
81
|
-
|
|
9
|
+
待办中心通过 Workflow 查询 `pending`、`handled`、`created`、`cc` 四种视图;消息中心默认向 Notification Hub 查询 `view=all`,沿用应用声明的渲染扩展。各入口按服务端 `detailNavigation` 打开详情,页面不自行拼接人员范围。
|
|
82
10
|
|
|
83
|
-
|
|
11
|
+
抄送由动态 Surface 提供 `cc` 操作,使用 `user_select` 多选组件收集 1 至 20 位人员。任务 Surface 可以正式包含实例抄送命令;前端按 `operation.execute.href` 和该 Surface 的 token 提交,不另造任务命令或 token。公共事件为 `openxiangda.workflow.instance.cc_added.v2`。
|
|
84
12
|
|
|
85
|
-
|
|
86
|
-
2. Persist `commandId` in the route and reload `ProcessCommandSurface` after refresh.
|
|
87
|
-
3. Answer only requirements emitted for that command revision.
|
|
88
|
-
4. Let the platform worker start the pinned Workflow exactly once.
|
|
89
|
-
5. Use the Surface revision/concurrency token for every command.
|
|
13
|
+
## 能力 owner
|
|
90
14
|
|
|
91
|
-
|
|
15
|
+
- Workflow Kernel v2 唯一拥有定义、实例、任务、参与人、命令、流转、委托、加签和流程审计。
|
|
16
|
+
- Application Events v2 唯一拥有已提交事实的 journal/outbox、顺序、投递和回执。
|
|
17
|
+
- Notification Hub v2 唯一拥有逻辑消息、模板、规则、渠道路由、渲染、发送、更新、关闭、外部绑定、动作收据和消息运维。
|
|
18
|
+
- Data API/App API 唯一拥有业务记录;Workflow 只保存 `dataRef` 和 revision-bound facts,消息只保存允许展示的安全投影。
|
|
19
|
+
- Native Identity/AuthZ v2 唯一拥有当前用户、当前应用角色并集和数据范围;Perspective 只影响读取展示,不改变 Workflow 的发起、任务与操作授权。
|
|
92
20
|
|
|
93
|
-
|
|
21
|
+
Workflow 发起只接受 `{ resourceCode, id }` 形式的 `dataRef`,并要求独立的
|
|
22
|
+
正整数 `dataRevision`。definition 的 `inputSchema` 根对象必须显式
|
|
23
|
+
`additionalProperties: false`,未声明的 facts/answers 直接拒绝。Provider
|
|
24
|
+
回调只接受带完整业务/数据/definition/binding/fact digest 的
|
|
25
|
+
`openxiangda.workflow-assignee-request/v2.1`。
|
|
94
26
|
|
|
95
|
-
|
|
27
|
+
## 标准工作流范围
|
|
96
28
|
|
|
97
|
-
|
|
98
|
-
- audiences such as current participant, initiator, historical participant or CC;
|
|
99
|
-
- a versioned template reference;
|
|
100
|
-
- an allowlisted variable projection;
|
|
101
|
-
- channel priority/fallback;
|
|
102
|
-
- default detail target;
|
|
103
|
-
- whether to show Workflow Surface actions;
|
|
104
|
-
- whether a separate result message is required.
|
|
29
|
+
首期只支持 `approval`、`condition`、`end`,以及 `single`、`any`、`all`、`sequence` 审批模式。标准操作为提交、同意、拒绝、退回、重新提交、转交、委托、前/后加签、撤回、管理员改派、管理员终止和不改变流程状态的催办。
|
|
105
30
|
|
|
106
|
-
|
|
31
|
+
复杂业务状态机继续放在应用领域服务。不要把任意 JavaScript、Service Task、BPMN、通用长事务或业务记录复制进 Workflow。
|
|
107
32
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
permission from application code.
|
|
33
|
+
所有页面和消息动作必须来自后端 Workflow Surface 的 `operations[]`。前端、模板和渠道 Adapter 不自行推断操作权限。
|
|
34
|
+
Surface 同时签发短期、一次性的 `commandToken`,绑定当前用户/会话、应用环境
|
|
35
|
+
Head、实例/任务版本、允许的命令集合与 CSRF。命令请求只提交
|
|
36
|
+
`commandToken + idempotencyKey + input`;旧的 caller-authored
|
|
37
|
+
`expectedTaskVersion`/`expectedInstanceVersion` 不再是合同。冲突必须刷新 Surface,
|
|
38
|
+
不得自动重放旧意图。
|
|
115
39
|
|
|
116
|
-
|
|
117
|
-
`OpenXiangdaBusinessNotificationService.sendFromEvent()`. The application
|
|
118
|
-
declares safe paths under event `data` for recipients, title and summary;
|
|
119
|
-
Notification Hub verifies the active durable delivery and resolves those paths
|
|
120
|
-
itself. The audit actor remains the application event consumer with eventId,
|
|
121
|
-
deliveryId and subscription code as initiating source. Missing or forged event
|
|
122
|
-
proof, arbitrary recipient values and user impersonation are rejected.
|
|
40
|
+
## 事实和消息投影
|
|
123
41
|
|
|
124
|
-
|
|
125
|
-
immutable package capability closure cannot omit Notification Hub:
|
|
42
|
+
Workflow 在同一 PostgreSQL 事务中提交状态、fact 和 outbox。每个实例使用严格单调的 `instanceSequence`,每位审批人拥有独立 participant 生命周期。
|
|
126
43
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
platformAccess: {
|
|
132
|
-
notification: { mode: 'business-standard' },
|
|
133
|
-
},
|
|
134
|
-
}
|
|
135
|
-
```
|
|
44
|
+
Native Data 事件的 capture plan 由当前 Head 的 Event、Data、AuthZ revision
|
|
45
|
+
与 resource 四元组共同确定。新增快照字段或扩展 RLS 会形成新的不可变 plan,
|
|
46
|
+
不会与旧 plan 冲突;历史 replay/re-emission 复制原 fact 的数据与 plan digest,
|
|
47
|
+
不会用当前 Head 重新投影历史事实。
|
|
136
48
|
|
|
137
|
-
|
|
138
|
-
`
|
|
49
|
+
每条 Workflow fact v2 都必须携带不可变实例身份信封:`workflowCode`、
|
|
50
|
+
`definitionVersion`、`bindingVersion`、`instanceId`、`generation`、
|
|
51
|
+
`businessKey`、`instanceSequence`、`revision`、`dataRef`、`dataRevision`、
|
|
52
|
+
`actor` 与 `cause`。消费者不得从当前 Workflow Head 反推历史事实版本。
|
|
139
53
|
|
|
140
|
-
|
|
54
|
+
配置中的 `workflows.activations` 是完整 desired set;删除声明即停用新发起,
|
|
55
|
+
不是增量 patch。definition 和 activation 必须一致声明
|
|
56
|
+
`acceptedCommandDeactivationPolicy`:`finish-pinned` 让已接受的 durable process
|
|
57
|
+
command 按固定版本完成,`cancel-on-deactivate` 在声明删除后取消尚未启动的命令;
|
|
58
|
+
既有 Workflow instance 始终按固定版本继续。所有审批人 Provider 的 `min/max`
|
|
59
|
+
默认 1/200,最大 200。
|
|
60
|
+
需要按流程实例串行投递时只声明 `ordering: 'workflow-instance'`,不接受下划线别名。
|
|
141
61
|
|
|
142
|
-
|
|
62
|
+
Notification Hub 消费事实:
|
|
143
63
|
|
|
144
|
-
|
|
64
|
+
- `participant.activated` 创建待处理消息;
|
|
65
|
+
- participant 完成、转交、委托、加签、暂停或恢复时更新对应收件人的逻辑消息;
|
|
66
|
+
- instance 完成、拒绝、撤回或终止时更新该实例全部历史消息;
|
|
67
|
+
- 消息回调必须调用 Workflow 命令,等待 Workflow 新事实后再更新卡片。
|
|
145
68
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
69
|
+
发送采用至少一次语义,以 `messageKey + recipient + channel` 幂等。RabbitMQ 只负责唤醒,PostgreSQL fact、message projection、outbox 和 receipt 才是恢复事实。
|
|
70
|
+
|
|
71
|
+
## 详情跳转
|
|
72
|
+
|
|
73
|
+
消息使用 canonical `navigationTarget`,而不是模板拼接环境 URL。它可以描述平台路由、应用路由或白名单外部地址,由平台按 tenant、environment 和 channel 解析 desktop/mobile/deep link。
|
|
74
|
+
|
|
75
|
+
详情地址不能携带登录 Token、Cookie 或任何身份替换凭据。点击后必须重新认证,并由 Data/AuthZ/Workflow 执行真实权限校验;当前 Perspective 只能重新应用读取投影。
|
|
76
|
+
|
|
77
|
+
“跳回业务记录”和“完全接管流程详情”是两个不同合同。标准 Workflow 页需要提供
|
|
78
|
+
“查看业务详情”时,在 subject resource 上声明明确的 route code 对:
|
|
149
79
|
|
|
150
80
|
```ts
|
|
151
81
|
const purchaseResource = {
|
|
@@ -159,165 +89,93 @@ const purchaseResource = {
|
|
|
159
89
|
} satisfies AppDataResourceDeclaration;
|
|
160
90
|
```
|
|
161
91
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Surface returns no business-record target. It never guesses a path from the
|
|
169
|
-
resource code, and a malformed active mapping fails closed.
|
|
92
|
+
两条 route 都必须是已认证的 `surface: 'user'` 页面并要求该资源的 read capability;
|
|
93
|
+
桌面 path 不得进入 `/m`,移动 path 必须以 `/m/` 开头,且各自只能包含一个动态记录参数。
|
|
94
|
+
参数名可以是业务语义名,例如 `:purchaseId` 或 `:applicationId`。编译器把映射写入
|
|
95
|
+
不可变资源 Contract 和生成的 `resourceDefinitions`,平台从同一环境 active Contract
|
|
96
|
+
读取并代入 Workflow Surface 已授权的 recordId。未声明时返回空业务记录目标;已发布
|
|
97
|
+
映射畸形时严格失败。平台和浏览器都不得再按 `resourceCode` 猜路径,也不得用别名补洞。
|
|
170
98
|
|
|
171
|
-
|
|
172
|
-
codes on the Workflow definition declaration. The desktop route must be an
|
|
173
|
-
`admin` route, the mobile route must be a `user` route, and both paths contain
|
|
174
|
-
exactly `:instanceId`:
|
|
99
|
+
流程需要完全接管详情页时,在 definition declaration 上声明桌面端与移动端 route code:
|
|
175
100
|
|
|
176
101
|
```ts
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
{
|
|
187
|
-
code: 'purchase-detail-mobile',
|
|
188
|
-
path: '/m/purchases/:instanceId',
|
|
189
|
-
label: '移动采购审批详情',
|
|
190
|
-
surface: 'user',
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
},
|
|
194
|
-
workflows: {
|
|
195
|
-
definitions: [{
|
|
196
|
-
version: 1,
|
|
197
|
-
definition: purchaseApproval,
|
|
198
|
-
launch: { mode: 'work-center-only' },
|
|
199
|
-
detailRouteCode: {
|
|
200
|
-
desktop: 'purchase-detail',
|
|
201
|
-
mobile: 'purchase-detail-mobile',
|
|
202
|
-
},
|
|
203
|
-
}],
|
|
204
|
-
bindings: [],
|
|
205
|
-
activations: [],
|
|
206
|
-
},
|
|
102
|
+
{
|
|
103
|
+
version: 1,
|
|
104
|
+
definition: purchaseApproval,
|
|
105
|
+
launch: { mode: 'work-center-only' },
|
|
106
|
+
detailRouteCode: {
|
|
107
|
+
desktop: 'purchase-detail',
|
|
108
|
+
mobile: 'purchase-detail-mobile',
|
|
109
|
+
},
|
|
110
|
+
}
|
|
207
111
|
```
|
|
208
112
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
`
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
`
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
`
|
|
234
|
-
|
|
235
|
-
`
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
`/workflows/:instanceId`
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
`
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
`
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
`custom-page` and `work-center-only` declarations do not expose the standard
|
|
279
|
-
launch Surface. After start, navigate through the device's standard instance
|
|
280
|
-
entry so the current `detailRouteCode` contract can redirect consistently.
|
|
281
|
-
|
|
282
|
-
An application-level inbox is enabled with
|
|
283
|
-
`frontend.user.applicationTodoCenter: true`. It projects Notification Hub messages and
|
|
284
|
-
per-recipient interaction for the current logged-in user only; it is not the
|
|
285
|
-
Notification Hub management console. Use its `去处理` navigation to enter the
|
|
286
|
-
authorized Workflow or application page, and keep approve/reject/transfer
|
|
287
|
-
commands on the destination Surface.
|
|
288
|
-
|
|
289
|
-
## Interactive channel actions
|
|
290
|
-
|
|
291
|
-
Channel callbacks enter the Notification Action Gateway. The gateway verifies tenant, environment, recipient identity, task/participant, Surface revision, operation, nonce, signature and expiry before calling Workflow.
|
|
292
|
-
|
|
293
|
-
The callback never writes Workflow or business state directly. It returns the Workflow command receipt, waits for the resulting fact and lets Notification Hub update the existing logical message. Duplicate or expired clicks return the current receipt/snapshot and refresh the card.
|
|
294
|
-
|
|
295
|
-
Use direct card actions first for approve/reject. Return, transfer, delegate and add-sign normally deep-link to the standard task page because they require structured input.
|
|
296
|
-
|
|
297
|
-
## Event handling
|
|
298
|
-
|
|
299
|
-
Consume typed participant/task/instance facts with durable idempotency. Declare
|
|
300
|
-
event delivery `ordering: 'workflow-instance'` when the handler requires the
|
|
301
|
-
platform to serialize by instance; `workflow_instance` is not an alias. Still
|
|
302
|
-
verify `instanceSequence` in the consumer and never assume global event order.
|
|
303
|
-
Terminal instance facts dominate stale participant facts and update every
|
|
304
|
-
historical message for that instance.
|
|
305
|
-
|
|
306
|
-
For Native Data facts, the platform owns the capture plan under the exact Event
|
|
307
|
-
revision, Data revision, AuthZ revision and resource tuple selected by the
|
|
308
|
-
Native Head. Adding a snapshot field or extending RLS may create a new plan;
|
|
309
|
-
applications do not version it and must not remove the field to satisfy an old
|
|
310
|
-
plan. Historical replay uses the data and plan digest frozen in the original
|
|
311
|
-
fact, so it never gains fields from the current Head.
|
|
312
|
-
|
|
313
|
-
Every Workflow fact v2 carries the immutable instance identity envelope:
|
|
314
|
-
`workflowCode`, `definitionVersion`, `bindingVersion`, `instanceId`,
|
|
315
|
-
`generation`, `businessKey`, `instanceSequence`, `revision`, `dataRef`, and
|
|
316
|
-
`dataRevision`, plus `actor` and `cause`. Treat missing identity as an invalid
|
|
317
|
-
fact; never infer historical versions from the current Workflow Head.
|
|
318
|
-
|
|
319
|
-
Use Fake Channel before a real Adapter. Verify create, update, close, reconcile, duplicate, out-of-order, restart, retryable failure, terminal failure and unknown delivery before DingTalk or campus canaries.
|
|
320
|
-
|
|
321
|
-
## Verification
|
|
322
|
-
|
|
323
|
-
Run the workspace generate/check/test/build gates plus real current-user positive and negative role-union cases. Acceptance must include full business-detail projection, bounded parent/subtable reads, Workflow-scoped files, independent desktop/mobile renderers, submit, approve, reject, return/resubmit, transfer, delegate, before/after add-sign, withdraw, terminate, duplicate commands, stale revisions, repeated callbacks, full terminal card updates, detail navigation and 1.x zero-touch evidence.
|
|
113
|
+
`desktop` 必须引用 `surface: 'admin'` 的 route,`mobile` 必须引用
|
|
114
|
+
`surface: 'user'` 的 route;两个 path 都必须且只能包含
|
|
115
|
+
`:instanceId`。任务入口由平台追加有界 `taskId` query。应用使用
|
|
116
|
+
`defineApplicationContributions(appRoutes, { pages })` 绑定桌面和移动组件,
|
|
117
|
+
页面通过 `openxiangda/core` 的 `loadWorkflowInstanceDetail`、
|
|
118
|
+
`loadWorkflowTaskDetail` 读取修订一致的聚合详情,只渲染 Surface 返回的操作。
|
|
119
|
+
旧 Surface 与 timeline 读取继续供兼容客户端使用。
|
|
120
|
+
|
|
121
|
+
平台在当前环境 Head 的不可变 Contract Bundle 上统一解析:桌面/移动工作台、
|
|
122
|
+
Notification Hub 的 `workflow.detail` 逻辑目标、钉钉卡片以及其他渠道快照得到
|
|
123
|
+
同一组自定义路径。未声明时保留标准详情;声明存在但 route 缺失、surface 错误或
|
|
124
|
+
path 不合法时严格失败,不退回标准页掩盖合同错误。
|
|
125
|
+
|
|
126
|
+
标准详情由平台统一渲染,不要求应用复制资源详情页。Workflow Surface 的
|
|
127
|
+
`presentation.businessDetail` 按实例固定的 Data logical revision、资源声明 digest 和
|
|
128
|
+
physical resource 定位结构,但每次读取同一 Native Data record 的当前字段值;发起时
|
|
129
|
+
`requestedRevision` 只用于命令 token/CAS,当前 `sourceRevision` 和当前值始终随重新读取更新,
|
|
130
|
+
禁止把业务字段值改成流程启动快照。字段投影同时应用当前用户策略并统一排除
|
|
131
|
+
`system: true`、平台内部字段与过滤后为空的分组;
|
|
132
|
+
父子表按同一 logical revision 和父记录外键读取,单表最多 200 行。页面同时消费
|
|
133
|
+
`presentation.summary`、服务端 timeline `display.entries` 和 operation descriptors。
|
|
134
|
+
桌面端与移动端拥有独立 DOM;同意/拒绝等决策操作固定在底部主操作区,转交、退回、
|
|
135
|
+
加签等低频操作进入“更多操作”,意见只在点击动作后的确认弹窗或移动端底部抽屉中输入。
|
|
136
|
+
`stale` 只是 revision 元数据,不显示常驻横幅也不预先阻断操作;只有服务端命令 token/CAS
|
|
137
|
+
返回真实 `freshSurface` 冲突时才提示“内容已更新,请刷新后重试”,且绝不自动重放命令。
|
|
138
|
+
同意、拒绝、退回、转交和加签记录并入对应纵向节点;实例撤回/终止是独立终态事件,
|
|
139
|
+
展示操作者、动作、意见和唯一 `primaryDisplayTime`,
|
|
140
|
+
不另设“操作记录”。标准业务页不渲染流程/任务/资源/记录 UUID、事件序列、revision 或失败指针。
|
|
141
|
+
申请人、审批人和操作者显示目录/快照解析名称、可选部门以及真实头像;缺失或加载失败时
|
|
142
|
+
统一使用平台默认头像,不生成姓名首字,也不回退 userId。
|
|
143
|
+
|
|
144
|
+
PC 标准任务与实例详情的 canonical 路径分别为 `/tasks/:taskId` 和
|
|
145
|
+
`/workflows/:instanceId`,使用独立全屏页面,不进入后台 Shell;不提供旧 admin 路径的
|
|
146
|
+
别名或重定向。移动端继续使用独立的 `/m/tasks/:taskId` 和 `/m/workflows/:instanceId` 页面。
|
|
147
|
+
|
|
148
|
+
流程详情中的附件、富文本图片和签名不直接复用普通 Data API 文件地址。浏览器调用
|
|
149
|
+
Workflow instance-scoped preview/content 路由,平台在每次文件读取时重新校验当前身份、
|
|
150
|
+
实例参与权限、固定业务记录、资源、字段和 file id;URL 不携带登录态或替代身份。
|
|
151
|
+
|
|
152
|
+
标准发起页使用独立的 `WorkflowLaunchSurface` 读取当前激活合同:桌面路径为
|
|
153
|
+
`/workflows/:workflowCode/start`,移动路径为
|
|
154
|
+
`/m/workflows/:workflowCode/start`。`standalone`/`hidden-handoff` 缺省使用同一个
|
|
155
|
+
compiler-owned `processOperationCode`、subject declaration 和标准 process commit;平台在一个
|
|
156
|
+
事务中写业务数据和 durable command。action-owned 资源改为声明
|
|
157
|
+
`launch.submission.kind: 'named-operation'`,显式绑定 create/existing 请求来源、响应 subject/
|
|
158
|
+
processCommand 路径和 allowlisted context 预填。标准页调用原 Named Action,由应用服务端保留
|
|
159
|
+
业务校验、幂等与原子提交;缺少/null processCommand 是成功的免审结果,不生成流程。页面只在
|
|
160
|
+
确实返回 command 时把 `commandId` 写入 URL,刷新、跨设备和 worker 恢复都重新读取
|
|
161
|
+
`ProcessCommandSurface`;`awaiting_input` 只回答已生成 requirement,`started` 再进入 pinned
|
|
162
|
+
instance entry。浏览器不再导出 prepare/start 发起协议。
|
|
163
|
+
|
|
164
|
+
通用应用待办页通过 `frontend.user.applicationTodoCenter: true` 启用,平台同时提供
|
|
165
|
+
`/todos` 和 `/m/todos`。它只投影当前登录用户的 Notification Hub 收件人数据,
|
|
166
|
+
`查看详情` 使用上述统一导航解析;待办页不常驻业务详情,审批命令仍在目标 Workflow
|
|
167
|
+
Surface 上执行。
|
|
168
|
+
|
|
169
|
+
## 应用声明
|
|
170
|
+
|
|
171
|
+
应用只在明确启用可选模块时,在 `openxiangda.config.ts` 声明 workflow definition、binding、assignee provider、notification profile、template reference 和安全字段投影。Git 声明是拓扑事实源;平台管理页面只负责版本、激活、预览、诊断和有界运行参数,不建立第二份活动定义。
|
|
172
|
+
|
|
173
|
+
事件处理器调用 `OpenXiangdaBusinessNotificationService.sendFromEvent()` 时,
|
|
174
|
+
对应订阅还必须声明
|
|
175
|
+
`platformAccess: { notification: { mode: 'business-standard' } }`。编译器将该声明写入
|
|
176
|
+
不可变 Configuration/Contract Bundle,并据此生成精确的 `notification-hub-v2`
|
|
177
|
+
能力闭包;不发送通知的订阅不得声明该依赖,未知 access 或 mode 直接失败。
|
|
178
|
+
|
|
179
|
+
## 验证
|
|
180
|
+
|
|
181
|
+
至少验证:完整标准详情投影、父子表边界、流程附件读取、桌面/移动独立渲染、全部标准操作、多人模式、重复/并发命令、stale revision、重启 replay、乱序事件、重复回调、消息终态全量更新、详情跳转、错用户/错租户/过期动作、渠道超时/unknown、死信重放和 1.x 零触碰。
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# OpenXiangda 2.0 Architecture
|
|
2
|
-
|
|
3
|
-
Keep one React application, one optional NestJS service and one platform-owned data contract. Application code directly depends only on `openxiangda`; use `openxiangda/config`, `openxiangda/core`, `openxiangda/field-kit`, `openxiangda/react`, `openxiangda/nest` and `openxiangda/testing` as the public subpaths. Do not import the physical `openxiangda-*` implementation packages.
|
|
4
|
-
|
|
5
|
-
Declare resources, routes, capabilities, row policies and operation-aware field policies once in `openxiangda.config.ts`. The compiler owns derived contracts; the root package owns Field Kit, CRUD Renderer, Shell, clients and Nest integration; the platform owns identity, authorization, data, environment Heads and deployment state.
|
|
6
|
-
|
|
7
|
-
The compiler alone derives the AppPackage `requiredPlatformCapabilities` closure. Each entry binds a capability `code`, exact `contractVersion` and deterministic `usageDigest` of only that capability's normalized declarations. Never write `platform.requiredCapabilities`, append requirements through a build API, edit the AppPackage or accept a feature whose status is not `available` or whose contract version differs. The platform authoritatively recomputes the closure from the sealed config and contract artifacts.
|
|
8
|
-
|
|
9
|
-
Ordinary list, detail, create, update and delete operations go directly through Data API. Add a NestJS endpoint only for a business action that cannot be represented as CRUD. Run `pnpm openxiangda check` after every contract change.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# OpenXiangda 2.0 Commands
|
|
2
|
-
|
|
3
|
-
> Generated from `DEVKIT_COMMANDS`. Do not edit manually.
|
|
4
|
-
|
|
5
|
-
| Command | Risk | Purpose |
|
|
6
|
-
| --- | --- | --- |
|
|
7
|
-
| `pnpm openxiangda create` | deploy | 创建、绑定并初始化应用 |
|
|
8
|
-
| `pnpm openxiangda dev` | write-local | 连接平台测试数据启动本地 Web,按需启动 Nest |
|
|
9
|
-
| `pnpm openxiangda check` | write-local | 生成契约并在目标平台预检后执行检查、测试和构建 |
|
|
10
|
-
| `pnpm openxiangda accept` | deploy | 按计划准备可选的真实预发验收身份 |
|
|
11
|
-
| `pnpm openxiangda deploy` | deploy | 部署测试环境或显式复用测试版本部署生产 |
|
|
12
|
-
| `pnpm openxiangda status` | read | 查询最近或指定部署状态 |
|
|
13
|
-
| `pnpm openxiangda logs` | read | 查询最近或指定部署日志 |
|
|
14
|
-
| `pnpm openxiangda cancel` | deploy | 幂等取消尚未提交激活的部署 |
|
|
15
|
-
| `pnpm openxiangda retry` | deploy | 显式重试可恢复的失败部署 |
|
|
16
|
-
| `pnpm openxiangda start` | deploy | 从当前不可变版本启动应用环境 |
|
|
17
|
-
| `pnpm openxiangda stop` | deploy | 将应用环境缩容为零并保留数据 |
|
|
18
|
-
| `pnpm openxiangda rollback` | deploy | 回滚测试或生产环境 |
|
|
19
|
-
| `pnpm openxiangda login` | write-local | 通过平台浏览器授权登录 |
|
|
20
|
-
| `pnpm openxiangda skill` | write-local | 安装当前版本的 AI Skill |
|
|
21
|
-
| `pnpm openxiangda spec` | write-local | 维护可选的 AppSpec 需求与变更辅助 |
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Requirement Discovery
|
|
2
|
-
|
|
3
|
-
Before editing, write a compact decision record covering:
|
|
4
|
-
|
|
5
|
-
- business objective and measurable outcome;
|
|
6
|
-
- actors and positive/negative scenarios;
|
|
7
|
-
- resources, field meanings, required values and lifecycle states;
|
|
8
|
-
- role memberships, operation capabilities, field restrictions and row scope;
|
|
9
|
-
- external systems, side effects, concurrency and idempotency;
|
|
10
|
-
- environment, security, volume and latency bounds;
|
|
11
|
-
- acceptance evidence for UI, API, PostgreSQL/RLS and delivery.
|
|
12
|
-
|
|
13
|
-
Separate known facts, assumptions and unresolved choices. Inspect the workspace protocol and existing declarations before inventing a contract. If a choice changes data meaning, authorization, external writes or release scope, resolve it before implementation. Ordinary naming or layout details may use a documented reasonable assumption.
|
|
14
|
-
|
|
15
|
-
The output is complete only when every requirement maps to an owner and a falsifiable acceptance check. Do not begin with source patches and reconstruct intent afterward.
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# OpenXiangda 2.0 Application Agent Contract
|
|
2
|
-
|
|
3
|
-
- Use only the workspace-pinned CLI: `pnpm openxiangda <command>`. Never invoke a bare global `openxiangda` inside a 2.0 task.
|
|
4
|
-
- Maintain business goals, task pages, permission decisions and acceptance examples in the existing `appspec/app.md`; complex modules may have focused capability records. `appspec/` is the OpenXiangda 2.0 business-intent layer. Before changing observable behavior, read the bounded index with `pnpm openxiangda spec context --json`, then select only the relevant stable ID; use no ChangeSpec for L0 work, one short ChangeSpec for L1, and add permission/rollback/concurrency detail only for L2/L3. Before close, the user—not AI—confirms `currentSpec=merged` or `not-applicable`. AppSpec is advisory and never a release gate. Never read, import or migrate 1.x `openspec/` or SDD.
|
|
5
|
-
- Use Vite, React Router, Refine Core and platform Field Kit. Use Ant Design for PC and the scoped openxiangda/mobile adapter for touch controls; wrap custom mobile pages in MobileSurface and import openxiangda/mobile/styles.css. Do not import the antd-mobile root with its global reset. Do not author raw business input/select/textarea/contentEditable elements. Keep mobile entrypoints under src/mobile/ or name them Mobile*.tsx / *.mobile.tsx so openxiangda check can inspect their local dependency graph. Do not add Umi, ProComponents or another admin shell.
|
|
6
|
-
- Use Ant Design's default component appearance on PC and the platform's scoped mobile adapter for touch controls. The UI provider supplies Chinese locale and contextual feedback only. Do not generate appearance preferences, color configuration, palette registries, visual state stores or document-wide color effects. Prefer platform components and keep CSS scoped to the component.
|
|
7
|
-
- The template includes Tailwind CSS v4 utilities for user pages. The application owns its minimal document reset; omit preflight from the shared runtime and do not scaffold custom palettes.
|
|
8
|
-
- Bind every generated `appRoutes` entry to its local page with `defineApplicationContributions`; desktop `admin` routes stay inside the platform Shell, while `user` routes render without an admin Shell for independent mobile/user experiences. Generated resource CRUD routes are compiler-owned under `/admin/resources/<resourceCode>...` and `/m/admin/resources/<resourceCode>...`; never recreate root resource paths, aliases or redirects. Explicit routes that have the same canonical shape as another explicit or generated route fail compilation, even when dynamic parameter names differ. Use only the typed `toolbar`, `row` and `detail` resource slots for generated resource actions. Declare the complete editable admin menu with `defineAdminNavigation` and its page/group helpers; the Shell renders only generated `adminNavigation` references and permissions only filter them. Do not create another router, menu store, layout, identity provider, permission store or copied CRUD page; route/action access uses capability or `allOf`/`anyOf`, while Data/App API and Workflow authorization remain server-owned.
|
|
9
|
-
- Application login is optional `frontend.authentication`: existing platform users only, registration rejected, exact desktop `/login` and mobile `/m/login`. Bind generated `authenticationSurfaces` to separate PC/mobile renderers through `defineApplicationContributions`; renderers own only brand visuals and call `ApplicationLoginSurfaceProps`. The platform alone owns passwords, providers, OAuth state/callbacks, secure cookies, current identity and authorization. Never put login in protected `appRoutes`, call a v1 auth route, store tokens, create users/roles, or add another Router/identity provider. Keep QA in generated `platformAuthManifest`, outside the protected route denominator.
|
|
10
|
-
- Branded standard user pages use the optional exact `standardUserSurfaces` contribution from `openxiangda/react`: provide separate desktop/mobile `frame` and `applicationTodoCenter` renderers or omit the property entirely. The platform still owns `/todos`, `/m/todos`, Workflow routes, `RuntimeBoundary`, current-user Notification Hub data, query/load/interaction callbacks and navigation. Never add a second Router, Todo API client, identity store or token prop.
|
|
11
|
-
- External users without platform accounts use only an exact static `surface: 'user'` route declared through `frontend.publicAccess`. Declare the one resource, bounded field sets, required operations, draft limits and named duplicate validations; consume only generated `anonymousPublicAccess` and `createAnonymousPublicClient`. `own.list`/`own.read` mean records submitted by the same platform-issued HttpOnly browser credential, not a verified natural person, and another browser or cleared cookie intentionally loses access. Never create a guest role/user, call the general Native Data API, expose an anonymous upload path, store identity locally or derive ownership from IP, user-agent or fingerprint.
|
|
12
|
-
- Define storage with `defineDataModel` in `modules/<business>/models.ts`; compose modules with `defineApplicationModule`. Select standard pages explicitly through `crud`, `defineResourceList` and `defineResourceForm`. Models without `crud` receive no pages or navigation. A business task page may consume several models. The root `openxiangda.config.ts` composes modules, routes and roles; it must not grow into one giant business definition. Existing `data.resources` remains a lower-level input to the same compiler. Keep internal fields `hidden: true`; this is presentation, never authorization. `system: true` marks server-owned values and hides them by default; `hidden: false` may show a business status or serial number. Form/detail/list field selections are exhaustive and ordered. A Workflow subject resource may declare explicit desktop/mobile `detailRouteCode` as documented in the workflow contract.
|
|
13
|
-
- Never write resource-level `schemaVersion`, `appCode`, `schema`, `surface`, `capabilities`, `fieldPolicies` or `platform/data` modules. The compiler derives the strict DataResource, CRUD capabilities, Surface and AI Schema. The application manifest still starts with its one top-level `schemaVersion: 3`.
|
|
14
|
-
- Ordinary list/get/create/update/delete, filters, export and batch operations use the platform Native Data API. Do not create Function CRUD or NestJS wrappers.
|
|
15
|
-
- Standard PC lists use one toolbar, on-demand filters/column settings and drawer create/edit over the retained list. Full-page entry uses the same platform form. Declare shared form/detail groups with `crud[].sections: [{ title, fields }]`; each view's field selection remains exhaustive and ordered. Small forms may omit groups. Keep only business columns by default, use lightweight sections and let platform components handle pending, errors and dirty cancellation. Do not recreate list/form layouts in application code.
|
|
16
|
-
- Add NestJS only for a named business action that needs a cross-resource transaction, an invariant or an external side effect. The blank app contains no apps/server source or Nest dependency importer. Declare backend.enabled or a server operation, then run pnpm openxiangda check or pnpm dev to initialize and install the optional backend once; existing source is never regenerated. Standard platform workflow definitions and activations do not require Nest.
|
|
17
|
-
- One model can declare up to 20 named standard views with `crud[].code` and `name`. Each selects its own list/form/detail fields, sections, generated operations and mobile availability. Bind navigation with `adminResourcePage(modelCode, { viewCode })`; only an unnamed CRUD declaration generates the original default routes. Views share model values, access and Data API ownership. Include writable required fields in create forms or set `generated.create: false`. Keep platform display preferences and authenticated drafts scoped to the selected view; do not copy models or invent a second draft store.
|
|
18
|
-
- Every interactive business action binds `@OpenXiangdaOperation(operation)` and injects `OpenXiangdaBusinessDataApiService`, `OpenXiangdaBusinessNotificationService`, or `OpenXiangdaStandardOperations`. The platform checks the action capability once at App API ingress; the trusted backend then has full Data/managed business-notification access only to its exact application/environment while audit retains the initiating user and action. Do not author `authorizationJSON`, forward user tokens, grant ordinary users `app:notification2:send`, or reapply the user's resource, row and field permissions inside the action.
|
|
19
|
-
- A NestJS backend declares only `enabled`, `isolation: 'shared' | 'dedicated'` and `resourceProfile: 'light' | 'standard'`. Never put raw Kubernetes resources, replicas, ports or environment maps in application metadata; the platform owns capacity and scaling.
|
|
20
|
-
- Use the current logged-in user and the union of that user's application roles. An app for every logged-in platform user may declare one existing package role through `authz.authenticatedUserRoleCode`; the platform materializes it on first access and unions it with manual and business-projected roles. Do not fabricate a browser fallback role or use a business membership resource for this universal audience. An optional declared Perspective projects only read visibility (pages, rows and fields); create/update/delete, workflows and custom actions continue to authorize against the complete union. Standard Data API calls inherit `X-OpenXiangda-Perspective` automatically. Use `@CurrentPerspective()` only when custom Nest code reads outside the standard Data API, and apply an equivalent read projection explicitly. Business code must not persist a platform Token or authorization result and must not implement a second identity path.
|
|
21
|
-
- Fields inherit the resource read/create/update capabilities. Use field `access` only to tighten them; arrays are all-of and `false` is explicit deny. There is no `write` fallback.
|
|
22
|
-
- Field `type` is semantic, never a hand-authored database type. The supported catalog is exactly `text.short`, `text.long`, `text.rich`, `number.integer`, `number.decimal`, `boolean`, `date`, `time`, `datetime`, `date-range`, `datetime-range`, `option.single`, `option.multiple`, `cascade.single`, `cascade.multiple`, `user.single`, `user.multiple`, `department.single`, `department.multiple`, `resource-ref.single`, `resource-ref.multiple`, `file`, `image`, `signature`, `address`, `location`, `json`, `serial-number`, `uuid` and `subtable`. The compiler alone derives PostgreSQL columns, constraints and indexes.
|
|
23
|
-
- Every `date-range` and `datetime-range` field explicitly declares `rangeBoundary: 'closed' | 'half-open'`. Values remain `{ start, end }`; callers never send or override the boundary. Closed ranges allow `start <= end` and use PostgreSQL `[]`; half-open ranges require `start < end` and use `[)`.
|
|
24
|
-
- Declare every custom operation capability in `authz.capabilities` with `kind: 'backend'`, then reference that same code from the operation and its allowed roles. Generated resource CRUD capabilities do not go in this catalog.
|
|
25
|
-
- Build App Operation request/response schemas with `resourceRecordSchema`, `schemaRef`, `composeJsonSchema` and `composeAppOperationSchemas` from `openxiangda/config`. They project the same Resource declaration and canonical field value protocols; never import a physical toolchain package or repeat user, department, resource-reference or managed-file schemas by hand.
|
|
26
|
-
- Visitor duplicate protection uses `createVisitorReservation({ duplicateMatch: { fieldCode: submittedValue }, ... })`. `duplicateMatch` is a non-empty value map, never a field-name array, and no mutable pre-read is allowed.
|
|
27
|
-
- Desktop and mobile pages share values, validation and authorization, but use separate renderers. Options, members and departments store display snapshots; resource references store direct JSON display values; attachments, images and signatures use platform-managed file references.
|
|
28
|
-
- `option.*`, `user.*`, `department.*` and `resource-ref.*` values never collapse to scalar IDs. Single values store one labeled object and multiple values store object arrays. `resource-ref.*` JSON is not a foreign key, trusted target snapshot or automatically refreshed copy; current target state is read by `resourceCode` plus `value`. A resource source `labelField` must be `text.short` or `text.long`; `serial-number` is allowed in source search, description and snapshot fields, but not as the label. `location` accepts only exact WGS84 coordinates captured by DingTalk or browser geolocation; it has no manual input or `manual` source. Roles that consume directory-backed fields explicitly include `app:<app-code>:directory:read`.
|
|
29
|
-
- Use `resourceRoleCapabilities(appCode, resourceCode, "read" | "manage" | [operations])` for explicit role grants. A read grant never expands into create/update/delete when a page is added. Confirm the business permission matrix before implementing multi-role behavior; `manage` is only for roles explicitly allowed all CRUD operations. Use `currentUserDataPolicy(...)` for current-user row scope.
|
|
30
|
-
- Do not add compatibility aliases, migration branches or silent fallbacks for an earlier 2.0 alpha contract. Replace an incorrect contract and regenerate the application.
|
|
31
|
-
- Standard Workflow and Notification Hub are optional 2.0 modules. Enable them only through canonical `openxiangda.config.ts` declarations and generated clients. `standalone`/`hidden-handoff` default to the compiler-owned process operation; action-owned submission must instead declare `launch.submission.kind: 'named-operation'` with sealed create/existing input and output bindings so the standard PC/mobile page calls the original Named Action and accepts an explicit no-Workflow result. Never add a browser save callback or call Workflow prepare/start directly. Custom pages launch only through a verified Named Action using `OpenXiangdaBusinessProcessService`. PC and mobile render the same `ProcessCommandSurface` independently and recover only by `commandId`. To replace Workflow detail, declare both desktop and mobile `detailRouteCode` values whose routes contain exactly `:instanceId`. Add the current-user todo page only with `frontend.user.applicationTodoCenter: true`; never query Notification Hub management APIs from a user page.
|
|
32
|
-
- Run `pnpm openxiangda check --json` after contract changes and read `data.sealedArtifact`; check never seals, and an older `.openxiangda/build/app-package.json` is not the current check result. Use `pnpm openxiangda accept --plan <file>` only for optional real preproduction identity acceptance; it never blocks delivery. Deploy with `pnpm openxiangda deploy`, inspect with `pnpm openxiangda status` and `pnpm openxiangda logs`, and use the platform rollback command rather than mutating K3s directly.
|
|
33
|
-
- AI-native clients start the workspace protocol through the same pinned binary: `pnpm exec openxiangda --mcp-stdio --cwd <workspace>`. Read `openxiangda://workspace/contracts` or call `contract_describe`; for first-time menu authoring, copy `data.adminNavigationAuthoring.suggestion.expression` once into `frontend.admin.navigation` with its listed `openxiangda/config` imports, then edit that application-owned declaration. Never treat the proposal as runtime discovery. When AppSpec is enabled, also read `openxiangda://workspace/appspec` or call `appspec_context`. Require `aiCatalog` and `aiCatalogDigest` to match the normal compiler output. This is a stdio transport mode, not another application-owned server. Never write an application MCP server, Catalog file, preview store or second authorization path.
|
|
34
|
-
|
|
35
|
-
- For an ordinary CRUD module, omit unused backend/platform blocks; the platform owns CRUD. `pnpm dev` uses connected development and starts Nest only when enabled or required by declared server capabilities. Run interface tests for changed business rules and browser tests for the actual PC/mobile create/edit/save/read journeys; passing HTTP tests alone is not acceptance.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## Standard data entry and list controls
|
|
39
|
-
|
|
40
|
-
Use the platform standard list for nested AND/OR conditions, display-column
|
|
41
|
-
selection/order/freezing and multiple sorting. Set `requiresSelection: true` on
|
|
42
|
-
selection-dependent toolbar contributions. Preserve hidden fields and query /
|
|
43
|
-
export parity. Column changes preview immediately and save to current-user
|
|
44
|
-
platform preferences explicitly.
|
|
45
|
-
|
|
46
|
-
Standard forms use Save draft / Submit. Consume platform authenticated drafts;
|
|
47
|
-
do not create application draft tables, localStorage or Nest draft endpoints.
|
|
48
|
-
PC drawer controls are Full screen / New page / Close, with a platform draft
|
|
49
|
-
handoff to the new page. Mobile entry uses grouped touch-field rows, a simple
|
|
50
|
-
title and a fixed action footer, with bottom sheets for drafts and recovery.
|
|
51
|
-
Do not add a mobile Back to list action or desktop form controls. Preserve edit
|
|
52
|
-
revisions and draft CAS; successful submission consumes the draft atomically.
|
|
53
|
-
|
|
54
|
-
## Optional application backend
|
|
55
|
-
|
|
56
|
-
The default scaffold contains Web and shared contracts only. Declare
|
|
57
|
-
`backend: { enabled: true }` or application server capabilities, then use the
|
|
58
|
-
existing `pnpm openxiangda check` / `pnpm dev` loop. The tool initializes Nest
|
|
59
|
-
source and its exact dependencies once, preserves authored source, and retries
|
|
60
|
-
an interrupted dependency install. Standard platform workflow activation does
|
|
61
|
-
not imply application Nest. See `docs/backend.md` and the runnable
|
|
62
|
-
`examples/business-action-extension` for SDK ownership and transaction retries.
|