openxiangda 1.0.131 → 1.0.133

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: openxiangda
3
- description: "Use OpenXiangda for any private low-code platform or sy-lowcode-app-workspace work: architecture design, app creation, forms, pages, resources, workflows, automations, JS_CODE, permissions, data views, connectors, notifications, publishing, deployment, diagnosis, feedback, profile/login state, and CLI commands. Trigger on OpenXiangda, 享搭, 私有化低代码, 低代码平台, .openxiangda/state.json, app-workspace.config.ts, or openxiangda CLI usage."
3
+ description: "Use OpenXiangda for any private low-code platform or sy-lowcode-app-workspace work: architecture design, app creation, forms, pages, resources, workflows, automations, JS_CODE, permissions, organization/account management, data views, connectors, notifications, publishing, deployment, diagnosis, feedback, profile/login state, and CLI commands. Trigger on OpenXiangda, 享搭, 私有化低代码, 低代码平台, .openxiangda/state.json, app-workspace.config.ts, or openxiangda CLI usage."
4
4
  ---
5
5
 
6
6
  # OpenXiangda
@@ -30,6 +30,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
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
  | 角色 / 权限组 / 字段权限 / 数据范围 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
33
+ | 平台部门 / 平台账号 / 重置密码 / 组织账号管理 | `openxiangda-architecture-design` + `openxiangda-page` | check `openxiangda organization capabilities --json`; use `sdk.organization` / `ctx.organization` only after `app:organization:manage` is granted |
33
34
  | 外部人员无需登录访问 / 公开报名 / 公开查询 / public page | `openxiangda-architecture-design` + `openxiangda-permission-settings` + `openxiangda-page` | 先确认公开范围、外部角色、ticket、grants;再声明 `routes` + `public-access`,并用 `OpenXiangdaProvider` + `OpenXiangdaPageProvider` + `PublicAccessGate` |
34
35
  | 看应用结构 / 快照 / 对比 / 诊断 / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot <APP_XXX> --profile <name> --json` |
35
36
  | 登录 / 切换平台 / profile / token / whoami | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
@@ -48,7 +49,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
48
49
  - ✅ 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.
49
50
  - ✅ 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`.
50
51
  - ✅ 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`.
51
- - ✅ Use first-class resource CLI commands for discovery, diagnosis, and small live fixes: `route`, `public-access`, `auth-config`, `function`, `connector`, `notification`, `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.
52
+ - ✅ 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.
52
53
  - ✅ For form-entry UX, pick components in this order: OpenXiangda platform form components → `antd` / `antd-mobile` wrappers → custom component only when neither exists.
53
54
  - ✅ In React SPA apps, keep imports on public package entrypoints such as `openxiangda`, `openxiangda/runtime`, and `openxiangda/runtime/react`. The default template already groups large vendor chunks; if a bundle is too large, use route-level lazy loading and Vite `manualChunks`, not `openxiangda/packages/sdk/dist/...` internal paths.
54
55
  - ✅ For image thumbnails or lightweight previews, use `imageCompression` on `ImageField` or `AttachmentField`. Keep the original `url` for the source file and use `thumbUrl` / `previewUrl` / `variants` for smaller images.
@@ -154,7 +155,8 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
154
155
  - Put engineering-managed resources in `src/resources/` and use `openxiangda resource validate|plan|publish|pull`. `workspace publish` publishes workspace forms/pages first, then runs non-destructive resource upsert. Only pass `--prune` when the user explicitly wants local manifests to delete platform-side extras.
155
156
  - For repeated read-only multi-form queries or predefined dashboard metrics, create a data view manifest in `src/resources/data-views/`. Use `storageMode: "materialized"` for refreshed lists/reports that tolerate delay; use `storageMode: "live"` for bounded real-time joins where source changes must appear immediately. Use row views plus `sdk.dataView.query` for joined lists/lookups; use `viewType: "aggregate"` plus `sdk.dataView.stats` for count/sum/avg/min/max statistics. Add `indexes` only for materialized views. Do not use data views for single-form CRUD, simple linkedForm selects, writes, write-back, unbounded realtime joins, or ad-hoc BI. Read `references/data-views.md` before designing one.
