openxiangda-skill-kit 2.1.11 → 2.2.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/package.json +2 -2
- package/skills/openxiangda-v2/references/application-foundation.md +7 -1
- package/skills/openxiangda-v2/references/backend.md +28 -0
- package/skills/openxiangda-v2/references/declarations-cheatsheet.md +8 -1
- package/skills/openxiangda-v2/references/delivery.md +1 -1
- package/skills/openxiangda-v2/references/frontend.md +41 -2
- package/skills/openxiangda-v2/references/workflow-events.md +15 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda-skill-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
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.
|
|
20
|
+
"openxiangda-devkit-core": "2.17.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"tsx": "4.23.12",
|
|
@@ -194,7 +194,13 @@ crud: [{
|
|
|
194
194
|
}]
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
排序字段可以是已声明字段,也可以是平台审计列(`created_at` / `updated_at` /
|
|
198
|
+
`created_by` / `updated_by` / `id` / `revision`)——它们由平台维护、所有记录必有值,
|
|
199
|
+
不需要也无法在模型 `fields` 中声明。"按真实创建时间倒序"直接写
|
|
200
|
+
`defaultSort: { field: 'created_at', order: 'desc' }`,标准 Admin 列表的内置
|
|
201
|
+
"创建时间/更新时间"列同样可点击排序;不要为排序复制一份业务时间字段。
|
|
202
|
+
引用其他未声明字段仍按编译错误处理;低层 `data.resources[].fields[].sortable`
|
|
203
|
+
语义不变。`filterFields` / `searchableFields` 仍只接受已声明字段。
|
|
198
204
|
|
|
199
205
|
## 资源详情路由声明 {#detail-route-code}
|
|
200
206
|
|
|
@@ -109,6 +109,34 @@ try {
|
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
事务守卫的 `errorCode` 必须匹配 `^OPENXIANGDA_[A-Z0-9_]{1,96}$`,例如 `OPENXIANGDA_REPAIR_REQUEST_NOT_PENDING`。
|
|
112
|
+
|
|
113
|
+
## 在同一事务中引用前序 create 生成的 id {#transaction-references}
|
|
114
|
+
|
|
115
|
+
一个事务内"先建主记录、再建引用它的子记录"不需要预先分配 id,也不需要两段式
|
|
116
|
+
暂存。后续 create/update 的 `data` 字段值可以直接写
|
|
117
|
+
`{ operationIndex, field: 'id' }` 引用本事务中**之前的 create** 生成的主键,
|
|
118
|
+
平台在提交前解析替换:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
await businessData.transaction({
|
|
122
|
+
schemaVersion: 'openxiangda.data-transaction-request/v2',
|
|
123
|
+
idempotencyKey: input.idempotencyKey,
|
|
124
|
+
operations: [
|
|
125
|
+
{ operation: 'create', resourceCode: 'clubs', data: { name: input.name } },
|
|
126
|
+
{ operation: 'create', resourceCode: 'club-memberships',
|
|
127
|
+
data: { clubId: { operationIndex: 0, field: 'id' }, userId: input.ownerId, role: 'owner' } },
|
|
128
|
+
{ operation: 'create', resourceCode: 'club-memberships',
|
|
129
|
+
data: { clubId: { operationIndex: 0, field: 'id' }, userId: input.memberId, role: 'member' } },
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
引用约束:引用对象只含 `operationIndex`(0..99 的整数)与 `field: 'id'` 两个键;
|
|
135
|
+
只能指向索引更小的 create 操作;引用不得嵌套在数组或对象里。违反形状报
|
|
136
|
+
`OPENXIANGDA_NATIVE_DATA_TRANSACTION_REFERENCE_INVALID`,嵌套报
|
|
137
|
+
`..._NESTED`,目标不可解析报 `..._UNRESOLVED`。任一操作失败时整个事务回滚,
|
|
138
|
+
不会留下无成员的 club。
|
|
139
|
+
|
|
112
140
|
## 业务动作与普通查询 {#business-action}
|
|
113
141
|
|
|
114
142
|
`OpenXiangdaDataApiService` 按当前用户的普通资源、行和字段权限执行。具名业务动作使用 `OpenXiangdaBusinessDataApiService`:入口先检查该动作 capability,平台在精确应用和环境内以受信任后端执行,并保留发起人与动作审计。业务动作不能接受任意模型/字段/用户 ID 后不做业务校验;应用负责该动作的输入约束和业务不变量。
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
| Workflow 详情接管的资源路由用模型级 `detailRouteCode` 表达(desktop/mobile 各引用一条 user surface 路由) | `defineDataModel({ code: 'x', detailRouteCode: { desktop: 'x-detail', mobile: 'x-detail-mobile' }, ... })` |
|
|
15
15
|
| 迁移工具/验收脚本需要看模块投影结果时,用公共出口的 `materializeApplicationModules`,不要引用 devkit 的 dist 文件路径 | `import { defineApplicationModule, materializeApplicationModules } from 'openxiangda/config';` → `const { resources } = materializeApplicationModules([module])` |
|
|
16
16
|
| system 字段(服务端赋值)可以进入查询与分组类选择(filterFields/searchableFields/sortableFields/defaultSort/sections.fields),不可进入展示与可写选择(list/form/detail.fields) | `filterFields: ['campaignId']`(system 外键筛选合法);hidden 字段任何选择都拒绝 |
|
|
17
|
+
| 平台审计列(created_at/updated_at/created_by/updated_by/id/revision)可直接作 `defaultSort`/`sortableFields`,无需声明;不可进入 filterFields/searchableFields/展示/可写选择 | `defaultSort: { field: 'created_at', order: 'desc' }`(按真实创建时间倒序,勿复制业务时间字段) |
|
|
17
18
|
|
|
18
19
|
## 字段声明
|
|
19
20
|
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
| `audit.read` 可写 `true`(绑定本资源读能力)或能力数组 | `audit: { read: true }` |
|
|
26
27
|
| `resource-ref.*` 必须带 `source` 来源协议 | `{ type: 'resource-ref.single', source: { kind: 'resource', resourceCode: 'repair-requests', labelField: 'title', searchFields: ['title'], pageSize: 20, loadMode: 'search' } }` |
|
|
27
28
|
| `labelField` 必须指向目标资源的 `text.short` / `text.long` 字段 | 不要用流水号/选项字段当 label |
|
|
28
|
-
| 列表可排序列用视图级 `sortableFields` 表达(`defaultSort.field`
|
|
29
|
+
| 列表可排序列用视图级 `sortableFields` 表达(`defaultSort.field` 隐式可排序);平台审计列(如 `created_at`)同样合法 | `list: { sortableFields: ['capacity'], defaultSort: { field: 'name', order: 'asc' } }`;`defaultSort: { field: 'created_at', order: 'desc' }` |
|
|
29
30
|
| 每个字段都必须带中文/业务 `label`(含子表外键与排序字段) | `{ code: 'requestId', type: 'uuid', label: '所属申请', required: true }` |
|
|
30
31
|
| 子表 `subtable` 的外键是子资源的 **uuid** 字段,排序字段是**可写 number.integer** | 子资源:`{ code: 'requestId', type: 'uuid', required: true }` + `{ code: 'sortOrder', type: 'number.integer', required: true }`;父表:`subtable: { resourceCode: 'repair-items', foreignKey: 'requestId', orderField: 'sortOrder', maxRows: 20 }` |
|
|
31
32
|
| 图片/附件的 `file` 限定数量与大小 | `file: { maxCount: 3, maxSizeMb: 10, accept: ['image/png', 'image/jpeg'] }` |
|
|
@@ -34,6 +35,12 @@
|
|
|
34
35
|
|
|
35
36
|
| 规则 | 正确片段 |
|
|
36
37
|
| --- | --- |
|
|
38
|
+
| 数据策略是**白名单**语义:规则 `roleCodes` 之外的角色若不在 `unrestrictedRoleCodes` 中会被 RLS 全拒(报错只有 FIELD_ROW_FORBIDDEN) | `unrestrictedRoleCodes: ['admin']` 必须列出所有"不受限"角色 |
|
|
39
|
+
| 基线角色(`authenticatedUserRoleCode`)进 `unrestrictedRoleCodes` = 策略对所有人失效(角色并集必含基线角色),编译器直接报错 | 把基线角色移出 unrestrictedRoleCodes,为其单独声明 rules |
|
|
40
|
+
| 匿名公开策略的 `ownRecordFields` 必须是 `fields` 的子集;`create` 必须配套 `draft`;`requiredFields` ⊆ `fields` | 先定 fields,再从中选 required/own |
|
|
41
|
+
| workflow definition 必须显式 `launch`(编译器强制) | `definitions: [{ version: 1, definition, launch: { mode: 'standalone' } }]` |
|
|
42
|
+
| option/user/department/resource-ref/cascade 字段投影进工作流事实是 { label, value } 对象,不能声明为标量;条件比较用 `<fact>.value` | `inputSchema.properties.urgency = { type: 'object', ... }` + `path: 'urgency.value'` |
|
|
43
|
+
| `cascade.*` 的写入/比较值形状是**数组路径** | `category: [{ label: '办公设备', value: 'office' }]` |
|
|
37
44
|
| 平台保留能力(如 `app:<app>:directory:read`)**不能**在 `capabilities` 里重复声明,直接在角色中引用即可 | `const directoryRead = \`app:\${APP_CODE}:directory:read\`` → `roles: [{ code: 'admin', capabilities: [directoryRead] }]` |
|
|
38
45
|
| 资源 CRUD 能力码用 `resourceCapabilityCodes(appCode, resourceCode)` 生成 | `const crud = resourceCapabilityCodes(APP_CODE, 'repair-requests')` → `capabilities: [crud.read, crud.create]` |
|
|
39
46
|
| `authenticatedUserRoleCode` 是平台登录用户的基线角色 | `authz: { authenticatedUserRoleCode: 'app-user', ... }` |
|
|
@@ -102,7 +102,7 @@ MCP 的 check_app、deployment_plan、deploy_app 使用与 CLI 相同的环境
|
|
|
102
102
|
|
|
103
103
|
## 构建前运行配额
|
|
104
104
|
|
|
105
|
-
`deploy --dry-run`(MCP `deployment_plan`)会只读查询目标 TEST 的运行配额,输出 `runtimeCapacity` 的核验时间、所需增量、各配额剩余量和缺口。`sufficient: false` 表示当前不足;`null` 表示无需新增或未核验,必须结合 `basis` 与 `capacity.checked`
|
|
105
|
+
`deploy --dry-run`(MCP `deployment_plan`)会只读查询目标 TEST 的运行配额,输出 `runtimeCapacity` 的核验时间、所需增量、各配额剩余量和缺口。`sufficient: false` 表示当前不足;`null` 表示无需新增或未核验,必须结合 `basis` 与 `capacity.checked` 阅读。专用命名空间未检查不能当成资源充足。同一预检还会比对源码 `workflows.activations` 与环境 Head,输出 `workflowActivation` 诊断:版本回退(error,拒绝部署)、源码缺失已激活流程(warning,本次部署会停用)、目录不可读(error,拒绝盲部署),详见 [Workflow 事件](workflow-events.md)。
|
|
106
106
|
|
|
107
107
|
正式 deploy 在检查脚本和镜像构建前预检;平台缺少配套能力或无法核验时明确停止。配额快照不预留资源,实际执行再次检查。已有可验证密封候选会携带摘要和幂等键,平台识别 `existing-run` 时返回原运行,不把它当作新副本;观察或恢复原运行使用 status/retry。不要为绕过配额创建新包或切换目标环境。
|
|
108
108
|
|
|
@@ -90,12 +90,13 @@ import { resourceSurfaces } from '@app/contracts';
|
|
|
90
90
|
|
|
91
91
|
const records = createNativeResourceClient('records', resourceSurfaces.records);
|
|
92
92
|
|
|
93
|
-
//
|
|
93
|
+
// 服务端过滤、排序、分页;where 字段必须已声明;排序字段用已声明字段或
|
|
94
|
+
// 平台审计列(created_at/updated_at/created_by/updated_by/id/revision)
|
|
94
95
|
const page = await records.list({
|
|
95
96
|
page: 1,
|
|
96
97
|
pageSize: 20,
|
|
97
98
|
where: { field: 'enabled', operator: 'eq', value: true },
|
|
98
|
-
sort: { field: '
|
|
99
|
+
sort: { field: 'created_at', order: 'desc' },
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
const record = await records.get(id);
|
|
@@ -126,6 +127,44 @@ capability,不使用角色名或影子字段判断。
|
|
|
126
127
|
|
|
127
128
|
DataQuery 使用有界 where 条件树,支持 and/or/not。标准列表的筛选、关键词、分页和导出共用已声明字段与同一查询条件,不在页面重写查询协议。
|
|
128
129
|
|
|
130
|
+
## 自建待办与消息中心 UI {#self-hosted-centers}
|
|
131
|
+
|
|
132
|
+
应用不满意默认待办中心/消息中心的呈现时,在自有 user 路由上用 `openxiangda/core`
|
|
133
|
+
公开客户端自建 UI;数据和事实仍由平台唯一拥有:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import {
|
|
137
|
+
loadWorkflowWorkCenter,
|
|
138
|
+
loadApplicationTodos,
|
|
139
|
+
recordApplicationTodoInteraction,
|
|
140
|
+
} from 'openxiangda/core';
|
|
141
|
+
|
|
142
|
+
// 待办四视图:created / pending / handled / cc,counts 随每次查询内嵌返回
|
|
143
|
+
const work = await loadWorkflowWorkCenter({ view: 'pending', limit: 20, offset: 0 });
|
|
144
|
+
// 消息视图:all / pending / informational / completed,未读与回执由 Notification Hub 投影
|
|
145
|
+
const todos = await loadApplicationTodos({ view: 'all', unread: false, keyword: '', limit: 12, offset: 0 });
|
|
146
|
+
// read/click 回执幂等;失败不阻断目标页自身的读取与鉴权
|
|
147
|
+
await recordApplicationTodoInteraction(item.messageId, 'click');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
边界(与默认标准页一致,`check` 与运行时都会强制):
|
|
151
|
+
|
|
152
|
+
- 不得注册、替换或重定向 `/todos`、`/m/todos`、`/work-center`、`/m/work-center`
|
|
153
|
+
及任何 Workflow 路由;自建页面必须挂在应用自己声明的路由上。
|
|
154
|
+
- 收件箱只读当前用户:没有跨用户查询、没有批量审批;待办页不执行
|
|
155
|
+
Workflow approve/reject/return 命令,操作一律进入平台解析的
|
|
156
|
+
`detailNavigation.desktopPath/mobilePath`,`navigationUnavailable` 时禁用入口。
|
|
157
|
+
- 视图语义由服务端裁定(待办的 created/cc 是 Workflow 事实,不是消息搜索的
|
|
158
|
+
客户端并集);合同没有的优先级、截止时间、排序与批量选择不得在浏览器伪造。
|
|
159
|
+
- 服务端集成使用 `openxiangda-nest` 的 `OpenXiangdaPlatformClient`:
|
|
160
|
+
`workflowWorkCenter` 支持 `view`(`created/handled/cc` 需要 view,
|
|
161
|
+
`status` 仅保留 pending/completed 兼容映射),`applicationTodos` 与回执
|
|
162
|
+
访问同名可用。
|
|
163
|
+
|
|
164
|
+
默认呈现的替换式定制仅限既有 contribution 面(用户面 frame、
|
|
165
|
+
`applicationTodoCenter` 渲染器);Workflow 待办中心目前没有渲染器替换钩子,
|
|
166
|
+
需要完全不同的呈现时走本节的自建路由方案。
|
|
167
|
+
|
|
129
168
|
## 标准后台扩展
|
|
130
169
|
|
|
131
170
|
### 未保存内容的离开保护
|
|
@@ -202,6 +202,15 @@ command 按固定版本完成,`cancel-on-deactivate` 在声明删除后取消
|
|
|
202
202
|
默认 1/200,最大 200。
|
|
203
203
|
需要按流程实例串行投递时只声明 `ordering: 'workflow-instance'`,不接受下划线别名。
|
|
204
204
|
|
|
205
|
+
平台按 desired set 直接覆盖环境 Head,不做版本比较。因此 `openxiangda deploy`
|
|
206
|
+
与 `deploy --dry-run` 会在构建前只读比对源码激活声明与环境 Head:
|
|
207
|
+
源码 `definitionVersion` 低于当前已激活版本时以
|
|
208
|
+
`DEPLOY_WORKFLOW_ACTIVATION_VERSION_REGRESSION` 拒绝部署——把高版本定义与激活
|
|
209
|
+
声明合入源码后再发,版本号与 digest 必须与已注册版本一致(同版本不同内容会被
|
|
210
|
+
平台以 `WORKFLOW_V2_DEFINITION_VERSION_IMMUTABLE` 拒绝);Head 已激活而源码
|
|
211
|
+
缺声明的流程给出 `DEPLOY_WORKFLOW_ACTIVATION_ABSENT` 警告(本次部署会停用它);
|
|
212
|
+
目录查询不可用时以 `WORKFLOW_HEAD_PREFLIGHT_UNAVAILABLE` 拒绝盲部署。
|
|
213
|
+
|
|
205
214
|
Notification Hub 消费事实:
|
|
206
215
|
|
|
207
216
|
- `participant.activated` 创建待处理消息;
|
|
@@ -294,7 +303,12 @@ Workflow instance-scoped preview/content 路由,平台在每次文件读取时
|
|
|
294
303
|
|
|
295
304
|
标准发起页使用独立的 `WorkflowLaunchSurface` 读取当前激活合同:桌面路径为
|
|
296
305
|
`/workflows/:workflowCode/start`,移动路径为
|
|
297
|
-
`/m/workflows/:workflowCode/start
|
|
306
|
+
`/m/workflows/:workflowCode/start`。definition 必须显式声明
|
|
307
|
+
`launch: { mode: 'standalone' | 'hidden-handoff' | ... }`,缺失会被编译器拒绝;
|
|
308
|
+
factProjection 把 option/user/department/resource-ref/cascade 字段投影为
|
|
309
|
+
`{ label, value }` 对象,对应 inputSchema 属性必须声明为 `type: 'object'`
|
|
310
|
+
(multiple 类字段为 array + object items),条件表达式用 `path: '<fact>.value'`
|
|
311
|
+
比较;声明成标量会在运行时 INPUT_SCHEMA_MISMATCH 并无限重试,编译器现已拦截。`standalone`/`hidden-handoff` 缺省使用同一个
|
|
298
312
|
compiler-owned `processOperationCode`、subject declaration 和标准 process commit;平台在一个
|
|
299
313
|
事务中写业务数据和 durable command。action-owned 资源改为声明
|
|
300
314
|
`launch.submission.kind: 'named-operation'`,显式绑定 create/existing 请求来源、响应 subject/
|