@microi.net/cli 5.9.2 → 5.9.4

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 (171) hide show
  1. package/.codebuddy-plugin/marketplace.json +2 -2
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.mcp.json +1 -1
  5. package/.workbuddy-plugin/marketplace.json +2 -2
  6. package/.workbuddy-plugin/plugin.json +1 -1
  7. package/assets/build-meta.json +6 -6
  8. package/cordis.patch.yml +1 -1
  9. package/package.json +1 -1
  10. package/scripts/codex-marketplace.json +20 -20
  11. package/scripts/mcp-codex-stdio-adapter.js +189 -189
  12. package/scripts/mcp-server.js +81 -81
  13. package/scripts/mcp-trae-windows-launcher.cmd +21 -21
  14. package/scripts/microi-cli-mcp.js +7 -7
  15. package/scripts/microi-codex-broker.js +21 -5
  16. package/scripts/microi-codex-router.js +17 -1
  17. package/scripts/microi-skills.meta.json +354 -348
  18. package/skills/.microi-skills-version.json +2 -2
  19. package/skills/.progressive-disclosure-manifest.json +122 -115
  20. package/skills/README.md +287 -287
  21. package/skills/ai-engine/SKILL.md +269 -269
  22. package/skills/ai-engine/references/ai-employees.md +48 -48
  23. package/skills/ai-engine/references/self-hosted-digital-human.md +59 -59
  24. package/skills/ai-platform-governance/SKILL.md +177 -177
  25. package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +190 -190
  26. package/skills/app-store/SKILL.md +525 -525
  27. package/skills/business-blueprint/SKILL.md +193 -193
  28. package/skills/datasource-engine/SKILL.md +93 -93
  29. package/skills/dos-orm/SKILL.md +101 -95
  30. package/skills/dos-orm/references/api-reference.md +229 -229
  31. package/skills/email-engine/SKILL.md +81 -81
  32. package/skills/email-engine/references/v8-email.md +34 -34
  33. package/skills/job-engine/SKILL.md +176 -176
  34. package/skills/message-notification/SKILL.md +156 -156
  35. package/skills/message-notification/references/contracts.md +102 -102
  36. package/skills/microi/SKILL.md +14 -14
  37. package/skills/microi-ai-application/SKILL.md +115 -115
  38. package/skills/microi-ai-application/references/frontend-baseline.md +164 -164
  39. package/skills/microi-client-frontend/SKILL.md +242 -242
  40. package/skills/microi-client-frontend/references/progressive-01-3-/345/212/250/346/200/201/346/214/211/351/222/256/347/263/273/347/273/237.md +144 -144
  41. package/skills/microi-client-frontend/references/progressive-02-8-/350/277/220/350/241/214/346/227/266/351/253/230/351/242/221/345/235/221/345/244/215/347/233/230.md +196 -196
  42. package/skills/microi-client-frontend/references/progressive-03-vue3-/345/211/215/347/253/257/345/276/256/346/234/215/345/212/241/345/256/277/344/270/273/350/247/204/345/210/231.md +167 -167
  43. package/skills/microi-codex-installer/SKILL.md +231 -231
  44. package/skills/microi-datasource-mapping/SKILL.md +122 -122
  45. package/skills/microi-db-schema/SKILL.md +175 -175
  46. package/skills/microi-db-schema/references/core-tables.md +695 -695
  47. package/skills/microi-db-schema/references/form-component-options.md +256 -256
  48. package/skills/microi-db-schema/references/schema-overview.md +202 -202
  49. package/skills/microi-db-schema/references/schema.md +646 -646
  50. package/skills/microi-db-schema/references/table-catalog.md +1599 -1599
  51. package/skills/microi-deployment/SKILL.md +239 -220
  52. package/skills/microi-deployment/references/deployment-matrix.md +109 -109
  53. package/skills/microi-docs-coverage/SKILL.md +133 -133
  54. package/skills/microi-docs-coverage/references/capability-map.md +90 -90
  55. package/skills/microi-form-engine/SKILL.md +338 -333
  56. package/skills/microi-form-engine/references/component-catalog.md +218 -218
  57. package/skills/microi-form-engine/references/data-source-events.md +124 -124
  58. package/skills/microi-frontend-sdk/SKILL.md +194 -194
  59. package/skills/microi-frontend-sdk/references/progressive-01-token-/345/275/223/345/211/215/347/231/273/345/275/225/347/224/250/346/210/267/344/270/216/345/275/223/345/211/215/347/273/210/347/253/257/347/231/273/345/275/225/345/215/217/350/256/256.md +176 -176
  60. package/skills/microi-left-right-layout/SKILL.md +141 -141
  61. package/skills/microi-microservice/SKILL.md +332 -328
  62. package/skills/microi-microservice/references/runtime-delivery.md +282 -278
  63. package/skills/microi-mobile-app-quality/SKILL.md +203 -203
  64. package/skills/microi-mobile-app-quality/references/progressive-01-4-/351/207/215/350/246/201/346/214/211/351/222/256/345/277/205/351/241/273/345/270/246/345/233/276/346/240/207.md +227 -227
  65. package/skills/microi-mobile-app-quality/references/progressive-02-9-/344/270/273/351/242/230/345/210/207/346/215/242/345/277/205/351/241/273/347/234/237/345/256/236/344/270/224/345/205/250/345/261/200/347/224/237/346/225/210.md +129 -129
  66. package/skills/microi-solution-quotation/SKILL.md +78 -78
  67. package/skills/microi-sso/SKILL.md +95 -91
  68. package/skills/microi-sso/references/acceptance.md +51 -49
  69. package/skills/microi-sso/references/configuration-and-security.md +53 -53
  70. package/skills/microi-sso/references/inbound.md +53 -53
  71. package/skills/microi-sso/references/outbound.md +43 -39
  72. package/skills/microi-ui/SKILL.md +192 -192
  73. package/skills/microi-ui/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/234/272/346/231/257/350/223/235/345/233/276.md +183 -183
  74. package/skills/microi-uniapp-frontend/SKILL.md +206 -204
  75. package/skills/microi-uniapp-frontend/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/210/206/347/261/273-/345/217/214/346/240/217/345/210/227/350/241/250/347/213/254/347/253/213/346/273/232/345/212/250.md +261 -261
  76. package/skills/microi-uniapp-frontend/references/progressive-02-/345/205/263/351/224/256/344/270/232/345/212/241/350/265/204/344/272/247/344/270/215/345/276/227/351/273/230/350/256/244/351/200/211/344/270/255.md +154 -154
  77. package/skills/module-engine/SKILL.md +255 -255
  78. package/skills/module-engine/references/module-config.md +204 -204
  79. package/skills/ocr-engine/SKILL.md +113 -113
  80. package/skills/page-engine/SKILL.md +206 -206
  81. package/skills/page-engine/references/progressive-01-/346/211/200/346/234/211/347/273/204/344/273/266/347/261/273/345/236/213.md +234 -234
  82. package/skills/page-engine/references/progressive-02-/347/211/210/346/234/254/345/216/206/345/217/262-/345/271/266/345/217/221/344/277/235/345/255/230/344/270/216/345/233/236/346/273/232.md +60 -60
  83. package/skills/performance-testing/SKILL.md +227 -221
  84. package/skills/playwright-e2e/SKILL.md +197 -196
  85. package/skills/playwright-e2e/references/progressive-01-/345/205/250/350/207/252/345/212/250/347/231/273/345/275/225-/345/205/215/351/252/214/350/257/201/347/240/201-/344/275/206/344/270/215/345/205/215/345/257/206/347/240/201-/345/277/205/350/257/273.md +173 -173
  86. package/skills/playwright-e2e/references/progressive-02-/346/226/207/345/255/227/345/257/271/346/257/224/345/272/246/344/270/216/345/217/257/350/257/273/346/200/247/350/207/252/345/212/250/345/214/226/346/243/200/346/237/245-/345/277/205/345/201/232.md +183 -183
  87. package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +221 -221
  88. package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +115 -115
  89. package/skills/print-engine/SKILL.md +259 -259
  90. package/skills/production-readonly-audit/SKILL.md +41 -41
  91. package/skills/report-engine/SKILL.md +71 -71
  92. package/skills/search-engine/SKILL.md +75 -75
  93. package/skills/spider-engine/SKILL.md +190 -190
  94. package/skills/system-observability/SKILL.md +259 -246
  95. package/skills/system-observability/references/memory-incident-triage.md +77 -77
  96. package/skills/translate-engine/SKILL.md +140 -140
  97. package/skills/ui-design/SKILL.md +223 -223
  98. package/skills/ui-design/assets/templates/MCI-DESIGN.md +206 -206
  99. package/skills/ui-design/references/design-pattern-library.md +184 -184
  100. package/skills/ui-design/references/mci-design-contract.md +163 -163
  101. package/skills/ui-design/references/motion-and-media.md +78 -78
  102. package/skills/ui-design/references/product-flow-recipes.md +94 -94
  103. package/skills/ui-design/references/progressive-01-/351/242/234/350/211/262/344/275/223/347/263/273-css-variables-/346/224/257/346/214/201/344/270/273/351/242/230/345/210/207/346/215/242.md +218 -218
  104. package/skills/ui-design/references/progressive-02-/345/255/227/344/275/223.md +164 -164
  105. package/skills/ui-design/references/progressive-03-/345/212/250/346/225/210/350/247/204/350/214/203-/344/270/260/345/257/214/344/275/206/344/270/215/345/215/241.md +235 -235
  106. package/skills/ui-design/references/progressive-04-/347/273/204/344/273/266/351/243/216/346/240/274/351/200/237/346/237/245.md +152 -152
  107. package/skills/ui-design/references/progressive-05-/347/247/273/345/212/250/347/253/257/344/270/223/347/224/250/350/247/204/350/214/203.md +238 -238
  108. package/skills/ui-design/references/progressive-06-/344/270/273/351/242/230/345/210/207/346/215/242/345/256/236/347/216/260.md +194 -194
  109. package/skills/ui-design/references/progressive-07-/351/200/237/346/237/245-/344/273/216/345/244/264/346/220/255/345/273/272/344/270/200/344/270/252/347/247/273/345/212/250/347/253/257/351/241/265/351/235/242.md +207 -207
  110. package/skills/ui-design/references/progressive-08-/350/241/250/345/215/225/345/210/206/347/273/204/350/247/204/350/214/203-tabs-vs-collapsegroup-/345/274/272/345/210/266.md +142 -142
  111. package/skills/uniapp-mall-assets/SKILL.md +176 -176
  112. package/skills/unity-integration/SKILL.md +178 -171
  113. package/skills/unity-integration/references/ai-app-delivery.md +119 -119
  114. package/skills/unity-integration/references/sdk-api.md +82 -82
  115. package/skills/unity-integration/references/toolbox-migration.md +66 -66
  116. package/skills/unity-integration/references/webgl-hosting.md +57 -57
  117. package/skills/v8-api-config/SKILL.md +388 -388
  118. package/skills/v8-cache-pattern/SKILL.md +312 -312
  119. package/skills/v8-crud-api/SKILL.md +179 -178
  120. package/skills/v8-crud-api/references/progressive-01-/346/237/245/350/257/242/345/210/227/350/241/250-/345/210/206/351/241/265.md +226 -226
  121. package/skills/v8-crud-api/references/progressive-02-where-/346/235/241/344/273/266/350/257/255/346/263/225/351/200/237/346/237/245.md +49 -49
  122. package/skills/v8-debugging/SKILL.md +284 -284
  123. package/skills/v8-explorer-tree/SKILL.md +228 -228
  124. package/skills/v8-export-import/SKILL.md +219 -219
  125. package/skills/v8-export-import/references/progressive-01-excellayout-/351/253/230/347/272/247/350/207/252/347/224/261/345/270/203/345/261/200.md +211 -211
  126. package/skills/v8-export-import/references/progressive-02-powerpoint-/345/257/274/345/207/272.md +202 -202
  127. package/skills/v8-export-import/references/progressive-03-/345/256/211/345/205/250-/346/200/247/350/203/275/346/263/250/346/204/217.md +42 -42
  128. package/skills/v8-file-upload/SKILL.md +255 -284
  129. package/skills/v8-file-upload/references/cdn-alias-repair.md +13 -0
  130. package/skills/v8-file-upload/references/progressive-01-/345/205/254/346/234/211/346/241/266-vs-/347/247/201/346/234/211/346/241/266.md +263 -263
  131. package/skills/v8-file-upload/references/progressive-02-office-/346/226/207/344/273/266/345/234/250/347/272/277/347/274/226/350/276/221/347/211/210/346/234/254/345/217/267/350/247/204/345/210/231.md +161 -161
  132. package/skills/v8-file-upload/references/progressive-03-/347/251/272/347/233/256/345/275/225/346/240/207/350/256/260/344/270/216/350/277/234/347/250/213/344/270/213/350/275/275.md +36 -0
  133. package/skills/v8-formengine-http/SKILL.md +238 -238
  134. package/skills/v8-frontend-events/SKILL.md +180 -180
  135. package/skills/v8-frontend-events/references/bluetooth-print-api.md +135 -135
  136. package/skills/v8-frontend-events/references/bluetooth-print.md +258 -258
  137. package/skills/v8-frontend-events/references/progressive-01-/345/210/227/350/241/250/344/272/213/344/273/266.md +219 -219
  138. package/skills/v8-http-integration/SKILL.md +182 -182
  139. package/skills/v8-http-integration/references/progressive-01-get-/350/257/267/346/261/202.md +220 -220
  140. package/skills/v8-http-integration/references/progressive-02-/351/224/231/350/257/257/345/244/204/347/220/206/346/250/241/345/274/217.md +44 -44
  141. package/skills/v8-image-processing/SKILL.md +190 -190
  142. package/skills/v8-image-processing/references/api-reference.md +623 -623
  143. package/skills/v8-menu-buttons/SKILL.md +186 -186
  144. package/skills/v8-menu-buttons/references/progressive-01-2-/346/214/211/351/222/256/345/257/271/350/261/241-schema.md +222 -222
  145. package/skills/v8-menu-buttons/references/progressive-02-8-/346/250/241/345/274/217-f-/345/220/216/345/217/260/344/273/273/345/212/241/346/214/211/351/222/256-/351/225/277/344/273/273/345/212/241.md +228 -228
  146. package/skills/v8-menu-buttons/references/progressive-03-10-/345/217/215/346/250/241/345/274/217-/351/201/277/345/205/215.md +104 -104
  147. package/skills/v8-mongodb/SKILL.md +200 -200
  148. package/skills/v8-mq-mqtt/SKILL.md +178 -176
  149. package/skills/v8-mq-mqtt/references/mqtt-production.md +352 -342
  150. package/skills/v8-mq-mqtt/references/progressive-01-v8-mqtt-iot-/347/211/251/350/201/224/347/275/221.md +181 -181
  151. package/skills/v8-saas-multi-tenant/SKILL.md +305 -305
  152. package/skills/v8-security/SKILL.md +227 -210
  153. package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +200 -200
  154. package/skills/v8-security/references/progressive-02-7-/346/227/245/345/277/227/350/256/260/345/275/225.md +160 -160
  155. package/skills/v8-sql-query/SKILL.md +302 -302
  156. package/skills/v8-table-event/SKILL.md +176 -176
  157. package/skills/v8-table-event/references/progressive-01-informv8-js-/350/241/250/345/215/225/346/211/223/345/274/200/344/272/213/344/273/266.md +216 -216
  158. package/skills/v8-table-event/references/progressive-02-/345/211/215/347/253/257/344/272/213/344/273/266/345/220/215-v8-eventname-/345/217/257/350/203/275/347/232/204/345/200/274.md +46 -46
  159. package/skills/v8-tcp-integration/SKILL.md +158 -147
  160. package/skills/v8-template-engine/SKILL.md +167 -167
  161. package/skills/v8-utilities/SKILL.md +104 -104
  162. package/skills/v8-utilities/references/client-api-index.md +143 -143
  163. package/skills/v8-utilities/references/platform-http-routes.md +83 -83
  164. package/skills/v8-utilities/references/server-api-index.md +188 -188
  165. package/skills/v8-workflow/SKILL.md +252 -252
  166. package/skills/v8-workflow/references/progressive-01-/350/212/202/347/202/271/345/274/200/345/247/213-v8-/344/272/213/344/273/266.md +180 -180
  167. package/skills/v8-workflow/references/workflow-configuration.md +49 -49
  168. package/skills/vision-engine/SKILL.md +160 -160
  169. package/skills/vision-engine/references/architecture-and-acceptance.md +194 -194
  170. package/skills/workspace-conventions/SKILL.md +13 -13
  171. package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +2 -2