156
157
  - 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.
157
- - 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`, 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.
158
+ - 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
+ - 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 })`.
158
160
  - 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.
159
161
  - 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.
160
162
  - 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.
@@ -194,7 +196,7 @@ Load these references on demand instead of memorizing them. Subskills cite the s
194
196
 
195
197
  Core CLI / state:
196
198
 
197
- - `references/openxiangda-api.md` — `/openxiangda-api/v1` request and response fields.
199
+ - `references/openxiangda-api.md` — `/openxiangda-api/v1` request and response fields, including app-scoped organization APIs.
198
200
  - `references/architecture-design.md` — OpenXiangda architecture design gate, anti-pattern rejection, question gates, final design template, and black-box demo scenario.
199
201
  - `references/workspace-state.md` — `.openxiangda/state.json` shape and profile isolation rules.
200
202
  - `references/connector-resources.md` — `src/resources` manifests, connector schema, App Function boundary, and platform runtime calls.
@@ -212,7 +214,7 @@ Form authoring:
212
214
  Page authoring:
213
215
 
214
216
  - `references/pages/workspace-structure.md` — `src/pages/<pageCode>/` layout and config.
215
- - `references/pages/page-sdk.md` — `openxiangda/runtime` data, connector, data view, notification, and App Function APIs.
217
+ - `references/pages/page-sdk.md` — `openxiangda/runtime` data, connector, data view, notification, organization, and App Function APIs.
216
218
  - `references/pages/publish-flow.md` — workspace publish steps for code pages.
217
219
 
218
220
  Workflow / automation / permissions:
