openxiangda 1.0.76 → 1.0.78

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 (30) hide show
  1. package/README.md +3 -1
  2. package/lib/cli.js +320 -10
  3. package/openxiangda-skills/SKILL.md +11 -9
  4. package/openxiangda-skills/references/automation-v3.md +17 -1
  5. package/openxiangda-skills/references/connector-resources.md +4 -1
  6. package/openxiangda-skills/references/data-views.md +23 -9
  7. package/openxiangda-skills/references/pages/page-sdk.md +15 -0
  8. package/openxiangda-skills/references/resource-manifest-cheatsheet.md +88 -19
  9. package/openxiangda-skills/references/workflow-v3.md +17 -1
  10. package/openxiangda-skills/skills/openxiangda-app/SKILL.md +7 -5
  11. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +7 -4
  12. package/openxiangda-skills/skills/openxiangda-form/SKILL.md +9 -9
  13. package/openxiangda-skills/skills/openxiangda-inspect/SKILL.md +2 -2
  14. package/openxiangda-skills/skills/openxiangda-page/SKILL.md +19 -16
  15. package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +4 -4
  16. package/openxiangda-skills/skills/openxiangda-workflow-automation/SKILL.md +27 -8
  17. package/package.json +1 -1
  18. package/packages/sdk/dist/components/index.cjs +1120 -1098
  19. package/packages/sdk/dist/components/index.cjs.map +1 -1
  20. package/packages/sdk/dist/components/index.mjs +857 -841
  21. package/packages/sdk/dist/components/index.mjs.map +1 -1
  22. package/packages/sdk/dist/runtime/index.cjs +1031 -989
  23. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  24. package/packages/sdk/dist/runtime/index.d.mts +18 -1
  25. package/packages/sdk/dist/runtime/index.d.ts +18 -1
  26. package/packages/sdk/dist/runtime/index.mjs +788 -752
  27. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  28. package/templates/openxiangda-react-spa/src/main.tsx +9 -1
  29. package/templates/sy-lowcode-app-workspace/scripts/build-js-code.mjs +8 -2
  30. package/templates/sy-lowcode-app-workspace/tsconfig.js-code-nodes.json +5 -1
@@ -254,6 +254,22 @@ Supported node types:
254
254
 
255
255
  Use trusted Node JS_CODE nodes for AI/admin automation logic that runs after an automation trigger. Typical cases include scheduled data cleanup, date-field reminders, cross-form synchronization after submit/update/delete, workflow-completed follow-up writes, calling internal platform APIs, calling external HTTP services, and other backend-only orchestration.
256
256
 
257
+ For reusable backend logic that should be shared by pages, multiple automations, and workflows, prefer an App Function under `src/functions/<functionCode>/index.ts` plus `src/resources/functions/<functionCode>.json`. Automation graphs can call it with a `function_call` node:
258
+
259
+ ```json
260
+ {
261
+ "id": "call_summary",
262
+ "type": "function_call",
263
+ "data": {
264
+ "functionCode": "reservation_reminder_summary",
265
+ "input": { "scope": "today" },
266
+ "saveResponseTo": "summary"
267
+ }
268
+ }
269
+ ```
270
+
271
+ Use JS_CODE V2 when the script is local to one automation graph node.
272
+
257
273
  ```json
258
274
  {
259
275
  "id": "sync_customer",
@@ -275,7 +291,7 @@ Author source in `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.
275
291
 
276
292
  The backend runs the snapshot in the trusted Node runtime, applies the node timeout (`30000` ms by default), stores execution logs, and writes the returned value to the node output and `variables.node_<nodeId>`. Scripts may use `export default async function (ctx) {}`, `module.exports = async (ctx) => {}`, `require`, `process`, `Buffer`, arbitrary HTTP, and `platform.api` for `/openxiangda-api/v1`.
277
293
 
278
- Runtime context includes `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, and `ctx.node`. Data/process bridge methods include `ctx.methods.queryOneData`, `queryManyData`, `updateOneData`, `updateDataByFormInstanceId`, `updateManyData`, `createOneData`, `terminateProcess`, and `getAllParentDepartments`.
294
+ Runtime context includes `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, and `ctx.node`. Prefer the higher-level resource helpers when available: `ctx.resources.resolveForm/resolveDataView/resolveConnector`, `ctx.form.queryMany/createOne/updateOne/updateById`, `ctx.dataView.query/stats`, `ctx.connector.call/invoke`, `ctx.notification.*`, and `ctx.platform.api.*`. Legacy data/process bridge methods remain available as `ctx.methods.queryOneData`, `queryManyData`, `updateOneData`, `updateDataByFormInstanceId`, `updateManyData`, `createOneData`, `terminateProcess`, and `getAllParentDepartments`.
279
295
 
280
296
  Code automation also exposes `ctx.logger.debug/info/warn/error(message, data?)`. AI-authored code should log input parsing, query conditions, external calls, writes, branch decisions, and caught errors. Logs are stored as full raw execution data by default. Use CLI diagnosis commands:
281
297
 
@@ -11,6 +11,7 @@ Common folders:
11
11
  - `workflows`
12
12
  - `automations`
13
13
  - `data-views`
14
+ - `functions`
14
15
  - `permissions/page-groups`
15
16
  - `permissions/form-groups`
16
17
  - `settings/forms`