@@ -1,238 +1,238 @@
1
- ---
2
- name: v8-formengine-http
3
- description: 移动端 / 外部系统通过 HTTP 直接调用 Microi FormEngine(GetTableData / GetFormData / Add / Upt / Del)的 RESTful 路由约定与排错指南
4
- ---
5
-
6
- > **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
7
-
8
- # FormEngine HTTP 路由约定(外部系统调用)
9
-
10
- > 适用于:uni-app H5、原生 App、Postman、第三方系统、Playwright/Cypress 自动化测试等任何**没有进入 V8 引擎**的客户端。
11
- > 不适用于:服务器端 V8 接口引擎内部 — 那种情况请直接 `V8.FormEngine.GetTableData(...)`。
12
-
13
- ## ⚠️ 最常见错误
14
-
15
- ```text
16
- ❌ POST /formengine/{表名}/gettabledata → 404
17
- ❌ POST /api/formengine/{表名}/gettabledata → 404
18
- ✅ POST /api/formengine/gettabledata-{表名} → OK (动态短路由)
19
- ✅ POST /api/formengine/GetTableData body 中带 FormEngineKey → OK (标准路由)
20
- ```
21
-
22
- 平台路由由 `Microi.Server/Microi.net.Api/Handler/DynamicApiEngine.cs` 的 `FormEngineRoutes` 字典决定,**只认上面两种形式**。
23
-
24
- ## 路由总表
25
-
26
- ### 形式一:标准 Controller 路由 — 推荐
27
- 全部为 `POST`,URL 不含表名,`FormEngineKey` 放在 Body 中。
28
-
29
- | URL | 动作 |
30
- | --- | --- |
31
- | `/api/formengine/GetFormData` | 取一条 |
32
- | `/api/formengine/GetTableData` | 取列表(分页) |
33
- | `/api/formengine/AddFormData` | 新增 |
34
- | `/api/formengine/UptFormData` | 按 Id 修改 |
35
- | `/api/formengine/UptFormDataByWhere` | 按 _Where 批量改 |
36
- | `/api/formengine/DelFormData` | 删除(Id / Ids) |
37
- | `/api/formengine/DelFormDataByWhere` | 按 _Where 批量删 |
38
- | `/api/formengine/GetFormDataAnonymous` | 匿名取一条 |
39
- | `/api/formengine/GetTableDataAnonymous` | 匿名取列表 |
40
- | `/api/formengine/AddFormDataAnonymous` | 匿名新增 |
41
-
42
- ### 形式二:动态短路由(表名写进 URL,URL-friendly)
43
- | URL 前缀 | 等价于 |
44
- | --- | --- |
45
- | `/api/formengine/getformdata-{table}` | `/api/formengine/GetFormData` |
46
- | `/api/formengine/get-formdata-{table}` | `/api/formengine/GetFormData` |
47
- | `/api/formengine/gettabledata-{table}` | `/api/formengine/GetTableData` |
48
- | `/api/formengine/get-tabledata-{table}` | `/api/formengine/GetTableData` |
49
- | `/api/formengine/addformdata-{table}` | `/api/formengine/AddFormData` |
50
- | `/api/formengine/add-formdata-{table}` | `/api/formengine/AddFormData` |
51
- | `/api/formengine/uptformdata-{table}` | `/api/formengine/UptFormData` |
52
- | `/api/formengine/upt-formdata-{table}` | `/api/formengine/UptFormData` |
53
- | `/api/formengine/delformdata-{table}` | `/api/formengine/DelFormData` |
54
- | `/api/formengine/del-formdata-{table}` | `/api/formengine/DelFormData` |
55
-
56
- URL 中的表名小写最稳,平台不区分大小写。匿名版本目前**只在形式一上有**(`GetTableDataAnonymous` 等)。
57
-
58
- ## 请求 Header
59
-
60
- | Header | 是否必填 | 说明 |
61
- | --- | --- | --- |
62
- | `Content-Type` | 必填 | `application/json` 推荐 |
63
- | `OsClient` | 必填 | 租户标识;亦可放 querystring `?OsClient=xxx` 或 body |
64
- | `authorization` | 鉴权接口必填 | `Bearer <Token>`;兼容旧客户端的 `Token` Header |
65
- | `did` | 登录及鉴权接口推荐 | 当前终端稳定设备标识;首次生成后持久化,不能每次请求随机变化 |
66
-
67
- 注意:上表是 FormEngine 路由约定。ApiEngine HTTP 复测和移动端 `callEngine` 使用稳定路径 `/apiengine/{key}`,租户通过唯一的 `osclient` Header 传递,JSON/Form Body 可冗余携带 `OsClient`。普通 POST/PUT/PATCH/DELETE 禁止追加 `--OsClient--...--`;特殊路径只用于无法设置 Header/Form/Query 的 GET/HEAD 或第三方回调。
68
-
69
- 平台可能通过响应 Header `authorization`(兼容 `token`)续签或替换 Token。客户端必须立即保存新 Token,并保证并发旧响应不能覆盖已经写入的新 Token。跨域部署还必须在 CORS 中暴露 `authorization`、`token` 等需要读取的响应 Header。Token 属于凭据,禁止写入 URL、日志、错误上报和页面源码。
70
-
71
- ## FormEngine 数据授权边界
72
-
73
- “Token 有效”只代表身份有效,不代表可以访问任意表。客户端 FormEngine CRUD 还会经过表、菜单、角色和行级范围授权:
74
-
75
- 1. 受保护的平台敏感表优先拒绝普通用户;匿名接口也不能绕过。
76
- 2. 标准菜单页应携带真实 `_SysMenuId`,或使用 `ModuleEngineKey`。服务器会严格校验该菜单是否绑定目标表、当前角色是否拥有菜单及对应操作权限;列表、计数、导出追加该菜单的数据范围。
77
- 3. 列表、写入、导入、导出显式传错 `_SysMenuId` / `ModuleEngineKey` 会直接拒绝,不会降级为其它菜单或表权限。单行详情只要当前角色拥有至少一个直接绑定同表的菜单(或精确表级 `Read` 权限)即可读取,不应用菜单 `SqlWhere` / `SqlJoin`;旧客户端携带过期菜单 Id 时也按该规则恢复。
78
- 4. 为兼容历史前端 V8/外部客户端,普通 CRUD 未传菜单上下文时,服务器会从当前用户的版本化授权缓存中查找“当前角色已获授权且直接绑定目标表”的菜单。无菜单列表仍安全合并候选菜单的数据范围,不能靠漏参绕过;详情按同表菜单访问,写入按 `Add/Edit/Del` 动作权限。也可以使用角色的精确表级 `Read/Add/Edit/Del` 授权。`JoinTables` 不是独立访问授权。
79
- 5. 导入、导出必须锚定具体菜单,不能依赖无菜单兼容推断。
80
- 6. `TableChild` 子表委托由标准表单运行时生成 `_TableChildAuth`,服务器重新校验父菜单、父记录、字段绑定、子表和外键,并强制父子范围。它是内部不透明上下文,外部客户端不要手工拼装。
81
- 7. `_InvokeType:'Client'` 只决定是否执行客户端语义的表单事件,不是权限开关。可信服务器调用标记也不能通过 HTTP Body 伪造。
82
-
83
- 因此:
84
-
85
- - 标准模块、详情页和其字段元数据请求优先传真实 `_SysMenuId`,使服务器确定菜单绑定与操作权限;其中只有列表、计数、导出应用菜单查询范围。
86
- - 历史无 `_SysMenuId` 请求可继续工作,但前提是当前用户确实拥有目标表对应的菜单或精确表权限;不能把兼容推断理解为“有 Token 即可查表”。
87
- - 历史单表字段请求可在菜单缺失/过期时回退到当前角色另一个引用同表的已授权菜单;`GetDiyFieldByDiyTables` 批量字段请求按“第一张主表必须授权、后续表逐张授权并过滤”兼容。后续未授权/保护表不会导致主表失败,也不会返回其字段配置;这些规则不授予数据行权限。
88
- - 表单设计器 `/api/DiyField/UptDiyFieldList` 属于 `Level >= 9999` 控制面:外层一次授权、字段归属校验、同事务批量更新、批次末一次缓存/版本刷新。禁止在 100+ 字段循环中逐条调用完整 `UptFormDataAsync("diy_field", ...)`,否则会重复执行授权、V8、日志及 SaaS/Redis 缓存工作。
89
- - 接口引擎内的 `V8.FormEngine.*` 属于服务器端调用,不要求客户端 `_SysMenuId`。但接口引擎本身必须正确限制谁能调用,并在服务端校验业务对象范围,不能把表名和条件原样交给不可信客户端。
90
- - 客户端新增、修改、删除分别校验真实菜单的 `Add`、`Edit`、`Del` 权限。`SqlWhere` / `SqlJoin` 是模块查询过滤,不是行级写权限:不得把它们追加到写入 SQL,也不得因查询包含 Join 拒绝已获授权的主表写入。
91
- - 进入 `SubmitBeforeServerV8` / `SubmitAfterServerV8` 后,事件内 FormEngine/数据库调用与接口引擎一样属于可信服务器执行,可实现当前租户内的跨表事务。需要“只能修改本人数据”等业务约束以及归属字段写入时,应在这里或专用接口引擎中完成。
92
-
93
- ## 原生详情关联数据
94
-
95
- - `POST /api/FormEngine/GetFormRelatedData` 接收父表 `ParentFormEngineKey`、父记录
96
- `ParentTableRowId`、真实 `_SysMenuId`,以及固定 `RelatedType`:
97
- `Counts/DataLog/DataComment/DataVersion`。身份、租户来自 DiyToken,不能透传请求的可信标记。
98
- - `AuthorizeClientTableOperationAsync(..., 'Read')` 仅完成表/菜单授权,不证明父行可读。
99
- 必须再执行 Client `GetFormDataAsync`,让父记录不存在或 `ServerDataV8` 拒绝阻断关联查询。
100
- - 固定辅助表查询使用服务端新构造的 CLR 参数与可信来源;`JObject` 中写
101
- `_InvokeType:'Server'` 仍是非可信参数。不得通过给普通角色开放辅助表权限来修复此类拒绝。
102
- - 日志和版本返回固定元信息,`DataAppend.HistoryContentMode='MetadataOnly'`;
103
- 当前行脱敏不授予历史 `Content/Data` 原文。客户端不能据元信息开放预览、对比、加载。
104
- - 评论必须同时按真实 `TableId + TableRowId` 过滤。缺少 `TableId` 时,Counts 返回
105
- `DataComment=null`,以 `DataAppend.DataCommentUnavailableReason/Message` 显式说明;
106
- 评论详情失败。不能按单键兼容、猜旧数据归属、假报零条或承诺不存在的可用升级包。
107
- - 回归须区分实际 HTTP 与替身组件测试:普通角色父行可读、同菜单另一归属被 DataFilter
108
- 拒绝、父 Id 不存在、同 Id 不同表、伪造租户/类型/可信标记、历史敏感原文不返回都需验收。
109
- 安全评论绑定写入及旧数据处理须独立交付后才能宣称评论完整可用。
110
-
111
- ## Body 结构(POST JSON)
112
-
113
- ```jsonc
114
- {
115
- "OsClient": "demo",
116
- "FormEngineKey": "mall_product", // 形式一必填;形式二可选(已在 URL 中)
117
- "_SysMenuId": "目标菜单Id", // 标准模块推荐;必须是真实绑定目标表且当前角色有权访问的菜单
118
- "_Where": [["Status","=","OnSale"], ["AND","Stock",">",0]],
119
- "_SelectFields": ["Id","Title","CurrentPrice","MainImg"],
120
- "_OrderBy": "SoldCount",
121
- "_OrderByType": "DESC",
122
- "_PageIndex": 1,
123
- "_PageSize": 20
124
- }
125
- ```
126
-
127
- 写操作(Add/Upt)将业务字段平铺到 body:
128
- ```jsonc
129
- { "OsClient":"demo", "FormEngineKey":"biz_order",
130
- "Id":"01ABC...", "Quantity": 2, "Selected": 1 }
131
- ```
132
-
133
- ## 响应 DosResult 标准格式
134
-
135
- ```jsonc
136
- { "Code": 1, "Data": [...], "DataCount": 123, "Msg": "" }
137
- ```
138
- | Code | 含义 |
139
- | --- | --- |
140
- | 1 | 成功 |
141
- | 0 | 业务失败(看 `Msg`) |
142
- | 1001 | 登录身份已过期 / Token 无效 |
143
- | 1002 | 身份验证失败(OsClient 与 Token 不匹配) |
144
-
145
- ## 客户端封装样板(uni-app)
146
-
147
- ```javascript
148
- const BASE = 'https://api.itdos.com';
149
- const OS_CLIENT = runtimeConfig.osClient;
150
- const TOKEN_KEY = 'mall_token';
151
- const DID_KEY = 'mall_did';
152
-
153
- function normalizeToken(value) {
154
- return String(value || '').replace(/^Bearer\s+/i, '').trim();
155
- }
156
- function getToken() {
157
- return normalizeToken(uni.getStorageSync(TOKEN_KEY));
158
- }
159
- function getDid() {
160
- let did = uni.getStorageSync(DID_KEY);
161
- if (!did) {
162
- did = `uni-${Date.now()}-${Math.random().toString(36).slice(2)}`;
163
- uni.setStorageSync(DID_KEY, did);
164
- }
165
- return did;
166
- }
167
- function readHeader(headers, name) {
168
- const key = Object.keys(headers || {}).find(k => k.toLowerCase() === name);
169
- return key ? headers[key] : '';
170
- }
171
- function applyResponseToken(headers, requestToken) {
172
- const responseToken = normalizeToken(
173
- readHeader(headers, 'authorization') || readHeader(headers, 'token')
174
- );
175
- if (!responseToken) return;
176
-
177
- const currentToken = getToken();
178
- // 旧请求若只回显旧 Token,不得覆盖其它请求/标签页已保存的新 Token。
179
- if (currentToken && requestToken && currentToken !== requestToken && responseToken === requestToken) {
180
- return;
181
- }
182
- uni.setStorageSync(TOKEN_KEY, responseToken);
183
- }
184
-
185
- function formEngineRequest(action, table, body = {}) {
186
- return new Promise((resolve, reject) => {
187
- const requestToken = getToken();
188
- uni.request({
189
- url: `${BASE}/api/formengine/${action}-${table}`,
190
- method: 'POST',
191
- header: {
192
- 'Content-Type': 'application/json',
193
- 'OsClient': OS_CLIENT,
194
- 'authorization': requestToken ? `Bearer ${requestToken}` : '',
195
- 'did': getDid()
196
- },
197
- data: { OsClient: OS_CLIENT, FormEngineKey: table, ...body },
198
- success: (res) => {
199
- applyResponseToken(res.header || res.headers, requestToken);
200
- resolve(res.data || {});
201
- },
202
- fail: reject
203
- });
204
- });
205
- }
206
- export const formEngineGet = (t, w) => formEngineRequest('gettabledata', t, w);
207
- export const formEngineGetOne = (t, w) => formEngineRequest('getformdata', t, w);
208
- export const formEngineAdd = (t, d) => formEngineRequest('addformdata', t, d);
209
- export const formEngineUpt = (t, d) => formEngineRequest('uptformdata', t, d);
210
- export const formEngineDel = (t, d) => formEngineRequest('delformdata', t, d);
211
- ```
212
-
213
- ## 排错速查
214
-
215
- | 现象 | 真实原因 |
216
- | --- | --- |
217
- | 404 Not Found | URL 缺 `/api/` 前缀,或表名/动作之间用 `/` 而不是 `-` |
218
- | 405 Method Not Allowed | 用了 GET(FormEngine 全部为 POST) |
219
- | 1001 登录身份已过期 | 没传 Token / Token 过期 / Redis 重启 |
220
- | 1002 身份验证失败 | OsClient 不匹配 |
221
- | `NoAuth` / `您没有权限做此操作` | Token 有效但菜单、表操作权限、表绑定角色、行级范围或敏感表策略不允许 |
222
- | 显式传 `_SysMenuId` 后无权限 | 列表/写入使用错误菜单会严格拒绝;唯一详情在当前角色仍拥有另一个同表菜单时可恢复,不应用菜单查询范围 |
223
- | 无 `_SysMenuId` 仍无权限 | 当前角色没有直接绑定该表的菜单授权,也没有精确表级授权 |
224
- | 并发后提示 TokenReplaced / MissingToken | 客户端没有接收响应新 Token,或旧请求/其它标签页覆盖或清除了共享新 Token |
225
- | Code:0 表不存在 | `FormEngineKey` 在 `diy_table` 不存在 |
226
- | Code:0 字段不存在 | `_Where` / `_SelectFields` 写了表上没有的字段 |
227
- | 返回 `null` 而不是 DosResult | Controller 抛了异常被吞,到后端日志看 `Microi.Core` 报错 |
228
-
229
- ## 与服务器端 V8 的对照
230
-
231
- | 客户端 HTTP 路由 | V8 内等价写法 |
232
- | --- | --- |
233
- | POST `/api/formengine/gettabledata-mall_product` | `V8.FormEngine.GetTableData('mall_product', {...})` |
234
- | POST `/api/formengine/getformdata-mall_member` | `V8.FormEngine.GetFormData('mall_member', {...})` |
235
- | POST `/api/formengine/uptformdata-mall_shopping_cart` | `V8.FormEngine.UptFormData('mall_shopping_cart', {...})` |
236
-
237
- > 客户端 HTTP 调用**会**触发 `SubmitBeforeServerV8`、`SubmitAfterServerV8`、`DataFilterV8` 等服务端事件;
238
- > 而 V8 引擎内调用 `V8.FormEngine.*` 默认**不**触发,除非显式传 `_InvokeType:'Client'`。`_InvokeType` 只影响事件语义,不授予任何客户端表权限。
1
+ ---
2
+ name: v8-formengine-http
3
+ description: 移动端 / 外部系统通过 HTTP 直接调用 Microi FormEngine(GetTableData / GetFormData / Add / Upt / Del)的 RESTful 路由约定与排错指南
4
+ ---
5
+
6
+ > **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
7
+
8
+ # FormEngine HTTP 路由约定(外部系统调用)
9
+
10
+ > 适用于:uni-app H5、原生 App、Postman、第三方系统、Playwright/Cypress 自动化测试等任何**没有进入 V8 引擎**的客户端。
11
+ > 不适用于:服务器端 V8 接口引擎内部 — 那种情况请直接 `V8.FormEngine.GetTableData(...)`。
12
+
13
+ ## ⚠️ 最常见错误
14
+
15
+ ```text
16
+ ❌ POST /formengine/{表名}/gettabledata → 404
17
+ ❌ POST /api/formengine/{表名}/gettabledata → 404
18
+ ✅ POST /api/formengine/gettabledata-{表名} → OK (动态短路由)
19
+ ✅ POST /api/formengine/GetTableData body 中带 FormEngineKey → OK (标准路由)
20
+ ```
21
+
22
+ 平台路由由 `Microi.Server/Microi.net.Api/Handler/DynamicApiEngine.cs` 的 `FormEngineRoutes` 字典决定,**只认上面两种形式**。
23
+
24
+ ## 路由总表
25
+
26
+ ### 形式一:标准 Controller 路由 — 推荐
27
+ 全部为 `POST`,URL 不含表名,`FormEngineKey` 放在 Body 中。
28
+
29
+ | URL | 动作 |
30
+ | --- | --- |
31
+ | `/api/formengine/GetFormData` | 取一条 |
32
+ | `/api/formengine/GetTableData` | 取列表(分页) |
33
+ | `/api/formengine/AddFormData` | 新增 |
34
+ | `/api/formengine/UptFormData` | 按 Id 修改 |
35
+ | `/api/formengine/UptFormDataByWhere` | 按 _Where 批量改 |
36
+ | `/api/formengine/DelFormData` | 删除(Id / Ids) |
37
+ | `/api/formengine/DelFormDataByWhere` | 按 _Where 批量删 |
38
+ | `/api/formengine/GetFormDataAnonymous` | 匿名取一条 |
39
+ | `/api/formengine/GetTableDataAnonymous` | 匿名取列表 |
40
+ | `/api/formengine/AddFormDataAnonymous` | 匿名新增 |
41
+
42
+ ### 形式二:动态短路由(表名写进 URL,URL-friendly)
43
+ | URL 前缀 | 等价于 |
44
+ | --- | --- |
45
+ | `/api/formengine/getformdata-{table}` | `/api/formengine/GetFormData` |
46
+ | `/api/formengine/get-formdata-{table}` | `/api/formengine/GetFormData` |
47
+ | `/api/formengine/gettabledata-{table}` | `/api/formengine/GetTableData` |
48
+ | `/api/formengine/get-tabledata-{table}` | `/api/formengine/GetTableData` |
49
+ | `/api/formengine/addformdata-{table}` | `/api/formengine/AddFormData` |
50
+ | `/api/formengine/add-formdata-{table}` | `/api/formengine/AddFormData` |
51
+ | `/api/formengine/uptformdata-{table}` | `/api/formengine/UptFormData` |
52
+ | `/api/formengine/upt-formdata-{table}` | `/api/formengine/UptFormData` |
53
+ | `/api/formengine/delformdata-{table}` | `/api/formengine/DelFormData` |
54
+ | `/api/formengine/del-formdata-{table}` | `/api/formengine/DelFormData` |
55
+
56
+ URL 中的表名小写最稳,平台不区分大小写。匿名版本目前**只在形式一上有**(`GetTableDataAnonymous` 等)。
57
+
58
+ ## 请求 Header
59
+
60
+ | Header | 是否必填 | 说明 |
61
+ | --- | --- | --- |
62
+ | `Content-Type` | 必填 | `application/json` 推荐 |
63
+ | `OsClient` | 必填 | 租户标识;亦可放 querystring `?OsClient=xxx` 或 body |
64
+ | `authorization` | 鉴权接口必填 | `Bearer <Token>`;兼容旧客户端的 `Token` Header |
65
+ | `did` | 登录及鉴权接口推荐 | 当前终端稳定设备标识;首次生成后持久化,不能每次请求随机变化 |
66
+
67
+ 注意:上表是 FormEngine 路由约定。ApiEngine HTTP 复测和移动端 `callEngine` 使用稳定路径 `/apiengine/{key}`,租户通过唯一的 `osclient` Header 传递,JSON/Form Body 可冗余携带 `OsClient`。普通 POST/PUT/PATCH/DELETE 禁止追加 `--OsClient--...--`;特殊路径只用于无法设置 Header/Form/Query 的 GET/HEAD 或第三方回调。
68
+
69
+ 平台可能通过响应 Header `authorization`(兼容 `token`)续签或替换 Token。客户端必须立即保存新 Token,并保证并发旧响应不能覆盖已经写入的新 Token。跨域部署还必须在 CORS 中暴露 `authorization`、`token` 等需要读取的响应 Header。Token 属于凭据,禁止写入 URL、日志、错误上报和页面源码。
70
+
71
+ ## FormEngine 数据授权边界
72
+
73
+ “Token 有效”只代表身份有效,不代表可以访问任意表。客户端 FormEngine CRUD 还会经过表、菜单、角色和行级范围授权:
74
+
75
+ 1. 受保护的平台敏感表优先拒绝普通用户;匿名接口也不能绕过。
76
+ 2. 标准菜单页应携带真实 `_SysMenuId`,或使用 `ModuleEngineKey`。服务器会严格校验该菜单是否绑定目标表、当前角色是否拥有菜单及对应操作权限;列表、计数、导出追加该菜单的数据范围。
77
+ 3. 列表、写入、导入、导出显式传错 `_SysMenuId` / `ModuleEngineKey` 会直接拒绝,不会降级为其它菜单或表权限。单行详情只要当前角色拥有至少一个直接绑定同表的菜单(或精确表级 `Read` 权限)即可读取,不应用菜单 `SqlWhere` / `SqlJoin`;旧客户端携带过期菜单 Id 时也按该规则恢复。
78
+ 4. 为兼容历史前端 V8/外部客户端,普通 CRUD 未传菜单上下文时,服务器会从当前用户的版本化授权缓存中查找“当前角色已获授权且直接绑定目标表”的菜单。无菜单列表仍安全合并候选菜单的数据范围,不能靠漏参绕过;详情按同表菜单访问,写入按 `Add/Edit/Del` 动作权限。也可以使用角色的精确表级 `Read/Add/Edit/Del` 授权。`JoinTables` 不是独立访问授权。
79
+ 5. 导入、导出必须锚定具体菜单,不能依赖无菜单兼容推断。
80
+ 6. `TableChild` 子表委托由标准表单运行时生成 `_TableChildAuth`,服务器重新校验父菜单、父记录、字段绑定、子表和外键,并强制父子范围。它是内部不透明上下文,外部客户端不要手工拼装。
81
+ 7. `_InvokeType:'Client'` 只决定是否执行客户端语义的表单事件,不是权限开关。可信服务器调用标记也不能通过 HTTP Body 伪造。
82
+
83
+ 因此:
84
+
85
+ - 标准模块、详情页和其字段元数据请求优先传真实 `_SysMenuId`,使服务器确定菜单绑定与操作权限;其中只有列表、计数、导出应用菜单查询范围。
86
+ - 历史无 `_SysMenuId` 请求可继续工作,但前提是当前用户确实拥有目标表对应的菜单或精确表权限;不能把兼容推断理解为“有 Token 即可查表”。
87
+ - 历史单表字段请求可在菜单缺失/过期时回退到当前角色另一个引用同表的已授权菜单;`GetDiyFieldByDiyTables` 批量字段请求按“第一张主表必须授权、后续表逐张授权并过滤”兼容。后续未授权/保护表不会导致主表失败,也不会返回其字段配置;这些规则不授予数据行权限。
88
+ - 表单设计器 `/api/DiyField/UptDiyFieldList` 属于 `Level >= 9999` 控制面:外层一次授权、字段归属校验、同事务批量更新、批次末一次缓存/版本刷新。禁止在 100+ 字段循环中逐条调用完整 `UptFormDataAsync("diy_field", ...)`,否则会重复执行授权、V8、日志及 SaaS/Redis 缓存工作。
89
+ - 接口引擎内的 `V8.FormEngine.*` 属于服务器端调用,不要求客户端 `_SysMenuId`。但接口引擎本身必须正确限制谁能调用,并在服务端校验业务对象范围,不能把表名和条件原样交给不可信客户端。
90
+ - 客户端新增、修改、删除分别校验真实菜单的 `Add`、`Edit`、`Del` 权限。`SqlWhere` / `SqlJoin` 是模块查询过滤,不是行级写权限:不得把它们追加到写入 SQL,也不得因查询包含 Join 拒绝已获授权的主表写入。
91
+ - 进入 `SubmitBeforeServerV8` / `SubmitAfterServerV8` 后,事件内 FormEngine/数据库调用与接口引擎一样属于可信服务器执行,可实现当前租户内的跨表事务。需要“只能修改本人数据”等业务约束以及归属字段写入时,应在这里或专用接口引擎中完成。
92
+
93
+ ## 原生详情关联数据
94
+
95
+ - `POST /api/FormEngine/GetFormRelatedData` 接收父表 `ParentFormEngineKey`、父记录
96
+ `ParentTableRowId`、真实 `_SysMenuId`,以及固定 `RelatedType`:
97
+ `Counts/DataLog/DataComment/DataVersion`。身份、租户来自 DiyToken,不能透传请求的可信标记。
98
+ - `AuthorizeClientTableOperationAsync(..., 'Read')` 仅完成表/菜单授权,不证明父行可读。
99
+ 必须再执行 Client `GetFormDataAsync`,让父记录不存在或 `ServerDataV8` 拒绝阻断关联查询。
100
+ - 固定辅助表查询使用服务端新构造的 CLR 参数与可信来源;`JObject` 中写
101
+ `_InvokeType:'Server'` 仍是非可信参数。不得通过给普通角色开放辅助表权限来修复此类拒绝。
102
+ - 日志和版本返回固定元信息,`DataAppend.HistoryContentMode='MetadataOnly'`;
103
+ 当前行脱敏不授予历史 `Content/Data` 原文。客户端不能据元信息开放预览、对比、加载。
104
+ - 评论必须同时按真实 `TableId + TableRowId` 过滤。缺少 `TableId` 时,Counts 返回
105
+ `DataComment=null`,以 `DataAppend.DataCommentUnavailableReason/Message` 显式说明;
106
+ 评论详情失败。不能按单键兼容、猜旧数据归属、假报零条或承诺不存在的可用升级包。
107
+ - 回归须区分实际 HTTP 与替身组件测试:普通角色父行可读、同菜单另一归属被 DataFilter
108
+ 拒绝、父 Id 不存在、同 Id 不同表、伪造租户/类型/可信标记、历史敏感原文不返回都需验收。
109
+ 安全评论绑定写入及旧数据处理须独立交付后才能宣称评论完整可用。
110
+
111
+ ## Body 结构(POST JSON)
112
+
113
+ ```jsonc
114
+ {
115
+ "OsClient": "demo",
116
+ "FormEngineKey": "mall_product", // 形式一必填;形式二可选(已在 URL 中)
117
+ "_SysMenuId": "目标菜单Id", // 标准模块推荐;必须是真实绑定目标表且当前角色有权访问的菜单
118
+ "_Where": [["Status","=","OnSale"], ["AND","Stock",">",0]],
119
+ "_SelectFields": ["Id","Title","CurrentPrice","MainImg"],
120
+ "_OrderBy": "SoldCount",
121
+ "_OrderByType": "DESC",
122
+ "_PageIndex": 1,
123
+ "_PageSize": 20
124
+ }
125
+ ```
126
+
127
+ 写操作(Add/Upt)将业务字段平铺到 body:
128
+ ```jsonc
129
+ { "OsClient":"demo", "FormEngineKey":"biz_order",
130
+ "Id":"01ABC...", "Quantity": 2, "Selected": 1 }
131
+ ```
132
+
133
+ ## 响应 DosResult 标准格式
134
+
135
+ ```jsonc
136
+ { "Code": 1, "Data": [...], "DataCount": 123, "Msg": "" }
137
+ ```
138
+ | Code | 含义 |
139
+ | --- | --- |
140
+ | 1 | 成功 |
141
+ | 0 | 业务失败(看 `Msg`) |
142
+ | 1001 | 登录身份已过期 / Token 无效 |
143
+ | 1002 | 身份验证失败(OsClient 与 Token 不匹配) |
144
+
145
+ ## 客户端封装样板(uni-app)
146
+
147
+ ```javascript
148
+ const BASE = 'https://api.itdos.com';
149
+ const OS_CLIENT = runtimeConfig.osClient;
150
+ const TOKEN_KEY = 'mall_token';
151
+ const DID_KEY = 'mall_did';
152
+
153
+ function normalizeToken(value) {
154
+ return String(value || '').replace(/^Bearer\s+/i, '').trim();
155
+ }
156
+ function getToken() {
157
+ return normalizeToken(uni.getStorageSync(TOKEN_KEY));
158
+ }
159
+ function getDid() {
160
+ let did = uni.getStorageSync(DID_KEY);
161
+ if (!did) {
162
+ did = `uni-${Date.now()}-${Math.random().toString(36).slice(2)}`;
163
+ uni.setStorageSync(DID_KEY, did);
164
+ }
165
+ return did;
166
+ }
167
+ function readHeader(headers, name) {
168
+ const key = Object.keys(headers || {}).find(k => k.toLowerCase() === name);
169
+ return key ? headers[key] : '';
170
+ }
171
+ function applyResponseToken(headers, requestToken) {
172
+ const responseToken = normalizeToken(
173
+ readHeader(headers, 'authorization') || readHeader(headers, 'token')
174
+ );
175
+ if (!responseToken) return;
176
+
177
+ const currentToken = getToken();
178
+ // 旧请求若只回显旧 Token,不得覆盖其它请求/标签页已保存的新 Token。
179
+ if (currentToken && requestToken && currentToken !== requestToken && responseToken === requestToken) {
180
+ return;
181
+ }
182
+ uni.setStorageSync(TOKEN_KEY, responseToken);
183
+ }
184
+
185
+ function formEngineRequest(action, table, body = {}) {
186
+ return new Promise((resolve, reject) => {
187
+ const requestToken = getToken();
188
+ uni.request({
189
+ url: `${BASE}/api/formengine/${action}-${table}`,
190
+ method: 'POST',
191
+ header: {
192
+ 'Content-Type': 'application/json',
193
+ 'OsClient': OS_CLIENT,
194
+ 'authorization': requestToken ? `Bearer ${requestToken}` : '',
195
+ 'did': getDid()
196
+ },
197
+ data: { OsClient: OS_CLIENT, FormEngineKey: table, ...body },
198
+ success: (res) => {
199
+ applyResponseToken(res.header || res.headers, requestToken);
200
+ resolve(res.data || {});
201
+ },
202
+ fail: reject
203
+ });
204
+ });
205
+ }
206
+ export const formEngineGet = (t, w) => formEngineRequest('gettabledata', t, w);
207
+ export const formEngineGetOne = (t, w) => formEngineRequest('getformdata', t, w);
208
+ export const formEngineAdd = (t, d) => formEngineRequest('addformdata', t, d);
209
+ export const formEngineUpt = (t, d) => formEngineRequest('uptformdata', t, d);
210
+ export const formEngineDel = (t, d) => formEngineRequest('delformdata', t, d);
211
+ ```
212
+
213
+ ## 排错速查
214
+
215
+ | 现象 | 真实原因 |
216
+ | --- | --- |
217
+ | 404 Not Found | URL 缺 `/api/` 前缀,或表名/动作之间用 `/` 而不是 `-` |
218
+ | 405 Method Not Allowed | 用了 GET(FormEngine 全部为 POST) |
219
+ | 1001 登录身份已过期 | 没传 Token / Token 过期 / Redis 重启 |
220
+ | 1002 身份验证失败 | OsClient 不匹配 |
221
+ | `NoAuth` / `您没有权限做此操作` | Token 有效但菜单、表操作权限、表绑定角色、行级范围或敏感表策略不允许 |
222
+ | 显式传 `_SysMenuId` 后无权限 | 列表/写入使用错误菜单会严格拒绝;唯一详情在当前角色仍拥有另一个同表菜单时可恢复,不应用菜单查询范围 |
223
+ | 无 `_SysMenuId` 仍无权限 | 当前角色没有直接绑定该表的菜单授权,也没有精确表级授权 |
224
+ | 并发后提示 TokenReplaced / MissingToken | 客户端没有接收响应新 Token,或旧请求/其它标签页覆盖或清除了共享新 Token |
225
+ | Code:0 表不存在 | `FormEngineKey` 在 `diy_table` 不存在 |
226
+ | Code:0 字段不存在 | `_Where` / `_SelectFields` 写了表上没有的字段 |
227
+ | 返回 `null` 而不是 DosResult | Controller 抛了异常被吞,到后端日志看 `Microi.Core` 报错 |
228
+
229
+ ## 与服务器端 V8 的对照
230
+
231
+ | 客户端 HTTP 路由 | V8 内等价写法 |
232
+ | --- | --- |
233
+ | POST `/api/formengine/gettabledata-mall_product` | `V8.FormEngine.GetTableData('mall_product', {...})` |
234
+ | POST `/api/formengine/getformdata-mall_member` | `V8.FormEngine.GetFormData('mall_member', {...})` |
235
+ | POST `/api/formengine/uptformdata-mall_shopping_cart` | `V8.FormEngine.UptFormData('mall_shopping_cart', {...})` |
236
+
237
+ > 客户端 HTTP 调用**会**触发 `SubmitBeforeServerV8`、`SubmitAfterServerV8`、`DataFilterV8` 等服务端事件;
238
+ > 而 V8 引擎内调用 `V8.FormEngine.*` 默认**不**触发,除非显式传 `_InvokeType:'Client'`。`_InvokeType` 只影响事件语义,不授予任何客户端表权限。