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.
- package/README.md +6 -0
- package/lib/cli.js +947 -16
- package/lib/design-gates.js +40 -10
- package/lib/sdd.js +777 -0
- package/lib/workspace-init.js +3 -0
- package/openxiangda-skills/SKILL.md +11 -1
- package/openxiangda-skills/references/best-practices.md +32 -5
- package/openxiangda-skills/references/openxiangda-api.md +32 -6
- package/openxiangda-skills/references/pages/page-sdk.md +3 -0
- package/openxiangda-skills/references/pages/publish-flow.md +16 -0
- package/openxiangda-skills/references/permission-design-patterns.md +216 -0
- package/openxiangda-skills/references/permissions-settings.md +58 -0
- package/openxiangda-skills/references/platform-data-model.md +19 -4
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +32 -1
- package/openxiangda-skills/skills/openxiangda-architecture-design/SKILL.md +8 -0
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +2 -0
- package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +38 -1
- package/package.json +2 -1
- package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.mts → ProcessPreview-Ci8_UsbN.d.mts} +4 -0
- package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.ts → ProcessPreview-Ci8_UsbN.d.ts} +4 -0
- package/packages/sdk/dist/build/index.cjs.map +1 -1
- package/packages/sdk/dist/build/index.d.mts +7 -0
- package/packages/sdk/dist/build/index.d.ts +7 -0
- package/packages/sdk/dist/build/index.mjs.map +1 -1
- package/packages/sdk/dist/components/index.cjs +32 -4
- package/packages/sdk/dist/components/index.cjs.map +1 -1
- package/packages/sdk/dist/components/index.d.mts +8 -2
- package/packages/sdk/dist/components/index.d.ts +8 -2
- package/packages/sdk/dist/components/index.mjs +32 -4
- package/packages/sdk/dist/components/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/index.cjs +181 -28
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +2 -2
- package/packages/sdk/dist/runtime/index.d.ts +2 -2
- package/packages/sdk/dist/runtime/index.mjs +181 -28
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +152 -25
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +57 -4
- package/packages/sdk/dist/runtime/react.d.ts +57 -4
- package/packages/sdk/dist/runtime/react.mjs +152 -25
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
- package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +8 -1
- package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +8 -1
- package/templates/openxiangda-react-spa/AGENTS.md +9 -1
- package/templates/openxiangda-react-spa/app-workspace.config.ts +14 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-resources.mdc +5 -1
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +7 -1
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-resources.md +6 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +7 -1
- package/templates/sy-lowcode-app-workspace/AGENTS.md +10 -1
- package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +14 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/README.md +3 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/access-governance.md +239 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/catalog.json +6 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/decision-guide.md +25 -1
- package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +12 -0
package/lib/workspace-init.js
CHANGED
|
@@ -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
|
-
| 角色 / 权限组 / 字段权限 / 数据范围
|
|
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
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|