@microi.net/cli 5.8.5 → 5.8.6

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 (211) 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/LICENSE +21 -21
  8. package/README.md +71 -71
  9. package/assets/build-meta.json +6 -6
  10. package/assets/feature-matrix.json +138 -138
  11. package/assets/logo.svg +4 -4
  12. package/cordis.patch.yml +1 -1
  13. package/package.json +1 -1
  14. package/scripts/codex-marketplace.json +20 -20
  15. package/scripts/mcp-codex-stdio-adapter.js +189 -189
  16. package/scripts/mcp-trae-windows-launcher.cmd +21 -21
  17. package/scripts/microi-cli-mcp.js +7 -7
  18. package/scripts/microi-cli.js +66 -65
  19. package/scripts/microi-codex-broker.js +450 -450
  20. package/scripts/microi-codex-router.js +618 -618
  21. package/scripts/microi-skills.meta.json +384 -384
  22. package/skills/.microi-skills-version.json +2 -2
  23. package/skills/.progressive-disclosure-manifest.json +3566 -3566
  24. package/skills/README.md +287 -287
  25. package/skills/ai-engine/SKILL.md +269 -265
  26. package/skills/ai-engine/agents/openai.yaml +4 -4
  27. package/skills/ai-engine/references/ai-employees.md +48 -48
  28. package/skills/ai-engine/references/self-hosted-digital-human.md +59 -59
  29. package/skills/ai-platform-governance/SKILL.md +177 -177
  30. package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +190 -190
  31. package/skills/app-store/SKILL.md +525 -525
  32. package/skills/app-store/agents/openai.yaml +4 -4
  33. package/skills/business-blueprint/SKILL.md +193 -193
  34. package/skills/datasource-engine/SKILL.md +93 -93
  35. package/skills/datasource-engine/agents/openai.yaml +4 -4
  36. package/skills/dos-orm/SKILL.md +97 -97
  37. package/skills/dos-orm/references/api-reference.md +229 -229
  38. package/skills/email-engine/SKILL.md +81 -81
  39. package/skills/email-engine/references/v8-email.md +34 -34
  40. package/skills/job-engine/SKILL.md +176 -176
  41. package/skills/job-engine/agents/openai.yaml +4 -4
  42. package/skills/message-notification/SKILL.md +156 -156
  43. package/skills/message-notification/agents/openai.yaml +5 -5
  44. package/skills/message-notification/references/contracts.md +102 -102
  45. package/skills/microi/SKILL.md +14 -14
  46. package/skills/microi-ai-app-auth.js +652 -652
  47. package/skills/microi-ai-application/SKILL.md +115 -115
  48. package/skills/microi-ai-application/agents/openai.yaml +4 -4
  49. package/skills/microi-ai-application/references/frontend-baseline.md +164 -164
  50. package/skills/microi-client-frontend/SKILL.md +244 -244
  51. 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
  52. 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
  53. 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
  54. package/skills/microi-codex/SKILL.md +102 -102
  55. package/skills/microi-codex-installer/SKILL.md +231 -231
  56. package/skills/microi-codex-installer/agents/openai.yaml +7 -7
  57. package/skills/microi-datasource-mapping/SKILL.md +122 -122
  58. package/skills/microi-db-schema/SKILL.md +175 -175
  59. package/skills/microi-db-schema/agents/openai.yaml +4 -4
  60. package/skills/microi-db-schema/references/core-tables.md +695 -695
  61. package/skills/microi-db-schema/references/form-component-options.md +256 -256
  62. package/skills/microi-db-schema/references/schema-overview.md +202 -202
  63. package/skills/microi-db-schema/references/schema.md +646 -646
  64. package/skills/microi-db-schema/references/table-catalog.md +1599 -1599
  65. package/skills/microi-deployment/SKILL.md +221 -221
  66. package/skills/microi-deployment/references/deployment-matrix.md +109 -109
  67. package/skills/microi-docs-coverage/SKILL.md +133 -133
  68. package/skills/microi-docs-coverage/references/capability-map.md +91 -91
  69. package/skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +894 -894
  70. package/skills/microi-form-engine/SKILL.md +333 -333
  71. package/skills/microi-form-engine/references/component-catalog.md +218 -218
  72. package/skills/microi-form-engine/references/data-source-events.md +124 -124
  73. package/skills/microi-form-layout/SKILL.md +205 -205
  74. package/skills/microi-form-layout/references/progressive-01-3-/344/270/211/347/247/215/345/210/206/347/273/204/347/232/204/345/255/230/345/202/250/344/270/216/351/205/215/347/275/256.md +235 -235
  75. package/skills/microi-frontend-sdk/SKILL.md +194 -194
  76. 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
  77. package/skills/microi-left-right-layout/SKILL.md +141 -141
  78. package/skills/microi-microservice/SKILL.md +326 -324
  79. package/skills/microi-microservice/references/runtime-delivery.md +278 -278
  80. package/skills/microi-mobile-app-quality/SKILL.md +185 -185
  81. 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 +213 -213
  82. 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 +128 -128
  83. package/skills/microi-solution-quotation/SKILL.md +78 -78
  84. package/skills/microi-solution-quotation/agents/openai.yaml +4 -4
  85. package/skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -296
  86. package/skills/microi-sso/SKILL.md +92 -92
  87. package/skills/microi-sso/references/acceptance.md +49 -49
  88. package/skills/microi-sso/references/configuration-and-security.md +53 -53
  89. package/skills/microi-sso/references/inbound.md +53 -53
  90. package/skills/microi-sso/references/outbound.md +39 -39
  91. package/skills/microi-system-delivery/SKILL.md +137 -137
  92. package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +193 -193
  93. package/skills/microi-system-delivery/references/progressive-02-/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/345/277/205/351/241/273/350/246/206/347/233/226/347/232/204/345/235/221.md +217 -217
  94. package/skills/microi-ui/SKILL.md +192 -192
  95. 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
  96. package/skills/microi-uniapp-frontend/SKILL.md +193 -193
  97. 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 +225 -225
  98. 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
  99. package/skills/microi.v8.js +1921 -1921
  100. package/skills/module-engine/SKILL.md +255 -255
  101. package/skills/module-engine/references/module-config.md +204 -204
  102. package/skills/ocr-engine/SKILL.md +113 -113
  103. package/skills/ocr-engine/agents/openai.yaml +4 -4
  104. package/skills/page-engine/SKILL.md +206 -206
  105. package/skills/page-engine/examples/compact-dashboard.json +1444 -1444
  106. 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
  107. 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
  108. package/skills/performance-testing/SKILL.md +221 -221
  109. package/skills/playwright-e2e/SKILL.md +196 -196
  110. 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
  111. 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
  112. package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +221 -221
  113. package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +115 -115
  114. package/skills/print-engine/SKILL.md +259 -259
  115. package/skills/production-readonly-audit/SKILL.md +41 -41
  116. package/skills/report-engine/SKILL.md +71 -71
  117. package/skills/report-engine/agents/openai.yaml +4 -4
  118. package/skills/scripts/optimize-progressive-disclosure.mjs +204 -204
  119. package/skills/scripts/refresh-progressive-disclosure.mjs +64 -64
  120. package/skills/scripts/sync-embedded-skills.mjs +46 -46
  121. package/skills/scripts/validate-progressive-disclosure.mjs +57 -57
  122. package/skills/search-engine/SKILL.md +75 -75
  123. package/skills/search-engine/agents/openai.yaml +4 -4
  124. package/skills/spider-engine/SKILL.md +190 -190
  125. package/skills/system-observability/SKILL.md +248 -248
  126. package/skills/system-observability/references/memory-incident-triage.md +77 -77
  127. package/skills/translate-engine/SKILL.md +140 -140
  128. package/skills/translate-engine/agents/openai.yaml +4 -4
  129. package/skills/ui-design/SKILL.md +223 -223
  130. package/skills/ui-design/assets/pattern-showcase/app.js +54 -54
  131. package/skills/ui-design/assets/pattern-showcase/index.html +163 -163
  132. package/skills/ui-design/assets/pattern-showcase/styles.css +311 -311
  133. package/skills/ui-design/assets/templates/MCI-DESIGN.md +206 -206
  134. package/skills/ui-design/references/design-pattern-library.md +184 -184
  135. package/skills/ui-design/references/mci-design-contract.md +163 -163
  136. package/skills/ui-design/references/motion-and-media.md +78 -78
  137. package/skills/ui-design/references/product-flow-recipes.md +94 -94
  138. 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
  139. package/skills/ui-design/references/progressive-02-/345/255/227/344/275/223.md +164 -164
  140. 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
  141. 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
  142. 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
  143. 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
  144. 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
  145. 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
  146. package/skills/uniapp-mall-assets/SKILL.md +176 -176
  147. package/skills/unity-integration/SKILL.md +171 -171
  148. package/skills/unity-integration/agents/openai.yaml +4 -4
  149. package/skills/unity-integration/references/ai-app-delivery.md +119 -119
  150. package/skills/unity-integration/references/sdk-api.md +82 -82
  151. package/skills/unity-integration/references/toolbox-migration.md +66 -66
  152. package/skills/unity-integration/references/webgl-hosting.md +57 -57
  153. package/skills/v8-api-config/SKILL.md +388 -388
  154. package/skills/v8-cache-pattern/SKILL.md +312 -312
  155. package/skills/v8-crud-api/SKILL.md +178 -178
  156. 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
  157. 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
  158. package/skills/v8-debugging/SKILL.md +284 -284
  159. package/skills/v8-explorer-tree/SKILL.md +228 -228
  160. package/skills/v8-export-import/SKILL.md +219 -219
  161. 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
  162. package/skills/v8-export-import/references/progressive-02-powerpoint-/345/257/274/345/207/272.md +202 -202
  163. 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
  164. package/skills/v8-file-upload/SKILL.md +284 -284
  165. 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
  166. 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
  167. package/skills/v8-formengine-http/SKILL.md +238 -238
  168. package/skills/v8-frontend-events/SKILL.md +180 -180
  169. package/skills/v8-frontend-events/references/bluetooth-print-api.md +135 -135
  170. package/skills/v8-frontend-events/references/bluetooth-print.md +258 -258
  171. package/skills/v8-frontend-events/references/progressive-01-/345/210/227/350/241/250/344/272/213/344/273/266.md +219 -219
  172. package/skills/v8-http-integration/SKILL.md +182 -182
  173. package/skills/v8-http-integration/references/progressive-01-get-/350/257/267/346/261/202.md +220 -220
  174. 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
  175. package/skills/v8-image-processing/SKILL.md +190 -190
  176. package/skills/v8-image-processing/agents/openai.yaml +4 -4
  177. package/skills/v8-image-processing/references/api-reference.md +623 -623
  178. package/skills/v8-menu-buttons/SKILL.md +186 -186
  179. 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
  180. 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
  181. 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
  182. package/skills/v8-mongodb/SKILL.md +200 -200
  183. package/skills/v8-mq-mqtt/SKILL.md +176 -176
  184. package/skills/v8-mq-mqtt/references/mqtt-production.md +342 -342
  185. package/skills/v8-mq-mqtt/references/progressive-01-v8-mqtt-iot-/347/211/251/350/201/224/347/275/221.md +181 -181
  186. package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +203 -203
  187. package/skills/v8-saas-multi-tenant/SKILL.md +305 -305
  188. package/skills/v8-security/SKILL.md +210 -210
  189. package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +200 -200
  190. package/skills/v8-security/references/progressive-02-7-/346/227/245/345/277/227/350/256/260/345/275/225.md +160 -160
  191. package/skills/v8-sql-query/SKILL.md +302 -302
  192. package/skills/v8-table-event/SKILL.md +176 -176
  193. 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
  194. 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
  195. package/skills/v8-tcp-integration/SKILL.md +147 -147
  196. package/skills/v8-tcp-integration/agents/openai.yaml +4 -4
  197. package/skills/v8-template-engine/SKILL.md +167 -167
  198. package/skills/v8-utilities/SKILL.md +104 -104
  199. package/skills/v8-utilities/references/client-api-index.md +143 -143
  200. package/skills/v8-utilities/references/platform-http-routes.md +83 -83
  201. package/skills/v8-utilities/references/server-api-index.md +188 -188
  202. package/skills/v8-workflow/SKILL.md +252 -252
  203. 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
  204. package/skills/v8-workflow/references/workflow-configuration.md +49 -49
  205. package/skills/vision-engine/SKILL.md +160 -160
  206. package/skills/vision-engine/agents/openai.yaml +4 -4
  207. package/skills/vision-engine/references/architecture-and-acceptance.md +194 -194
  208. package/skills/workspace-conventions/SKILL.md +273 -273
  209. package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +209 -209
  210. package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +217 -217
  211. 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 +27 -27
