openxiangda 2.13.2 → 2.15.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/dist/browser/components/platform-fields/rich-text-value.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/rich-text-value.js +7 -2
- package/dist/browser/components/platform-fields/rich-text-value.js.map +1 -1
- package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
- package/dist/browser/platform-client.d.ts +11 -1
- package/dist/browser/platform-client.d.ts.map +1 -1
- package/dist/browser/platform-client.js +24 -2
- package/dist/browser/platform-client.js.map +1 -1
- package/documentation/AGENTS.md +2 -2
- package/documentation/backend.md +1 -1
- package/documentation/data-authz.md +15 -1
- package/documentation/delivery.md +4 -7
- package/documentation/development.md +21 -0
- package/documentation/frontend.md +37 -0
- package/documentation/getting-started.md +7 -7
- package/documentation/manifest.json +9 -9
- package/documentation/public-access.md +81 -2
- package/documentation/testing.md +1 -1
- package/package.json +29 -20
- package/releases/2.14.0.json +41 -0
- package/releases/2.15.0.json +40 -0
- package/skills/manifest.json +1 -1
- package/skills/openxiangda-v2/SKILL.md +5 -5
- package/skills/openxiangda-v2/references/backend.md +1 -1
- package/skills/openxiangda-v2/references/data-authz.md +15 -1
- package/skills/openxiangda-v2/references/delivery.md +4 -7
- package/skills/openxiangda-v2/references/development.md +21 -0
- package/skills/openxiangda-v2/references/frontend.md +37 -0
- package/skills/openxiangda-v2/references/getting-started.md +7 -7
- package/skills/openxiangda-v2/references/public-access.md +81 -2
- package/skills/openxiangda-v2/references/testing.md +1 -1
|
@@ -63,6 +63,43 @@ capability、应用角色和数据策略。前端只根据当前登录用户完
|
|
|
63
63
|
可替换 Data API adapter。不要调用自定义 Nest CRUD、Function 或 Workflow 来绕过
|
|
64
64
|
Data API。只有真正需要事务或外部系统的动作才使用同源 `/api`。
|
|
65
65
|
|
|
66
|
+
### 自定义页面消费平台数据 {#data-access}
|
|
67
|
+
|
|
68
|
+
自定义报表、工具页和用户端页面从 `openxiangda/core` 导入 `createNativeResourceClient`,
|
|
69
|
+
用生成契约里的 surface 直接获得该资源的权威读写客户端;行、字段与操作授权由平台在
|
|
70
|
+
每次请求时执行,页面不需要也不得复制权限逻辑:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { createNativeResourceClient } from 'openxiangda/core';
|
|
74
|
+
import { resourceSurfaces } from '@app/contracts';
|
|
75
|
+
|
|
76
|
+
const records = createNativeResourceClient('records', resourceSurfaces.records);
|
|
77
|
+
|
|
78
|
+
// 服务端过滤、排序、分页;字段必须已声明,未声明字段直接报错
|
|
79
|
+
const page = await records.list({
|
|
80
|
+
page: 1,
|
|
81
|
+
pageSize: 20,
|
|
82
|
+
where: { field: 'enabled', operator: 'eq', value: true },
|
|
83
|
+
sort: { field: 'createdAt', order: 'desc' },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const record = await records.get(id);
|
|
87
|
+
await records.create(data);
|
|
88
|
+
await records.update(id, expectedRevision, data); // revision 冲突时明确报错
|
|
89
|
+
await records.remove(id, expectedRevision);
|
|
90
|
+
await records.upload('attachment', file, recordId);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
跨模型原子写使用 `transactNativeData(operations, idempotencyKey)`:一次平台事务提交
|
|
94
|
+
多个资源操作,携带幂等键,不确定的响应复用同一载荷重试,不产生第二份业务效果。
|
|
95
|
+
多指标统计使用 `batchAggregateNativeResources` 在服务端聚合。导出使用
|
|
96
|
+
`records.exportCsv(query, select)`,与列表共用同一查询条件。
|
|
97
|
+
|
|
98
|
+
要求服务端校验、状态前置或角色核对时,优先事务守卫(见
|
|
99
|
+
[判定是否真的需要 Nest 后端](./development.md#backend-decision));只有在真实外部
|
|
100
|
+
副作用下才声明 Nest operation。自写 controller 转发单一资源的增删改查无法通过 `check`:
|
|
101
|
+
每个应用路由必须以 `@OpenXiangdaOperation(appOperations.<code>)` 绑定已声明的 operation。
|
|
102
|
+
|
|
66
103
|
默认仪器模块有 30 个字段,其中 `id/revision` 是 Data API 系统字段,28 个业务
|
|
67
104
|
字段由资源声明。新增、编辑和详情共用同一份字段元数据。五个边界字段使用五个独立
|
|
68
105
|
capability,不使用角色名或影子字段判断。
|
|
@@ -66,10 +66,10 @@ MCP 服务随项目根包一起安装,AI 客户端的 stdio 连接仍需配置
|
|
|
66
66
|
以下命令的版本占位符由随包资料替换为该根包的精确版本。网站源码阅读者应先确认要使用的发行版本。
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
pnpm dlx openxiangda@2.
|
|
70
|
-
pnpm dlx openxiangda@2.
|
|
71
|
-
pnpm dlx openxiangda@2.
|
|
72
|
-
pnpm dlx openxiangda@2.
|
|
69
|
+
pnpm dlx openxiangda@2.15.0 skill install --force
|
|
70
|
+
pnpm dlx openxiangda@2.15.0 auth status --base-url <平台地址> --json
|
|
71
|
+
pnpm dlx openxiangda@2.15.0 login --cwd my-app --base-url https://platform.example.com
|
|
72
|
+
pnpm dlx openxiangda@2.15.0 create my-app --base-url https://platform.example.com
|
|
73
73
|
cd my-app
|
|
74
74
|
pnpm openxiangda context --json
|
|
75
75
|
pnpm openxiangda dev
|
|
@@ -171,9 +171,9 @@ MCP 的 `docs_read` 可以读取本说明,当前没有独立的源码操作 MC
|
|
|
171
171
|
无需本地工作区,使用本 Skill 随包精确版本或已安装的对应 CLI:
|
|
172
172
|
|
|
173
173
|
```bash
|
|
174
|
-
pnpm dlx openxiangda@2.
|
|
175
|
-
pnpm dlx openxiangda@2.
|
|
176
|
-
pnpm dlx openxiangda@2.
|
|
174
|
+
pnpm dlx openxiangda@2.15.0 auth status --base-url <平台> --json
|
|
175
|
+
pnpm dlx openxiangda@2.15.0 source resolve <仓库URL> --base-url <平台> --json
|
|
176
|
+
pnpm dlx openxiangda@2.15.0 source clone <仓库URL> <新目录> --base-url <平台> --json
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
登录缺失或站点不匹配时,先按该平台执行 login。resolve 根据平台已经登记的绑定返回
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "openxiangda.documentation/v1",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"topics": [
|
|
5
5
|
{
|
|
6
6
|
"id": "getting-started",
|
|
7
7
|
"title": "安装与开始开发",
|
|
8
8
|
"file": "getting-started.md",
|
|
9
|
-
"sha256": "
|
|
9
|
+
"sha256": "8ce8ca74bff94aa582c4bce708a0d25529478c5062ed67ea7fb5227927d4fcd5"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"id": "product-design",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"id": "development",
|
|
43
43
|
"title": "需求与开发流程",
|
|
44
44
|
"file": "development.md",
|
|
45
|
-
"sha256": "
|
|
45
|
+
"sha256": "c172cf5a6a20340b4142a1443b653d3e312f8a4464f990e69055046033011366"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"id": "application-foundation",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"id": "frontend",
|
|
67
67
|
"title": "页面与标准组件扩展",
|
|
68
68
|
"file": "frontend.md",
|
|
69
|
-
"sha256": "
|
|
69
|
+
"sha256": "61eedc337b1e64d404f5521eedf3ed6e3afc7ac341a4c56972ffe1000bb9bfd4"
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"id": "field-components",
|
|
@@ -78,13 +78,13 @@
|
|
|
78
78
|
"id": "data-authz",
|
|
79
79
|
"title": "数据查询与权限",
|
|
80
80
|
"file": "data-authz.md",
|
|
81
|
-
"sha256": "
|
|
81
|
+
"sha256": "64fb55b2c9048ed3b62158c01f126984d35c394230aeb176e562bac8a59317ce"
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
"id": "public-access",
|
|
85
85
|
"title": "无账号的匿名公开访问",
|
|
86
86
|
"file": "public-access.md",
|
|
87
|
-
"sha256": "
|
|
87
|
+
"sha256": "a93476aab6bc64b321974dfb130a31e8416e0dbfd953bfb51564dbf8667cd1c2"
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"id": "workflow-events",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"id": "backend",
|
|
97
97
|
"title": "按需后端与业务动作",
|
|
98
98
|
"file": "backend.md",
|
|
99
|
-
"sha256": "
|
|
99
|
+
"sha256": "f934afc1b7db45dca107de0088db5573005fcc7601b12f15c0d049a5c82feaae"
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
"id": "administration",
|
|
@@ -108,13 +108,13 @@
|
|
|
108
108
|
"id": "testing",
|
|
109
109
|
"title": "检查与真实业务验收",
|
|
110
110
|
"file": "testing.md",
|
|
111
|
-
"sha256": "
|
|
111
|
+
"sha256": "241f800e906213a3bc5a6dbd8931f9a6d55cb0d01d0e5f5be355e7bec6078f5f"
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
"id": "delivery",
|
|
115
115
|
"title": "部署、生产晋级与恢复",
|
|
116
116
|
"file": "delivery.md",
|
|
117
|
-
"sha256": "
|
|
117
|
+
"sha256": "32f07cfe6b946975bb3795e6ca3a01370e4d6bcc89f1b52b8df19085438e92c2"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
"id": "upgrading",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
OpenXiangda 2.0 支持没有平台账号的外部访客打开一个明确公开的用户页面,保存并续填草稿、
|
|
4
4
|
上传平台托管附件、执行具名重复校验、正式提交,并在同一浏览器中查看自己已提交的列表和详情。
|
|
5
|
+
应用也可以显式发布某个资源的部分记录字段,让外部浏览器分页查询公开记录或读取一条公开记录。
|
|
5
6
|
|
|
6
7
|
该能力识别的是“持有同一个平台 HttpOnly 浏览器凭证的访问者”,不是经过实名验证的自然人。
|
|
7
8
|
清除 Cookie、无痕模式、另一浏览器或另一设备都会成为新的匿名访问者,不能找回原草稿和记录。
|
|
@@ -21,7 +22,7 @@ User-Agent 或浏览器指纹猜测同一个人。
|
|
|
21
22
|
## 应用声明
|
|
22
23
|
|
|
23
24
|
资源仍然按普通 Native Resource 声明。公开能力只在一个静态 `surface: 'user'` 路由上增加一个
|
|
24
|
-
严格有界的 `frontend.publicAccess`
|
|
25
|
+
严格有界的 `frontend.publicAccess` 策略;同一路由可以按设备或资源声明多条策略,调用方必须带策略码:
|
|
25
26
|
|
|
26
27
|
```ts
|
|
27
28
|
export default defineOpenXiangdaApp({
|
|
@@ -104,9 +105,83 @@ export default defineOpenXiangdaApp({
|
|
|
104
105
|
| `create` | 以幂等键正式提交当前草稿 |
|
|
105
106
|
| `own.list` | 分页查看同一浏览器正式提交的记录 |
|
|
106
107
|
| `own.read` | 查看同一浏览器的一条正式提交详情 |
|
|
108
|
+
| `public.list` | 分页查看当前策略明确发布的资源记录 |
|
|
109
|
+
| `public.read` | 读取当前策略明确发布的一条资源记录 |
|
|
107
110
|
|
|
108
111
|
`own.list` 和 `own.read` 不是一般查询权限。服务端固定注入匿名主体、当前公开策略和已提交草稿
|
|
109
|
-
回执条件,不接受调用方的 where
|
|
112
|
+
回执条件,不接受调用方的 where、排序、投影或统计表达式。`public.list` 和 `public.read` 同样不是
|
|
113
|
+
一般查询权限:它们只读取策略绑定资源在当前租户、应用和环境下的记录,服务端固定按创建时间和 id
|
|
114
|
+
倒序分页,只返回 `publicRecordFields` 中显式列出的字段和记录 `id`。附件、图片和清洗后的富文本
|
|
115
|
+
通过平台托管文件路由公开,不返回对象存储地址。子表字段必须在 `publicSubtableFields` 中再次显式
|
|
116
|
+
列出子资源字段,服务端按声明顺序和行数上限返回一层子表数据。签名字段仍不公开。当前不支持调用方
|
|
117
|
+
筛选、排序、聚合或导出。
|
|
118
|
+
|
|
119
|
+
## `draft` 与公共读取
|
|
120
|
+
|
|
121
|
+
`draft` 是匿名提交的服务端事务载体,不是“外部访问”本身,也不是公共读取的前置条件。提交表单时,
|
|
122
|
+
平台需要先把不完整的输入保存到当前匿名浏览器的草稿,并用 `revision` 做并发控制;附件元数据绑定
|
|
123
|
+
到草稿;最终 `create` 会在同一数据库事务中锁定草稿、重新执行必填和重复校验、创建业务记录、标记
|
|
124
|
+
草稿已提交,并用幂等键保证不重复创建。因此声明 `create` 必须同时声明 `draft: { enabled: true }`,
|
|
125
|
+
而且同一策略的 `draft.read` 与 `draft.update` 必须成对出现。
|
|
126
|
+
|
|
127
|
+
公共只读场景不需要草稿。只声明 `public.list`/`public.read` 和 `publicRecordFields` 的策略可以直接
|
|
128
|
+
调用公共查询;它不会获得 `create`、`draft`、`own.*` 或普通 Native Data API 权限。不要为了查询已发布
|
|
129
|
+
数据创建一个“空草稿”,也不要把 `draft id` 传给浏览器。
|
|
130
|
+
|
|
131
|
+
例如,目录页面可以只发布明确选定的字段:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
{
|
|
135
|
+
code: 'catalog-public',
|
|
136
|
+
routeCode: 'catalog',
|
|
137
|
+
mode: 'anonymous',
|
|
138
|
+
resourceCode: 'catalog-items',
|
|
139
|
+
operations: ['public.list', 'public.read'],
|
|
140
|
+
fields: ['name', 'category', 'available', 'internalNote'],
|
|
141
|
+
publicRecordFields: ['name', 'category', 'available', 'items'],
|
|
142
|
+
publicSubtableFields: { items: ['sku', 'quantity'] },
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`publicRecordFields` 必须是 `fields` 和资源字段的子集;附件、图片和 `text.rich` 可公开,签名仍被
|
|
147
|
+
拒绝。公开子表字段必须配置 `publicSubtableFields`,其键是父资源的子表字段,值是子资源字段列表;
|
|
148
|
+
编译器会拒绝未声明字段、嵌套子表和缺少子字段投影。公共读取沿用明确公开的 `frontend.publicAccess`
|
|
149
|
+
路由和匿名浏览器凭证,不创建 guest 角色或虚拟内部用户。
|
|
150
|
+
|
|
151
|
+
需要隐藏停用、归档或租户标记记录时,使用固定 `publicFilters`,由服务端对每次 `public.list`/
|
|
152
|
+
`public.read` 强制追加。当前只支持最多 16 个不同字段的 `eq` 等值条件,字段必须属于策略的
|
|
153
|
+
`fields` 且仅允许布尔、文本、数值、日期和时间标量;调用方不能覆盖、追加或删除这些条件:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
publicFilters: [{ field: 'enabled', operator: 'eq', value: true }]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
匿名创建需要平台生成的不可预测字段时,使用 `serverGeneratedFields`。这些字段不属于
|
|
160
|
+
`fields`,调用方不能在草稿中写入;提交事务会由平台生成随机值并在提交回执的 `generated`
|
|
161
|
+
对象中返回。`random-token` 只适用于不承载身份信息的核验令牌等用途:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
serverGeneratedFields: [{ field: 'qrToken', kind: 'random-token' }]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
需要跨资源复核预约窗口等业务不变量时,可声明 `schedule`,绑定两个只读公开策略和资源字段。
|
|
168
|
+
平台会在最终创建事务中重新读取启用校区与规则,校验星期、日期范围、提前小时数和离散时段;页面端
|
|
169
|
+
校验只能改善体验,不能替代这次服务端复核。
|
|
170
|
+
|
|
171
|
+
子表中的文件引用会自动带上受控的 `resourceCode` 与父字段绑定;应用如需为附件生成下载地址,使用
|
|
172
|
+
`fileContentUrl(fileId, disposition, variant, resourceCode, parentFieldCode)`,不要自行拼接文件路径。
|
|
173
|
+
|
|
174
|
+
## 为什么不开放普通 Native Data API
|
|
175
|
+
|
|
176
|
+
普通 Native Data API 是内部或应用后端的可信数据边界,允许调用方提交
|
|
177
|
+
`select`、`where`、`order`、批量查询、聚合和导出,并按当前登录用户角色执行行列权限。匿名
|
|
178
|
+
公开发布的语义不同:它必须只绑定一个资源和不可变字段白名单,不接受调用方筛选、排序、聚合、
|
|
179
|
+
导出或自带角色,也必须把文件绑定到公开记录和公开字段后再读取。
|
|
180
|
+
|
|
181
|
+
直接把普通 Native Data API 暴露给浏览器会允许枚举内部资源和字段、通过筛选和计数推断未公开数据,
|
|
182
|
+
放大查询资源消耗,并增加文件 ID 猜测、审计字段泄露和权限合同混用的风险。因此公共端点继续是
|
|
183
|
+
专用的 `public.list`/`public.read`;底层可以复用 Native 的 RLS 和文件所有权校验,但不把 Native
|
|
184
|
+
Data API 的输入面开放给匿名调用方。
|
|
110
185
|
|
|
111
186
|
## 页面客户端
|
|
112
187
|
|
|
@@ -133,6 +208,10 @@ const receipt = await client.submit(withPhoto.revision, crypto.randomUUID());
|
|
|
133
208
|
|
|
134
209
|
const page = await client.listOwn({ pageSize: 20 });
|
|
135
210
|
const detail = await client.getOwn(receipt.recordId);
|
|
211
|
+
|
|
212
|
+
// 只读公开目录不需要先读取或保存 draft。
|
|
213
|
+
const publicPage = await client.listPublic({ pageSize: 20 });
|
|
214
|
+
const publicDetail = await client.getPublic(publicPage.items[0].data.id as string);
|
|
136
215
|
```
|
|
137
216
|
|
|
138
217
|
必须先 `bootstrap()`。草稿更新始终使用最近返回的 revision,冲突时重新读取,不能覆盖写。
|
package/documentation/testing.md
CHANGED
|
@@ -18,7 +18,7 @@ CI、离线开发或尚未发布的候选包使用 `pnpm openxiangda check --loc
|
|
|
18
18
|
|
|
19
19
|
检查会写本地生成结果并按需初始化后端,不是只读操作。前置阶段失败后,下游阶段标为 skipped,不继续构建或上传。保留错误码、pointer、details 和下一步,修正原因后重试。
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
每次检查都直接执行当前工作区声明的 check/test/build 脚本;构建工具和包管理器可以自行使用缓存。目标平台的权限、密钥和模型条件仍在正式检查和部署前预检。Devkit 不维护额外的工作区摘要凭据,也不会因为分支、提交或文档变化引入候选复用分支。
|
|
22
22
|
|
|
23
23
|
成功检查返回 `sealedArtifact.state: check-did-not-seal`、`sealed: false`、`usableForDeploy: false`。旧 AppPackage 不是当前检查结果。要发布测试环境可直接运行 deploy,它已包含完整检查;不要连续重复执行 check、test 和 build。
|
|
24
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"description": "OpenXiangda 2.0 的统一命令、应用 SDK、MCP 与中文 AI 技能资料。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"antd-mobile": "5.42.3",
|
|
61
61
|
"dayjs": "1.11.18",
|
|
62
62
|
"docx-preview": "0.3.7",
|
|
63
|
-
"openxiangda-cli": "2.4.
|
|
64
|
-
"openxiangda-contracts": "2.
|
|
65
|
-
"openxiangda-devkit-core": "2.
|
|
63
|
+
"openxiangda-cli": "2.4.4",
|
|
64
|
+
"openxiangda-contracts": "2.12.0",
|
|
65
|
+
"openxiangda-devkit-core": "2.11.0",
|
|
66
66
|
"openxiangda-legacy": "npm:openxiangda@1.0.269",
|
|
67
|
-
"openxiangda-mcp": "2.0.
|
|
68
|
-
"openxiangda-nest": "2.3.
|
|
69
|
-
"openxiangda-skill-kit": "2.1.
|
|
67
|
+
"openxiangda-mcp": "2.0.19",
|
|
68
|
+
"openxiangda-nest": "2.3.3",
|
|
69
|
+
"openxiangda-skill-kit": "2.1.3",
|
|
70
70
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
@@ -132,34 +132,43 @@
|
|
|
132
132
|
},
|
|
133
133
|
"openxiangdaRelease": {
|
|
134
134
|
"schemaVersion": "openxiangda.release-notes/v1",
|
|
135
|
-
"version": "2.
|
|
135
|
+
"version": "2.15.0",
|
|
136
136
|
"status": "reviewed",
|
|
137
|
-
"title": "OpenXiangda 2.
|
|
138
|
-
"summary": "
|
|
137
|
+
"title": "OpenXiangda 2.15.0:CRUD 复用 Data API 引导与 Nest 路由门禁",
|
|
138
|
+
"summary": "为普通 CRUD 补齐浏览器端 Data API 正面路径文档与 Nest 启用决策表,并在 check/dev 拒绝未绑定已声明 operation 的应用 controller 路由,使数据增删改查默认回到平台统一接口。",
|
|
139
139
|
"newFeatures": [
|
|
140
|
-
"
|
|
140
|
+
"开发资料新增「自定义页面消费平台数据」:createNativeResourceClient 的列表/详情/写入/删除/上传用法、expectedRevision 乐观锁、transactNativeData 幂等事务与 batchAggregateNativeResources 服务端聚合。",
|
|
141
|
+
"需求与开发流程新增「判定是否真的需要 Nest 后端」决策表,工作区 AGENTS 模板与按需后端资料同步指向;只有真实外部副作用或无法声明的跨资源不变量才启用 Nest。",
|
|
142
|
+
"devkit check/dev 新增应用 controller operation 门禁:未以 `@OpenXiangdaOperation(appOperations.具名操作)` 绑定已声明 operation 的路由返回 `OPENXIANGDA_NEST_CONTROLLER_OPERATION_REQUIRED`,手写契约字面量返回 `OPENXIANGDA_NEST_OPERATION_CONTRACT_MUST_BE_DECLARED`。"
|
|
141
143
|
],
|
|
142
144
|
"fixes": [
|
|
143
|
-
"
|
|
145
|
+
"修复交付流程简化后 CLI 黑盒仍期望已删除的 DELIVERY_GIT_COMMIT_REQUIRED 的问题;黑盒改为断言 AppSpec 章节门禁,verify:affected 恢复可用。"
|
|
144
146
|
],
|
|
145
147
|
"affectedUsers": [
|
|
146
|
-
"
|
|
148
|
+
"所有 OpenXiangda 2.0 应用开发者和 AI 辅助开发会话;新指引让列表、表单、详情、删除优先复用平台 Data API 与事务守卫。",
|
|
149
|
+
"维护含自写 Nest controller 的既有应用:升级后 check 会要求绑定已声明 operation 或移除该路由。"
|
|
147
150
|
],
|
|
148
151
|
"upgradeSteps": [
|
|
149
|
-
"
|
|
150
|
-
"
|
|
152
|
+
"将应用精确依赖升级到 openxiangda 2.15.0,刷新项目 Skill 与工作区指引后运行 check。",
|
|
153
|
+
"对报 `OPENXIANGDA_NEST_CONTROLLER_OPERATION_REQUIRED` 的路由:普通 CRUD 改用 createNativeResourceClient 或标准 CRUD 页面;真实业务动作先在 openxiangda.config.ts 声明 operation(capability kind: 'backend'),再绑定 `@OpenXiangdaOperation(appOperations.具名操作)`。",
|
|
154
|
+
"按 development#backend-decision 决策表复查既有后端使用面,完成 check、test、build 后按常规测试发布流程验证。"
|
|
151
155
|
],
|
|
152
156
|
"knownLimitations": [
|
|
153
|
-
"
|
|
157
|
+
"门禁是静态判定:已声明 operation 但方法体仍只转发普通 CRUD 的 controller 不会被拒绝;该层由设计评审与后续 AppSpec 能力复用清单兜底。",
|
|
158
|
+
"类级别 @OpenXiangdaOperation 绑定不被接受,需要逐路由方法绑定同一生成契约。",
|
|
159
|
+
"文档与门禁不替代目标环境的真实角色、浏览器和性能验收。"
|
|
154
160
|
],
|
|
155
161
|
"compatibility": {
|
|
156
162
|
"node": ">=24",
|
|
157
|
-
"
|
|
158
|
-
|
|
163
|
+
"workspaceGenerations": [
|
|
164
|
+
"v2"
|
|
165
|
+
],
|
|
166
|
+
"platform": "不要求平台服务端变更;已有应用升级后 check 行为变化见升级步骤。",
|
|
167
|
+
"v1": "不改变 V1 运行时或应用。"
|
|
159
168
|
},
|
|
160
169
|
"issues": [],
|
|
161
|
-
"sha256": "
|
|
162
|
-
"url": "https://github.com/1377385356/openxiangda/releases/tag/v2.
|
|
170
|
+
"sha256": "45fe1d3ad3b0a04d8ec4b3f657a201dee14823576fe8b48a369a92f7d0c9e029",
|
|
171
|
+
"url": "https://github.com/1377385356/openxiangda/releases/tag/v2.15.0"
|
|
163
172
|
},
|
|
164
173
|
"scripts": {
|
|
165
174
|
"build": "node ../../scripts/prune-package-dist.mjs && tsc -p tsconfig.json && node scripts/copy-assets.mjs",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "openxiangda.release-notes/v1",
|
|
3
|
+
"version": "2.14.0",
|
|
4
|
+
"status": "reviewed",
|
|
5
|
+
"title": "OpenXiangda 2.14.0:匿名公开预约契约闭环",
|
|
6
|
+
"summary": "为匿名公开数据读取和提交补齐固定过滤、服务端生成字段、跨资源预约规则校验与同路由多策略支持,并统一客户端、编译器和平台契约。",
|
|
7
|
+
"newFeatures": [
|
|
8
|
+
"匿名公开策略支持固定服务端过滤、服务端随机令牌生成和跨资源 schedule 校验;提交事务返回 generated 字段。",
|
|
9
|
+
"同一用户路由可以按设备或资源声明多条匿名策略,浏览器客户端以策略码隔离 bootstrap 缓存并显式协商客户端契约版本。",
|
|
10
|
+
"匿名公开客户端支持公开列表/详情读取、草稿提交回执和受控的文件、富文本及子表字段投影。"
|
|
11
|
+
],
|
|
12
|
+
"fixes": [
|
|
13
|
+
"修复匿名公开策略把合法驼峰字段名(例如 visitDate、openAt、qrToken)按全小写稳定 code 错误拒绝的问题;JSON Schema、Devkit 和原生编译器现在使用统一字段 code 规则。",
|
|
14
|
+
"平台服务端强制应用公开过滤,匿名草稿过期时清理关联文件,并在缺少客户端版本声明时收窄 source hosting 与 OAuth 能力。"
|
|
15
|
+
],
|
|
16
|
+
"affectedUsers": [
|
|
17
|
+
"需要在没有平台账号的情况下读取公开数据或提交草稿的 OpenXiangda 2.0 应用开发者。",
|
|
18
|
+
"使用匿名预约、服务端生成核验令牌或跨资源提交校验的应用,以及部署匹配平台服务端的运维人员。"
|
|
19
|
+
],
|
|
20
|
+
"upgradeSteps": [
|
|
21
|
+
"先部署包含匿名公开固定过滤、生成字段和 schedule 校验的匹配平台组合,再将应用精确依赖升级到 openxiangda 2.14.0。",
|
|
22
|
+
"刷新项目 Skill 和工作区指引,重新生成契约并运行项目 check、test、build;匿名策略中的字段引用可继续使用合法驼峰命名。",
|
|
23
|
+
"在测试环境验证跨浏览器草稿隔离、幂等提交、停用前置资源拒绝和管理员核验,再使用同一测试版本晋级生产。"
|
|
24
|
+
],
|
|
25
|
+
"knownLimitations": [
|
|
26
|
+
"schedule 首版只表达有界的预约窗口复核,不提供容量配额、取消改期、通知或审批流程;这些能力需要单独的应用变更评审。",
|
|
27
|
+
"匿名公开接口仍只接受策略声明的固定过滤和字段投影,不能由调用方动态追加筛选、排序或投影。",
|
|
28
|
+
"发行包和平台部署检查不能替代目标环境的真实角色、浏览器和性能验收。"
|
|
29
|
+
],
|
|
30
|
+
"compatibility": {
|
|
31
|
+
"node": ">=24",
|
|
32
|
+
"workspaceGenerations": [
|
|
33
|
+
"v2"
|
|
34
|
+
],
|
|
35
|
+
"platform": "需要匹配 OpenXiangda 2.0 匿名公开策略、固定过滤、服务端生成字段和 schedule 校验的服务端契约;V1 引擎保持独立。",
|
|
36
|
+
"v1": "不改变 V1 运行时或应用。"
|
|
37
|
+
},
|
|
38
|
+
"issues": [],
|
|
39
|
+
"sha256": "5b2dfbcf66a0e14d8619914b1d19123f84d35640978ffac4a6c2bef3e8deef8a",
|
|
40
|
+
"url": "https://github.com/1377385356/openxiangda/releases/tag/v2.14.0"
|
|
41
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "openxiangda.release-notes/v1",
|
|
3
|
+
"version": "2.15.0",
|
|
4
|
+
"status": "reviewed",
|
|
5
|
+
"title": "OpenXiangda 2.15.0:CRUD 复用 Data API 引导与 Nest 路由门禁",
|
|
6
|
+
"summary": "为普通 CRUD 补齐浏览器端 Data API 正面路径文档与 Nest 启用决策表,并在 check/dev 拒绝未绑定已声明 operation 的应用 controller 路由,使数据增删改查默认回到平台统一接口。",
|
|
7
|
+
"newFeatures": [
|
|
8
|
+
"开发资料新增「自定义页面消费平台数据」:createNativeResourceClient 的列表/详情/写入/删除/上传用法、expectedRevision 乐观锁、transactNativeData 幂等事务与 batchAggregateNativeResources 服务端聚合。",
|
|
9
|
+
"需求与开发流程新增「判定是否真的需要 Nest 后端」决策表,工作区 AGENTS 模板与按需后端资料同步指向;只有真实外部副作用或无法声明的跨资源不变量才启用 Nest。",
|
|
10
|
+
"devkit check/dev 新增应用 controller operation 门禁:未以 `@OpenXiangdaOperation(appOperations.具名操作)` 绑定已声明 operation 的路由返回 `OPENXIANGDA_NEST_CONTROLLER_OPERATION_REQUIRED`,手写契约字面量返回 `OPENXIANGDA_NEST_OPERATION_CONTRACT_MUST_BE_DECLARED`。"
|
|
11
|
+
],
|
|
12
|
+
"fixes": [
|
|
13
|
+
"修复交付流程简化后 CLI 黑盒仍期望已删除的 DELIVERY_GIT_COMMIT_REQUIRED 的问题;黑盒改为断言 AppSpec 章节门禁,verify:affected 恢复可用。"
|
|
14
|
+
],
|
|
15
|
+
"affectedUsers": [
|
|
16
|
+
"所有 OpenXiangda 2.0 应用开发者和 AI 辅助开发会话;新指引让列表、表单、详情、删除优先复用平台 Data API 与事务守卫。",
|
|
17
|
+
"维护含自写 Nest controller 的既有应用:升级后 check 会要求绑定已声明 operation 或移除该路由。"
|
|
18
|
+
],
|
|
19
|
+
"upgradeSteps": [
|
|
20
|
+
"将应用精确依赖升级到 openxiangda 2.15.0,刷新项目 Skill 与工作区指引后运行 check。",
|
|
21
|
+
"对报 `OPENXIANGDA_NEST_CONTROLLER_OPERATION_REQUIRED` 的路由:普通 CRUD 改用 createNativeResourceClient 或标准 CRUD 页面;真实业务动作先在 openxiangda.config.ts 声明 operation(capability kind: 'backend'),再绑定 `@OpenXiangdaOperation(appOperations.具名操作)`。",
|
|
22
|
+
"按 development#backend-decision 决策表复查既有后端使用面,完成 check、test、build 后按常规测试发布流程验证。"
|
|
23
|
+
],
|
|
24
|
+
"knownLimitations": [
|
|
25
|
+
"门禁是静态判定:已声明 operation 但方法体仍只转发普通 CRUD 的 controller 不会被拒绝;该层由设计评审与后续 AppSpec 能力复用清单兜底。",
|
|
26
|
+
"类级别 @OpenXiangdaOperation 绑定不被接受,需要逐路由方法绑定同一生成契约。",
|
|
27
|
+
"文档与门禁不替代目标环境的真实角色、浏览器和性能验收。"
|
|
28
|
+
],
|
|
29
|
+
"compatibility": {
|
|
30
|
+
"node": ">=24",
|
|
31
|
+
"workspaceGenerations": [
|
|
32
|
+
"v2"
|
|
33
|
+
],
|
|
34
|
+
"platform": "不要求平台服务端变更;已有应用升级后 check 行为变化见升级步骤。",
|
|
35
|
+
"v1": "不改变 V1 运行时或应用。"
|
|
36
|
+
},
|
|
37
|
+
"issues": [],
|
|
38
|
+
"sha256": "45fe1d3ad3b0a04d8ec4b3f657a201dee14823576fe8b48a369a92f7d0c9e029",
|
|
39
|
+
"url": "https://github.com/1377385356/openxiangda/releases/tag/v2.15.0"
|
|
40
|
+
}
|
package/skills/manifest.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
"name": "openxiangda-v2",
|
|
6
6
|
"description": "使用 OpenXiangda 2.0 从模糊业务想法、已有资料或具体变更出发,通过对话发现模块、完成详细产品设计,由 AI 在工作区内调用 OpenDesign 原版 CLI/Skill/MCP 形成整体视觉与可运行原型,再开发、检查和交付应用。OpenDesign 客户端只作为可选预览器;维护 1.x 应用时使用对应的 1.x 技能。",
|
|
7
|
-
"sha256": "
|
|
7
|
+
"sha256": "37ddcd0b0d5bb157e76bac263609f910ed97e589ad6cbfebe15ca37c1874ecb0"
|
|
8
8
|
}
|
|
9
9
|
]
|
|
10
10
|
}
|
|
@@ -40,10 +40,10 @@ AI 接到新应用、页面或改版任务时,在同一个 OpenXiangda 工作
|
|
|
40
40
|
未创建工作区时使用本 Skill 随根包发布的精确版本:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
pnpm dlx openxiangda@2.
|
|
44
|
-
pnpm dlx openxiangda@2.
|
|
45
|
-
pnpm dlx openxiangda@2.
|
|
46
|
-
pnpm dlx openxiangda@2.
|
|
43
|
+
pnpm dlx openxiangda@2.15.0 auth status --cwd <应用目录> --base-url <平台地址> --json
|
|
44
|
+
pnpm dlx openxiangda@2.15.0 login --cwd <应用目录> --base-url <平台地址>
|
|
45
|
+
pnpm dlx openxiangda@2.15.0 create <应用目录> --base-url <同一平台地址>
|
|
46
|
+
pnpm dlx openxiangda@2.15.0 skill install --force
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
创建前把产品要求的目标平台明确带入命令,不从旧登录态推断站点。已有工作区从原绑定恢复,平台不一致时先解决登录与目标,不改 link 文件跨站创建。
|
|
@@ -99,7 +99,7 @@ pnpm exec openxiangda --mcp-stdio --cwd <workspace>
|
|
|
99
99
|
|
|
100
100
|
平台拥有身份、授权、业务数据、环境和部署状态。应用只声明自己的模型、页面和规则;普通 CRUD 走 Data API,标准审批和通知按需声明,真实业务动作才启用 Nest。编译器生成契约,应用不改生成输出、不维护第二份权限或能力目录。菜单建议只供初次复制到应用声明,不是运行时自动发现。
|
|
101
101
|
|
|
102
|
-
匿名访问使用 frontend.publicAccess、createAnonymousPublicClient 与平台浏览器凭证,不建立 guest 角色或公开普通 Data API
|
|
102
|
+
匿名访问使用 frontend.publicAccess、createAnonymousPublicClient 与平台浏览器凭证,不建立 guest 角色或公开普通 Data API。提交策略的 `create` 必须配套 `draft`;只读外部数据使用 `public.list`/`public.read` 和显式 `publicRecordFields`,附件/图片/清洗后的富文本走平台代理 URL,子表用 `publicSubtableFields` 显式投影,不需要 draft,且不接受任意筛选、排序或投影。角色并集来自当前用户,Perspective 只收窄读取。
|
|
103
103
|
|
|
104
104
|
## 完成与失败
|
|
105
105
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# NestJS 后端
|
|
2
2
|
|
|
3
|
-
默认模板只包含 Web 和共享契约。只有需要执行服务端业务动作时才增加 NestJS。记录列表、详情、新增、编辑和删除直接由浏览器调用平台 Data API,不在 controller
|
|
3
|
+
默认模板只包含 Web 和共享契约。只有需要执行服务端业务动作时才增加 NestJS。记录列表、详情、新增、编辑和删除直接由浏览器调用平台 Data API,不在 controller 中重写一遍。启用前先按[判定是否真的需要 Nest 后端](development.md#backend-decision)逐行核对:幂等、时间窗、状态前置、角色核对、聚合、导入导出都有声明式答案;只有真实外部副作用或无法声明的跨资源不变量才是启用理由。`check` 会拒绝未以 `@OpenXiangdaOperation(appOperations.<code>)` 绑定已声明 operation 的应用路由。
|
|
4
4
|
|
|
5
5
|
平台网关验证当前用户完整的应用角色并集,并把经平台重验的角色、capability 与可选 Perspective 交给 Nest SDK。业务 controller 使用生成的 operation 合同和 capability 装饰器;平台仍是身份与授权的唯一所有者。请求作用域 `OpenXiangdaDataApiService` 自动继承 Perspective 读取投影;绕过 Data API 的自定义读取才使用 `@CurrentPerspective()` 显式投影。应用代码不替换身份、不保存平台凭据,也不建立第二套用户或权限状态。
|
|
6
6
|
|
|
@@ -41,7 +41,21 @@ impersonation token。mutation 必须携带 UUID `operationId`、`reason`,更
|
|
|
41
41
|
|
|
42
42
|
匿名外部访问不属于 RBAC 角色或 current-user 行策略。公开表单、续填、附件、重复校验和同一
|
|
43
43
|
浏览器的本人记录访问只通过[`frontend.publicAccess` 专用合同](public-access.md)开放;平台继续在
|
|
44
|
-
专用端点和 PostgreSQL/RLS
|
|
44
|
+
专用端点和 PostgreSQL/RLS 中强制匿名主体、字段、策略及提交回执边界。需要向外部发布目录、公告或
|
|
45
|
+
可用性列表时,使用同一合同的 `public.list`/`public.read` 与 `publicRecordFields`,明确绑定资源和
|
|
46
|
+
字段;`file`、`image` 和清洗后的 `text.rich` 可以公开,返回的托管文件引用只能通过匿名文件内容路由
|
|
47
|
+
读取,不暴露对象存储地址。子表字段必须在 `publicSubtableFields` 中再次选择子资源字段,例如:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
publicRecordFields: ['name', 'cover', 'description', 'items'],
|
|
51
|
+
publicSubtableFields: { items: ['sku', 'quantity'] },
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
子表只支持一层、固定子字段和有界行数;嵌套子表、签名字段、未声明字段都会在编译或运行时拒绝。
|
|
55
|
+
公共读取不需要 `draft`,不继承角色权限,也不开放普通 Native Data API、where、排序、聚合或导出参数。
|
|
56
|
+
普通 Native Data API 的 `select`、`where`、批量、聚合和导出输入只适用于已认证用户或应用后端;把它暴露给匿名
|
|
57
|
+
浏览器会产生资源/字段枚举、条件推断、查询放大和文件 ID 猜测面。公共端点可以复用 Native RLS 和文件
|
|
58
|
+
绑定校验,但必须保留固定资源、固定字段、固定排序和固定分页的较小输入面。
|
|
45
59
|
|
|
46
60
|
数值边界直接声明在字段上,`min`/`max` 为闭区间,并且只允许用于
|
|
47
61
|
`number.integer` 和 `number.decimal`。跨字段约束声明在资源的
|
|
@@ -4,13 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
## 测试部署
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
本地构建仍是当前交付方式;源码提交存在不等于平台已独立验证制品由该提交构建。
|
|
7
|
+
源码托管仍可通过 `pnpm openxiangda source push` 提交和推送,但不是测试发布的前置步骤。
|
|
8
|
+
发布使用当前工作区内容,源码提交、分支名称和是否存在未提交修改不会阻塞测试环境;包元数据会保留实际来源信息。
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
开发开始时先同步主线并读取项目现状,开发完成包括提交、推送与主线整合。每个工作区保持一个写者;需要并行时使用独立目录并明确各任务范围。日常 dev/check 仍可验证未提交源码。没有 Git 远端的项目应先建立并绑定仓库再发布。
|
|
10
|
+
开发者可以从当前任务分支或本地工作区直接发布。团队是否要求合入默认分支属于协作规范,不由 Devkit 在每次发布时重复执行。
|
|
14
11
|
|
|
15
12
|
准备部署时直接执行 deploy,它已经包含兼容性预检、生成、检查、测试和构建。只想检查代码时使用 [check](testing.md),无需在 deploy 前重复运行全套检查。
|
|
16
13
|
|
|
@@ -37,7 +34,7 @@ pnpm openxiangda status <deployment-id> --watch
|
|
|
37
34
|
|
|
38
35
|
网络响应不确定时先查询原运行,不凭本地输出创建重复部署。平台部署成功后,仍需执行真实角色的业务验收。
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
重复发布直接使用当前源码重新构建。pnpm、Docker 和平台可以自行提供构建缓存,Devkit 不维护另一套工作区摘要或候选状态。提交响应不确定时使用原 DeploymentRun 的 status/logs 和 recovery 继续处理。
|
|
41
38
|
|
|
42
39
|
## 测试环境验收
|
|
43
40
|
|
|
@@ -27,6 +27,27 @@
|
|
|
27
27
|
- 标准审批、待办与通知:按需声明平台能力,见[工作流](workflow-events.md)。
|
|
28
28
|
- 真实事务或外部集成:使用[按需后端](backend.md),不为每张表重写 CRUD 控制器。
|
|
29
29
|
|
|
30
|
+
### 判定是否真的需要 Nest 后端 {#backend-decision}
|
|
31
|
+
|
|
32
|
+
想写后端接口时,先按顺序核对平台已有的声明式答案;命中前几行的需求不得启用 Nest。
|
|
33
|
+
普通数据增删改查永远由浏览器直接调用平台 Data API 或标准 CRUD 页面完成,
|
|
34
|
+
应用 controller 不做记录列表、详情、新增、编辑、删除的转发。
|
|
35
|
+
|
|
36
|
+
| 你以为需要写后端 | 平台已有的声明式答案 | 参考 |
|
|
37
|
+
| --- | --- | --- |
|
|
38
|
+
| 列表、筛选、排序、分页接口 | `createNativeResourceClient` 的 `list`,服务端条件树与分页 | [前端数据访问](frontend.md#data-access) |
|
|
39
|
+
| 新增 / 编辑 / 删除接口 | 标准 CRUD 页面,或同一客户端的 `create` / `update` / `remove`(`expectedRevision` 乐观锁) | [前端数据访问](frontend.md#data-access) |
|
|
40
|
+
| 提交防重、幂等重试 | `transactNativeData` / 事务请求自带 `idempotencyKey` 幂等回执 | [前端数据访问](frontend.md#data-access)、[按需后端](backend.md#business-action) |
|
|
41
|
+
| 时间窗、状态前置、指定人角色校验 | 平台事务守卫:`operation-time`、`record-assert`、`record-exists`、`role-member` | [按需后端](backend.md#business-action) |
|
|
42
|
+
| 统计报表数据 | Data API 服务端聚合 `batchAggregateNativeResources`(单个指标也用它),前端不拉全量求和 | [前端](frontend.md#component-selection) |
|
|
43
|
+
| 导入 / 导出 | 标准 CRUD 的 `import` / `export` 动作声明 | [业务模块](application-foundation.md) |
|
|
44
|
+
| 跨模型原子写、外部 API、硬件或第三方推送 | Nest 具名 operation + 平台事务,必要时事务内 `emitEvent` | [按需后端](backend.md) |
|
|
45
|
+
|
|
46
|
+
启用 Nest 的唯一充分条件是:真实外部副作用,或现有守卫无法声明的跨资源业务不变量,
|
|
47
|
+
且该动作已作为 operation 声明能力(`kind: 'backend'`)并由角色显式引用。
|
|
48
|
+
"需要一点校验""需要默认值""需要联动查询"不是启用理由;校验优先字段规则与事务守卫,
|
|
49
|
+
默认值优先服务端字段责任,联动查询优先 Data API 条件树。
|
|
50
|
+
|
|
30
51
|
## 实施与交接 {#iteration}
|
|
31
52
|
|
|
32
53
|
开发使用 `pnpm openxiangda dev`,过程中运行必要的聚焦测试。交接前按[检查与验收](testing.md)验证;授权发布后按[交付](delivery.md)部署。失败保留错误码、位置和原始候选,依据平台恢复指令继续。
|
|
@@ -63,6 +63,43 @@ capability、应用角色和数据策略。前端只根据当前登录用户完
|
|
|
63
63
|
可替换 Data API adapter。不要调用自定义 Nest CRUD、Function 或 Workflow 来绕过
|
|
64
64
|
Data API。只有真正需要事务或外部系统的动作才使用同源 `/api`。
|
|
65
65
|
|
|
66
|
+
### 自定义页面消费平台数据 {#data-access}
|
|
67
|
+
|
|
68
|
+
自定义报表、工具页和用户端页面从 `openxiangda/core` 导入 `createNativeResourceClient`,
|
|
69
|
+
用生成契约里的 surface 直接获得该资源的权威读写客户端;行、字段与操作授权由平台在
|
|
70
|
+
每次请求时执行,页面不需要也不得复制权限逻辑:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { createNativeResourceClient } from 'openxiangda/core';
|
|
74
|
+
import { resourceSurfaces } from '@app/contracts';
|
|
75
|
+
|
|
76
|
+
const records = createNativeResourceClient('records', resourceSurfaces.records);
|
|
77
|
+
|
|
78
|
+
// 服务端过滤、排序、分页;字段必须已声明,未声明字段直接报错
|
|
79
|
+
const page = await records.list({
|
|
80
|
+
page: 1,
|
|
81
|
+
pageSize: 20,
|
|
82
|
+
where: { field: 'enabled', operator: 'eq', value: true },
|
|
83
|
+
sort: { field: 'createdAt', order: 'desc' },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const record = await records.get(id);
|
|
87
|
+
await records.create(data);
|
|
88
|
+
await records.update(id, expectedRevision, data); // revision 冲突时明确报错
|
|
89
|
+
await records.remove(id, expectedRevision);
|
|
90
|
+
await records.upload('attachment', file, recordId);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
跨模型原子写使用 `transactNativeData(operations, idempotencyKey)`:一次平台事务提交
|
|
94
|
+
多个资源操作,携带幂等键,不确定的响应复用同一载荷重试,不产生第二份业务效果。
|
|
95
|
+
多指标统计使用 `batchAggregateNativeResources` 在服务端聚合。导出使用
|
|
96
|
+
`records.exportCsv(query, select)`,与列表共用同一查询条件。
|
|
97
|
+
|
|
98
|
+
要求服务端校验、状态前置或角色核对时,优先事务守卫(见
|
|
99
|
+
[判定是否真的需要 Nest 后端](development.md#backend-decision));只有在真实外部
|
|
100
|
+
副作用下才声明 Nest operation。自写 controller 转发单一资源的增删改查无法通过 `check`:
|
|
101
|
+
每个应用路由必须以 `@OpenXiangdaOperation(appOperations.<code>)` 绑定已声明的 operation。
|
|
102
|
+
|
|
66
103
|
默认仪器模块有 30 个字段,其中 `id/revision` 是 Data API 系统字段,28 个业务
|
|
67
104
|
字段由资源声明。新增、编辑和详情共用同一份字段元数据。五个边界字段使用五个独立
|
|
68
105
|
capability,不使用角色名或影子字段判断。
|