openxiangda 1.0.134 → 1.0.136

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.
Files changed (57) hide show
  1. package/README.md +6 -0
  2. package/lib/cli.js +947 -16
  3. package/lib/design-gates.js +40 -10
  4. package/lib/sdd.js +777 -0
  5. package/lib/workspace-init.js +3 -0
  6. package/openxiangda-skills/SKILL.md +11 -1
  7. package/openxiangda-skills/references/best-practices.md +32 -5
  8. package/openxiangda-skills/references/openxiangda-api.md +32 -6
  9. package/openxiangda-skills/references/pages/page-sdk.md +3 -0
  10. package/openxiangda-skills/references/pages/publish-flow.md +16 -0
  11. package/openxiangda-skills/references/permission-design-patterns.md +216 -0
  12. package/openxiangda-skills/references/permissions-settings.md +58 -0
  13. package/openxiangda-skills/references/platform-data-model.md +19 -4
  14. package/openxiangda-skills/references/resource-manifest-cheatsheet.md +32 -1
  15. package/openxiangda-skills/skills/openxiangda-architecture-design/SKILL.md +8 -0
  16. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +2 -0
  17. package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +38 -1
  18. package/package.json +2 -1
  19. package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.mts → ProcessPreview-Ci8_UsbN.d.mts} +4 -0
  20. package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.ts → ProcessPreview-Ci8_UsbN.d.ts} +4 -0
  21. package/packages/sdk/dist/build/index.cjs.map +1 -1
  22. package/packages/sdk/dist/build/index.d.mts +7 -0
  23. package/packages/sdk/dist/build/index.d.ts +7 -0
  24. package/packages/sdk/dist/build/index.mjs.map +1 -1
  25. package/packages/sdk/dist/components/index.cjs +32 -4
  26. package/packages/sdk/dist/components/index.cjs.map +1 -1
  27. package/packages/sdk/dist/components/index.d.mts +8 -2
  28. package/packages/sdk/dist/components/index.d.ts +8 -2
  29. package/packages/sdk/dist/components/index.mjs +32 -4
  30. package/packages/sdk/dist/components/index.mjs.map +1 -1
  31. package/packages/sdk/dist/runtime/index.cjs +181 -28
  32. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  33. package/packages/sdk/dist/runtime/index.d.mts +2 -2
  34. package/packages/sdk/dist/runtime/index.d.ts +2 -2
  35. package/packages/sdk/dist/runtime/index.mjs +181 -28
  36. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  37. package/packages/sdk/dist/runtime/react.cjs +152 -25
  38. package/packages/sdk/dist/runtime/react.cjs.map +1 -1
  39. package/packages/sdk/dist/runtime/react.d.mts +57 -4
  40. package/packages/sdk/dist/runtime/react.d.ts +57 -4
  41. package/packages/sdk/dist/runtime/react.mjs +152 -25
  42. package/packages/sdk/dist/runtime/react.mjs.map +1 -1
  43. package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +8 -1
  44. package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +8 -1
  45. package/templates/openxiangda-react-spa/AGENTS.md +9 -1
  46. package/templates/openxiangda-react-spa/app-workspace.config.ts +14 -0
  47. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-resources.mdc +5 -1
  48. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +7 -1
  49. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-resources.md +6 -0
  50. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +7 -1
  51. package/templates/sy-lowcode-app-workspace/AGENTS.md +10 -1
  52. package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +14 -0
  53. package/templates/sy-lowcode-app-workspace/examples/best-practices/README.md +3 -0
  54. package/templates/sy-lowcode-app-workspace/examples/best-practices/access-governance.md +239 -0
  55. package/templates/sy-lowcode-app-workspace/examples/best-practices/catalog.json +6 -0
  56. package/templates/sy-lowcode-app-workspace/examples/best-practices/decision-guide.md +25 -1
  57. package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +12 -0
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { spawnSync } = require('child_process');
4
4
  const { getProfile, loadConfig, saveProjectState } = require('./config');
5
+ const { initSddWorkspace } = require('./sdd');
5
6
 
6
7
  const ROOT_DIR = path.join(__dirname, '..');
7
8
  const LEGACY_TEMPLATE_DIR = path.join(ROOT_DIR, 'templates', 'sy-lowcode-app-workspace');