@@ -1,342 +1,342 @@
1
- # Microi MQTT 生产参考
2
-
3
- 本参考用于设计或审查 Microi MQTT 配置、事件接口引擎、设备路由、服务端下行、
4
- 安全边界和生产部署。优先以目标版本源码与实际租户元数据为准;旧部署可能尚未
5
- 具备本文列出的全部字段或行为。
6
-
7
- ## 目录
8
-
9
- - [事实源与适用边界](#事实源与适用边界)
10
- - [接入架构与协议边界](#接入架构与协议边界)
11
- - [SaaS 配置矩阵](#saas-配置矩阵)
12
- - [租户识别与连接认证](#租户识别与连接认证)
13
- - [Topic ACL 与规范化](#topic-acl-与规范化)
14
- - [事件与字段可用性](#事件与字段可用性)
15
- - [V8 返回值和失败关闭](#v8-返回值和失败关闭)
16
- - [设备级接口引擎](#设备级接口引擎)
17
- - [安全的遥测处理模式](#安全的遥测处理模式)
18
- - [服务端安全下行](#服务端安全下行)
19
- - [数据分层与可观测性](#数据分层与可观测性)
20
- - [单节点与多节点部署](#单节点与多节点部署)
21
- - [上线验收清单](#上线验收清单)
22
- - [自动覆盖检查的证据边界](#自动覆盖检查的证据边界)
23
-
24
- ## 事实源与适用边界
25
-
26
- 按以下顺序确认当前能力,不要仅凭示例或历史文档推断:
27
-
28
- 1. `Microi.Server/Microi.Core/Model/MqttParam.cs`:`V8.MQTT` 可用字段。
29
- 2. `Microi.Server/Microi.MQTT/MicroiMQTT.cs`:Broker、认证、Topic ACL、事件、
30
- 返回值、设备缓存和日志的真实行为。
31
- 3. `Microi.Server/Microi.Core/SaaSEngine/TenantConfigurationSecurity.cs`:共享监听
32
- 配置、租户凭据、Topic 规范化和敏感字段边界。
33
- 4. `Microi.Server/Microi.Core/Interface/IMicroiMQTT.cs`:
34
- `IMicroiMQTT.PublishAsync(osClient, ...)` 可信后端发布和节点状态接口。
35
- 5. `Microi.Server/Microi.Core/V8Engine/Runtime/V8Method.PlatformPluginRuntimes.cs`
36
- 与 `Microi.Server/OfficialApplications/Resource/platform-mqtt.js`:平台管理员、当前节点
37
- 状态、租户 Topic 边界与接口引擎交付入口。
38
- 6. `microi.doc/docs/doc/system-engine/mqtt-engine.md`:面向用户的完整能力说明。
39
-
40
- 目标服务器可能落后于当前源码。编写事件代码前回读其 `sys_osclients` 字段和运行
41
- 版本;缺少字段时走官方应用包/版本升级,不要在 V8 中伪造配置。
42
-
43
- ## 接入架构与协议边界
44
-
45
- Microi 当前直接处理 MQTT,不直接解析 RS-485、ZigBee、BLE 或 Modbus 帧:
46
-
47
- ```text
48
- 现场设备 -> 边缘网关/协议转换 -> MQTT TCP/TLS -> Microi Broker
49
- -> 租户认证与 Topic ACL -> mci_mqtt_client / mci_mqtt_log
50
- -> V8.EventName + V8.MQTT -> 业务表、MongoDB、HTTP、告警、工单
51
- ```
52
-
53
- 边缘网关负责现场总线、采样与协议解析;Microi 负责租户边界、实时业务规则、数据
54
- 治理和应用联动。浏览器 MQTT 需要独立的 WebSocket MQTT 网关;当前隐藏元数据
55
- `MqttWsPort` 不代表内嵌 Broker 已启用 WebSocket。
56
-
57
- ## SaaS 配置矩阵
58
-
59
- | 字段 | 作用域 | 当前行为与默认值 | 变更生效 |
60
- | --- | --- | --- | --- |
61
- | `MqttEnable` | 每租户 | 主租户为 `1` 才启动 Broker;子租户为 `1` 才允许其连接 | 启停监听需重启 MQTT 节点 |
62
- | `MqttPort` | 主租户共享 | TCP 监听端口,默认 `1883` | 重启 MQTT 节点 |
63
- | `MqttUseTls` | 主租户共享 | `1` 时尝试启用 TLS 端点 | 重启 MQTT 节点 |
64
- | `MqttTlsPort` | 主租户共享 | TLS 端口,默认 `8883` | 重启 MQTT 节点 |
65
- | `MqttCertPath` | 主租户共享 | 进程/容器内可读的 PFX 路径 | 挂载证书后重启 |
66
- | `MqttCertPassword` | 主租户共享秘密 | PFX 密码,只允许可信后端读取 | 轮换后重启 |
67
- | `MqttFallbackPort` | 主租户运行时 | Windows 主端口被拒绝时使用;无有效配置则 `21883` | 重启 MQTT 节点 |
68
- | `MqttWsPort` | 保留元数据 | 当前未创建 WebSocket 监听 | 不得宣称已生效 |
69
- | `MqttAccount` | 每租户凭据 | 子租户必须独立、完整,不能与其它租户账号重复 | 新连接使用新值 |
70
- | `MqttPwd` | 每租户秘密 | 子租户必须独立、完整,不能与其它租户密码重复 | 新连接使用新值 |
71
- | `MqttApiEngine` | 每租户 | 默认 MQTT 事件接口引擎;兼容历史 GUID/ULID Id 和当前 `ApiEngineKey` | 按接口引擎缓存规则 |
72
- | `MqttAllowAnonymous` | 仅主租户兼容 | 只有主租户显式为 `1` 才可能匿名;生产不建议 | 新连接使用新值 |
73
- | `MqttTopicIsolation` | 兼容元数据 | 不能关闭强制租户 Topic ACL,子租户设为 `0` 也不放宽 | 无放宽语义 |
74
-
75
- 把 PFX 以只读 Secret/持久卷挂载,不把证书密码、MQTT 密码写入代码、URL、日志、
76
- 前端或普通 V8。运行时读取了某字段不等于所有历史数据库都已经拥有该字段;部署前
77
- 必须回读目标表结构。
78
-
79
- 当前 `MqttUseTls=1` 是在默认明文 TCP 端点之外增加 TLS 1.2 端点,不是 TLS-only
80
- 模式;证书路径无效时会写诊断,但默认 TCP Broker 仍可能启动。要求强制加密时,
81
- 除验证 TLS 握手外,还要在防火墙/入口层关闭公网明文端口,不能只看 `IsRunning`。
82
-
83
- ## 租户识别与连接认证
84
-
85
- 连接验证按以下优先级解析租户:
86
-
87
- 1. MQTT v5 User Property `OsClient`;
88
- 2. Username 的 `<OsClient>:<MqttAccount>` 前缀;
89
- 3. ClientId 的 `<OsClient>:<设备Id>` 前缀;
90
- 4. 旧主租户客户端仅在 Username 精确等于主租户 `MqttAccount` 时兼容。
91
-
92
- 同时提供多个来源时必须全部指向同一租户。显式未知租户、未启用租户、空/非法
93
- ClientId、错误密码和跨租户 ClientId 冲突均失败关闭,不回退主租户。
94
-
95
- 子租户还必须满足:
96
-
97
- - 账号与密码都非空;
98
- - 账号不能与任一其它租户账号相同;
99
- - 密码不能与任一其它租户密码相同;
100
- - 不能通过 `MqttAllowAnonymous=1` 或 `MqttTopicIsolation=0` 绕过边界。
101
-
102
- 凭据使用常量时间字符串比较。不要在 Payload 中传一个 `OsClient` 后自行切换租户;
103
- 业务代码只信任 `V8.MQTT.OsClient`。
104
-
105
- 同一 ClientId 快速重连时,每次有效连接持有独立会话令牌。旧连接的延迟
106
- `Disconnected` 会被记录为 `StaleDisconnectIgnored`,不会删除替代连接的租户映射
107
- 或错误标记新会话下线。
108
-
109
- ## Topic ACL 与规范化
110
-
111
- 业务 Topic 会被收敛为:
112
-
113
- ```text
114
- tenant/{lowerOsClient}/{businessTopic}
115
- ```
116
-
117
- | 输入 | 结果 |
118
- | --- | --- |
119
- | `sensor/temperature` | 自动加当前租户前缀 |
120
- | `tenant/<当前租户>/sensor/temperature` | 保留并规范化租户大小写 |
121
- | `<当前租户>/sensor/temperature` | 兼容旧前缀并转为标准格式 |
122
- | `tenant/<其它租户>/...` | 拒绝 |
123
- | `$SYS/...`、`$share/...` | 拒绝 |
124
- | 发布 Topic 包含 `+` 或 `#` | 拒绝 |
125
- | 订阅 `sensor/+/state` 或 `sensor/#` | 允许合法完整段通配符并加租户前缀 |
126
- | 包含控制字符、反斜杠、`//`、`.` 或 `..` 路径段 | 拒绝 |
127
-
128
- 发布、订阅、Retained Message、可信后端下行和 MQTT v5 `ResponseTopic` 都执行同一
129
- 租户边界。`#` 只能是订阅的最后一个完整段,`+` 只能作为完整段。
130
-
131
- ## 事件与字段可用性
132
-
133
- | `V8.EventName` | 触发时机 | 主要字段 | 返回值影响 |
134
- | --- | --- | --- | --- |
135
- | `StartServer` | Broker 成功启动后,逐个启用且配置引擎的租户 | `OsClient` | 不改变启动结果 |
136
- | `Connected` | 认证成功、设备表/日志更新后 | `ClientId`、`OsClient`、`UserName`、`UserProperties` | 不能否决连接 |
137
- | `Disconnected` | 当前有效会话断开、设备表/日志更新后 | `ClientId`、`OsClient`、`UserProperties` | 不能否决断开 |
138
- | `Subscribing` | Topic ACL 已通过、订阅日志写入后 | `ClientId`、`OsClient`、`Topic`、`UserProperties` | 不能否决订阅 |
139
- | `MessageReceived` | 发布 Topic 通过 ACL、接收日志写入后 | `ClientId`、`OsClient`、`Topic`、`Payload`、`PayloadRaw`、`Qos`、`Retain`、`UserProperties` | `Code != 1` 阻止广播 |
140
- | `MessageChanged` | Retained Message 变化并通过 ACL 后 | `ClientId`、`OsClient`、`Topic`、`Payload`、`PayloadRaw`、`Qos`、`Retain` | 返回值不改变结果 |
141
- | `StopServer` | Broker 正常停止后,逐个启用且配置引擎的租户 | `OsClient` | 不改变停止结果 |
142
-
143
- `V8.MQTT` 完整模型:
144
-
145
- | 字段 | 类型 | 说明 |
146
- | --- | --- | --- |
147
- | `ClientId` | `string` | 设备/客户端 Id |
148
- | `Payload` | `object / string` | JSON 自动反序列化;失败时为原始字符串 |
149
- | `PayloadRaw` | `string` | 原始 UTF-8 文本,适合验签与审计 |
150
- | `Topic` | `string` | 已规范化的完整 Topic |
151
- | `OsClient` | `string` | 已校验租户 |
152
- | `UserName` | `string` | 连接用户名,仅部分事件有值 |
153
- | `Qos` | `number` | `0`、`1`、`2` |
154
- | `Retain` | `boolean` | Retain 标记 |
155
- | `UserProperties` | `object` | MQTT v5 用户属性;没有时为空 |
156
-
157
- 按事件读取字段,不要假设生命周期事件也有 ClientId/Payload,或连接事件已有 Topic。
158
-
159
- ## V8 返回值和失败关闭
160
-
161
- 只有 `MessageReceived` 把接口引擎返回值作为发布策略:
162
-
163
- - `null`/无返回值:放行;
164
- - 字符串、数字等普通历史返回值:放行;
165
- - 没有 `Code` 的对象:放行;
166
- - `Code: 1`:放行;
167
- - 显式 `Code != 1`:阻止向订阅者广播并写系统诊断;
168
- - 已配置的事件引擎执行异常:归一为 `Code: 0`,失败关闭。
169
-
170
- 没有配置 `MqttApiEngine` 时不存在 V8 策略闸门,Broker 仍只执行内置认证与 Topic
171
- ACL。接收日志在 V8 执行前进入 `mci_mqtt_log`,因此规则拒绝的消息仍可审计。
172
-
173
- `Connected`、`Disconnected`、`Subscribing`、`MessageChanged` 和生命周期事件是业务
174
- 观察/联动入口,当前返回值不会反向改变底层协议动作。不要误写“在 `Connected`
175
- 返回 `Code: 0` 即可拒绝连接”或“在 `Subscribing` 返回失败即可拒绝订阅”。
176
-
177
- ## 设备级接口引擎
178
-
179
- `mci_mqtt_client.ApiEngineId` 可覆盖租户 `sys_osclients.MqttApiEngine`。连接时平台:
180
-
181
- 1. 新增或更新 `ClientId`、`LastConnectTime`、`IsOnline`;
182
- 2. 把已有设备 `ApiEngineId` 放进当前节点缓存;
183
- 3. 优先使用设备引擎处理当前连接期间的 MQTT 事件;
184
- 4. 缺少设备引擎时回退租户默认引擎。
185
-
186
- 历史 JoinForm 配置可能保存 `sys_apiengine.Id`(GUID/ULID);运行时会解析为真实
187
- `ApiEngineKey`。新配置优先保存 Key。
188
-
189
- 设备级缓存是当前节点、当前连接的优化,不是共享事实。修改 `ApiEngineId` 后让设备
190
- 重新连接。当前源码在有效断开时先移除连接与设备引擎缓存,因此
191
- `Disconnected` 事件会走租户默认引擎;需要设备专属离线业务时,在租户默认引擎
192
- 按 `ClientId` 查询设备配置,不要假设断开事件仍持有设备缓存。
193
-
194
- ## 安全的遥测处理模式
195
-
196
- ```javascript
197
- var mqtt = V8.MQTT || {};
198
-
199
- if (V8.EventName !== 'MessageReceived') {
200
- return { Code: 1 };
201
- }
202
-
203
- var data = mqtt.Payload;
204
- if (typeof data === 'string') {
205
- try {
206
- data = JSON.parse(data);
207
- } catch (ex) {
208
- return { Code: 0, Msg: 'Payload 必须是合法 JSON。' };
209
- }
210
- }
211
-
212
- if (!data || !data.eventId) {
213
- return { Code: 0, Msg: '缺少稳定的 eventId。' };
214
- }
215
-
216
- var temperature = Number(data.temperature);
217
- if (isNaN(temperature) || temperature < -80 || temperature > 200) {
218
- return { Code: 0, Msg: 'temperature 超出允许范围。' };
219
- }
220
-
221
- // iot_telemetry_ingest 必须按 mqtt.OsClient + data.eventId 做唯一约束/inbox 去重。
222
- var result = V8.ApiEngine.Run('iot_telemetry_ingest', {
223
- eventId: data.eventId,
224
- osClient: mqtt.OsClient,
225
- clientId: mqtt.ClientId,
226
- topic: mqtt.Topic,
227
- temperature: temperature,
228
- qos: mqtt.Qos,
229
- retain: mqtt.Retain,
230
- payloadRaw: mqtt.PayloadRaw
231
- });
232
-
233
- return result && result.Code === 1
234
- ? { Code: 1 }
235
- : { Code: 0, Msg: (result && result.Msg) || '遥测处理失败。' };
236
- ```
237
-
238
- 不要在重试时用 `NewUlid()` 重新生成业务幂等键。稳定 `EventId` 应由设备/网关生成,
239
- 或由网关基于设备序列号、消息序号和采样时间确定性构造。消费端仍需数据库唯一约束、
240
- inbox/outbox、状态机或条件更新,QoS 2 也不能代替业务幂等。
241
-
242
- ## 服务端安全下行
243
-
244
- 可信后端只调用带租户上下文的接口:
245
-
246
- ```csharp
247
- await mqttService.PublishAsync(
248
- osClient,
249
- $"device/{deviceId}/command",
250
- JsonConvert.SerializeObject(new { Action = "restart", EventId = eventId }),
251
- qos: 1,
252
- retain: false);
253
- ```
254
-
255
- 运行时会规范化 Topic、`ResponseTopic`,覆盖 User Property 中的 `OsClient`,并用内部
256
- 租户 SenderClientId 注入消息。缺少 `osClient` 的旧原生重载会直接抛错拒绝。
257
-
258
- `V8.MQTT` 是只读事件上下文,不是 `Publish` API。若需要 V8 下行,先在 C# 提供
259
- 最小、租户隔离、不可覆盖基础设施秘密的原子能力,再由接口引擎做菜单/表/行权限、
260
- 状态机、稳定 EventId、审计和业务编排;不要开放匿名通用发布 Controller。
261
-
262
- ## 数据分层与可观测性
263
-
264
- | 数据 | 推荐位置 | 原因 |
265
- | --- | --- | --- |
266
- | 设备档案、阈值、归属、工单、告警状态 | FormEngine/关系库 | 需要事务、权限和后台维护 |
267
- | 高频遥测、采样序列 | MongoDB/专用时序存储 | 便于分区、保留和批量查询 |
268
- | 图片、音频、固件、大文件 | 对象存储/文件服务 | MQTT 只传文件 Id、哈希和元数据 |
269
- | Broker 运行审计 | `mci_mqtt_log` | 排障,不代替长期遥测仓 |
270
- | 当前设备接入台账 | `mci_mqtt_client` | 最后连接、基础在线状态、设备引擎 |
271
-
272
- 系统日志 `Type=MQTT` 记录端口占用、TLS、认证拒绝、Topic ACL、V8 异常和旧连接
273
- 断开忽略等诊断。当前 `mci_mqtt_log` 的 `Receive` 审计会保存解析后的 Payload 与
274
- `PayloadRaw`;不要在 MQTT Payload 携带密码、Token 等秘密,并为该表配置严格权限、
275
- 脱敏、保留、归档和容量策略。日志/设备表写入失败只记录告警,不会停止消息主流程,
276
- 因此它们不能单独作为“消息一定持久化”的证明。
277
-
278
- `mci_mqtt_client.IsOnline` 只能反映运行时最后写入的基础状态。节点崩溃不会保证
279
- 产生 `Disconnected`;业务在线判断应结合共享数据库中的心跳、最后活动时间、超时
280
- 窗口和设备状态机。
281
-
282
- ## 单节点与多节点部署
283
-
284
- ### 单节点或独立 MQTT 节点
285
-
286
- - 显式映射 `MqttPort` 和 `MqttTlsPort`,开放防火墙/负载入口。
287
- - 把证书挂载为只读文件;轮换后重启 MQTT 节点。
288
- - 把 MQTT TCP/TLS 流量稳定路由到该节点,不要求 HTTP 会话粘滞来保证业务正确。
289
- - `GetConnectedClients(osClient)` 和管理状态接口只用于当前节点诊断。
290
-
291
- ### 多 API 节点
292
-
293
- 内嵌 Broker 的连接、会话、订阅、Retained Message 和内存缓存不会跨 API 节点共享。
294
- 不要让负载均衡后的每个 API 节点各启动一套 Broker,再宣称它们组成一个集群。
295
-
296
- 选择以下一种生产架构:
297
-
298
- 1. 仅在独立 MQTT 节点启用内嵌 Broker,TCP/TLS 入口固定路由到该节点;
299
- 2. 使用支持持久化与集群的外部 Broker,并通过租户感知网关/适配器进入 Microi
300
- 事件链;外部 Broker 本身不会自动触发当前进程内 `V8.MQTT`;
301
- 3. 所有业务副作用使用稳定 EventId、数据库唯一约束、inbox/outbox 和条件更新。
302
-
303
- 若要求掉电或强杀窗口内零丢失,必须在返回成功前获得外部 Broker 持久化、共享
304
- outbox 或同步 WAL 的确认;内存状态与异步稍后写库不能覆盖该窗口。
305
-
306
- ## 上线验收清单
307
-
308
- - [ ] 主租户启用后 TCP 端口真实监听;TLS 证书链、端口和客户端握手真实可用。
309
- - [ ] 正确凭据连接成功;错误密码、未知/未启用租户、凭据碰撞均被拒绝。
310
- - [ ] 多租户来源冲突、跨租户 ClientId、空或非法 ClientId 被拒绝。
311
- - [ ] 普通 Topic 自动加租户前缀;其它租户、`$SYS`、`$share`、非法路径被拒绝。
312
- - [ ] 合法订阅通配符、QoS 0/1/2、Retain、`ResponseTopic` 按预期工作。
313
- - [ ] 七类事件按实际触发时机进入正确租户,字段可用性与本参考一致。
314
- - [ ] `MessageReceived` 返回 `Code: 0` 或执行异常时订阅端收不到广播,审计仍存在。
315
- - [ ] JSON 与 UTF-8 文本载荷都按策略处理,非法载荷不会写业务表。
316
- - [ ] 设备级 `ApiEngineId` 重连后生效,历史 Id 能解析为 `ApiEngineKey`。
317
- - [ ] 同一 ClientId 快速重连时,旧断开不会把新连接标记为离线。
318
- - [ ] 可信后端下行被强制限制在当前租户 Topic,旧无租户重载被拒绝。
319
- - [ ] 重复消息、接口超时、数据库短故障、节点重启后,业务副作用仍然至多一次。
320
- - [ ] 多节点场景验证入口路由、故障转移与共享业务状态,不把节点快照当全局事实。
321
- - [ ] 峰值压测记录 Broker、V8、关系库/MongoDB、日志的 CPU、内存、延迟与磁盘增长。
322
-
323
- 静态源码、自动测试、本地 Broker、生产网络和真实硬件属于不同证据层。未执行某一层
324
- 时必须明确说明,不能用另一层的成功替代。
325
-
326
- ## 自动覆盖检查的证据边界
327
-
328
- 运行:
329
-
330
- ```powershell
331
- node microi.skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs
332
- ```
333
-
334
- 脚本验证当前源码提取到的 MQTT 事件与 `MqttParam` 属性都出现在主 Skill、生产参考、
335
- 官网 MQTT 文档和 V8 后端索引中,并检查配置、安全、设备路由、下行、节点状态与
336
- 部署关键字。它不能证明:
337
-
338
- - 字段已升级到某台目标服务器;
339
- - Broker 已监听或证书有效;
340
- - 外部 Broker/网关适配器已实现;
341
- - V8 示例在目标表结构上运行成功;
342
- - 真实设备、网络抖动、吞吐、掉电和多节点故障转移已经实测。
1
+ # Microi MQTT 生产参考
2
+
3
+ 本参考用于设计或审查 Microi MQTT 配置、事件接口引擎、设备路由、服务端下行、
4
+ 安全边界和生产部署。优先以目标版本源码与实际租户元数据为准;旧部署可能尚未
5
+ 具备本文列出的全部字段或行为。
6
+
7
+ ## 目录
8
+
9
+ - [事实源与适用边界](#事实源与适用边界)
10
+ - [接入架构与协议边界](#接入架构与协议边界)
11
+ - [SaaS 配置矩阵](#saas-配置矩阵)
12
+ - [租户识别与连接认证](#租户识别与连接认证)
13
+ - [Topic ACL 与规范化](#topic-acl-与规范化)
14
+ - [事件与字段可用性](#事件与字段可用性)
15
+ - [V8 返回值和失败关闭](#v8-返回值和失败关闭)
16
+ - [设备级接口引擎](#设备级接口引擎)
17
+ - [安全的遥测处理模式](#安全的遥测处理模式)
18
+ - [服务端安全下行](#服务端安全下行)
19
+ - [数据分层与可观测性](#数据分层与可观测性)
20
+ - [单节点与多节点部署](#单节点与多节点部署)
21
+ - [上线验收清单](#上线验收清单)
22
+ - [自动覆盖检查的证据边界](#自动覆盖检查的证据边界)
23
+
24
+ ## 事实源与适用边界
25
+
26
+ 按以下顺序确认当前能力,不要仅凭示例或历史文档推断:
27
+
28
+ 1. `Microi.Server/Microi.Core/Model/MqttParam.cs`:`V8.MQTT` 可用字段。
29
+ 2. `Microi.Server/Microi.MQTT/MicroiMQTT.cs`:Broker、认证、Topic ACL、事件、
30
+ 返回值、设备缓存和日志的真实行为。
31
+ 3. `Microi.Server/Microi.Core/SaaSEngine/TenantConfigurationSecurity.cs`:共享监听
32
+ 配置、租户凭据、Topic 规范化和敏感字段边界。
33
+ 4. `Microi.Server/Microi.Core/Interface/IMicroiMQTT.cs`:
34
+ `IMicroiMQTT.PublishAsync(osClient, ...)` 可信后端发布和节点状态接口。
35
+ 5. `Microi.Server/Microi.Core/V8Engine/Runtime/V8Method.PlatformPluginRuntimes.cs`
36
+ 与 `Microi.Server/OfficialApplications/Resource/platform-mqtt.js`:平台管理员、当前节点
37
+ 状态、租户 Topic 边界与接口引擎交付入口。
38
+ 6. `microi.doc/docs/doc/system-engine/mqtt-engine.md`:面向用户的完整能力说明。
39
+
40
+ 目标服务器可能落后于当前源码。编写事件代码前回读其 `sys_osclients` 字段和运行
41
+ 版本;缺少字段时走官方应用包/版本升级,不要在 V8 中伪造配置。
42
+
43
+ ## 接入架构与协议边界
44
+
45
+ Microi 当前直接处理 MQTT,不直接解析 RS-485、ZigBee、BLE 或 Modbus 帧:
46
+
47
+ ```text
48
+ 现场设备 -> 边缘网关/协议转换 -> MQTT TCP/TLS -> Microi Broker
49
+ -> 租户认证与 Topic ACL -> mci_mqtt_client / mci_mqtt_log
50
+ -> V8.EventName + V8.MQTT -> 业务表、MongoDB、HTTP、告警、工单
51
+ ```
52
+
53
+ 边缘网关负责现场总线、采样与协议解析;Microi 负责租户边界、实时业务规则、数据
54
+ 治理和应用联动。浏览器 MQTT 需要独立的 WebSocket MQTT 网关;当前隐藏元数据
55
+ `MqttWsPort` 不代表内嵌 Broker 已启用 WebSocket。
56
+
57
+ ## SaaS 配置矩阵
58
+
59
+ | 字段 | 作用域 | 当前行为与默认值 | 变更生效 |
60
+ | --- | --- | --- | --- |
61
+ | `MqttEnable` | 每租户 | 主租户为 `1` 才启动 Broker;子租户为 `1` 才允许其连接 | 启停监听需重启 MQTT 节点 |
62
+ | `MqttPort` | 主租户共享 | TCP 监听端口,默认 `1883` | 重启 MQTT 节点 |
63
+ | `MqttUseTls` | 主租户共享 | `1` 时尝试启用 TLS 端点 | 重启 MQTT 节点 |
64
+ | `MqttTlsPort` | 主租户共享 | TLS 端口,默认 `8883` | 重启 MQTT 节点 |
65
+ | `MqttCertPath` | 主租户共享 | 进程/容器内可读的 PFX 路径 | 挂载证书后重启 |
66
+ | `MqttCertPassword` | 主租户共享秘密 | PFX 密码,只允许可信后端读取 | 轮换后重启 |
67
+ | `MqttFallbackPort` | 主租户运行时 | Windows 主端口被拒绝时使用;无有效配置则 `21883` | 重启 MQTT 节点 |
68
+ | `MqttWsPort` | 保留元数据 | 当前未创建 WebSocket 监听 | 不得宣称已生效 |
69
+ | `MqttAccount` | 每租户凭据 | 子租户必须独立、完整,不能与其它租户账号重复 | 新连接使用新值 |
70
+ | `MqttPwd` | 每租户秘密 | 子租户必须独立、完整,不能与其它租户密码重复 | 新连接使用新值 |
71
+ | `MqttApiEngine` | 每租户 | 默认 MQTT 事件接口引擎;兼容历史 GUID/ULID Id 和当前 `ApiEngineKey` | 按接口引擎缓存规则 |
72
+ | `MqttAllowAnonymous` | 仅主租户兼容 | 只有主租户显式为 `1` 才可能匿名;生产不建议 | 新连接使用新值 |
73
+ | `MqttTopicIsolation` | 兼容元数据 | 不能关闭强制租户 Topic ACL,子租户设为 `0` 也不放宽 | 无放宽语义 |
74
+
75
+ 把 PFX 以只读 Secret/持久卷挂载,不把证书密码、MQTT 密码写入代码、URL、日志、
76
+ 前端或普通 V8。运行时读取了某字段不等于所有历史数据库都已经拥有该字段;部署前
77
+ 必须回读目标表结构。
78
+
79
+ 当前 `MqttUseTls=1` 是在默认明文 TCP 端点之外增加 TLS 1.2 端点,不是 TLS-only
80
+ 模式;证书路径无效时会写诊断,但默认 TCP Broker 仍可能启动。要求强制加密时,
81
+ 除验证 TLS 握手外,还要在防火墙/入口层关闭公网明文端口,不能只看 `IsRunning`。
82
+
83
+ ## 租户识别与连接认证
84
+
85
+ 连接验证按以下优先级解析租户:
86
+
87
+ 1. MQTT v5 User Property `OsClient`;
88
+ 2. Username 的 `<OsClient>:<MqttAccount>` 前缀;
89
+ 3. ClientId 的 `<OsClient>:<设备Id>` 前缀;
90
+ 4. 旧主租户客户端仅在 Username 精确等于主租户 `MqttAccount` 时兼容。
91
+
92
+ 同时提供多个来源时必须全部指向同一租户。显式未知租户、未启用租户、空/非法
93
+ ClientId、错误密码和跨租户 ClientId 冲突均失败关闭,不回退主租户。
94
+
95
+ 子租户还必须满足:
96
+
97
+ - 账号与密码都非空;
98
+ - 账号不能与任一其它租户账号相同;
99
+ - 密码不能与任一其它租户密码相同;
100
+ - 不能通过 `MqttAllowAnonymous=1` 或 `MqttTopicIsolation=0` 绕过边界。
101
+
102
+ 凭据使用常量时间字符串比较。不要在 Payload 中传一个 `OsClient` 后自行切换租户;
103
+ 业务代码只信任 `V8.MQTT.OsClient`。
104
+
105
+ 同一 ClientId 快速重连时,每次有效连接持有独立会话令牌。旧连接的延迟
106
+ `Disconnected` 会被记录为 `StaleDisconnectIgnored`,不会删除替代连接的租户映射
107
+ 或错误标记新会话下线。
108
+
109
+ ## Topic ACL 与规范化
110
+
111
+ 业务 Topic 会被收敛为:
112
+
113
+ ```text
114
+ tenant/{lowerOsClient}/{businessTopic}
115
+ ```
116
+
117
+ | 输入 | 结果 |
118
+ | --- | --- |
119
+ | `sensor/temperature` | 自动加当前租户前缀 |
120
+ | `tenant/<当前租户>/sensor/temperature` | 保留并规范化租户大小写 |
121
+ | `<当前租户>/sensor/temperature` | 兼容旧前缀并转为标准格式 |
122
+ | `tenant/<其它租户>/...` | 拒绝 |
123
+ | `$SYS/...`、`$share/...` | 拒绝 |
124
+ | 发布 Topic 包含 `+` 或 `#` | 拒绝 |
125
+ | 订阅 `sensor/+/state` 或 `sensor/#` | 允许合法完整段通配符并加租户前缀 |
126
+ | 包含控制字符、反斜杠、`//`、`.` 或 `..` 路径段 | 拒绝 |
127
+
128
+ 发布、订阅、Retained Message、可信后端下行和 MQTT v5 `ResponseTopic` 都执行同一
129
+ 租户边界。`#` 只能是订阅的最后一个完整段,`+` 只能作为完整段。
130
+
131
+ ## 事件与字段可用性
132
+
133
+ | `V8.EventName` | 触发时机 | 主要字段 | 返回值影响 |
134
+ | --- | --- | --- | --- |
135
+ | `StartServer` | Broker 成功启动后,逐个启用且配置引擎的租户 | `OsClient` | 不改变启动结果 |
136
+ | `Connected` | 认证成功、设备表/日志更新后 | `ClientId`、`OsClient`、`UserName`、`UserProperties` | 不能否决连接 |
137
+ | `Disconnected` | 当前有效会话断开、设备表/日志更新后 | `ClientId`、`OsClient`、`UserProperties` | 不能否决断开 |
138
+ | `Subscribing` | Topic ACL 已通过、订阅日志写入后 | `ClientId`、`OsClient`、`Topic`、`UserProperties` | 不能否决订阅 |
139
+ | `MessageReceived` | 发布 Topic 通过 ACL、接收日志写入后 | `ClientId`、`OsClient`、`Topic`、`Payload`、`PayloadRaw`、`Qos`、`Retain`、`UserProperties` | `Code != 1` 阻止广播 |
140
+ | `MessageChanged` | Retained Message 变化并通过 ACL 后 | `ClientId`、`OsClient`、`Topic`、`Payload`、`PayloadRaw`、`Qos`、`Retain` | 返回值不改变结果 |
141
+ | `StopServer` | Broker 正常停止后,逐个启用且配置引擎的租户 | `OsClient` | 不改变停止结果 |
142
+
143
+ `V8.MQTT` 完整模型:
144
+
145
+ | 字段 | 类型 | 说明 |
146
+ | --- | --- | --- |
147
+ | `ClientId` | `string` | 设备/客户端 Id |
148
+ | `Payload` | `object / string` | JSON 自动反序列化;失败时为原始字符串 |
149
+ | `PayloadRaw` | `string` | 原始 UTF-8 文本,适合验签与审计 |
150
+ | `Topic` | `string` | 已规范化的完整 Topic |
151
+ | `OsClient` | `string` | 已校验租户 |
152
+ | `UserName` | `string` | 连接用户名,仅部分事件有值 |
153
+ | `Qos` | `number` | `0`、`1`、`2` |
154
+ | `Retain` | `boolean` | Retain 标记 |
155
+ | `UserProperties` | `object` | MQTT v5 用户属性;没有时为空 |
156
+
157
+ 按事件读取字段,不要假设生命周期事件也有 ClientId/Payload,或连接事件已有 Topic。
158
+
159
+ ## V8 返回值和失败关闭
160
+
161
+ 只有 `MessageReceived` 把接口引擎返回值作为发布策略:
162
+
163
+ - `null`/无返回值:放行;
164
+ - 字符串、数字等普通历史返回值:放行;
165
+ - 没有 `Code` 的对象:放行;
166
+ - `Code: 1`:放行;
167
+ - 显式 `Code != 1`:阻止向订阅者广播并写系统诊断;
168
+ - 已配置的事件引擎执行异常:归一为 `Code: 0`,失败关闭。
169
+
170
+ 没有配置 `MqttApiEngine` 时不存在 V8 策略闸门,Broker 仍只执行内置认证与 Topic
171
+ ACL。接收日志在 V8 执行前进入 `mci_mqtt_log`,因此规则拒绝的消息仍可审计。
172
+
173
+ `Connected`、`Disconnected`、`Subscribing`、`MessageChanged` 和生命周期事件是业务
174
+ 观察/联动入口,当前返回值不会反向改变底层协议动作。不要误写“在 `Connected`
175
+ 返回 `Code: 0` 即可拒绝连接”或“在 `Subscribing` 返回失败即可拒绝订阅”。
176
+
177
+ ## 设备级接口引擎
178
+
179
+ `mci_mqtt_client.ApiEngineId` 可覆盖租户 `sys_osclients.MqttApiEngine`。连接时平台:
180
+
181
+ 1. 新增或更新 `ClientId`、`LastConnectTime`、`IsOnline`;
182
+ 2. 把已有设备 `ApiEngineId` 放进当前节点缓存;
183
+ 3. 优先使用设备引擎处理当前连接期间的 MQTT 事件;
184
+ 4. 缺少设备引擎时回退租户默认引擎。
185
+
186
+ 历史 JoinForm 配置可能保存 `sys_apiengine.Id`(GUID/ULID);运行时会解析为真实
187
+ `ApiEngineKey`。新配置优先保存 Key。
188
+
189
+ 设备级缓存是当前节点、当前连接的优化,不是共享事实。修改 `ApiEngineId` 后让设备
190
+ 重新连接。当前源码在有效断开时先移除连接与设备引擎缓存,因此
191
+ `Disconnected` 事件会走租户默认引擎;需要设备专属离线业务时,在租户默认引擎
192
+ 按 `ClientId` 查询设备配置,不要假设断开事件仍持有设备缓存。
193
+
194
+ ## 安全的遥测处理模式
195
+
196
+ ```javascript
197
+ var mqtt = V8.MQTT || {};
198
+
199
+ if (V8.EventName !== 'MessageReceived') {
200
+ return { Code: 1 };
201
+ }
202
+
203
+ var data = mqtt.Payload;
204
+ if (typeof data === 'string') {
205
+ try {
206
+ data = JSON.parse(data);
207
+ } catch (ex) {
208
+ return { Code: 0, Msg: 'Payload 必须是合法 JSON。' };
209
+ }
210
+ }
211
+
212
+ if (!data || !data.eventId) {
213
+ return { Code: 0, Msg: '缺少稳定的 eventId。' };
214
+ }
215
+
216
+ var temperature = Number(data.temperature);
217
+ if (isNaN(temperature) || temperature < -80 || temperature > 200) {
218
+ return { Code: 0, Msg: 'temperature 超出允许范围。' };
219
+ }
220
+
221
+ // iot_telemetry_ingest 必须按 mqtt.OsClient + data.eventId 做唯一约束/inbox 去重。
222
+ var result = V8.ApiEngine.Run('iot_telemetry_ingest', {
223
+ eventId: data.eventId,
224
+ osClient: mqtt.OsClient,
225
+ clientId: mqtt.ClientId,
226
+ topic: mqtt.Topic,
227
+ temperature: temperature,
228
+ qos: mqtt.Qos,
229
+ retain: mqtt.Retain,
230
+ payloadRaw: mqtt.PayloadRaw
231
+ });
232
+
233
+ return result && result.Code === 1
234
+ ? { Code: 1 }
235
+ : { Code: 0, Msg: (result && result.Msg) || '遥测处理失败。' };
236
+ ```
237
+
238
+ 不要在重试时用 `NewUlid()` 重新生成业务幂等键。稳定 `EventId` 应由设备/网关生成,
239
+ 或由网关基于设备序列号、消息序号和采样时间确定性构造。消费端仍需数据库唯一约束、
240
+ inbox/outbox、状态机或条件更新,QoS 2 也不能代替业务幂等。
241
+
242
+ ## 服务端安全下行
243
+
244
+ 可信后端只调用带租户上下文的接口:
245
+
246
+ ```csharp
247
+ await mqttService.PublishAsync(
248
+ osClient,
249
+ $"device/{deviceId}/command",
250
+ JsonConvert.SerializeObject(new { Action = "restart", EventId = eventId }),
251
+ qos: 1,
252
+ retain: false);
253
+ ```
254
+
255
+ 运行时会规范化 Topic、`ResponseTopic`,覆盖 User Property 中的 `OsClient`,并用内部
256
+ 租户 SenderClientId 注入消息。缺少 `osClient` 的旧原生重载会直接抛错拒绝。
257
+
258
+ `V8.MQTT` 是只读事件上下文,不是 `Publish` API。若需要 V8 下行,先在 C# 提供
259
+ 最小、租户隔离、不可覆盖基础设施秘密的原子能力,再由接口引擎做菜单/表/行权限、
260
+ 状态机、稳定 EventId、审计和业务编排;不要开放匿名通用发布 Controller。
261
+
262
+ ## 数据分层与可观测性
263
+
264
+ | 数据 | 推荐位置 | 原因 |
265
+ | --- | --- | --- |
266
+ | 设备档案、阈值、归属、工单、告警状态 | FormEngine/关系库 | 需要事务、权限和后台维护 |
267
+ | 高频遥测、采样序列 | MongoDB/专用时序存储 | 便于分区、保留和批量查询 |
268
+ | 图片、音频、固件、大文件 | 对象存储/文件服务 | MQTT 只传文件 Id、哈希和元数据 |
269
+ | Broker 运行审计 | `mci_mqtt_log` | 排障,不代替长期遥测仓 |
270
+ | 当前设备接入台账 | `mci_mqtt_client` | 最后连接、基础在线状态、设备引擎 |
271
+
272
+ 系统日志 `Type=MQTT` 记录端口占用、TLS、认证拒绝、Topic ACL、V8 异常和旧连接
273
+ 断开忽略等诊断。当前 `mci_mqtt_log` 的 `Receive` 审计会保存解析后的 Payload 与
274
+ `PayloadRaw`;不要在 MQTT Payload 携带密码、Token 等秘密,并为该表配置严格权限、
275
+ 脱敏、保留、归档和容量策略。日志/设备表写入失败只记录告警,不会停止消息主流程,
276
+ 因此它们不能单独作为“消息一定持久化”的证明。
277
+
278
+ `mci_mqtt_client.IsOnline` 只能反映运行时最后写入的基础状态。节点崩溃不会保证
279
+ 产生 `Disconnected`;业务在线判断应结合共享数据库中的心跳、最后活动时间、超时
280
+ 窗口和设备状态机。
281
+
282
+ ## 单节点与多节点部署
283
+
284
+ ### 单节点或独立 MQTT 节点
285
+
286
+ - 显式映射 `MqttPort` 和 `MqttTlsPort`,开放防火墙/负载入口。
287
+ - 把证书挂载为只读文件;轮换后重启 MQTT 节点。
288
+ - 把 MQTT TCP/TLS 流量稳定路由到该节点,不要求 HTTP 会话粘滞来保证业务正确。
289
+ - `GetConnectedClients(osClient)` 和管理状态接口只用于当前节点诊断。
290
+
291
+ ### 多 API 节点
292
+
293
+ 内嵌 Broker 的连接、会话、订阅、Retained Message 和内存缓存不会跨 API 节点共享。
294
+ 不要让负载均衡后的每个 API 节点各启动一套 Broker,再宣称它们组成一个集群。
295
+
296
+ 选择以下一种生产架构:
297
+
298
+ 1. 仅在独立 MQTT 节点启用内嵌 Broker,TCP/TLS 入口固定路由到该节点;
299
+ 2. 使用支持持久化与集群的外部 Broker,并通过租户感知网关/适配器进入 Microi
300
+ 事件链;外部 Broker 本身不会自动触发当前进程内 `V8.MQTT`;
301
+ 3. 所有业务副作用使用稳定 EventId、数据库唯一约束、inbox/outbox 和条件更新。
302
+
303
+ 若要求掉电或强杀窗口内零丢失,必须在返回成功前获得外部 Broker 持久化、共享
304
+ outbox 或同步 WAL 的确认;内存状态与异步稍后写库不能覆盖该窗口。
305
+
306
+ ## 上线验收清单
307
+
308
+ - [ ] 主租户启用后 TCP 端口真实监听;TLS 证书链、端口和客户端握手真实可用。
309
+ - [ ] 正确凭据连接成功;错误密码、未知/未启用租户、凭据碰撞均被拒绝。
310
+ - [ ] 多租户来源冲突、跨租户 ClientId、空或非法 ClientId 被拒绝。
311
+ - [ ] 普通 Topic 自动加租户前缀;其它租户、`$SYS`、`$share`、非法路径被拒绝。
312
+ - [ ] 合法订阅通配符、QoS 0/1/2、Retain、`ResponseTopic` 按预期工作。
313
+ - [ ] 七类事件按实际触发时机进入正确租户,字段可用性与本参考一致。
314
+ - [ ] `MessageReceived` 返回 `Code: 0` 或执行异常时订阅端收不到广播,审计仍存在。
315
+ - [ ] JSON 与 UTF-8 文本载荷都按策略处理,非法载荷不会写业务表。
316
+ - [ ] 设备级 `ApiEngineId` 重连后生效,历史 Id 能解析为 `ApiEngineKey`。
317
+ - [ ] 同一 ClientId 快速重连时,旧断开不会把新连接标记为离线。
318
+ - [ ] 可信后端下行被强制限制在当前租户 Topic,旧无租户重载被拒绝。
319
+ - [ ] 重复消息、接口超时、数据库短故障、节点重启后,业务副作用仍然至多一次。
320
+ - [ ] 多节点场景验证入口路由、故障转移与共享业务状态,不把节点快照当全局事实。
321
+ - [ ] 峰值压测记录 Broker、V8、关系库/MongoDB、日志的 CPU、内存、延迟与磁盘增长。
322
+
323
+ 静态源码、自动测试、本地 Broker、生产网络和真实硬件属于不同证据层。未执行某一层
324
+ 时必须明确说明,不能用另一层的成功替代。
325
+
326
+ ## 自动覆盖检查的证据边界
327
+
328
+ 运行:
329
+
330
+ ```powershell
331
+ node microi.skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs
332
+ ```
333
+
334
+ 脚本验证当前源码提取到的 MQTT 事件与 `MqttParam` 属性都出现在主 Skill、生产参考、
335
+ 官网 MQTT 文档和 V8 后端索引中,并检查配置、安全、设备路由、下行、节点状态与
336
+ 部署关键字。它不能证明:
337
+
338
+ - 字段已升级到某台目标服务器;
339
+ - Broker 已监听或证书有效;
340
+ - 外部 Broker/网关适配器已实现;
341
+ - V8 示例在目标表结构上运行成功;
342
+ - 真实设备、网络抖动、吞吐、掉电和多节点故障转移已经实测。