openxiangda 1.0.261 → 1.0.263
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/README.md +1 -1
- package/lib/delivery-v2-package.js +17 -0
- package/openxiangda-skills/SKILL.md +2 -1
- 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 +3 -2
- 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 +40 -2
- package/openxiangda-skills/skills/openxiangda-page/SKILL.md +1 -1
- 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
- package/templates/openxiangda-react-spa/AGENTS.md +1 -1
- package/templates/sy-lowcode-app-workspace/AGENTS.md +1 -1
package/README.md
CHANGED
|
@@ -415,7 +415,7 @@ openxiangda form export customer --mode package --profile dev --output ./exports
|
|
|
415
415
|
|
|
416
416
|
运行时页面读取当前用户信息时,优先使用 `sdk.user.getCurrent<PageUserRecord>()`。用户对象会返回常规组织成员关系 `departments`,也会返回系统维护的所属单位字段 `affiliatedDepartmentId` / `affiliatedDepartment`。`departments` 表示用户真实所在的部门、班级、专业等成员关系;`affiliatedDepartment` 表示业务上用于统计、筛选和展示的归属单位,通常是学院、单位或在源单位缺失时可用的具体部门节点,不用于替代权限部门成员关系。
|
|
417
417
|
|
|
418
|
-
钉钉家校通讯录的家长、学生、教师身份和明确监护关系使用 `sdk.organization.schoolContact.*`(页面)或 `ctx.organization.schoolContact.*`(App Function
|
|
418
|
+
钉钉家校通讯录的家长、学生、教师身份和明确监护关系使用 `sdk.organization.schoolContact.*`(页面)或 `ctx.organization.schoolContact.*`(App Function)。已登录非游客用户无需给应用角色额外绑定权限,默认可查询当前租户全部关系,返回平台用户 ID、手机号、钉钉 userid 和姓名;应用可显式声明 `:self:read` / `:class:read` 收紧范围。完整示例见 [`docs/dingtalk-school-contact-relations.md`](docs/dingtalk-school-contact-relations.md)。
|
|
419
419
|
|
|
420
420
|
```ts
|
|
421
421
|
import type { PageUserRecord } from "openxiangda/runtime"
|
|
@@ -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,
|
|
@@ -193,7 +193,8 @@ The lease is app-level promotion ownership, while worktree ownership prevents tw
|
|
|
193
193
|
- Use official public SDK entrypoints and platform form/file components.
|
|
194
194
|
- Read school-contact guardian/student relationships through
|
|
195
195
|
`sdk.organization.schoolContact` or the trusted App Function bridge. Keep the
|
|
196
|
-
default tenant-wide scope
|
|
196
|
+
authenticated non-guest default tenant-wide scope without adding an app-role
|
|
197
|
+
permission binding unless the product explicitly requires self/class
|
|
197
198
|
restrictions; never infer relations from department membership.
|
|
198
199
|
- Report platform defects or repeated workarounds with `openxiangda feedback submit --yes` when a configured feedback channel is available.
|
|
199
200
|
- Run `openxiangda update check --json` once at the start of substantial work, when a version mismatch is suspected, or when the cached check is older than one day. Do not repeat it on every confirmation turn.
|
|
@@ -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": [],
|
|
@@ -19,7 +19,8 @@ Guidelines:
|
|
|
19
19
|
- Use `sdk.export.create` and `sdk.export.get` for asynchronous XLSX exports. Simple pages may send a declarative workbook definition. Complex or reusable exports should send only a published App Function `definitionCode`, the current query snapshot, export scope, and stable selected row IDs. The server executes `structured_export_provider_v1` with fresh user permissions; never upload executable rendering code from the browser. See `docs/structured-export-v1.md`.
|
|
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
|
-
- Use `sdk.organization.schoolContact.*` for synchronized guardian/student relationships.
|
|
22
|
+
- Use `sdk.organization.schoolContact.*` for synchronized guardian/student relationships. Authenticated non-guest users default to all relationships in the current tenant without an app-role permission binding. 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
|
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## 权限与默认范围
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
已登录非游客用户无需给应用角色绑定家校权限,默认查询当前租户的全部关系,范围为 `all`。跨租户、游客和匿名访问仍然拒绝。
|
|
8
|
+
|
|
9
|
+
应用若要显式保留全量范围,可以在 `src/resources/roles/<code>.json` 中声明:
|
|
8
10
|
|
|
9
11
|
```json
|
|
10
12
|
{
|
|
@@ -14,15 +16,51 @@
|
|
|
14
16
|
}
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
`app:organization:school-contact:read`
|
|
19
|
+
`app:organization:school-contact:read` 与未声明权限时的默认范围相同。只有产品明确要求收紧时,才改用以下权限之一:
|
|
18
20
|
|
|
19
21
|
- `app:organization:school-contact:self:read`:只返回当前操作者作为家长或学生直接参与的关系。
|
|
20
22
|
- `app:organization:school-contact:class:read`:只返回当前操作者以 `teacher` 身份加入的班级关系。
|
|
21
23
|
|
|
24
|
+
同一应用角色拥有多项家校能力时按 `all > class > self` 取最宽范围。平台身份角色只表达用户身份,不参与通用应用权限计算;默认 `all` 由家校关系服务统一执行。
|
|
25
|
+
|
|
22
26
|
默认全量可见不等于公开访问:跨租户、游客和匿名页面仍然拒绝。不要通过公开访问策略给游客授予家庭关系能力。
|
|
23
27
|
|
|
24
28
|
## 页面 SDK
|
|
25
29
|
|
|
30
|
+
### 平台内置身份角色
|
|
31
|
+
|
|
32
|
+
每次成功的钉钉家校通讯录全量同步都会对账并维护三种平台层系统身份角色:
|
|
33
|
+
|
|
34
|
+
| 展示名 | 稳定角色编码 | 含义 |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| 家长 | `SCHOOL_GUARDIAN` | 在家校成员关系中作为 guardian |
|
|
37
|
+
| 学生 | `SCHOOL_STUDENT` | 在家校成员关系中作为 student |
|
|
38
|
+
| 老师 | `SCHOOL_TEACHER` | 在家校成员关系中作为 teacher |
|
|
39
|
+
|
|
40
|
+
身份角色可组合,一个平台用户可以同时是老师和家长;角色由同步维护,不能在平台角色管理中手工分配、修改、删除,也不会被当前平台角色切换隐藏。应用权限组把这些编码填入 `platformRoleCodes`,与应用角色 `roles` 是并列的 OR 条件;两个数组都为空仍表示不限制。
|
|
41
|
+
|
|
42
|
+
运行时页面权限会在 `permissions.platformRoleCodes` 返回当前用户的身份编码,它与 `permissions.roleCodes` 分开:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const isGuardian =
|
|
46
|
+
sdk.context.permissions?.platformRoleCodes?.includes("SCHOOL_GUARDIAN")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
App Function 的调用受众可以声明平台身份角色,服务端按真实操作者的同步结果校验:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"runtimeInvoke": {
|
|
54
|
+
"audience": {
|
|
55
|
+
"type": "platform_roles",
|
|
56
|
+
"platformRoleCodes": ["SCHOOL_GUARDIAN", "SCHOOL_TEACHER"]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
函数可信上下文同时提供 `ctx.operator.platformRoleCodes` 和 `ctx.permissions.platformRoleCodes`。不要信任页面输入中的角色编码;需要家庭双方或班级关系时,继续使用下方的 `schoolContact` 关系 SDK。
|
|
63
|
+
|
|
26
64
|
在 React SPA 或代码页中通过 Page SDK 查询,不要硬编码接口地址:
|
|
27
65
|
|
|
28
66
|
```ts
|
|
@@ -36,7 +36,7 @@ openxiangda workspace publish --profile <name> --page <pageCode>
|
|
|
36
36
|
- ✅ For external APIs use `src/resources/connectors/<code>.json` + `sdk.connector.invoke()`; for joined read-only lists use row data views + `sdk.dataView.query`; for stable dashboard metrics use aggregate data views + `sdk.dataView.stats`.
|
|
37
37
|
- ✅ Choose data view `storageMode` deliberately: `materialized` for refreshed reports/lists that tolerate delay, `live` for bounded real-time multi-form reads.
|
|
38
38
|
- ✅ For reusable backend orchestration use App Function (`src/functions/<functionCode>/index.ts` + `src/resources/functions/<functionCode>.json`) and call `sdk.function.invoke(code, { input })`; keep page components out of multi-table business orchestration.
|
|
39
|
-
- ✅ 家长、学生、教师身份或监护关系页面使用 `sdk.organization.schoolContact.*`;先读 `references/school-contact-relations.md
|
|
39
|
+
- ✅ 家长、学生、教师身份或监护关系页面使用 `sdk.organization.schoolContact.*`;先读 `references/school-contact-relations.md`,默认使用已登录用户的租户内全部关系,仅在业务要求时声明 self/class 收紧范围,并保持服务端分页。
|
|
40
40
|
- ✅ Filters, search bars, modal forms, drawers, and inline edits use platform components for platform data fields, otherwise `antd` / `antd-mobile` controls.
|
|
41
41
|
- ❌ Single-file giant pages. Split per `references/best-practices.md`.
|
|
42
42
|
- ❌ Hardcoded `/view/...&isRenderNav=false` URLs scattered through page code; use the runtime navigation helper.
|