@@ -32,7 +32,7 @@ Notification manifests live under `src/resources/notifications/` and contain `te
32
32
 
33
33
  Data view manifests live under `src/resources/data-views/` and define read-only joined or aggregate query resources. Use `storageMode: "materialized"` for refreshed lists/reports where lag is acceptable, and `storageMode: "live"` for bounded real-time query shapes. Use row views with `sdk.dataView.query` and aggregate views with `sdk.dataView.stats`. Before generating one, confirm freshness tolerance, query bounds, and indexes for materialized filters/sort fields/dimensions/date buckets. Do not use them for single-form CRUD, simple linkedForm selects, writes, write-back, or ad-hoc BI. See `data-views.md` before generating one.
34
34
 
35
- App Function manifests live under `src/resources/functions/`, with source in `src/functions/<functionCode>/index.ts`. Use them for reusable server-side logic that pages, automations, and workflows can share through `sdk.function.invoke` or `function_call` nodes. App Functions expose controlled runtime helpers such as `ctx.resources`, `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, and `ctx.platform.api`; they do not expose raw SQL or Redis in the current MVP. Runtime page invocations also expose trusted role context on `ctx.operator.roleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, and `ctx.permissions`; use that context for server-side authorization, and never trust page-submitted role codes for sensitive actions. App Function form writes are trusted backend writes governed by declared `resources.forms` and function invocation authorization, not by direct page-user submit access to the target form. Use JS_CODE V2 only for node-local workflow/automation scripts.
35
+ App Function manifests live under `src/resources/functions/`, with source in `src/functions/<functionCode>/index.ts`. Use them for reusable server-side logic that pages, automations, and workflows can share through `sdk.function.invoke` or `function_call` nodes. App Functions expose controlled runtime helpers such as `ctx.resources`, `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, `ctx.organization`, and `ctx.platform.api`; they do not expose raw SQL or Redis in the current MVP. Runtime page invocations also expose trusted role context on `ctx.operator.roleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, and `ctx.permissions`; use that context for server-side authorization, and never trust page-submitted role codes for sensitive actions. App Function form writes are trusted backend writes governed by declared `resources.forms` and function invocation authorization, not by direct page-user submit access to the target form. App Function organization writes require `app:organization:manage` on the real operator/audit actor. Use JS_CODE V2 only for node-local workflow/automation scripts.
36
36
 
37
37
  Auth manifests live under `src/resources/auth/`. Use them to enable app-level login methods and bind phone-code/CAS/custom providers to App Functions. Auth provider functions are called only by the platform auth flow. They validate external credentials and return identity assertions such as `phone`, `email`, `externalId`, or `unionId`; they must not issue tokens, set cookies, or mutate platform user/binding tables.
38
38
 
@@ -45,6 +45,62 @@ Allowed channels: `inapp`, `email`, `dingding`, `wechat`, `thirdparty_todo`.
45
45
 
46
46
  Do not include `token`, `secret`, `password`, `authorization`, `auth`, `credential`, or `headers` fields. Platform admins configure channel credentials outside resources.
47
47
 
48
+ ## DingTalk Cards
49
+
50
+ DingTalk notifications prefer cards by default when the tenant has a default card template configured. Application resources should not include `appKey`, `appSecret`, or `agentId`; only override non-secret card settings when the app owns a custom DingTalk card.
51
+
52
+ Standard card example:
53
+
54
+ ```json
55
+ {
56
+ "channelsConfig": {
57
+ "dingding": {
58
+ "enabled": true,
59
+ "content": "{{title}}",
60
+ "config": {
61
+ "deliveryMode": "card_preferred",
62
+ "fallbackToWorkNotice": true,
63
+ "card": {
64
+ "mode": "standard",
65
+ "cardTemplateId": "${DINGTALK_CARD_TEMPLATE_ID}",
66
+ "title": "{{title}}",
67
+ "summary": "{{content}}",
68
+ "jumpUrl": "{{detailUrl}}",
69
+ "fieldConfigs": [
70
+ { "fieldId": "ticketNo", "label": "工单号", "order": 1, "required": false }
71
+ ]
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ Delivery modes:
80
+
81
+ - `card_preferred`: send a DingTalk card first, then fallback to a work notification if card delivery fails.
82
+ - `card_only`: card failure is a send failure.
83
+ - `work_notice_only`: send only a DingTalk work notification.
84
+
85
+ Custom cards use the variables defined in the DingTalk developer console:
86
+
87
+ ```json
88
+ {
89
+ "deliveryMode": "card_only",
90
+ "card": {
91
+ "mode": "custom",
92
+ "cardTemplateId": "${DINGTALK_TICKET_CARD_ID}",
93
+ "paramMap": {
94
+ "title": "{{title}}",
95
+ "ticketNo": "{{ticketNo}}",
96
+ "status": "{{status}}"
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ Standard card payload variables include `title`, `lastMessage`, `content`, `contentList`, `jumpUrl`, `createTime`, `status`, `operator`, `currentUser`, `formData.current`, `workflowData`, `variables`, and `app`. General notifications do not show approval buttons by default.
103
+
48
104
  ## Runtime Calls
49
105
 
50
106
  Code pages:
@@ -61,6 +117,27 @@ await sdk.notification.sendByType({
61
117
  });
62
118
  ```
63
119
 
120
+ DingTalk-specific helpers are available when the caller wants to inspect or force the DingTalk channel:
121
+
122
+ ```ts
123
+ const capabilities = await sdk.notification.capabilities();
124
+
125
+ const preview = await sdk.notification.previewDingTalk({
126
+ notificationType: "reservation_reminder",
127
+ payload: {
128
+ title: "预约提醒",
129
+ },
130
+ });
131
+
132
+ await sdk.notification.sendDingTalk({
133
+ notificationType: "reservation_reminder",
134
+ recipientId: userId,
135
+ payload: {
136
+ title: "预约提醒",
137
+ },
138
+ });
139
+ ```
140
+
64
141
  User-facing in-app message centers should read the platform inbox instead of
65
142
  creating app-specific message/log forms:
66
143
 
@@ -86,6 +163,10 @@ CLI smoke tests:
86
163
 
87
164
  ```bash
88
165
  openxiangda notification preview reservation_reminder --body-json '{"payload":{"title":"测试"}}'
166
+ openxiangda notification capabilities --json
167
+ openxiangda notification dingding-preview reservation_reminder --body-json '{"payload":{"title":"测试"}}'
168
+ openxiangda notification dingding-preview --template-code reservation_reminder --body-json '{"payload":{"title":"测试"}}'
169
+ openxiangda notification dingding-send reservation_reminder --body-json '{"recipientId":"USER_ID","payload":{"title":"测试"}}' --force
89
170
  openxiangda notification send reservation_reminder --body-json '{"recipientId":"USER_ID","payload":{"title":"测试"}}' --force
90
171
  openxiangda notification batch-send reservation_reminder --body-json '{"recipients":[{"recipientId":"USER_ID","payload":{"title":"测试"}}]}' --force
91
172
  ```
@@ -103,6 +184,11 @@ export default async function notify(ctx) {
103
184
  startTime: ctx.formData.current.start_time,
104
185
  },
105
186
  });
187
+
188
+ await ctx.notification.previewDingTalk({
189
+ notificationType: "reservation_reminder",
190
+ payload: { title: "预约提醒" },
191
+ });
106
192
  }
107
193
  ```
108
194
 
@@ -318,6 +318,7 @@ Requires Bearer token. Updates menu sorting and parent relationships in batch.
318
318
  ### POST `/apps/:appType/notifications/send-by-type`
319
319
 
320
320
  Requires Bearer token. Sends a notification in the current app scope.
321
+ Each returned message can include `deliveryMeta` with provider message id, outTrackId, card template id, fallback usage, and provider error summary.
321
322
 
322
323
  ```json
323
324
  {
@@ -360,6 +361,45 @@ Requires app admin permission. Upserts an app/form notification template by code
360
361
  ### POST `/apps/:appType/notifications/templates/preview`
361
362
 
362
363
  Requires Bearer token. Body uses `templateCode` or `templateId` plus `payload`. The CLI command is `openxiangda notification preview <templateCode> --body-json '{"payload":{...}}'`.
364
+ If DingTalk is enabled, the response includes `dingding.cardPreview` with `resolvedCardTemplateId`, `cardParamMap`, `outTrackId`, `workNoticeContent`, `missingVariables`, and `warnings`.
365
+
366
+ ### GET `/apps/:appType/notifications/dingtalk/capabilities`
367
+
368
+ Requires Bearer token. Returns whether the tenant DingTalk channel is enabled, whether app credentials/agent/default card template are configured, and supported delivery/card modes.
369
+
370
+ ### POST `/apps/:appType/notifications/dingtalk/preview`
371
+
372
+ Requires Bearer token. Body can use a notification binding:
373
+
374
+ ```json
375
+ {
376
+ "notificationType": "reservation_reminder",
377
+ "payload": {
378
+ "title": "预约提醒"
379
+ }
380
+ }
381
+ ```
382
+
383
+ or a direct DingTalk config:
384
+
385
+ ```json
386
+ {
387
+ "config": {
388
+ "deliveryMode": "card_preferred",
389
+ "card": {
390
+ "mode": "standard",
391
+ "title": "{{title}}"
392
+ }
393
+ },
394
+ "payload": {
395
+ "title": "预约提醒"
396
+ }
397
+ }
398
+ ```
399
+
400
+ ### POST `/apps/:appType/notifications/dingtalk/send`
401
+
402
+ Requires Bearer token. Sends the resolved `notificationType` through the DingTalk channel only. The CLI command is `openxiangda notification dingding-send <notificationType> --body-json '{"recipientId":"USER_ID","payload":{"title":"测试"}}' --force`.
363
403
 
364
404
  ### GET `/apps/:appType/notifications/type-configs`
365
405
 
@@ -373,6 +413,115 @@ Requires Bearer token. Resolves the active binding by notification type and opti
373
413
 
374
414
  Requires app admin permission. Upserts a binding with `templateCode` or `templateId`.
375
415
 
416
+ ## Organization
417
+
418
+ Organization APIs are app-scoped and require `app:organization:manage` on the target app. Platform admins pass through the same permission service. Runtime service principals are not accepted by themselves: trusted runtime calls must carry a real audit actor, and permission is checked against that user.
419
+
420
+ Use these endpoints for OpenXiangda app-managed platform departments and accounts. Do not use legacy `/user` or `/department` write endpoints for new app organization management.
421
+
422
+ ### GET `/apps/:appType/organization/capabilities`
423
+
424
+ Requires Bearer token. Returns whether the current actor can manage organization data for the app, the permission code, and supported operations.
425
+
426
+ CLI:
427
+
428
+ ```bash
429
+ openxiangda organization capabilities --json
430
+ ```
431
+
432
+ ### GET `/apps/:appType/organization/departments`
433
+
434
+ Requires `app:organization:manage`. Lists platform departments in the current tenant.
435
+
436
+ ### GET `/apps/:appType/organization/departments/:departmentId`
437
+
438
+ Requires `app:organization:manage`. Returns one platform department.
439
+
440
+ ### POST `/apps/:appType/organization/departments`
441
+
442
+ Requires `app:organization:manage`. Creates a platform department and runs existing org cache invalidation.
443
+
444
+ ```json
445
+ {
446
+ "name": "销售部",
447
+ "parentId": "optional-parent-id",
448
+ "externalId": "optional-external-id",
449
+ "corpId": "optional-corp-id",
450
+ "visibilityScope": "public",
451
+ "memberViewScope": "self_and_children",
452
+ "supervisorUserIds": ["user-1"]
453
+ }
454
+ ```
455
+
456
+ CLI:
457
+
458
+ ```bash
459
+ openxiangda organization department-create --body-json '{"name":"销售部"}' --force
460
+ ```
461
+
462
+ ### POST `/apps/:appType/organization/departments/:departmentId`
463
+
464
+ Requires `app:organization:manage`. Updates department metadata, visibility/member scopes, and supervisors.
465
+
466
+ ### GET `/apps/:appType/organization/accounts`
467
+
468
+ Requires `app:organization:manage`. Lists tenant platform accounts. Supports `ids`, `departmentIds`, `keyword`, `name`, `username`, `phone`, `email`, `jobNumber`, `page`, and `pageSize`.
469
+
470
+ ### GET `/apps/:appType/organization/accounts/:userId`
471
+
472
+ Requires `app:organization:manage`. Returns one platform account. Password, login failure counters, and lock fields are removed from the response.
473
+
474
+ ### POST `/apps/:appType/organization/accounts`
475
+
476
+ Requires `app:organization:manage`. Creates a platform account.
477
+
478
+ ```json
479
+ {
480
+ "id": "optional-user-id",
481
+ "username": "alice",
482
+ "password": "initial-password",
483
+ "name": "Alice",
484
+ "phone": "13800000000",
485
+ "email": "alice@example.com",
486
+ "jobNumber": "E001",
487
+ "departmentIds": ["dept-1"],
488
+ "affiliatedDepartmentId": "dept-1",
489
+ "validFrom": "2026-07-01T00:00:00.000Z",
490
+ "validTo": null
491
+ }
492
+ ```
493
+
494
+ ### POST `/apps/:appType/organization/accounts/:userId`
495
+
496
+ Requires `app:organization:manage`. Updates account fields except password. If `password` is present, the request is rejected; use the dedicated password endpoint.
497
+
498
+ ### POST `/apps/:appType/organization/accounts/:userId/password/reset`
499
+
500
+ Requires `app:organization:manage`. Resets the target account password, writes the existing sha512 password hash, and clears `failedLoginAttempts`, `lockUntil`, and `lockReason`.
501
+
502
+ ```json
503
+ {
504
+ "newPassword": "new-password"
505
+ }
506
+ ```
507
+
508
+ CLI:
509
+
510
+ ```bash
511
+ openxiangda organization account-reset-password user-1 --body-json '{"newPassword":"new-password"}' --force
512
+ ```
513
+
514
+ ### POST `/apps/:appType/organization/accounts/me/password/change`
515
+
516
+ Requires `app:organization:manage` for the current real user. Validates `oldPassword` before changing the current user's password.
517
+
518
+ ```json
519
+ {
520
+ "oldPassword": "old-password",
521
+ "newPassword": "new-password"
522
+ }
523
+ ```
524
+
376
525
  ### GET `/apps/:appType/workflows`
377
526
 
378
527
  Requires Bearer token. Lists workflow definitions in the app. Supports `formUuid`, `isPublished`, `page`, and `pageSize` query parameters.
@@ -16,6 +16,7 @@ Guidelines:
16
16
  - Use `sdk.function.invoke(code, { input })` for reusable backend business logic declared under `src/resources/functions/` and `src/functions/`. Do not implement multi-form orchestration, connector fan-out, notification orchestration, or permission-sensitive backend rules directly in a page component.
17
17
  - Use `sdk.connector.invoke`, `sdk.connector.call("connector.api")`, or `sdk.connector.download` for external services. The SDK calls the platform runtime connector endpoint; it must not call third-party domains directly.
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
+ - 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.
19
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.
20
21
  - 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.
21
22
  - 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.
@@ -113,6 +114,42 @@ same value is also available as `ctx.input` for compatibility.
113
114
 
114
115
  Use App Functions when the logic must run server-side and be reusable by pages, automations, or workflows. Runtime invocation requires app automation management permission by default. To let ordinary app users call a function from a page, declare the allowed current app roles in `definitionJson.runtimeInvoke.roleCodes`; keep sensitive admin-only functions without broad grants. Inside the function, authorize sensitive actions with trusted runtime context such as `ctx.operator.roleCodes`, `ctx.operator.currentRoleCode`, `ctx.operator.hasFullAccess`, `ctx.currentUser`, or `ctx.permissions`; do not trust role codes sent in the page input. When the function writes forms with `ctx.form.createOne/updateOne/updateById`, treat the write as a trusted backend operation over declared `resources.forms`; do not open direct submit permission on internal forms just for that page action.
115
116
 
117
+ Organization management:
118
+
119
+ ```ts
120
+ const capabilities = await sdk.organization.capabilities()
121
+ if (!capabilities.result?.canManage) {
122
+ throw new Error("当前用户缺少组织账号管理权限")
123
+ }
124
+
125
+ await sdk.organization.departments.create({
126
+ name: "销售部",
127
+ parentId: rootDepartmentId,
128
+ })
129
+
130
+ const accounts = await sdk.organization.accounts.list({
131
+ keyword,
132
+ departmentIds: [rootDepartmentId],
133
+ page: 1,
134
+ pageSize: 20,
135
+ })
136
+
137
+ await sdk.organization.accounts.create({
138
+ username: "alice",
139
+ password: initialPassword,
140
+ name: "Alice",
141
+ jobNumber: "E001",
142
+ departmentIds: [rootDepartmentId],
143
+ affiliatedDepartmentId: rootDepartmentId,
144
+ })
145
+
146
+ await sdk.organization.accounts.resetPassword("alice", {
147
+ newPassword,
148
+ })
149
+ ```
150
+
151
+ Use `ctx.organization.departments.*` and `ctx.organization.accounts.*` inside App Functions or trusted JS nodes for the same capability. Runtime service principals must resolve a real operator/audit actor; permission is checked against that user, not against the runtime service account. Keep password changes on dedicated endpoints: update account metadata with `update`, reset another user's password with `resetPassword`, and change the current user's password with `changeMyPassword({ oldPassword, newPassword })`.
152
+
116
153
  Work center:
117
154
 
118
155
  ```ts
@@ -423,7 +423,20 @@ const stats = await sdk.dataView.stats("ticket_stats_by_customer", {
423
423
  "variables": ["title", "instrumentName", "startTime"],
424
424
  "channelsConfig": {
425
425
  "inapp": { "enabled": true, "content": "{{instrumentName}} 将于 {{startTime}} 开始" },
426
- "dingding": { "enabled": true, "content": "{{title}}" }
426
+ "dingding": {
427
+ "enabled": true,
428
+ "content": "{{title}}",
429
+ "config": {
430
+ "deliveryMode": "card_preferred",
431
+ "fallbackToWorkNotice": true,
432
+ "card": {
433
+ "mode": "standard",
434
+ "title": "{{title}}",
435
+ "summary": "{{instrumentName}} 将于 {{startTime}} 开始",
436
+ "jumpUrl": "{{detailUrl}}"
437
+ }
438
+ }
439
+ }
427
440
  }
428
441
  }
429
442
  ],
@@ -450,6 +463,8 @@ await sdk.notification.sendByType({
450
463
 
451
464
  JS_CODE 调用:`ctx.notification.sendByType({ ... })`。允许的 channels:`inapp` / `email` / `dingding` / `wechat` / `thirdparty_todo`。完整规则见 [`notifications.md`](notifications.md)。
452
465
 
466
+ 钉钉卡片预览/发送:`sdk.notification.previewDingTalk({ notificationType, payload })`、`sdk.notification.sendDingTalk({ notificationType, recipientId, payload })`;CLI 为 `openxiangda notification dingding-preview` 和 `openxiangda notification dingding-send --force`。
467
+
453
468
  ## 4. App Function — `src/resources/functions/<functionCode>.json` + `src/functions/<functionCode>/index.ts`
454
469
 
455
470
  ```json
@@ -389,7 +389,7 @@ For reusable backend logic that should be shared by pages, automations, and work
389
389
 
390
390
  Use JS_CODE V2 when the script is local to one workflow node.
391
391
 
392
- Scripts can export `export default async function (ctx) {}` or `module.exports = async (ctx) => {}`. The runtime exposes `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, `ctx.resources`, `ctx.form`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, `ctx.platform.api.*`, `ctx.utils`, `require`, `process`, and `Buffer`. Prefer `ctx.resources.resolveForm/resolveDataView/resolveConnector`, `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`, `ctx.dataView.query/stats`, and `ctx.connector.call/invoke` for platform-side work. App Function and trusted-node form writes are backend operations over declared resources, not direct page-user submit access; keep internal forms closed to raw user submission unless the business requires it. Legacy data/process bridge methods remain available as `ctx.methods.queryOneData/queryManyData/getDataByFormInstanceId/updateOneData/updateDataByFormInstanceId/updateManyData/createOneData/terminateProcess/getAllParentDepartments`.
392
+ Scripts can export `export default async function (ctx) {}` or `module.exports = async (ctx) => {}`. The runtime exposes `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, `ctx.resources`, `ctx.form`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, `ctx.organization`, `ctx.platform.api.*`, `ctx.utils`, `require`, `process`, and `Buffer`. Prefer `ctx.resources.resolveForm/resolveDataView/resolveConnector`, `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`, `ctx.dataView.query/stats`, and `ctx.connector.call/invoke` for platform-side work. Use `ctx.organization.departments.*` and `ctx.organization.accounts.*` only for intentional organization-management flows; the real operator/audit actor must hold `app:organization:manage`. App Function and trusted-node form writes are backend operations over declared resources, not direct page-user submit access; keep internal forms closed to raw user submission unless the business requires it. Legacy data/process bridge methods remain available as `ctx.methods.queryOneData/queryManyData/getDataByFormInstanceId/updateOneData/updateDataByFormInstanceId/updateManyData/createOneData/terminateProcess/getAllParentDepartments`.
393
393
 
394
394
  Example `src/js-code-nodes/sync_customer/index.ts`:
395
395
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.131",
3
+ "version": "1.0.133",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -12259,7 +12259,7 @@ async function handleApproval(request, params) {
12259
12259
  async function withdrawProcess(request, params) {
12260
12260
  const response = await request({
12261
12261
  url: `/workflow/instance/${params.instanceId}/withdraw`,
12262
- method: "put",
12262
+ method: "post",
12263
12263
  data: { reason: params.reason }
12264
12264
  });
12265
12265
  return unwrapRuntimeResponse(response);
@@ -12267,7 +12267,7 @@ async function withdrawProcess(request, params) {
12267
12267
  async function transferTask(request, params) {
12268
12268
  const response = await request({
12269
12269
  url: `/workflow/task/${params.taskId}/transfer`,
12270
- method: "put",
12270
+ method: "post",
12271
12271
  data: { newAssignee: params.newAssignee, reason: params.reason }
12272
12272
  });
12273
12273
  return unwrapRuntimeResponse(response);
@@ -12275,7 +12275,7 @@ async function transferTask(request, params) {
12275
12275
  async function returnTask(request, params) {
12276
12276
  const response = await request({
12277
12277
  url: `/workflow/task/${params.taskId}/return`,
12278
- method: "put",
12278
+ method: "post",
12279
12279
  data: { targetNodeId: params.targetNodeId, reason: params.reason }
12280
12280
  });
12281
12281
  return unwrapRuntimeResponse(response);