@@ -28,7 +29,9 @@ Connector manifests use stable `code` values. The platform maps connector `code`
28
29
 
29
30
  Notification manifests live under `src/resources/notifications/` and contain `templates` plus `typeConfigs`. See `notifications.md` before generating reminders or message templates.
30
31
 
31
- Data view manifests live under `src/resources/data-views/` and define read-only materialized views for repeated multi-form joins or fixed aggregate statistics. Use row views with `sdk.dataView.query` for joined list/report/lookup sources, and aggregate views with `sdk.dataView.stats` for dashboard metrics where refresh lag is acceptable. Before generating one, confirm freshness tolerance and add indexes for common filters, sort fields, dimensions, and date buckets. Do not use them for single-form CRUD, simple linkedForm selects, real-time writes, write-back, or ad-hoc BI. See `data-views.md` before generating one.
32
+ 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.
33
+
34
+ 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`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, and `ctx.platform.api`; they do not expose raw SQL or Redis in the current MVP. Use JS_CODE V2 only for node-local workflow/automation scripts.
32
35
 
33
36
  ```json
34
37
  {
@@ -1,6 +1,6 @@
1
1
  # Data View Resources
2
2
 
3
- Data views are OpenXiangda-managed read-only PostgreSQL materialized views. They turn repeated multi-form joins and predefined aggregate statistics into published resources under `src/resources/data-views/`.
3
+ Data views are OpenXiangda-managed read-only query resources under `src/resources/data-views/`. They can run as PostgreSQL materialized views (`storageMode: "materialized"`, the default) or as live logical views (`storageMode: "live"`). Use them to publish repeated multi-form joins and predefined aggregate statistics as reusable app resources.
4
4
 
5
5
  Use a data view when the app needs read-only joined data from multiple forms, such as:
6
6
 
@@ -10,13 +10,14 @@ Use a data view when the app needs read-only joined data from multiple forms, su
10
10
  - Dashboard statistics such as monthly ticket count, order amount by customer, or task count by status.
11
11
  - Reusable lookup or report data consumed by several pages or automations.
12
12
  - Large list pages where repeated client-side cross-form joins would be slow or inconsistent.
13
+ - Real-time multi-form reads where the query shape is fixed and the source data volume is bounded (`storageMode: "live"`).
13
14
 
14
15
  Do not use a data view for:
15
16
 
16
17
  - Single-form CRUD. Use `sdk.form.advancedSearch`, form pages, or `DataManagementList`.
17
18
  - Simple one-form dropdown options. Use `SelectField` with `optionSource.type: "linkedForm"`.
18
19
  - Writes or write-back. Data views are read-only.
19
- - Strong real-time views after every form update. Data views update after manual or scheduled refresh.
20
+ - Heavy real-time joins that cannot be paginated, indexed, filtered, or otherwise bounded.
20
21
  - Raw SQL, incremental refresh, source-table trigger refresh, ad-hoc BI/pivot/window analysis, or write-heavy realtime dashboards.
21
22
 
22
23
  ## Authoring
@@ -25,8 +26,13 @@ Place manifests in `src/resources/data-views/*.json`. Use logical `formCode` val
25
26
 
26
27
  Two view types are supported:
27
28
 
28
- - `viewType: "row"` or omitted: row-level joined materialized view using `select`.
29
- - `viewType: "aggregate"`: grouped statistics materialized view using `dimensions` and `measures`; query it with `stats`.
29
+ - `viewType: "row"` or omitted: row-level joined view using `select`.
30
+ - `viewType: "aggregate"`: grouped statistics view using `dimensions` and `measures`; query it with `stats`.
31
+
32
+ Two storage modes are supported:
33
+
34
+ - `storageMode: "materialized"` or omitted: create a PostgreSQL materialized view. Results may be stale until manual or scheduled refresh. Use `indexes` for common filters, sort fields, aggregate dimensions, and date buckets.
35
+ - `storageMode: "live"`: do not create a materialized view. Every query is compiled and executed against source forms in real time. `indexes` are ignored and refresh APIs do not apply. Use it only for bounded real-time query shapes.
30
36
 
31
37
  Row view example:
32
38
 
@@ -34,6 +40,7 @@ Row view example:
34
40
  {
35
41
  "code": "ticket_with_customer",
36
42
  "name": "Ticket With Customer",
43
+ "storageMode": "materialized",
37
44
  "base": { "formCode": "service_ticket", "alias": "ticket" },
38
45
  "joins": [
39
46
  {
@@ -74,6 +81,7 @@ Aggregate statistics example:
74
81
  "code": "ticket_stats_by_customer",
75
82
  "name": "Ticket Stats By Customer",
76
83
  "viewType": "aggregate",
84
+ "storageMode": "materialized",
77
85
  "base": { "formCode": "service_ticket", "alias": "ticket" },
78
86
  "joins": [
79
87
  {
@@ -144,9 +152,11 @@ Filters:
144
152
 
145
153
  Refresh:
146
154
 
155
+ - Refresh applies only to materialized data views.
147
156
  - `manual` means refresh only when an administrator runs refresh or the resource is recreated.
148
157
  - `scheduled` uses cron, for example `0 */10 * * * *`.
149
- - Query results may be stale. Show or inspect `lastRefreshedAt` when freshness matters.
158
+ - Materialized query results may be stale. Show or inspect `lastRefreshedAt` when freshness matters.
159
+ - Live data views have no refresh state; `lastRefreshedAt` is `null` and refresh requests should be rejected.
150
160
  - Do not choose a high-frequency schedule by default. Ask or infer how stale the data may be before setting `scheduled`.
151
161
 
152
162
  Indexes:
@@ -159,11 +169,12 @@ Indexes:
159
169
 
160
170
  ## Performance And Freshness
161
171
 
162
- Data views move expensive joins and aggregations from page runtime to materialized-view refresh time. They reduce repeated page pressure only when the view shape, refresh cadence, and indexes are chosen deliberately.
172
+ Materialized data views move expensive joins and aggregations from page runtime to refresh time. Live data views keep results current by executing the same declared query shape on each request. Both modes reduce repeated page code complexity only when the view shape, data scope, and query bounds are chosen deliberately.
163
173
 
164
174
  Before creating a data view, confirm these points:
165
175
 
166
176
  - **Freshness tolerance**: ask whether users need near-real-time data, 10-30 minute freshness, hourly/daily reports, or manual snapshots.
177
+ - **Storage mode**: use `materialized` for read-heavy reports that tolerate refresh lag; use `live` for bounded real-time joins where source-table changes must appear immediately.
167
178
  - **Query shape**: list the fields users will filter, sort, group, or drill down by. These should become output aliases and usually indexes.
168
179
  - **Data scope**: if the business only needs active, recent, or in-scope records, put that rule in definition `where` so old/source-irrelevant rows are filtered before materialization.
169
180
  - **Visible fields**: output only fields the page or permission rules need. Use hidden scope aliases for data permissions when needed, but do not build one huge catch-all view.
@@ -174,11 +185,12 @@ Refresh cadence guidance:
174
185
  - Use `scheduled` every 10-30 minutes for normal dashboards, joined report lists, and recurring management views.
175
186
  - Use hourly or daily schedules for leadership reports, historical statistics, and large aggregate views.
176
187
  - Avoid intervals below 5 minutes unless the user explicitly confirms the time sensitivity and expected source-table volume. High-frequency refreshes can pressure both source tables and the materialized view.
177
- - For strong real-time behavior after each form save, do not use a data view; query the source form directly or design a different backend workflow.
188
+ - For strong real-time behavior after each form save, use `storageMode: "live"` only when the query is bounded. If the logic includes writes, side effects, raw platform orchestration, or expensive unbounded joins, use source-form queries, App Functions, or a different backend workflow instead.
178
189
 
179
190
  Index guidance:
180
191
 
181
- - Index only materialized-view output aliases, not source references such as `customer.name`.
192
+ - For materialized mode, index only output aliases, not source references such as `customer.name`.
193
+ - Live data views ignore `indexes`; keep their runtime filters and page size conservative.
182
194
  - For row views, index IDs used in drill-down (`ticketId`, `customerId`) and common filters/order fields (`statusValue`, `ownerId`, `createdAt`).
183
195
  - For aggregate views, index the dimensions/time buckets used in runtime `filters`, `having` drill-downs, and `order` (`customerId`, `statusValue`, `createdMonth`).
184
196
  - Keep compound indexes aligned with common query prefixes. For example, dashboard filters by customer then month should use `["customerId", "createdMonth"]`.
@@ -219,7 +231,7 @@ Diagnostic commands:
219
231
  ```bash
220
232
  openxiangda data-view list --profile dev
221
233
  openxiangda data-view status ticket_with_customer --profile dev
222
- openxiangda data-view refresh ticket_with_customer --profile dev
234
+ openxiangda data-view refresh ticket_with_customer --profile dev # materialized only
223
235
  openxiangda data-view query ticket_with_customer --profile dev --fields ticketId,customerName
224
236
  openxiangda data-view query ticket_with_customer --profile dev --query-json query.json
225
237
  openxiangda data-view stats ticket_stats_by_customer --profile dev --fields customerName,ticketCount
@@ -228,6 +240,8 @@ openxiangda data-view stats ticket_stats_by_customer --profile dev --query-json
228
240
 
229
241
  ## Runtime SDK
230
242
 
243
+ `sdk.dataView.query` and `sdk.dataView.stats` work for both materialized and live data views. Responses include `storageMode`; materialized responses may include `lastRefreshedAt`, while live responses return current data and `lastRefreshedAt: null`.
244
+
231
245
  Direct query:
232
246
 
233
247
  ```ts
@@ -7,9 +7,11 @@ Guidelines:
7
7
  - Do not hardcode `/openxiangda-api` calls inside end-user page components unless the page is explicitly an admin tool.
8
8
  - Prefer SDK modules for form data, user context, permissions, and platform navigation.
9
9
  - Use `sdk.dataView.query` for published read-only multi-form row data views. Use `sdk.dataView.stats` for aggregate data views declared with `viewType: "aggregate"`.
10
+ - Data views can be `storageMode: "materialized"` for refreshed report/list data or `storageMode: "live"` for bounded real-time joins. Page code calls the same SDK methods for both modes and should inspect `response.storageMode` / `response.lastRefreshedAt` when freshness is visible to users.
10
11
  - Data view runtime queries can filter, sort, paginate, and select only output aliases.
11
12
  - Keep data view page calls paginated and field-scoped. Runtime filters/order should use aliases that the data view indexes, especially for aggregate dimensions and date buckets.
12
13
  - Use `sdk.dataSource.run()` with a page data source descriptor when the page config should own the data view code, default fields, or default filters.
14
+ - 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.
13
15
  - 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.
14
16
  - 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`.
15
17
  - For the current user's department hierarchy, use `sdk.department.getCurrentUserParentDepartments()`; do not hardcode `GET /department/:id/parentDepartments` in page code.
@@ -87,6 +89,19 @@ const response = await sdk.dataSource.run("tickets", {
87
89
 
88
90
  Data view filters, having, and fields use output aliases such as `customerName` or `ticketCount`, not source references such as `customer.name`. If the page only needs one form, prefer `sdk.form.advancedSearch`. If the page only needs a simple one-form dropdown, prefer linkedForm options.
89
91
 
92
+ App Function invocation:
93
+
94
+ ```ts
95
+ const result = await sdk.function.invoke("reservation_reminder_summary", {
96
+ input: {
97
+ scope: "today",
98
+ keyword,
99
+ },
100
+ })
101
+ ```
102
+
103
+ Use App Functions when the logic must run server-side and be reusable by pages, automations, or workflows. Current runtime invocation requires the caller to have app automation management permission; ordinary page users should normally consume function-backed data through a permission-aware page or a narrower backend resource.
104
+
90
105
  Logout and current-user role switching:
91
106
 
92
107
  ```ts
@@ -66,13 +66,15 @@ const data = await sdk.connector.call("crm.getCustomer", { body: { keyword } });
66
66
  - 多表只读联表列表 / lookup / 报表明细:用行级 data view,页面调用 `sdk.dataView.query`。
67
67
  - 固定口径统计 / 看板指标 / 图表数据源:用 `viewType: "aggregate"`,页面调用 `sdk.dataView.stats`。
68
68
  - ECharts 或自定义 dashboard 只负责展示;统计口径稳定时,数据源优先沉淀成 aggregate data view。
69
- - 发布前为常用筛选、排序、维度和时间桶声明 `indexes`,并确认刷新延迟是否能被业务接受。
70
- - 不用于单表 CRUD、简单 `linkedForm` 下拉、强实时、写回或临时 BI 查询。
69
+ - 默认 `storageMode: "materialized"`,适合读多写少和可接受刷新延迟的列表/报表;发布前为常用筛选、排序、维度和时间桶声明 `indexes`。
70
+ - `storageMode: "live"` 每次查询实时执行,适合强实时但数据量可控的固定复杂查询;live 模式忽略 `indexes` 且不能 refresh。
71
+ - 不用于单表 CRUD、简单 `linkedForm` 下拉、写回、无边界重查询或临时 BI 查询。
71
72
 
72
73
  ```json
73
74
  {
74
75
  "code": "ticket_with_customer",
75
76
  "name": "Ticket With Customer",
77
+ "storageMode": "materialized",
76
78
  "base": { "formCode": "service_ticket", "alias": "ticket" },
77
79
  "joins": [
78
80
  {
@@ -137,7 +139,7 @@ const stats = await sdk.dataView.stats("ticket_stats_by_customer", {
137
139
  });
138
140
  ```
139
141
 
140
- 性能要点:索引只能引用输出 alias;行级视图索引常用 filter/order 字段;聚合视图索引 dimensions/date buckets;`countDistinct` 和高基数维度刷新成本较高;低于 5 分钟的 scheduled refresh 需要用户明确确认时间敏感度。完整规则见 [`data-views.md`](data-views.md)。
142
+ 性能要点:索引只能引用输出 alias;行级视图索引常用 filter/order 字段;聚合视图索引 dimensions/date buckets;`countDistinct` 和高基数维度刷新成本较高;低于 5 分钟的 scheduled refresh 需要用户明确确认时间敏感度。live 视图不刷新、不建索引,必须控制字段、过滤和分页。完整规则见 [`data-views.md`](data-views.md)。
141
143
 
142
144
  ## 3. Notification — `src/resources/notifications/<code>.json`
143
145
 
@@ -178,7 +180,69 @@ await sdk.notification.sendByType({
178
180
 
179
181
  JS_CODE 调用:`ctx.notification.sendByType({ ... })`。允许的 channels:`inapp` / `email` / `dingding` / `wechat` / `thirdparty_todo`。完整规则见 [`notifications.md`](notifications.md)。
180
182
 
181
- ## 4. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
183
+ ## 4. App Function — `src/resources/functions/<functionCode>.json` + `src/functions/<functionCode>/index.ts`
184
+
185
+ ```json
186
+ {
187
+ "code": "reservation_reminder_summary",
188
+ "name": "Reservation Reminder Summary",
189
+ "sourceFile": { "localPath": "src/functions/reservation_reminder_summary/index.ts" },
190
+ "runtimeMode": "trusted_node",
191
+ "timeout": 30000,
192
+ "resources": {
193
+ "forms": ["reservation_order"],
194
+ "dataViews": ["reservation_order_overview"],
195
+ "connectors": ["crm"]
196
+ }
197
+ }
198
+ ```
199
+
200
+ ```ts
201
+ // src/functions/reservation_reminder_summary/index.ts
202
+ export default async function reservationReminderSummary(ctx, input) {
203
+ const orders = await ctx.form.queryMany({
204
+ formCode: "reservation_order",
205
+ filters: [{ field: "status", operator: "=", value: "pending" }],
206
+ pageSize: 50,
207
+ });
208
+
209
+ const overview = await ctx.dataView.query("reservation_order_overview", {
210
+ pageSize: 20,
211
+ });
212
+
213
+ return {
214
+ pendingCount: orders.totalCount || orders.data?.length || 0,
215
+ overview: overview.data || [],
216
+ input,
217
+ };
218
+ }
219
+ ```
220
+
221
+ 调用方式:
222
+
223
+ ```ts
224
+ const result = await sdk.function.invoke("reservation_reminder_summary", {
225
+ input: { scope: "today" },
226
+ });
227
+ ```
228
+
229
+ 自动化 / 流程图节点调用:
230
+
231
+ ```json
232
+ {
233
+ "id": "call_summary",
234
+ "type": "function_call",
235
+ "data": {
236
+ "functionCode": "reservation_reminder_summary",
237
+ "input": { "scope": "today" },
238
+ "saveResponseTo": "summary"
239
+ }
240
+ }
241
+ ```
242
+
243
+ 适用边界:可复用后端业务逻辑、跨页面/自动化/流程共享的查询编排、连接器调用、通知编排、受控平台 API 调用。当前 MVP 不暴露原始 SQL/Redis;运行时接口需要应用自动化管理权限,自动化/流程内部调用走服务端上下文。
244
+
245
+ ## 5. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
182
246
 
183
247
  ```jsonc
184
248
  // src/resources/workflows/customer_approval/workflow.json
@@ -208,7 +272,7 @@ export default defineWorkflow({
208
272
 
209
273
  CLI 编译为 `definition.v3.json` + `preview.json`,平台运行时仍走标准工作流引擎。完整规则见 [`workflow-v3.md`](workflow-v3.md)。
210
274
 
211
- ## 5. Automation — `src/resources/automations/<code>/{definition.code.json,preview.json}` + `src/automations/<code>/index.ts`
275
+ ## 6. Automation — `src/resources/automations/<code>/{definition.code.json,preview.json}` + `src/automations/<code>/index.ts`
212
276
 
213
277
  ```jsonc
214
278
  // src/resources/automations/notify_on_submit/definition.code.json
@@ -242,17 +306,18 @@ export default async function (ctx) {
242
306
 
243
307
  `trigger_v2` 事件源:`form_data` / `form_field` / `workflow_task` / `workflow_process` / `mode: "scheduled"`(fixed_time / form_date_field)。完整规则见 [`automation-v3.md`](automation-v3.md)。
244
308
 
245
- ## 6. JS_CODE V2 — `src/js-code-nodes/<scriptCode>/index.ts`
309
+ ## 7. JS_CODE V2 — `src/js-code-nodes/<scriptCode>/index.ts`
310
+
311
+ JS_CODE V2 适合自动化/流程图里的节点级脚本。跨页面、跨自动化、需要一个稳定后端入口的可复用逻辑优先写 App Function,再用 `function_call` 节点或 `sdk.function.invoke` 调用。
246
312
 
247
313
  ```ts
248
314
  export default async function (ctx) {
249
- const { formData, operator, methods, notification, platform, utils, console } = ctx;
250
- const result = await methods.queryManyData({
315
+ const { formData, operator, form, dataView, connector, notification, platform, utils, console } = ctx;
316
+ const result = await form.queryMany({
251
317
  formCode: "customer",
252
- formUuid: ctx.app.formUuids.customer,
253
- searchCondition: [/* 查询条件 */],
318
+ filters: [/* 查询条件 */],
254
319
  });
255
- return { count: result.length };
320
+ return { count: result.totalCount || result.data?.length || 0 };
256
321
  }
257
322
  ```
258
323
 
@@ -275,7 +340,9 @@ export default async function (ctx) {
275
340
 
276
341
  构建:`pnpm build-js-code --script sync_customer`(先 `tsc` 再打包到 `dist/js-code-nodes/<code>/index.cjs`)。CLI validate/create/publish 时会上传快照、用 `{ bucketName, objectName, sha256 }` 替换 `sourceFile.localPath`。
277
342
 
278
- ## 7. Role `src/resources/roles/<code>.json`
343
+ `ctx.methods.*` 仍可用于兼容旧脚本;新脚本优先使用 `ctx.resources`、`ctx.form`、`ctx.dataView`、`ctx.connector`、`ctx.notification` 和 `ctx.platform.api`。
344
+
345
+ ## 8. Role — `src/resources/roles/<code>.json`
279
346
 
280
347
  ```json
281
348
  {
@@ -284,7 +351,7 @@ export default async function (ctx) {
284
351
  }
285
352
  ```
286
353
 
287
- ## 8. Page Permission Group — `src/resources/permissions/page-groups/<code>.json`
354
+ ## 9. Page Permission Group — `src/resources/permissions/page-groups/<code>.json`
288
355
 
289
356
  ```json
290
357
  {
@@ -298,7 +365,7 @@ export default async function (ctx) {
298
365
 
299
366
  `formCodes` / `pageCodes` / `menuCodes` 留空表示对匹配角色全部可见。
300
367
 
301
- ## 9. Form Permission Group — `src/resources/permissions/form-groups/<formCode>/<groupCode>.json`
368
+ ## 10. Form Permission Group — `src/resources/permissions/form-groups/<formCode>/<groupCode>.json`
302
369
 
303
370
  ```json
304
371
  {
@@ -321,7 +388,7 @@ export default async function (ctx) {
321
388
  }
322
389
  ```
323
390
 
324
- ## 10. Form Settings — `src/resources/settings/forms/<formCode>.json`
391
+ ## 11. Form Settings — `src/resources/settings/forms/<formCode>.json`
325
392
 
326
393
  ```json
327
394
  {
@@ -339,7 +406,7 @@ export default async function (ctx) {
339
406
  }
340
407
  ```
341
408
 
342
- ## 11. Menu — `src/resources/menus/<code>.json`
409
+ ## 12. Menu — `src/resources/menus/<code>.json`
343
410
 
344
411
  ```json
345
412
  {
@@ -359,16 +426,17 @@ export default async function (ctx) {
359
426
  ```text
360
427
  需要展示数据?
361
428
  ├─ 单表 CRUD → 表单页 + DataManagementList(不要 data view)
362
- ├─ 多表只读联表 → src/resources/data-views/
429
+ ├─ 多表只读联表 → src/resources/data-views/(materialized 或 live)
363
430
  ├─ 表单字段下拉 → SelectField + optionSource.type: "linkedForm"(不要 data view)
364
431
  └─ 大屏 / 报表 → 原生报表 → ECharts page
365
432
 
366
433
  需要后端逻辑?
367
434
  ├─ 真有审批 → workflow(src/workflows/<code>/workflow.ts)
368
435
  ├─ 状态流转 → 表单 status 字段 + 状态机 + automation
436
+ ├─ 可复用服务逻辑 → App Function(src/functions/<code>/index.ts + function_call / sdk.function.invoke)
369
437
  ├─ 提交/字段触发 → automation(trigger_v2 + src/automations/<code>/index.ts)
370
438
  ├─ 定时任务 → automation(mode: "scheduled")
371
- └─ 复杂后端逻辑 → JS_CODE V2 trusted_node(src/js-code-nodes/<code>/index.ts)
439
+ └─ 流程/自动化节点内脚本 → JS_CODE V2 trusted_node(src/js-code-nodes/<code>/index.ts)
372
440
 
373
441
  需要外部数据?
374
442
  ├─ 第三方 HTTP → src/resources/connectors/ + sdk.connector.call
@@ -380,5 +448,6 @@ export default async function (ctx) {
380
448
  - ❌ 把 `formUuid` / `pageId` 等平台 ID 写进 manifest。**用 code,CLI 自动解析。**
381
449
  - ❌ 把 API key、token、密码写进 manifest。**用占位符,平台后台填真实值。**
382
450
  - ❌ 同时在 manifest 与平台后台编辑同一个资源(漂移源)。**统一以 manifest 为单一来源**,需要看平台版本就 `resource pull`。
383
- - ❌ 用 data view 代替 `linkedForm` 下拉、单表 CRUD 或写回。**data view 是只读 + 延迟刷新。**
451
+ - ❌ 用 data view 代替 `linkedForm` 下拉、单表 CRUD 或写回。**data view 是只读;materialized 有刷新延迟,live 要控制查询边界。**
452
+ - ❌ 把跨页面/跨流程复用的后端逻辑都塞进 JS_CODE。**优先 App Function,JS_CODE 只做节点内脚本。**
384
453
  - ❌ 在 page 源码里 hardcode `/api/notification-config/*` 或 `/connectors/actions/invoke`。**用 `sdk.notification` / `sdk.connector`。**
@@ -135,7 +135,23 @@ File snapshot:
135
135
 
136
136
  AI-authored JS_CODE source must be TypeScript under `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`. When validating or creating, the CLI runs `pnpm build-js-code --script <scriptCode>`, which runs TypeScript validation first, bundles to `dist/js-code-nodes/<scriptCode>/index.cjs`, uploads the bundle, and replaces `sourceFile.localPath` with immutable snapshot metadata. The backend verifies snapshot `sha256`, runs it in the trusted Node runtime, applies the node timeout (`30000` ms by default), stores execution logs, and writes the returned value to the node output and `variables.node_<nodeId>`.
137
137
 
138
- 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.methods.*`, `ctx.platform.api.*`, `ctx.utils`, `require`, `process`, and `Buffer`. Use `ctx.methods.queryOneData/queryManyData/updateOneData/updateDataByFormInstanceId/updateManyData/createOneData/terminateProcess/getAllParentDepartments` for platform-side data and process operations.
138
+ For reusable backend logic that should be shared by pages, automations, and workflows, prefer an App Function under `src/functions/<functionCode>/index.ts` plus `src/resources/functions/<functionCode>.json`. Workflow graphs that support App Function nodes can call it with:
139
+
140
+ ```json
141
+ {
142
+ "id": "call_summary",
143
+ "type": "function_call",
144
+ "data": {
145
+ "functionCode": "reservation_reminder_summary",
146
+ "input": { "scope": "process" },
147
+ "saveResponseTo": "summary"
148
+ }
149
+ }
150
+ ```
151
+
152
+ Use JS_CODE V2 when the script is local to one workflow node.
153
+
154
+ 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.queryMany/createOne/updateOne/updateById`, `ctx.dataView.query/stats`, and `ctx.connector.call/invoke` for platform-side work. Legacy data/process bridge methods remain available as `ctx.methods.queryOneData/queryManyData/updateOneData/updateDataByFormInstanceId/updateManyData/createOneData/terminateProcess/getAllParentDepartments`.
139
155
 
140
156
  Example `src/js-code-nodes/sync_customer/index.ts`:
141
157
 
@@ -99,7 +99,7 @@ openxiangda workspace bind --profile <name> --app-type APP_XXX
99
99
  - Do not search platform apps or reuse similar app names unless the user explicitly asks to reuse an existing app or gives an `appType`.
100
100
  - Store platform-specific IDs only in `.openxiangda/state.json`.
101
101
  - Use logical local codes for forms, pages, workflows, automations, and menus.
102
- - Before scaffolding a new business app, read `../../references/best-practices.md` and pick an architecture from the initialized `examples/best-practices/` catalog. Do not generate a large single-file app page when a template pattern already covers the scenario.
102
+ - Before scaffolding a new business app, read `references/best-practices.md` and pick an architecture from the initialized `examples/best-practices/` catalog. Do not generate a large single-file app page when a template pattern already covers the scenario.
103
103
  - For Phase 6 React SPA apps, use `--runtime react-spa`. The generated app owns routing/layouts/menus with React Router and Tailwind CSS; platform permissions still come from backend runtime bootstrap and route checks.
104
104
  - React SPA form publish is schema-only by default. Do not add OSS credentials or old embedded form bundles unless explicitly maintaining legacy compatibility with `--legacy-form-bundle`.
105
105
  - When publishing after app edits, prefer targeted commands (`workspace publish --changed --dry-run`, then `--changed`, `--page`, `--form`, or `--only`). Do not publish all forms/pages just because one page changed.
@@ -107,10 +107,12 @@ openxiangda workspace bind --profile <name> --app-type APP_XXX
107
107
 
108
108
  ## Resource State
109
109
 
110
- Read `../../references/workspace-state.md` when changing `.openxiangda/state.json`.
110
+ Read `references/workspace-state.md` when changing `.openxiangda/state.json`.
111
111
 
112
- Read `../../references/openxiangda-api.md` when exact endpoint fields are needed.
112
+ Read `references/openxiangda-api.md` when exact endpoint fields are needed.
113
113
 
114
- Read `../../references/architecture-patterns.md` to understand the overall app structure (how forms, pages, menus, workflows, and permissions compose into an app, and the recommended `src/forms` / `src/pages` layout) before scaffolding or restructuring an app workspace.
114
+ Read `references/resource-manifest-cheatsheet.md` before creating shared resources such as data views, connectors, notifications, App Functions, workflows, automations, permissions, settings, or menus.
115
115
 
116
- Read `../../references/best-practices.md` before implementing app-level patterns such as status lifecycles, role governance, PC/mobile portals, high-performance data management pages, workflow boundaries, and automation.
116
+ Read `references/architecture-patterns.md` to understand the overall app structure (how forms, pages, menus, workflows, and permissions compose into an app, and the recommended `src/forms` / `src/pages` layout) before scaffolding or restructuring an app workspace.
117
+
118
+ Read `references/best-practices.md` before implementing app-level patterns such as status lifecycles, role governance, PC/mobile portals, high-performance data management pages, workflow boundaries, and automation.
@@ -287,7 +287,10 @@ It is responsible for:
287
287
 
288
288
  ## References
289
289
 
290
- - `../../references/openxiangda-api.md` — exact endpoint contracts.
291
- - `../../references/workspace-state.md` — `.openxiangda/state.json` shape and profile-scoped resource maps.
292
- - `../../references/architecture-patterns.md` — overall architecture of an OpenXiangda app workspace (CRUD data flow, page/form layering, recommended directory organization). Read this when you need to understand how forms, pages, workflows, and platform state fit together end-to-end.
293
- - `../../references/best-practices.md` — initialized best-practice templates and rules for modular pages, status lifecycles, role governance, data isolation, query performance, and workflow boundaries.
290
+ - `references/openxiangda-api.md` — exact endpoint contracts.
291
+ - `references/workspace-state.md` — `.openxiangda/state.json` shape and profile-scoped resource maps.
292
+ - `references/resource-manifest-cheatsheet.md` — quick templates for connectors, data views, App Functions, workflows, automations, permissions, settings, and menus.
293
+ - `references/data-views.md` — materialized/live data view authoring, refresh behavior, permissions, indexes, and runtime SDK usage.
294
+ - `references/connector-resources.md` — engineering resource folders, connector usage, App Function boundary, and platform runtime calls.
295
+ - `references/architecture-patterns.md` — overall architecture of an OpenXiangda app workspace (CRUD data flow, page/form layering, recommended directory organization). Read this when you need to understand how forms, pages, workflows, and platform state fit together end-to-end.
296
+ - `references/best-practices.md` — initialized best-practice templates and rules for modular pages, status lifecycles, role governance, data isolation, query performance, and workflow boundaries.
@@ -110,12 +110,12 @@ Do not use `openxiangda form create` as the normal way to generate a user-facing
110
110
 
111
111
  Read these references only when writing or reviewing schema:
112
112
 
113
- - `../../references/forms/form-schema.md`
114
- - `../../references/forms/component-registry.md`
115
- - `../../references/forms/layout-and-rules.md`
116
- - `../../references/platform-data-model.md` — how each field type is persisted (JSONB, `{label, value}` for option fields, attachment shape). Consult before designing schema or wiring values.
117
- - `../../references/component-guide.md` — platform component first, `antd` / `antd-mobile` fallback, and native-control ban for AI-authored app code.
118
- - `../../references/best-practices.md` — status lifecycle fields, ownership redundancy, role-governance fields, and the boundary between normal forms and workflow forms.
113
+ - `references/forms/form-schema.md`
114
+ - `references/forms/component-registry.md`
115
+ - `references/forms/layout-and-rules.md`
116
+ - `references/platform-data-model.md` — how each field type is persisted (JSONB, `{label, value}` for option fields, attachment shape). Consult before designing schema or wiring values.
117
+ - `references/component-guide.md` — platform component first, `antd` / `antd-mobile` fallback, and native-control ban for AI-authored app code.
118
+ - `references/best-practices.md` — status lifecycle fields, ownership redundancy, role-governance fields, and the boundary between normal forms and workflow forms.
119
119
 
120
120
  ## Rules
121
121
 
@@ -133,13 +133,13 @@ Read these references only when writing or reviewing schema:
133
133
  - Do not copy `formUuid` from dev to prod unless the target platform explicitly already uses that ID.
134
134
  - Keep `schema.ts` and `page.tsx` as the source for fields/layout/rules and presentation; generated build output is not the source of truth.
135
135
  - In `schema.ts`, import `defineFormSchema` from `openxiangda` and default-export the schema.
136
- - Treat `schema.ts` as the field/component contract. Prefer platform field components listed in `../../references/forms/component-registry.md`; do not replace them with raw JSX controls in `page.tsx`.
136
+ - Treat `schema.ts` as the field/component contract. Prefer platform field components listed in `references/forms/component-registry.md`; do not replace them with raw JSX controls in `page.tsx`.
137
137
  - Keep `page.tsx` presentation-only. It may customize layout, wrappers, sections, or routing, but should render a complete OpenXiangda standard form/runtime rather than assembling isolated raw controls.
138
138
  - If a platform field is missing for a real business need, submit feedback with `openxiangda feedback submit --yes` and then use an `antd` / `antd-mobile` wrapper as a local workaround. Report the workaround and fingerprint to the user.
139
139
  - Put validation on field-level `rules`; never generate old top-level validation arrays under `schema.rules`. Top-level `rules` is only for `FormEffect[]` with `when` and `then`.
140
- - Always provide `options` for option components. `SelectField`, `MultiSelectField`, `RadioField`, `CheckboxField`, and `CascadeSelectField` must not be emitted with `options` undefined. See `../../references/component-guide.md` for the full list and `../../references/platform-data-model.md` for the `{label, value}` storage contract.
140
+ - Always provide `options` for option components. `SelectField`, `MultiSelectField`, `RadioField`, `CheckboxField`, and `CascadeSelectField` must not be emitted with `options` undefined. See `references/component-guide.md` for the full list and `references/platform-data-model.md` for the `{label, value}` storage contract.
141
141
  - Do not emit raw native form controls in app/workspace source. For basic entry use `TextField`, `TextAreaField`, `NumberField`, `DateField`, `SelectField`, `RadioField`, etc.; for platform-specific entry use `UserSelectField`, `DepartmentSelectField`, `AttachmentField`, `ImageField`, `EditorField`, `DigitalSignatureField`, and `LocationField`.
142
142
  - Workflow form pages use the same workspace form structure plus workflow v3 configuration. In legacy workspaces, publish the form page bundle through workspace publish before treating it as complete. In React SPA workspaces, `workspace publish --form` syncs schema only, and the SPA default process pages render through `runtime deploy`.
143
143
  - After editing one form, use `workspace publish --form <formCode>` or preview `--changed --dry-run`; do not run full workspace publish by default.
144
144
  - Use `openxiangda form pull` or app snapshot before overwriting an existing form.
145
- - Before assuming a value shape (e.g. dates, attachments, member fields, option fields), verify against `../../references/platform-data-model.md` instead of guessing.
145
+ - Before assuming a value shape (e.g. dates, attachments, member fields, option fields), verify against `references/platform-data-model.md` instead of guessing.
@@ -59,6 +59,6 @@ Use logical local codes first. If a code is missing from `.openxiangda/state.jso
59
59
 
60
60
  ## References
61
61
 
62
- - `../../references/platform-data-model.md` — how each field type is persisted on the platform (JSONB, `{label, value}` for option fields, attachment shape, etc.). Consult this when a snapshot value looks unexpected so you can tell broken data from normal storage format.
63
- - `../../references/troubleshooting.md` — catalog of known failure modes (missing styles, `options` runtime errors, mismatched option labels, etc.). Cross-check inspection findings against this list before reporting an issue or proposing a fix.
62
+ - `references/platform-data-model.md` — how each field type is persisted on the platform (JSONB, `{label, value}` for option fields, attachment shape, etc.). Consult this when a snapshot value looks unexpected so you can tell broken data from normal storage format.
63
+ - `references/troubleshooting.md` — catalog of known failure modes (missing styles, `options` runtime errors, mismatched option labels, etc.). Cross-check inspection findings against this list before reporting an issue or proposing a fix.
64
64