@@ -37,6 +38,7 @@ function initWorkspace(options = {}) {
37
38
  copyTemplate(templateDir, targetDir, {
38
39
  __WORKSPACE_PACKAGE_NAME__: packageName,
39
40
  });
41
+ const sdd = initSddWorkspace({ cwd: targetDir });
40
42
 
41
43
  let bound = null;
42
44
  if (profileName && appType) {
@@ -88,6 +90,7 @@ function initWorkspace(options = {}) {
88
90
  templateDir,
89
91
  installedDependencies: install,
90
92
  bound,
93
+ sdd,
91
94
  nextSteps: buildNextSteps(targetDir, install, bound),
92
95
  };
93
96
  }
@@ -29,7 +29,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
29
29
  | 自动化 / 定时任务 / 提交触发 / cron | `openxiangda-workflow-automation` | `openxiangda automation validate / create / publish / enable` |
30
30
  | App Function / function_call / 可复用后端逻辑 | `openxiangda-workflow-automation` | declare `src/resources/functions/<code>.json` + `src/functions/<code>/index.ts` |
31
31
  | 应用登录 / Auth SDK / 手机号验证码 / CAS / 钉钉免登 | `openxiangda-architecture-design` + `openxiangda-page` | design security gate first, then declare `src/resources/auth/<code>.json` and use `createAuthClient` / `LoginPage` |
32
- | 角色 / 权限组 / 字段权限 / 数据范围 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
32
+ | 账号权限 / 角色 / 权限组 / 字段权限 / 数据范围 / RBAC | `openxiangda-permission-settings` | `openxiangda design gates --topic permissions --json` → choose permission mode → `openxiangda permission ...` / `openxiangda settings ...` |
33
33
  | 平台部门 / 平台账号 / 重置密码 / 组织账号管理 | `openxiangda-architecture-design` + `openxiangda-page` | check `openxiangda organization capabilities --json`; use `sdk.organization` / `ctx.organization` only after `app:organization:manage` is granted |
34
34
  | 外部人员无需登录访问 / 公开报名 / 公开查询 / public page | `openxiangda-architecture-design` + `openxiangda-permission-settings` + `openxiangda-page` | 先确认公开范围、外部角色、ticket、grants;再声明 `routes` + `public-access`,并用 `OpenXiangdaProvider` + `OpenXiangdaPageProvider` + `PublicAccessGate` |
35
35
  | 看应用结构 / 快照 / 对比 / 诊断 / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot <APP_XXX> --profile <name> --json` |
@@ -40,6 +40,9 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
40
40
  ### Hard rules — always
41
41
 
42
42
  - ✅ Publish classic workspaces through `openxiangda workspace publish --profile <name>`; publish React SPA forms first with `openxiangda workspace publish --form <formCode>`, then resources/runtime through `openxiangda resource validate` → `openxiangda resource publish --dry-run` → `openxiangda resource publish` → `openxiangda runtime deploy`.
43
+ - ✅ High-risk changes must be attached to SDD records before implementation/publish. Run `openxiangda sdd context --json`; for forms/pages/resources/functions/automations/workflows/JS_CODE/runtime/config changes create or continue `openxiangda sdd propose <change>`, wait for user confirmation, run `openxiangda sdd approve <change>`, then verify with `openxiangda sdd verify <change> --changed`.
44
+ - ✅ Before publishing AI-authored changes, run `openxiangda workspace plan --changed --json` and `openxiangda workspace check --changed`. Fix reported fake IDs, fallback data, direct `/openxiangda-api` page calls, missing schema, or resource/runtime omissions before publish.
45
+ - ✅ When unsure about runtime response shape, run `openxiangda contract explain <contract-name> --json`. `sdk.form.create` returns identifiers and generated serial number values only; fetch detail explicitly for full row data, formula results, or other server-generated fields.
43
46
  - ✅ Architecture-class requests are plan-gated by default. For 新应用、复杂页面、登录注册、公开访问、权限数据范围、流程自动化、连接器/通知、外部集成, first run `openxiangda doctor --json` when a workspace exists and `openxiangda design gates --topic <code> --json`; then ask the user to confirm the design. Before confirmation, only read files, inspect/snapshot, dry-run, ask questions, and write/output design docs. Do not edit source files, write platform resources, send notifications, publish, or deploy.
44
47
  - ✅ `runtime deploy` defaults to staged multipart `dist/` uploads plus a final manifest. Use `--upload-mode legacy-json` only for old platform servers.
45
48
  - ✅ User token lives in `~/.openxiangda/profiles.json`; project state in `.openxiangda/state.json` (IDs only).
@@ -48,6 +51,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
48
51
  - ✅ For non-trivial app requirements, run the architecture design gate first: forms, fields, pages, permissions, data views, status/workflow, automation, notifications, and development tasks must be decided before coding.
49
52
  - ✅ For app login/auth requirements, run the auth security gate before coding: enabled methods, registration policy, identity matching keys, provider boundary, default roles, rate limits, audit fields, and third-party ownership must be confirmed.
50
53
  - ✅ For public/no-login access requirements, run the public access design gate before coding: public routes, external role codes, guest vs ticket, form/dataView/function/connector grants, backend permission groups, and visible loading/error fallback states must be confirmed. New React SPA apps use `/view/:appType/public/*`, `src/resources/routes/`, `src/resources/public-access/`, and `PublicAccessGate`; route children that use Page SDK hooks must be inside `OpenXiangdaProvider` + `OpenXiangdaPageProvider`.
54
+ - ✅ For account/role/permission/data-scope/query-param authorization requirements, run `openxiangda design gates --topic permissions --json` before coding. Pick one mode from `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, write the permission matrix, and use roles/page-groups/form-groups/App Functions for real enforcement.
51
55
  - ✅ Public/no-login validation must inspect the JSON envelope, not only HTTP status. HTTP 200 with `code: "PUBLIC_GRANT_DENIED"` is a denied request; success requires `code` `200`, `"200"`, or compatible `0`, and no `success: false`.
52
56
  - ✅ Use first-class resource CLI commands for discovery, diagnosis, and small live fixes: `route`, `public-access`, `auth-config`, `function`, `connector`, `notification`, `organization`, `data-view`, `menu`, and `permission`. For multi-resource formal development, still prefer `src/resources/**` + `openxiangda resource validate|plan|publish`. If a direct CLI write is intentionally used, add `--write-manifest` when the repository should stay the source of truth.
53
57
  - ✅ For form-entry UX, pick components in this order: OpenXiangda platform form components → `antd` / `antd-mobile` wrappers → custom component only when neither exists.
@@ -65,6 +69,8 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
65
69
  - ❌ Using `openxiangda form create` / `form publish` / `page publish` as the normal page generation path. They are low-level repair commands.
66
70
  - ❌ Implementing an architecture-class request before the user confirms the design. This includes creating files, mutating platform resources, publishing runtime, sending notifications, or calling live write/delete endpoints.
67
71
  - ❌ Running a full publish after editing one file. Default to `--changed --dry-run` → `--changed`, or targeted `--page` / `--form`.
72
+ - ❌ Generating fake form instance IDs, mock rows, fallback schemas, or fallback display data to make a broken API path look successful.
73
+ - ❌ Treating query parameters, frontend button hiding, hardcoded roles, mock role context, or `PermissionBoundary` alone as sensitive authorization. Query parameters may provide context or ticket input only; sensitive data still needs public-access grants, platform roles, form permission groups, or App Function checks.
68
74
  - ❌ Storing tokens, AK, SK, or third-party API secrets in project files. Shared env (`APP_OSS_*`, feedback robot) goes to `~/.openxiangda/.env`.
69
75
  - ❌ Raw native HTML form controls in AI-authored workspace code (`<input>`, `<select>`, `<textarea>`, file inputs, hand-written pickers, hand-written upload controls). They are allowed only inside OpenXiangda SDK/platform component internals.
70
76
  - ❌ Using `?publicAccess=guest` or form `publicAccess` settings for new React SPA apps. Those are legacy `sy-lowcode-view` compatibility only.
@@ -157,6 +163,8 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
157
163
  - For external APIs, create a connector manifest in `src/resources/connectors/` and call it from pages through `sdk.connector`; never put third-party API keys in page source.
158
164
  - For reusable backend logic shared by pages, automations, and workflows, create an App Function in `src/resources/functions/<functionCode>.json` plus `src/functions/<functionCode>/index.ts`. Call it from pages with `sdk.function.invoke`, from automation/workflow graphs with `function_call`, or from the runtime endpoint when the caller has app automation management permission. Current App Function MVP exposes controlled helpers such as `ctx.resources`, `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, `ctx.organization`, and `ctx.platform.api`; it does not expose raw SQL or Redis. Runtime functions also receive trusted role context on `ctx.operator.roleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, and `ctx.permissions`; use these for server-side business authorization. Do not trust role codes sent in page input for sensitive actions. App Function form writes are trusted backend writes governed by declared `resources.forms` and function invocation authorization; do not grant normal users raw submit permission to internal forms just to let `ctx.form.createOne/updateOne/updateById` run.
159
165
  - For app-managed platform departments/accounts, use `sdk.organization.departments.*`, `sdk.organization.accounts.*`, `ctx.organization.departments.*`, or `ctx.organization.accounts.*`. The real current user/audit actor must hold `app:organization:manage` on the app; runtime service principals do not bypass this permission. Do not call legacy `/user` or `/department` write endpoints from apps. Do not include `password` in account updates; reset target accounts with `resetPassword`, and change the current user's password with `changeMyPassword({ oldPassword, newPassword })`.
166
+ - For account, role, data-scope, organization-account, RBAC, or query-param authorization designs, read `references/permission-design-patterns.md` and choose a permission mode before writing forms/pages/resources. Formal backends should use platform roles, page permission groups, form permission groups, public-access grants, and App Function role/scope checks; frontend hiding is display only.
167
+ - When a role such as 校区管理员 will create app roles, assign role members, grant role permissions, maintain permission groups, or manage organization accounts, its role manifest must declare the needed `apiPermissionCodes` such as `app:role:manage`, `app:page-permission-group:manage`, `app:form-permission-group:manage`, and `app:organization:manage`. If that administrator creates another administrator role, the new role must also declare its own `apiPermissionCodes`; permissions are not inherited from the creator.
160
168
  - For application login, declare auth resources in `src/resources/auth/<code>.json` and publish them with `openxiangda resource publish`. App Function auth providers may validate external credentials and return only an identity assertion; they must never issue tokens, set cookies, or write platform user/binding tables directly. The platform auth service decides create/bind/reject and issues tokens.
161
169
  - For external/no-login pages in React SPA apps, declare `src/resources/routes/<code>.json` and `src/resources/public-access/<code>.json`, put the page under `/view/:appType/public/*`, and use `PublicAccessGate` or `createPublicAccessClient`. The route tree must also include `OpenXiangdaPageProvider` inside `OpenXiangdaProvider` before any `usePageSdk()` / `usePageContext()` / `useDataSource()` call; otherwise pages throw `usePageSdkStore 必须在 PageProvider 内使用`. Always provide `fallback` and `errorFallback` for public routes so slow networks, missing tickets, or expired tickets do not render blank pages. Public guest access to forms, dataViews, functions, and connectors is denied unless policy `grants` explicitly names the resource; backend permission groups still apply.
162
170
  - When verifying public access, parse the response body and reject string business errors such as `PUBLIC_GRANT_DENIED` even if the HTTP status is 200. Do not use `response.ok` alone as the success condition.
@@ -204,6 +212,7 @@ Core CLI / state:
204
212
  - `references/data-views.md` — `src/resources/data-views` materialized/live data view resources, DSL, permissions, refresh, CLI commands, and runtime SDK usage.
205
213
  - `references/notifications.md` — `src/resources/notifications`, notification templates/type bindings, and `sdk.notification` / `ctx.notification`.
206
214
  - `references/best-practices.md` — initialized examples for modular pages, state lifecycles, role governance, permission isolation, high-performance queries, portal shells, workflow boundaries, and automation patterns.
215
+ - `references/permission-design-patterns.md` — account/role/permission design modes: managed platform account, existing user assignment, static role permission, query-param context, and the required resource/permission matrix.
207
216
 
208
217
  Form authoring:
209
218
 
@@ -222,6 +231,7 @@ Workflow / automation / permissions:
222
231
  - `references/workflow-v3.md` — workflow v3 definitions, JS_CODE nodes, App Function `function_call`, and trusted_node mode.
223
232
  - `references/automation-v3.md` — automation triggers, conditions, App Function `function_call`, and publishing.
224
233
  - `references/permissions-settings.md` — roles, page/form permission groups, settings.
234
+ - `references/permission-design-patterns.md` — required when designing accounts, roles, RBAC, organization-account governance, data scope, or query-param authorization.
225
235
 
226
236
  Platform domain knowledge (read these first when generating forms or pages so the output matches the live platform behavior):
227
237
 
@@ -27,7 +27,7 @@ form codes, field names, permissions, and page route names.
27
27
  - business status lifecycle
28
28
  - real approval workflow
29
29
  - automation / JS_CODE
30
- - role governance / permissions
30
+ - access governance / role governance / permissions
31
31
  - optional admin UI template:
32
32
  `glass-home-dashboard`, `mint-analytics-dashboard`,
33
33
  `ops-monitor-dashboard`, or `work-order-list-drawer`
@@ -104,10 +104,25 @@ same `domain/` and `shared/services/` when the business behavior is the same.
104
104
 
105
105
  ## Permission And Data Isolation
106
106
 
107
- For apps with dynamic roles:
108
-
109
- 1. Create an app role maintenance form, such as `app-role`.
110
- 2. Use automation / JS_CODE to sync role records to platform roles.
107
+ For account, role, RBAC, data-scope, or query-parameter authorization
108
+ requirements, read `permission-design-patterns.md` and choose one mode before
109
+ writing code:
110
+
111
+ - `managed-platform-account`: the app owns platform account/department lifecycle
112
+ and role synchronization.
113
+ - `existing-platform-user-assignment`: the app assigns existing platform users
114
+ to app roles and business scopes.
115
+ - `static-role-permission`: roles and permission groups are fixed resources.
116
+ - `query-param-context`: query parameters are context, filters, or ticket input
117
+ only; sensitive access still needs real authorization.
118
+
119
+ For apps with managed accounts or dynamic roles:
120
+
121
+ 1. Create the minimum governance forms required by the chosen mode:
122
+ `organization_unit`, `system_account`, `role_assignment`, or a smaller app
123
+ role maintenance form such as `app-role`.
124
+ 2. Use App Functions, automation, or JS_CODE to sync organization units,
125
+ platform accounts, and role records to platform departments/accounts/roles.
111
126
  3. Model visible scope fields with maintainable controls:
112
127
  - personnel and departments use platform personnel/department fields
113
128
  - enum scopes use `SelectField` / `RadioField` with `options`
@@ -126,11 +141,20 @@ For apps with dynamic roles:
126
141
  5. Create page permission groups for entry visibility.
127
142
  6. Create form permission groups with condition-based data permissions for real
128
143
  data isolation.
144
+ 7. For delegated administrators, add role API permissions in
145
+ `src/resources/roles/<code>.json` with `apiPermissionCodes`, for example
146
+ `app:role:manage`, `app:page-permission-group:manage`,
147
+ `app:form-permission-group:manage`, and `app:organization:manage`.
148
+ 8. Put sensitive writes behind App Functions with server-side role/scope checks.
129
149
 
130
150
  Frontend button hiding is only user experience. It is not permission control.
131
151
  Every sensitive action must still be protected by platform role/form permission
132
152
  groups or backend-side JS_CODE checks.
133
153
 
154
+ Query parameters are not permission control either. They may prefill context,
155
+ select filters, or carry a signed/expiring ticket, but tampering with a query
156
+ parameter must not expand sensitive data access.
157
+
134
158
  ## Form Copy And Field Visibility
135
159
 
136
160
  - Every visible form field should have a short, user-facing placeholder.
@@ -220,6 +244,9 @@ groups or backend-side JS_CODE checks.
220
244
  - `service-ticket-ops`: custom data management page based on
221
245
  `DataManagementList`, split into page, components, hook, query builder, and
222
246
  detail drawer.
247
+ - `access-governance`: account/role permission mode decision, organization,
248
+ account, role assignment, sync function, roles, page-group, and form-group
249
+ skeletons.
223
250
  - `role-governance`: role maintenance form, role sync JS_CODE, maintainable
224
251
  scope fields, hidden permission keys, and permission-group resource examples.
225
252
  - `pc-portal-shell`: app-shell PC portal with routes, modules, components, and
@@ -636,11 +636,13 @@ Requires Bearer token. Returns one execution record with raw `nodeExecutionLogs`
636
636
 
637
637
  ### GET `/apps/:appType/roles`
638
638
 
639
- Requires Bearer token. Lists app roles visible to the current user.
639
+ Requires Bearer token and app role management permission for role-setting
640
+ operations. Lists app roles visible to the current user.
640
641
 
641
642
  ### POST `/apps/:appType/roles`
642
643
 
643
- Requires Bearer token. Creates an app-scoped role.
644
+ Requires Bearer token with `app:role:manage` on the app. Creates an app-scoped
645
+ role.
644
646
 
645
647
  Body:
646
648
 
@@ -658,11 +660,13 @@ Requires Bearer token. Returns app role detail.
658
660
 
659
661
  ### PUT `/apps/:appType/roles/:roleId`
660
662
 
661
- Requires Bearer token. Updates app role metadata.
663
+ Requires Bearer token with `app:role:manage` on the app. Updates app role
664
+ metadata.
662
665
 
663
666
  ### DELETE `/apps/:appType/roles/:roleId`
664
667
 
665
- Requires Bearer token. Deletes a non-system app role.
668
+ Requires Bearer token with `app:role:manage` on the app. Deletes a non-system
669
+ app role.
666
670
 
667
671
  ### GET `/apps/:appType/roles/:roleId/users`
668
672
 
@@ -670,11 +674,33 @@ Requires Bearer token. Lists users under an app role.
670
674
 
671
675
  ### POST `/apps/:appType/roles/:roleId/users`
672
676
 
673
- Requires Bearer token. Adds users to an app role.
677
+ Requires Bearer token with `app:role:manage` on the app. Adds users to an app
678
+ role.
674
679
 
675
680
  ### DELETE `/apps/:appType/roles/:roleId/users/:userId`
676
681
 
677
- Requires Bearer token. Removes one user from an app role.
682
+ Requires Bearer token with `app:role:manage` on the app. Removes one user from
683
+ an app role.
684
+
685
+ When publishing role resources, OpenXiangda can also assign app-scoped API
686
+ permissions to the role:
687
+
688
+ ```json
689
+ {
690
+ "code": "campus_admin",
691
+ "name": "校区管理员",
692
+ "apiPermissionMode": "merge",
693
+ "apiPermissionCodes": [
694
+ "app:role:manage",
695
+ "app:page-permission-group:manage",
696
+ "app:form-permission-group:manage"
697
+ ]
698
+ }
699
+ ```
700
+
701
+ This is required when the role itself should create roles, assign role members,
702
+ or maintain permission groups. The first grant is normally done by a platform
703
+ administrator or an existing app role that already has `app:role:manage`.
678
704
 
679
705
  ### GET `/apps/:appType/page-permission-groups`
680
706
 
@@ -18,6 +18,9 @@ Guidelines:
18
18
  - Use `sdk.notification.sendByType` and `batchSendByType` for reusable business messages. Custom notification types must be declared in `src/resources/notifications/` and published with `openxiangda resource publish`.
19
19
  - Use `sdk.organization.departments.*` and `sdk.organization.accounts.*` only for admin-style app pages that intentionally manage platform departments/accounts. The current user needs `app:organization:manage` on the app. Do not call legacy `/user` or `/department` write endpoints from pages.
20
20
  - 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.
21
+ - `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.
22
+ - Missing `formInstId` / `formInstanceId` is a real error in strict mode. Do not create fake IDs, mock rows, or fallback display data to hide it.
23
+ - `lookupAfterCreate` is legacy compatibility only. New pages should save, read the returned instance ID, then explicitly query detail if needed.
21
24
  - Use `createAuthClient` from `openxiangda/runtime` or `LoginPage` / `useAuth` from `openxiangda/runtime/react` for application login pages. Auth provider App Functions return identity assertions only; platform auth owns create/bind/reject/token decisions.
22
25
  - Use `PublicAccessGate` from `openxiangda/runtime/react` or `createPublicAccessClient` from `openxiangda/runtime` for React SPA public pages under `/view/:appType/public/*`. Do not append old `?publicAccess=guest` links in new apps.
23
26
  - In React SPA apps, wrap route children with `OpenXiangdaPageProvider` inside `OpenXiangdaProvider` before using `usePageSdk()`, `usePageContext()`, `useDataSource()`, or `useFormViewPermissions()`. `OpenXiangdaProvider` alone is not enough and missing the page provider causes `usePageSdkStore 必须在 PageProvider 内使用`.
@@ -16,6 +16,10 @@ activate. Use `openxiangda workspace publish ...` as the external entry.
16
16
  For day-to-day AI edits, prefer a targeted publish:
17
17
 
18
18
  ```bash
19
+ # First ask OpenXiangda for the release plan and reliability checks.
20
+ openxiangda workspace plan --changed --json
21
+ openxiangda workspace check --changed
22
+
19
23
  # Preview what git-touched forms/pages would publish.
20
24
  openxiangda workspace publish --profile <name> --changed --dry-run
21
25
 
@@ -39,6 +43,18 @@ Targeted publish skips `src/resources` by default. Add `--resources` if resource
39
43
  manifests changed too, or run `openxiangda resource plan|publish` separately.
40
44
  Use `--skip-resources` when you explicitly want forms/pages only.
41
45
 
46
+ React SPA workspaces use a fixed release order:
47
+
48
+ ```bash
49
+ openxiangda workspace plan --changed --json
50
+ openxiangda workspace publish --profile <name> --form <formCode>
51
+ openxiangda resource publish --profile <name>
52
+ openxiangda runtime deploy --profile <name>
53
+ ```
54
+
55
+ If `workspace check --changed` reports risky AI fallback code, missing schema,
56
+ or direct platform API calls, fix those issues before publishing.
57
+
42
58
  The CLI injects:
43
59
 
44
60
  - `OPENXIANGDA_PROFILE`
@@ -0,0 +1,216 @@
1
+ # Permission Design Patterns
2
+
3
+ Use this reference before implementing OpenXiangda requirements that mention
4
+ accounts, roles, permission groups, organization scope, data scope, or query
5
+ parameter based access.
6
+
7
+ The rule is simple: choose the permission mode first, then design forms,
8
+ resources, App Functions, pages, and publish order. Do not start by hiding
9
+ buttons in the frontend.
10
+
11
+ ## Mode Selection
12
+
13
+ ### `managed-platform-account`
14
+
15
+ The app owns account governance. Use this for formal admin systems, external
16
+ account lifecycle, school/company tenant management, or any workflow where the
17
+ app must create, update, disable, reset, or place platform accounts into
18
+ departments and roles.
19
+
20
+ Required design:
21
+
22
+ - `organization_unit` form: business organization tree, parent key, manager,
23
+ platform department ID, sync status, sync message, last synced time.
24
+ - `system_account` form: login name, display name, phone/email, organization
25
+ unit, enabled state, platform user ID, sync status, last synced time.
26
+ - `role_assignment` form: account, role codes, business scope fields, hidden
27
+ scalar scope keys, platform role member sync state.
28
+ - `src/resources/roles/*.json`: stable app role codes.
29
+ - `src/resources/permissions/page-groups/*.json`: menu, route, and page access.
30
+ - `src/resources/permissions/form-groups/*.json`: submit/view/manage, data
31
+ scope, operations, and `fieldAccessPolicy`.
32
+ - `apiPermissionCodes` on roles that are allowed to manage roles, role members,
33
+ permission groups, or organization accounts.
34
+ - Sync App Functions or trusted JS_CODE: sync organization units, accounts, and
35
+ role assignments to the platform.
36
+ - Page `PermissionBoundary` and runtime menus for display control only.
37
+ - Backend role/scope checks in App Functions for sensitive writes.
38
+
39
+ Platform account writes must use `sdk.organization.*` or `ctx.organization.*`.
40
+ The real current operator must have `app:organization:manage`. Do not call
41
+ legacy `/user` or `/department` write endpoints from app code.
42
+
43
+ ### `existing-platform-user-assignment`
44
+
45
+ The platform already has users and departments. The app only assigns existing
46
+ users to app roles and business scopes.
47
+
48
+ Required design:
49
+
50
+ - Role assignment form with `UserSelectField` / department fields for members.
51
+ - Business scope fields such as region, department, project, customer, class, or
52
+ college; derive hidden scalar keys with `valueSync` when permission
53
+ conditions need scalar matching.
54
+ - Role sync App Function or automation only updates app role members, not
55
+ account records.
56
+ - Roles, page groups, and form groups remain the backend enforcement layer.
57
+
58
+ Use this when HR/IT or the platform admin owns account lifecycle.
59
+
60
+ ### `static-role-permission`
61
+
62
+ Roles are fixed and membership is maintained by platform admins or resource
63
+ manifests. Use this for small internal apps, stable admin roles, and apps where
64
+ no in-app role maintenance screen is required.
65
+
66
+ Required design:
67
+
68
+ - `roles` resources.
69
+ - Page permission groups for menus/routes.
70
+ - Form permission groups for submit/view/manage/data scope/field access.
71
+ - Optional `PermissionBoundary` for UI display.
72
+
73
+ Do not create role/account governance forms just because a page has two static
74
+ roles.
75
+
76
+ ### `query-param-context`
77
+
78
+ Query parameters provide context only. Use this for low-risk links, prefilled
79
+ filters, external ticket input, public lookup keys, or deep links.
80
+
81
+ Allowed:
82
+
83
+ - select an initial tab, filter, date range, organization code, or public ticket
84
+ code
85
+ - prefill a form field before the user submits
86
+ - carry a signed/expiring ticket that is validated by public-access policy or an
87
+ App Function
88
+
89
+ Not allowed:
90
+
91
+ - granting sensitive data access only because `?role=admin` or `?dept=...` is
92
+ present
93
+ - deciding form data scope only in the frontend
94
+ - bypassing roles, form permission groups, public-access grants, or App
95
+ Function checks
96
+
97
+ If the page reads sensitive data, performs writes, or exposes internal records,
98
+ combine query parameters with real authorization: public-access grants, roles,
99
+ form permission groups, or server-side App Function checks.
100
+
101
+ ## Standard Resource Closure
102
+
103
+ A complete high-governance permission design usually has this closure:
104
+
105
+ ```text
106
+ organization_unit
107
+ -> system_account
108
+ -> role_assignment
109
+ -> src/resources/roles
110
+ -> permissions/page-groups
111
+ -> permissions/form-groups
112
+ -> sync App Functions / JS_CODE
113
+ -> PermissionBoundary for display
114
+ -> backend role/scope checks for sensitive writes
115
+ ```
116
+
117
+ Pages may show or hide navigation with `useAppMenus`, `useCanAccessRoute`, and
118
+ `PermissionBoundary`, but these are not the authority. Real authority is the
119
+ platform permission group, public-access grant, or App Function role/scope
120
+ check.
121
+
122
+ ## Role Setting Delegation
123
+
124
+ Setting application roles is itself permission controlled. A user can be a
125
+ business administrator in the app and still fail to create roles, assign role
126
+ members, or grant role permissions unless that user's current app role has the
127
+ matching API permission.
128
+
129
+ Use role `apiPermissionCodes` in `src/resources/roles/<code>.json` for delegated
130
+ administrators:
131
+
132
+ ```json
133
+ {
134
+ "code": "campus_admin",
135
+ "name": "校区管理员",
136
+ "description": "维护本校区账号、角色和权限",
137
+ "apiPermissionMode": "merge",
138
+ "apiPermissionCodes": [
139
+ "app:role:manage",
140
+ "app:page-permission-group:manage",
141
+ "app:form-permission-group:manage",
142
+ "app:organization:manage"
143
+ ]
144
+ }
145
+ ```
146
+
147
+ Platform behavior to design against:
148
+
149
+ - `app:role:manage` is required to create/update/delete app roles, assign app
150
+ roles to users, and assign app-scoped API permissions to roles.
151
+ - `app:page-permission-group:manage` is required to create/update/delete page
152
+ permission groups.
153
+ - `app:form-permission-group:manage` is required to create/update/delete form
154
+ permission groups.
155
+ - `app:organization:manage` is required for platform account/department writes.
156
+ - Platform roles still require platform-admin authority; delegated app admins
157
+ can only manage app-scoped roles and app-scoped API permissions.
158
+
159
+ When a platform administrator first grants a role such as `campus_admin`, that
160
+ role must include the API permissions needed for its future work. If
161
+ `campus_admin` later creates another role that also manages accounts, roles, or
162
+ permission groups, the new role must also be published with its own
163
+ `apiPermissionCodes`. Do not rely on inherited privilege from the creator.
164
+
165
+ `apiPermissionMode` defaults to `merge`. Use `replace` only when you explicitly
166
+ want the manifest to become the full API permission list for the role.
167
+ `openxiangda resource publish` fails if a declared permission code is missing
168
+ from the platform seed data, which is preferable to silently creating an
169
+ administrator role that cannot administer.
170
+
171
+ ## Design Matrix
172
+
173
+ Before implementation, write a matrix with at least these columns:
174
+
175
+ | Area | Decision |
176
+ | ---- | -------- |
177
+ | Permission mode | `managed-platform-account` / `existing-platform-user-assignment` / `static-role-permission` / `query-param-context` |
178
+ | Account source | app-created platform account / existing platform user / static member / public context |
179
+ | Role source | role assignment form / platform role admin / roles manifest / public external role |
180
+ | Role setting delegation | which roles need `apiPermissionCodes`, who grants them first, and whether delegation can continue |
181
+ | Business scope fields | visible fields and hidden scalar keys |
182
+ | Page access | menu codes, route codes, path patterns, role codes |
183
+ | Form access | submit/view/manage operations, data scope, field access policy |
184
+ | Backend checks | App Functions that validate role/scope and deny bad input |
185
+ | Query parameters | context only, ticket validation, tamper-deny test |
186
+ | Publish order | forms -> resources -> functions/runtime |
187
+
188
+ ## Anti-Patterns
189
+
190
+ - Query parameters as sensitive authorization.
191
+ - Frontend-only button hiding or route hiding.
192
+ - Hardcoded role strings scattered through pages.
193
+ - Delegated administrators without `app:role:manage` or required permission
194
+ group management API permissions.
195
+ - New roles created by delegated administrators without their own
196
+ `apiPermissionCodes` when they are expected to administer accounts or roles.
197
+ - Mock role context, fake account IDs, fake form instance IDs, or empty-array
198
+ fallback displays.
199
+ - Giving users all form data and filtering in the browser.
200
+ - Direct platform account API calls that bypass OpenXiangda organization SDK.
201
+ - Reusing public visitor roles as internal admin roles.
202
+
203
+ ## Acceptance
204
+
205
+ - `openxiangda design gates --topic permissions --json` was run and its open
206
+ questions are answered.
207
+ - Permission mode and matrix are recorded in SDD/design docs.
208
+ - Admin roles that can manage roles, members, permission groups, or accounts
209
+ declare `apiPermissionCodes`.
210
+ - `openxiangda permission audit --json` has no high-risk gaps.
211
+ - Allowed role paths and denied role paths are both tested.
212
+ - Query parameter tampering does not expand sensitive access.
213
+ - App Functions that mutate sensitive data verify `ctx.operator.roleCodes`,
214
+ `ctx.operator.currentRoleCode`, business scope, or explicit public ticket.
215
+ - Managed account flows verify `app:organization:manage` before account or
216
+ department writes.
@@ -2,6 +2,41 @@
2
2
 
3
3
  OpenXiangda permission APIs use ordinary user Bearer tokens. The effective permissions are the same as the current frontend user.
4
4
 
5
+ Before implementing account, role, RBAC, organization-account, data-scope, or
6
+ query-parameter authorization requirements, run:
7
+
8
+ ```bash
9
+ openxiangda design gates --topic permissions --json
10
+ ```
11
+
12
+ Then choose one permission mode from
13
+ `references/permission-design-patterns.md`:
14
+
15
+ - `managed-platform-account`: the app creates/maintains platform accounts,
16
+ departments, and role members.
17
+ - `existing-platform-user-assignment`: the app selects existing platform users
18
+ and assigns app roles/business scopes.
19
+ - `static-role-permission`: fixed roles and permission groups, no account
20
+ governance screens.
21
+ - `query-param-context`: query parameters are context, filters, or ticket input
22
+ only; they are not sensitive authorization.
23
+
24
+ Frontend hiding, `PermissionBoundary`, route guards, and query parameters are
25
+ display or context tools. Sensitive data access must be enforced by platform
26
+ roles, page permission groups, form permission groups, public-access grants, or
27
+ backend App Function role/scope checks.
28
+
29
+ Role management is permission controlled too. A role such as "校区管理员" that
30
+ will create app roles, assign role members, or grant role permissions must be
31
+ published with API permissions on the role resource. Use `apiPermissionCodes`
32
+ in `src/resources/roles/<code>.json`; otherwise the user may have the business
33
+ role but still fail when calling role-setting APIs.
34
+
35
+ For app-managed platform accounts or departments, use `sdk.organization.*` or
36
+ `ctx.organization.*`. The real current operator must have
37
+ `app:organization:manage`; do not bypass OpenXiangda with legacy user or
38
+ department write APIs.
39
+
5
40
  ## Role Codes
6
41
 
7
42
  Use stable local role codes:
@@ -16,6 +51,29 @@ Use stable local role codes:
16
51
 
17
52
  Role IDs are platform-specific. Store them only in `.openxiangda/state.json` under the active profile.
18
53
 
54
+ For delegated role administrators, include API permission codes on the role:
55
+
56
+ ```json
57
+ {
58
+ "code": "campus_admin",
59
+ "name": "校区管理员",
60
+ "description": "维护本校区账号、角色和权限",
61
+ "apiPermissionMode": "merge",
62
+ "apiPermissionCodes": [
63
+ "app:role:manage",
64
+ "app:page-permission-group:manage",
65
+ "app:form-permission-group:manage",
66
+ "app:organization:manage"
67
+ ]
68
+ }
69
+ ```
70
+
71
+ `app:role:manage` covers app role creation, role member assignment, and assigning
72
+ app-scoped API permissions to roles. Page/form permission group maintenance use
73
+ their own permission codes. If a delegated administrator creates another role
74
+ that should also administer accounts or roles, that new role also needs its own
75
+ `apiPermissionCodes`; permissions are not inherited from the creator.
76
+
19
77
  ## Page Permission Groups
20
78
 
21
79
  Page permission groups map role codes to visible menu targets. Form menus use their `FORM_...`
@@ -19,9 +19,16 @@
19
19
  - **写入端**:前端/Agent 提交什么格式,数据库就存什么格式。
20
20
  - **读取端**:API 查询返回的就是原始 JSON 结构,**不会**额外执行字典翻译、用户名补全或部门名拼接。
21
21
  - **展示端**:选择类字段的中文名(label)已经随数据写入,前端直接取 `item.label` 即可,**禁止**再次基于 `value` 去查字典。
22
- - **操作端**:业务逻辑、过滤、查询条件统一以 `item.value` 为准(稳定、不随显示文案变化)。
23
-
24
- > 这意味着:label 是"快照",value 是"语义键"。一旦字段值落库,label 与 value 一起被冻结,后续即便选项字典改名也不会影响历史数据展示。
22
+ - **操作端**:业务逻辑、过滤、查询条件统一以 `item.value` 为准(稳定、不随显示文案变化)。
23
+
24
+ > 这意味着:label 是"快照",value 是"语义键"。一旦字段值落库,label 与 value 一起被冻结,后续即便选项字典改名也不会影响历史数据展示。
25
+
26
+ ### 1.3 保存接口响应契约
27
+
28
+ - `sdk.form.create` / `saveFormData` / `submitFormData` 只承诺返回实例标识和保存时已生成的流水号字段,例如 `formInstId`、`formInstanceId`、`processInstanceId`、`serialNumber`、`serialNumbers`。
29
+ - 保存接口**不返回完整业务数据**,也不承诺返回公式回填或其他服务端格式化后的字段值。
30
+ - 需要读取完整行数据、公式回填或非流水号服务端字段时,必须在拿到 `formInstId` 后显式调用 `sdk.form.getDetail({ formUuid, formInstId })`。
31
+ - 禁止生成假 ID(例如 `inst_${Date.now()}`),禁止在业务代码中手写 `response.data || response.result` 猜返回结构。
25
32
 
26
33
  ---
27
34
 
@@ -64,7 +71,15 @@ type MultiSelectValue = Option[];
64
71
  ```
65
72
 
66
73
  - **单选/多选的判定**取决于字段配置 `multiple: boolean`,而非字段类型本身。
67
- - **多选字段始终为数组**,即使用户只勾选了一项,也是 `[{label, value}]`,**不要降级为对象**。
74
+ - **多选字段始终为数组**,即使用户只勾选了一项,也是 `[{label, value}]`,**不要降级为对象**。
75
+
76
+ ### 2.2 字段类型变更
77
+
78
+ - 字段创建后对应数据库列类型已经固定。`TextField` 是 `TEXT`,`SelectField` / `RadioField` / 附件 / 子表等复杂字段是 `JSONB`。
79
+ - 把 `TextField` 改成 `SelectField` 不是纯 UI 改动,而是 `TEXT -> JSONB` 的破坏性存储类型变更。
80
+ - 平台会阻断已有列的破坏性类型变更。需要迁移时采用显式 shadow migration:旧列重命名为 `fieldId__legacy_yyyymmdd`,再创建原 `fieldId` 的新类型列。
81
+ - 回填必须提供明确转换规则,例如文本 `"待处理"` 转 `{ "label": "待处理", "value": "pending" }`;平台和 AI 都不能自动猜。
82
+ - 发布前可用 `openxiangda form schema-plan <formCode> --schema-json <file> --json` 检查 `safeAdd`、`compatibleNoop`、`breakingTypeChanges`。
68
83
 
69
84
  ---
70
85
 
@@ -656,13 +656,44 @@ export default async function (ctx) {
656
656
 
657
657
  ## 8. Role — `src/resources/roles/<code>.json`
658
658
 
659
+ Before creating roles or permission groups for account/role/data-scope work,
660
+ run `openxiangda design gates --topic permissions --json` and choose a
661
+ permission mode: `managed-platform-account`,
662
+ `existing-platform-user-assignment`, `static-role-permission`, or
663
+ `query-param-context`. Query parameters and `PermissionBoundary` are never
664
+ sensitive authorization by themselves.
665
+
659
666
  ```json
660
667
  {
661
668
  "code": "sales",
662
- "name": "销售"
669
+ "name": "销售",
670
+ "description": "销售人员"
663
671
  }
664
672
  ```
665
673
 
674
+ Delegated admin roles that manage roles, role members, permission groups, or
675
+ organization accounts must also declare API permissions:
676
+
677
+ ```json
678
+ {
679
+ "code": "campus_admin",
680
+ "name": "校区管理员",
681
+ "description": "维护本校区账号、角色和权限",
682
+ "apiPermissionMode": "merge",
683
+ "apiPermissionCodes": [
684
+ "app:role:manage",
685
+ "app:page-permission-group:manage",
686
+ "app:form-permission-group:manage",
687
+ "app:organization:manage"
688
+ ]
689
+ }
690
+ ```
691
+
692
+ `apiPermissionMode` is `merge` by default. Use `replace` only when the manifest
693
+ should become the full API permission list for that role. Platform role setup
694
+ requires platform-admin authority; app roles can only receive app-scoped API
695
+ permission codes.
696
+
666
697
  ## 9. Page Permission Group — `src/resources/permissions/page-groups/<code>.json`
667
698
 
668
699
  ```json