openxiangda 1.0.261 → 1.0.262
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/lib/delivery-v2-package.js +17 -0
- package/openxiangda-skills/references/connector-resources.md +1 -1
- package/openxiangda-skills/references/openxiangda-api.md +9 -1
- package/openxiangda-skills/references/pages/page-sdk.md +2 -1
- package/openxiangda-skills/references/permissions-settings.md +8 -0
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +5 -2
- package/openxiangda-skills/references/school-contact-relations.md +34 -0
- package/package.json +1 -1
- package/packages/sdk/dist/runtime/index.cjs +34 -34
- package/packages/sdk/dist/runtime/react.cjs +46 -46
- package/packages/sdk/dist/runtime/react.d.mts +13 -0
- package/packages/sdk/dist/runtime/react.d.ts +13 -0
|
@@ -335,6 +335,7 @@ function discoverResourceInventory(workspaceRoot, files) {
|
|
|
335
335
|
workflow: new Map(),
|
|
336
336
|
};
|
|
337
337
|
const formPermissionGroupForms = new Map();
|
|
338
|
+
const automationSourceFilesByCode = new Map();
|
|
338
339
|
const addFingerprint = (bucket, code, file) => {
|
|
339
340
|
if (!code || !file) return;
|
|
340
341
|
if (!bucket.has(code)) bucket.set(code, new Map());
|
|
@@ -396,6 +397,13 @@ function discoverResourceInventory(workspaceRoot, files) {
|
|
|
396
397
|
) {
|
|
397
398
|
continue;
|
|
398
399
|
}
|
|
400
|
+
if (prefix === 'src/automations/') {
|
|
401
|
+
if (!automationSourceFilesByCode.has(code)) {
|
|
402
|
+
automationSourceFilesByCode.set(code, []);
|
|
403
|
+
}
|
|
404
|
+
automationSourceFilesByCode.get(code).push(file);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
399
407
|
bucket.add(code);
|
|
400
408
|
addFingerprint(fingerprintBucket, code, file);
|
|
401
409
|
}
|
|
@@ -497,6 +505,15 @@ function discoverResourceInventory(workspaceRoot, files) {
|
|
|
497
505
|
}
|
|
498
506
|
}
|
|
499
507
|
}
|
|
508
|
+
// Automation source directories may contain shared TypeScript helpers such
|
|
509
|
+
// as src/automations/_shared. Only a resource manifest declares a deployable
|
|
510
|
+
// automation; source files belonging to a declared code still participate in
|
|
511
|
+
// that automation's fingerprint and transitive dependency expansion.
|
|
512
|
+
for (const code of inventory.automations) {
|
|
513
|
+
for (const file of automationSourceFilesByCode.get(code) || []) {
|
|
514
|
+
addFingerprint(fingerprints.automations, code, file);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
500
517
|
for (const bucket of [
|
|
501
518
|
fingerprints.forms,
|
|
502
519
|
fingerprints.functions,
|
|
@@ -32,7 +32,7 @@ Notification manifests live under `src/resources/notifications/` and contain `te
|
|
|
32
32
|
|
|
33
33
|
Data view manifests live under `src/resources/data-views/` and define read-only joined or aggregate query resources. Use `storageMode: "materialized"` for refreshed lists/reports where lag is acceptable, and `storageMode: "live"` for bounded real-time query shapes. Use row views with `sdk.dataView.query` and aggregate views with `sdk.dataView.stats`. Before generating one, confirm freshness tolerance, query bounds, and indexes for materialized filters/sort fields/dimensions/date buckets. Do not use them for single-form CRUD, simple linkedForm selects, writes, write-back, or ad-hoc BI. See `data-views.md` before generating one.
|
|
34
34
|
|
|
35
|
-
App Function manifests live under `src/resources/functions/`, with source in `src/functions/<functionCode>/index.ts`. Use them for reusable server-side logic that pages, automations, and workflows can share through `sdk.function.invoke` or `function_call` nodes. App Functions expose controlled runtime helpers such as `ctx.resources`, `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`, `ctx.process.startFromExistingInstance/resolveCapabilities/resubmitTask/withdraw/transferTask`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, `ctx.organization`, `ctx.platform.roles`, and `ctx.platform.api`; they do not expose raw SQL or Redis in the current MVP. Published trusted code may use all application-owned Forms/DataViews in the current tenant/app. Function `resources` remains optional code-to-ID mapping and audit metadata, while cross-app/cross-tenant access stays forbidden. Use `ctx.platform.roles.list/findByCode/addUsers/removeUser` for app role lookup and membership changes; raw `ctx.platform.api` returns an HTTP response plus platform envelope. Runtime page invocations also expose trusted role context on `ctx.operator.roleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, and `ctx.permissions`;
|
|
35
|
+
App Function manifests live under `src/resources/functions/`, with source in `src/functions/<functionCode>/index.ts`. Use them for reusable server-side logic that pages, automations, and workflows can share through `sdk.function.invoke` or `function_call` nodes. App Functions expose controlled runtime helpers such as `ctx.resources`, `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`, `ctx.process.startFromExistingInstance/resolveCapabilities/resubmitTask/withdraw/transferTask`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, `ctx.organization`, `ctx.platform.roles`, and `ctx.platform.api`; they do not expose raw SQL or Redis in the current MVP. Published trusted code may use all application-owned Forms/DataViews in the current tenant/app. Function `resources` remains optional code-to-ID mapping and audit metadata, while cross-app/cross-tenant access stays forbidden. Use `ctx.platform.roles.list/findByCode/addUsers/removeUser` for app role lookup and membership changes; raw `ctx.platform.api` returns an HTTP response plus platform envelope. Runtime page invocations also expose trusted role context on `ctx.operator.roleCodes`, `ctx.operator.platformRoleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, and `ctx.permissions`; `platformRoleCodes` contains the synchronized platform identities `SCHOOL_GUARDIAN`, `SCHOOL_STUDENT`, and `SCHOOL_TEACHER`, while `roleCodes` contains app roles. Use that context for business authorization, and never trust page-submitted role codes for sensitive actions. Page-call grants should use `definitionJson.runtimeInvoke.audience` (`authenticated`, `page_permission_group`, `app_roles`, `platform_roles`, or `scope_policy`); use `roleCodes` for current app-role grants and `platformRoleCodes` for synchronized identities. App Function form writes are trusted backend operations, not direct page-user submit access to the target form. For internal function-only forms, publish form settings with `runtimeWrite.mode="function_only"` so raw write endpoints are closed. App Function organization writes require `app:organization:manage` on the real operator/audit actor. Use JS_CODE V2 only for node-local workflow/automation scripts.
|
|
36
36
|
|
|
37
37
|
Auth manifests live under `src/resources/auth/`. Use them to enable app-level login methods and bind phone-code/CAS/custom providers to App Functions. Auth provider functions are called only by the platform auth flow. They validate external credentials and return identity assertions such as `phone`, `email`, `externalId`, or `unionId`; they must not issue tokens, set cookies, or mutate platform user/binding tables.
|
|
38
38
|
|
|
@@ -743,10 +743,16 @@ Body:
|
|
|
743
743
|
{
|
|
744
744
|
"name": "销售可见页面",
|
|
745
745
|
"roles": ["sales"],
|
|
746
|
+
"platformRoleCodes": ["SCHOOL_TEACHER"],
|
|
746
747
|
"menuFormUuids": ["FORM_XXX", "MENU_ID_FOR_CODE_PAGE"]
|
|
747
748
|
}
|
|
748
749
|
```
|
|
749
750
|
|
|
751
|
+
`platformRoleCodes` is optional. It matches synchronized platform identities in addition to
|
|
752
|
+
`roles` (OR semantics): `SCHOOL_GUARDIAN`, `SCHOOL_STUDENT`, and `SCHOOL_TEACHER`. Both arrays
|
|
753
|
+
empty means unrestricted. These three system roles are maintained by the daily DingTalk
|
|
754
|
+
school-contact sync and cannot be assigned through role-management APIs.
|
|
755
|
+
|
|
750
756
|
An empty `menuFormUuids` array means all menus/pages are visible to the matched roles. For form menus this field can contain form UUIDs. For custom code page menus, the editable permission group should contain the menu ID, which is what the platform permission editor uses for tree check state. OpenXiangda CLI resolves `--page-codes` / `--menu-codes` to an editable menu-ID group and creates a companion `(运行时别名)` group for required page ID, route key, and legacy `PAGE_...` runtime aliases by default. This keeps the main group editable while satisfying `/view` runtime guards. Use `--no-runtime-aliases` only for deployments whose runtime checks menu IDs directly.
|
|
751
757
|
|
|
752
758
|
### GET `/apps/:appType/page-permission-groups/:groupId`
|
|
@@ -763,7 +769,8 @@ Requires Bearer token. Deletes a page permission group.
|
|
|
763
769
|
|
|
764
770
|
### GET `/apps/:appType/page-permission-groups/user-menu-permissions`
|
|
765
771
|
|
|
766
|
-
Requires Bearer token. Returns the current user's menu visibility summary for the app
|
|
772
|
+
Requires Bearer token. Returns the current user's menu visibility summary for the app,
|
|
773
|
+
including the synchronized `platformRoleCodes`.
|
|
767
774
|
|
|
768
775
|
### GET `/apps/:appType/forms/:formUuid/permission-groups`
|
|
769
776
|
|
|
@@ -780,6 +787,7 @@ Body:
|
|
|
780
787
|
"name": "销售查看",
|
|
781
788
|
"type": "view",
|
|
782
789
|
"roles": ["sales"],
|
|
790
|
+
"platformRoleCodes": ["SCHOOL_GUARDIAN"],
|
|
783
791
|
"dataScope": [{ "type": "self" }],
|
|
784
792
|
"operations": ["view"],
|
|
785
793
|
"fieldPermissions": [],
|
|
@@ -20,6 +20,7 @@ Guidelines:
|
|
|
20
20
|
- Use `AttachmentPreviewList`, `ImagePreviewGrid`, or `useFilePreview` from `openxiangda/runtime/react` for in-page attachment previews in a custom React SPA page. They use the current PageSdk context and enforce the platform capability and ticket contracts. Use `sdk.createFileAccessTicket(bucketName, objectName, fileName, "preview", { appType })` only when the page needs a shareable or new-window preview link. `appType` defaults to the current page context. Open `response.result.previewPageUrl`; do not use `previewUrl` or `/service/file/preview-by-ticket/:ticket` as the page entry.
|
|
21
21
|
- Use `sdk.organization.departments.*` and `sdk.organization.accounts.*` only for intentional organization pages. Read-only list/detail pages need `app:organization:read` or `app:organization:manage`; writes and password operations need `app:organization:manage`. Do not call legacy `/user` or `/department` endpoints from pages.
|
|
22
22
|
- Use `sdk.organization.schoolContact.*` for synchronized guardian/student relationships. Declare `app:organization:school-contact:read`; it defaults to all relationships in the current tenant. Use the optional `self:read` or `class:read` permission only when the product explicitly needs a narrower data scope. The response intentionally includes platform user ID, mobile, DingTalk userid, and name. Read `references/school-contact-relations.md` before building a family or class page.
|
|
23
|
+
- Synchronized school-contact users also receive platform identity codes: `SCHOOL_GUARDIAN`, `SCHOOL_STUDENT`, and `SCHOOL_TEACHER`. Page permission groups select them with `platformRoleCodes` (additive to app `roles`), and runtime permissions expose them in `permissions.platformRoleCodes`. App Functions can use a `platform_roles` invocation audience and trusted `ctx.operator.platformRoleCodes`; these roles are maintained only by the daily organization sync.
|
|
23
24
|
- Use `sdk.workCenter.listItems({ boxType })` and `sdk.workCenter.getStats()` for current-user work center lists and counts. `boxType` is one of `todo`, `done`, `cc`, or `initiated`. This is the end-user task/handled/cc/initiated surface; do not build todo pages by reading workflow operation logs, automation logs, or raw process-task tables.
|
|
24
25
|
- `sdk.form.create` returns identifiers and generated serial number values only. Treat `formInstId` / `formInstanceId` as the required success contract; `serialNumber` / `serialNumbers` are present only when the form has `SerialNumberField`. Do not expect the save response to contain the full row, formula results, or other server-generated field values. Call `sdk.form.getDetail` explicitly after create when those values are needed.
|
|
25
26
|
- Missing `formInstId` / `formInstanceId` is a real error in strict mode. Do not create fake IDs, mock rows, or fallback display data to hide it.
|
|
@@ -189,7 +190,7 @@ App Function source should prefer `export default async function(ctx, input) {}`
|
|
|
189
190
|
The second argument is the `input` passed by `sdk.function.invoke`, and the
|
|
190
191
|
same value is also available as `ctx.input` for compatibility.
|
|
191
192
|
|
|
192
|
-
Use App Functions when the logic must run server-side and be reusable by pages, automations, or workflows. Runtime invocation requires app automation management permission by default. To let ordinary app users call a function from a page, declare `definitionJson.runtimeInvoke.audience` with `authenticated`, `page_permission_group`, `app_roles`, or `scope_policy`; use `roleCodes` only for current app-role grants. Do not use `"*"` or `"all-app-roles"` as role codes. Inside the function, authorize sensitive actions with trusted runtime context such as `ctx.operator.roleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, or `ctx.permissions`; do not trust role codes sent in the page input. Published trusted code may write any form in its current tenant/application; per-function `resources.forms` is optional mapping and audit metadata, not an authorization allowlist. Do not open direct submit permission on internal forms just for that page action.
|
|
193
|
+
Use App Functions when the logic must run server-side and be reusable by pages, automations, or workflows. Runtime invocation requires app automation management permission by default. To let ordinary app users call a function from a page, declare `definitionJson.runtimeInvoke.audience` with `authenticated`, `page_permission_group`, `app_roles`, `platform_roles`, or `scope_policy`; use `roleCodes` only for current app-role grants and `platformRoleCodes` for synchronized identities. Do not use `"*"` or `"all-app-roles"` as role codes. Inside the function, authorize sensitive actions with trusted runtime context such as `ctx.operator.roleCodes`, `ctx.operator.platformRoleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, or `ctx.permissions`; do not trust role codes sent in the page input. Published trusted code may write any form in its current tenant/application; per-function `resources.forms` is optional mapping and audit metadata, not an authorization allowlist. Do not open direct submit permission on internal forms just for that page action.
|
|
193
194
|
|
|
194
195
|
Server-defined export:
|
|
195
196
|
|
|
@@ -87,10 +87,16 @@ group so the platform tree can still round-trip the menu selection.
|
|
|
87
87
|
{
|
|
88
88
|
"name": "销售页面",
|
|
89
89
|
"roles": ["sales"],
|
|
90
|
+
"platformRoleCodes": [],
|
|
90
91
|
"menuFormUuids": ["FORM_CUSTOMER", "FORM_ORDER", "MENU_ID_FOR_CODE_PAGE"]
|
|
91
92
|
}
|
|
92
93
|
```
|
|
93
94
|
|
|
95
|
+
`platformRoleCodes` is optional and is evaluated alongside `roles` using OR semantics. Use the
|
|
96
|
+
daily DingTalk school-contact identity codes `SCHOOL_GUARDIAN`, `SCHOOL_STUDENT`, and
|
|
97
|
+
`SCHOOL_TEACHER`; the platform maintains these system roles and applications must not assign
|
|
98
|
+
them manually. If both arrays are empty, the group is unrestricted.
|
|
99
|
+
|
|
94
100
|
For custom code pages, keep the editable menu tree targets separate from runtime aliases. The
|
|
95
101
|
platform permission editor only reliably checks real menu IDs, while `/view` runtime guards on
|
|
96
102
|
some private deployments still check the code page id, route key, or legacy `PAGE_...` id. When
|
|
@@ -129,6 +135,7 @@ Submit group:
|
|
|
129
135
|
"name": "销售提交",
|
|
130
136
|
"type": "submit",
|
|
131
137
|
"roles": ["sales"],
|
|
138
|
+
"platformRoleCodes": [],
|
|
132
139
|
"operations": ["submit"]
|
|
133
140
|
}
|
|
134
141
|
```
|
|
@@ -140,6 +147,7 @@ View group:
|
|
|
140
147
|
"name": "销售查看",
|
|
141
148
|
"type": "view",
|
|
142
149
|
"roles": ["sales"],
|
|
150
|
+
"platformRoleCodes": [],
|
|
143
151
|
"dataScope": [{ "type": "self" }],
|
|
144
152
|
"operations": ["view", "edit"],
|
|
145
153
|
"fieldPermissions": [
|
|
@@ -614,10 +614,13 @@ export default async function reservationReminderSummary(
|
|
|
614
614
|
{ headers: { authorization: `Bearer ${providerToken}` } },
|
|
615
615
|
);
|
|
616
616
|
const roleCodes = ctx.operator?.roleCodes || ctx.permissions?.roleCodes || [];
|
|
617
|
+
const platformRoleCodes =
|
|
618
|
+
ctx.operator?.platformRoleCodes || ctx.permissions?.platformRoleCodes || [];
|
|
617
619
|
const canManage =
|
|
618
620
|
ctx.operator?.hasFullAccess === true ||
|
|
619
621
|
ctx.permissions?.hasFullAccess === true ||
|
|
620
|
-
roleCodes.includes("union_admin")
|
|
622
|
+
roleCodes.includes("union_admin") ||
|
|
623
|
+
platformRoleCodes.includes("SCHOOL_TEACHER");
|
|
621
624
|
if (!canManage) throw new Error("当前账号无权执行该操作。");
|
|
622
625
|
|
|
623
626
|
const orders = await ctx.form.queryMany({
|
|
@@ -678,7 +681,7 @@ const result = await sdk.function.invoke("reservation_reminder_summary", {
|
|
|
678
681
|
}
|
|
679
682
|
```
|
|
680
683
|
|
|
681
|
-
适用边界:可复用后端业务逻辑、跨页面/自动化/流程共享的查询编排、连接器调用、通知编排、受控平台 API 调用。App Function 支持 `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`、`ctx.dataView`、`ctx.connector`、`ctx.notification`、`ctx.platform.roles`、`ctx.platform.api` 等受控 helper,当前 MVP 不暴露原始 SQL/Redis。应用角色查询和成员维护优先使用 `ctx.platform.roles.list/findByCode/addUsers/removeUser`;底层 `ctx.platform.api` 返回 HTTP 包装与平台 envelope,需要自行解包。已发布可信代码可以访问当前租户、当前应用内的资源;function manifest 的 `resources` 是可选映射、审计和影响分析信息,不再是逐函数权限白名单。页面用户仍不能直接提交内部表单,跨应用和跨租户访问仍被拒绝。运行时接口默认需要应用自动化管理权限;普通用户页面要调用时,用 `definitionJson.runtimeInvoke.audience` 声明 `authenticated`、`page_permission_group`、`app_roles` 或 `scope_policy`,不要把 `"*"`、`"all-app-roles"`
|
|
684
|
+
适用边界:可复用后端业务逻辑、跨页面/自动化/流程共享的查询编排、连接器调用、通知编排、受控平台 API 调用。App Function 支持 `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`、`ctx.dataView`、`ctx.connector`、`ctx.notification`、`ctx.platform.roles`、`ctx.platform.api` 等受控 helper,当前 MVP 不暴露原始 SQL/Redis。应用角色查询和成员维护优先使用 `ctx.platform.roles.list/findByCode/addUsers/removeUser`;底层 `ctx.platform.api` 返回 HTTP 包装与平台 envelope,需要自行解包。已发布可信代码可以访问当前租户、当前应用内的资源;function manifest 的 `resources` 是可选映射、审计和影响分析信息,不再是逐函数权限白名单。页面用户仍不能直接提交内部表单,跨应用和跨租户访问仍被拒绝。运行时接口默认需要应用自动化管理权限;普通用户页面要调用时,用 `definitionJson.runtimeInvoke.audience` 声明 `authenticated`、`page_permission_group`、`app_roles`、`platform_roles` 或 `scope_policy`,使用 `roleCodes` 匹配应用角色、使用 `platformRoleCodes` 匹配同步身份 `SCHOOL_GUARDIAN`、`SCHOOL_STUDENT`、`SCHOOL_TEACHER`,不要把 `"*"`、`"all-app-roles"` 写进角色编码。若表单只能由函数/流程写入,在 `src/resources/settings/forms/<formCode>.json` 设置 `runtimeWrite.mode="function_only"` 关闭原始写入接口。
|
|
682
685
|
|
|
683
686
|
## 5. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
|
|
684
687
|
|
|
@@ -23,6 +23,40 @@
|
|
|
23
23
|
|
|
24
24
|
## 页面 SDK
|
|
25
25
|
|
|
26
|
+
### 平台内置身份角色
|
|
27
|
+
|
|
28
|
+
每次成功的钉钉家校通讯录全量同步都会对账并维护三种平台层系统身份角色:
|
|
29
|
+
|
|
30
|
+
| 展示名 | 稳定角色编码 | 含义 |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| 家长 | `SCHOOL_GUARDIAN` | 在家校成员关系中作为 guardian |
|
|
33
|
+
| 学生 | `SCHOOL_STUDENT` | 在家校成员关系中作为 student |
|
|
34
|
+
| 老师 | `SCHOOL_TEACHER` | 在家校成员关系中作为 teacher |
|
|
35
|
+
|
|
36
|
+
身份角色可组合,一个平台用户可以同时是老师和家长;角色由同步维护,不能在平台角色管理中手工分配、修改、删除,也不会被当前平台角色切换隐藏。应用权限组把这些编码填入 `platformRoleCodes`,与应用角色 `roles` 是并列的 OR 条件;两个数组都为空仍表示不限制。
|
|
37
|
+
|
|
38
|
+
运行时页面权限会在 `permissions.platformRoleCodes` 返回当前用户的身份编码,它与 `permissions.roleCodes` 分开:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const isGuardian =
|
|
42
|
+
sdk.context.permissions?.platformRoleCodes?.includes("SCHOOL_GUARDIAN")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
App Function 的调用受众可以声明平台身份角色,服务端按真实操作者的同步结果校验:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"runtimeInvoke": {
|
|
50
|
+
"audience": {
|
|
51
|
+
"type": "platform_roles",
|
|
52
|
+
"platformRoleCodes": ["SCHOOL_GUARDIAN", "SCHOOL_TEACHER"]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
函数可信上下文同时提供 `ctx.operator.platformRoleCodes` 和 `ctx.permissions.platformRoleCodes`。不要信任页面输入中的角色编码;需要家庭双方或班级关系时,继续使用下方的 `schoolContact` 关系 SDK。
|
|
59
|
+
|
|
26
60
|
在 React SPA 或代码页中通过 Page SDK 查询,不要硬编码接口地址:
|
|
27
61
|
|
|
28
62
|
```ts
|