@microi.net/cli 5.2.6 → 5.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codebuddy-plugin/marketplace.json +2 -2
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.workbuddy-plugin/marketplace.json +2 -2
- package/.workbuddy-plugin/plugin.json +1 -1
- package/assets/build-meta.json +5 -5
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/mcp-server.js +59 -59
- package/scripts/microi-cli.js +52 -29
- package/scripts/microi-skills.meta.json +221 -221
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +95 -95
- package/skills/ai-engine/SKILL.md +12 -4
- package/skills/app-store/SKILL.md +41 -20
- package/skills/message-notification/SKILL.md +8 -1
- package/skills/microi-client-frontend/SKILL.md +1 -1
- 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 +1 -1
- 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 +2 -2
- package/skills/microi-docs-coverage/references/capability-map.md +2 -1
- package/skills/microi-form-layout/SKILL.md +6 -6
- package/skills/microi-frontend-sdk/SKILL.md +6 -6
- package/skills/microi-sso/SKILL.md +1 -1
- package/skills/microi-sso/references/acceptance.md +1 -1
- package/skills/microi-sso/references/configuration-and-security.md +1 -1
- package/skills/microi-system-delivery/SKILL.md +2 -2
- package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +1 -1
- package/skills/microi.v8.js +1 -1
- package/skills/system-observability/SKILL.md +25 -7
- package/skills/translate-engine/SKILL.md +3 -0
- package/skills/ui-design/SKILL.md +1 -1
- package/skills/v8-api-config/SKILL.md +31 -9
- package/skills/v8-cache-pattern/SKILL.md +304 -289
- package/skills/v8-file-upload/SKILL.md +11 -8
- 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 +1 -1
- package/skills/v8-menu-buttons/SKILL.md +1 -1
- package/skills/v8-menu-buttons/references/progressive-01-2-/346/214/211/351/222/256/345/257/271/350/261/241-schema.md +1 -1
- package/skills/v8-mongodb/SKILL.md +58 -18
- package/skills/v8-mq-mqtt/SKILL.md +130 -109
- package/skills/v8-mq-mqtt/references/mqtt-production.md +3 -2
- package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +14 -16
- package/skills/v8-saas-multi-tenant/SKILL.md +3 -0
- package/skills/v8-security/SKILL.md +10 -6
- package/skills/v8-table-event/SKILL.md +1 -1
- 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 +1 -1
- package/skills/v8-utilities/SKILL.md +1 -1
- package/skills/v8-utilities/references/platform-http-routes.md +4 -2
- package/skills/v8-utilities/references/server-api-index.md +31 -10
- package/skills/v8-workflow/SKILL.md +1 -1
- package/skills/workspace-conventions/SKILL.md +1 -1
|
@@ -33,18 +33,21 @@ description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI
|
|
|
33
33
|
- `RichText.Limit=false` 只用于需匿名长期访问的官网公告、商品详情等公开正文;内部内容用 `true`。普通交互式帐号即使传 `false`,后端仍可按安全策略强制私有,客户端必须以上传响应的实际 `Limit` 为准。
|
|
34
34
|
- RichText 分别配置 `Image`、`Video`、`File` 的 `Enabled/MaxSize/MaxCount`;图片另传 `Preview/CompressMaxSize/CompressMaxWidth`,并继续遵循“原图私有、展示图公有或私有”的压缩链路。附件类型白名单用 `File.Accept` 进一步收紧,不能放宽服务端白名单。
|
|
35
35
|
- 私有正文持久化 `/__microi_richtext_private__/...` 稳定对象标识,严禁保存对象存储签名 URL、`OpenPrivateFile` Ticket、DiyToken 或其它会过期的凭据。每次打开记录时携带 `FormEngineKey/FormDataId/FieldId/SysMenuId` 批量换取短效审计代理地址。
|
|
36
|
-
- 私有文件后端授权必须重新校验当前租户、菜单、表、行和 RichText 字段,并确认所请求路径精确存在于 `img/video/source.src` 或 `a.href
|
|
36
|
+
- 私有文件后端授权必须重新校验当前租户、菜单、表、行和 RichText 字段,并确认所请求路径精确存在于 `img/video/source.src` 或 `a.href`;普通上传字段的对象/数组只认 `Path/FilePath/FilePathName`,不得递归把 `Name/Size/Metadata` 等任意标量当作路径。未经当前租户 FileServer 主机权威校验的绝对 HTTP(S) URL 不得等价为本地对象 Key;正文文字、`data-src/data-href`、脚本标签和前缀相似路径都必须失败关闭。
|
|
37
37
|
- 外部匿名页面没有后台记录权限上下文,不能解析私有标识。公开文章必须由有权发布公有资产的超级管理员显式使用公有桶,不得通过延长私有 URL 有效期模拟公开资源。
|
|
38
38
|
|
|
39
|
+
官网客户端读取私有文件统一调用 `/apiengine/platform-private-file-url`,提交 `FilePathName` 或有界 `FilePathNames`,并按资源类型提供权威定位参数:普通表单字段使用 `FormEngineKey + FormDataId + FieldId + SysMenuId`;用户头像使用 `ResourceKind=UserAvatar + ResourceId=用户Id`;菜单/部门导入模板分别使用 `MenuImportTemplate`、`DeptImportTemplate` 与对应记录 Id。CAD 私有派生预览使用 `ResourceKind=FormFieldDerivedPreview`,除表单四元组外必须同时提交字段中保存的 `OriginalFilePathName` 和单个派生 `FilePathName`;后端只接受同目录同 basename 的 DWG→`_preview.dxf`、STEP/STP→`_preview.stl` 唯一映射,并在对象存在后签名。文件柜对象使用 `ResourceKind=FileManagerObject`,`ResourceId` 必须与单个 `FilePathName` 大小写精确相同,并提交能力探针返回的当前租户权威 `SysMenuId`;此类签名只允许平台超级管理员 DiyToken 会话,访问密钥和普通菜单用户一律拒绝。后端会从权威字段或对象存储重新读取并精确匹配路径;管理员也不能只传裸路径绕过对象引用,普通客户端禁止换取私有文件原始 Byte/Stream。旧 `/api/HDFS/GetPrivateFileUrl` 与 `/api/HDFS/MallFileUrl` 只保留令牌格式兼容并转发同一 Managed 接口,新代码不得继续引用。
|
|
40
|
+
|
|
39
41
|
<!-- microi-progressive:begin -->
|
|
40
|
-
<!-- microi-progressive:chunk id=v8-file-upload-000 sha256=
|
|
42
|
+
<!-- microi-progressive:chunk id=v8-file-upload-000 sha256=841bb634227ea21cf97abcbee5dc6220042e73139170e6a6c304bfce83274e7a -->
|
|
41
43
|
## 核心 API
|
|
42
44
|
|
|
43
45
|
| API | 说明 |
|
|
44
46
|
|-----|------|
|
|
45
47
|
| `V8.FilesByteBase64` | 接收上传时携带的文件字典 `{ FileName: base64 }` |
|
|
46
48
|
| `V8.Method.Upload({...})` | 服务端上传文件到 HDFS(推荐) |
|
|
47
|
-
| `V8.Method.GetPrivateFileUrl({FilePathName})` | 生成私有桶临时访问 URL |
|
|
49
|
+
| `V8.Method.GetPrivateFileUrl({FilePathName})` | 生成私有桶临时访问 URL |
|
|
50
|
+
| `/apiengine/platform-private-file-url` | 官网 PC/UniApp 按菜单、记录、字段和对象引用换取私有文件短链 |
|
|
48
51
|
| `V8.Http.GetResponse({Url}).RawBytes` | 下载远程文件为字节数组 |
|
|
49
52
|
| 接口返回 `{ FileName, ContentType, FileByteBase64 }` | 接口直接响应文件 |
|
|
50
53
|
|
|
@@ -63,10 +66,10 @@ description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI
|
|
|
63
66
|
可信后端 V8 可用 `V8.Http.GetResponse({ Url: url }).RawBytes` 下载,再用 `System.Convert.ToBase64String` 和 `V8.Method.Upload` 上传。该路径同样必须校验域名、大小、Content-Type、后缀和最终重定向目标。
|
|
64
67
|
|
|
65
68
|
<!-- /microi-progressive:chunk -->
|
|
66
|
-
<!-- microi-progressive:chunk id=v8-file-upload-002 sha256=
|
|
69
|
+
<!-- microi-progressive:chunk id=v8-file-upload-002 sha256=c20863b68d13b3bd41caee90a5e12878501a35b3d9f8d40a80efe3f6b46125a9 -->
|
|
67
70
|
## 接收前端上传的文件
|
|
68
71
|
|
|
69
|
-
前端发起文件上传时,平台自动把文件以 base64 形式注入到 `V8.FilesByteBase64`:
|
|
72
|
+
前端发起文件上传时,平台自动把文件以 base64 形式注入到 `V8.FilesByteBase64`:
|
|
70
73
|
|
|
71
74
|
```javascript
|
|
72
75
|
// V8.FilesByteBase64 = { '文件名1.png': 'base64...', '文件名2.pdf': 'base64...' }
|
|
@@ -165,7 +168,7 @@ Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进
|
|
|
165
168
|
- 生产 H5 不能只依赖 `uni.uploadFile`。页面从 `uni.chooseImage` 得到的 `tempFiles[0].file`、`tempFiles[0]`、`blob:` / `data:` 临时路径都要传给 `V8.uploadFile`,并设置 `preferFetch:true`;SDK 必须能用 `fetch + FormData` 兜底,否则线上可能报 `未找到 MicroiV8 上传适配器。`。
|
|
166
169
|
|
|
167
170
|
<!-- /microi-progressive:chunk -->
|
|
168
|
-
<!-- microi-progressive:chunk id=v8-file-upload-003 sha256=
|
|
171
|
+
<!-- microi-progressive:chunk id=v8-file-upload-003 sha256=d02dfde4abd2349cd92de1daee129bc08ba42142b5f6229736588cb3e0dbf43c -->
|
|
169
172
|
## 跨平台文件同步登录会话
|
|
170
173
|
|
|
171
174
|
文件柜、文件同步等需要连接另一套 Microi API 的工具,必须把远程平台视为独立登录会话:
|
|
@@ -173,9 +176,9 @@ Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进
|
|
|
173
176
|
- 用户必须先完成远程登录,登录成功后显示远程用户名称、帐号、ApiBase、OsClient 和登录状态,并提供明确的退出登录操作。
|
|
174
177
|
- 历史远程连接通过 `mci_` 前缀表保存,并按 `V8.CurrentUser.Id` 做行级隔离;不得把帐号、密码或 Token 放入 `localStorage`。
|
|
175
178
|
- 密码和 Token 只能由受保护的接口引擎写入、读取和清理。数据库必须保存可校验的加密密文,普通 FormEngine 列表不得返回密文字段。
|
|
176
|
-
-
|
|
179
|
+
- 密码和 Token 使用 `V8.Method.ProtectApiEngineSecret/UnprotectApiEngineSecret`,由宿主把密文绑定当前 `OsClient + ApiEngineKey`;不得从已脱敏的 `V8.OsClientModel` 读取 `AuthSecret/DbConn`,也不得使用进程级临时密钥。接口引擎 Key 必须稳定,确保服务重启和应用升级后仍能解密历史连接。
|
|
177
180
|
- 历史连接列表只返回脱敏元数据;一键重连时再按记录 Id 和当前用户读取凭据。删除连接必须同时清除保存的密码和 Token。
|
|
178
|
-
-
|
|
181
|
+
- 远程目标登录后必须调用文件柜能力探针 `mci_file_sync_capability` 检查同步协议版本,并使用其 `Data.FileManagerSysMenuId` 作为目标租户权威文件柜菜单;禁止硬编码发布端菜单 Id。接口不存在、未返回菜单 Id、返回 404/非标准结果或协议版本过低时,提示目标平台更新【文件柜】应用,不得继续同步。
|
|
179
182
|
- 验收至少覆盖:登录成功显示身份、退出后 Token 清空、历史连接一键重连、删除连接、密文落库、服务重启后仍可解密、目标平台缺少能力接口时的升级提示。
|
|
180
183
|
|
|
181
184
|
<!-- /microi-progressive:chunk -->
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
4
|
|
|
5
|
-
<!-- microi-progressive:chunk id=v8-file-upload-005 sha256=
|
|
5
|
+
<!-- microi-progressive:chunk id=v8-file-upload-005 sha256=6136a38af209aad174ca1cacd61fcfc66e3428e1e8bf75be8a2490bcb094b71e -->
|
|
6
6
|
## 公有桶 vs 私有桶
|
|
7
7
|
|
|
8
8
|
### 应用商城 ZIP
|
|
@@ -76,7 +76,7 @@ V8.ConfirmTips('确认领取该任务?', function () {
|
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
<!-- /microi-progressive:chunk -->
|
|
79
|
-
<!-- microi-progressive:chunk id=v8-menu-buttons-003 sha256=
|
|
79
|
+
<!-- microi-progressive:chunk id=v8-menu-buttons-003 sha256=d04e7a71ffe8baa47c2430077d0fd0bb44dffc62c7e7ce2bca9d02b220ae24f5 -->
|
|
80
80
|
## 4.1 模式 B2:在线微服务定制页(OpenAppDialog)
|
|
81
81
|
|
|
82
82
|
当弹窗包含复杂布局、多步骤交互、实时校验或后续需要 AI 在线维护时,优先把页面实现为在线微服务,按钮 V8 代码只负责打开页面、传入上下文和接收结果。不要把长篇 HTML/CSS 写进 `V8Code`。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
4
|
|
|
5
|
-
<!-- microi-progressive:chunk id=v8-menu-buttons-004 sha256=
|
|
5
|
+
<!-- microi-progressive:chunk id=v8-menu-buttons-004 sha256=9550f2e3a75583bb6f2ab940d423931511f39b6ad8a11fc982c6b24bca6416d1 -->
|
|
6
6
|
## 2. 按钮对象 Schema
|
|
7
7
|
|
|
8
8
|
```jsonc
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: v8-mongodb
|
|
3
|
-
description: Microi V8 MongoDB 指南。用于使用 V8.MongoDb AddFormData、UptFormData、DelFormData、GetFormData、GetTableData、对象过滤和文档 Id。
|
|
3
|
+
description: Microi V8 MongoDB 指南。用于使用 V8.MongoDb AddFormData、UptFormData、UptFormDataByWhere、DelFormData、DelFormDataByWhere、GetFormData、GetTableData、对象过滤和文档 Id。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
@@ -14,8 +14,10 @@ description: Microi V8 MongoDB 指南。用于使用 V8.MongoDb AddFormData、Up
|
|
|
14
14
|
| 方法 | 说明 |
|
|
15
15
|
|------|------|
|
|
16
16
|
| `V8.MongoDb.AddFormData({...})` | 新增文档 |
|
|
17
|
-
| `V8.MongoDb.UptFormData({...})` | 修改文档(按 Id) |
|
|
18
|
-
| `V8.MongoDb.
|
|
17
|
+
| `V8.MongoDb.UptFormData({...})` | 修改文档(按 Id) |
|
|
18
|
+
| `V8.MongoDb.UptFormDataByWhere({...})` | 按非空 `_Where` 批量修改,禁止修改 `_id` |
|
|
19
|
+
| `V8.MongoDb.DelFormData({...})` | 删除文档(按 Id) |
|
|
20
|
+
| `V8.MongoDb.DelFormDataByWhere({...})` | 按非空 `_Where` 批量删除 |
|
|
19
21
|
| `V8.MongoDb.GetFormData({...})` | 查询单个文档(按 Id) |
|
|
20
22
|
| `V8.MongoDb.GetTableData({...})` | 查询文档列表 |
|
|
21
23
|
| `V8.MongoDb.NewId()` | 生成 MongoDB Id |
|
|
@@ -40,7 +42,7 @@ V8.MongoDb.AddFormData({
|
|
|
40
42
|
## 修改文档
|
|
41
43
|
|
|
42
44
|
```javascript
|
|
43
|
-
V8.MongoDb.UptFormData({
|
|
45
|
+
V8.MongoDb.UptFormData({
|
|
44
46
|
DbName: 'sys_log_2024',
|
|
45
47
|
TableName: 'log_2024_12',
|
|
46
48
|
Id: V8.Param.id, // 必传
|
|
@@ -48,18 +50,50 @@ V8.MongoDb.UptFormData({
|
|
|
48
50
|
Action: '更新操作',
|
|
49
51
|
UpdateTime: DateNow('yyyy-MM-dd HH:mm:ss')
|
|
50
52
|
}
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 按条件批量修改
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
var result = V8.MongoDb.UptFormDataByWhere({
|
|
60
|
+
DbName: 'diy_chat_' + V8.OsClient.toLowerCase(),
|
|
61
|
+
TableName: 'chat_' + DateNow('yyyy'),
|
|
62
|
+
_Where: [
|
|
63
|
+
['FromUserId', '=', V8.Param.PeerUserId],
|
|
64
|
+
['ToUserId', '=', V8.CurrentUser.Id],
|
|
65
|
+
['IsRead', '=', false]
|
|
66
|
+
],
|
|
67
|
+
_FormData: { IsRead: true }
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`_Where` 缺失、为空或包含无效条件时必须返回失败,不得执行全集合更新。`_FormData` 禁止设置 `_id` 或 `_id.*`。返回值的 `Data` 包含 `MatchedCount` 和 `ModifiedCount`。
|
|
53
72
|
|
|
54
73
|
## 删除文档
|
|
55
74
|
|
|
56
75
|
```javascript
|
|
57
|
-
V8.MongoDb.DelFormData({
|
|
76
|
+
V8.MongoDb.DelFormData({
|
|
58
77
|
DbName: 'sys_log_2024',
|
|
59
78
|
TableName: 'log_2024_12',
|
|
60
79
|
Id: V8.Param.id // 必传
|
|
61
|
-
});
|
|
62
|
-
```
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 按条件批量删除
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
var result = V8.MongoDb.DelFormDataByWhere({
|
|
87
|
+
DbName: 'diy_chat_' + V8.OsClient.toLowerCase(),
|
|
88
|
+
TableName: 'chat_last_contact',
|
|
89
|
+
_Where: [
|
|
90
|
+
['UserId', '=', V8.CurrentUser.Id],
|
|
91
|
+
['ContactUserId', '=', V8.Param.PeerUserId]
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
删除条件必须包含当前权威用户/资源边界,不能只用可伪造的前端参数。返回值的 `Data` 包含 `DeletedCount`。
|
|
63
97
|
|
|
64
98
|
## 查询单个文档
|
|
65
99
|
|
|
@@ -74,14 +108,18 @@ var result = V8.MongoDb.GetFormData({
|
|
|
74
108
|
## 查询文档列表
|
|
75
109
|
|
|
76
110
|
```javascript
|
|
77
|
-
var result = V8.MongoDb.GetTableData({
|
|
111
|
+
var result = V8.MongoDb.GetTableData({
|
|
78
112
|
DbName: 'sys_log_2024',
|
|
79
113
|
TableName: 'log_2024_12',
|
|
80
114
|
_Where: [
|
|
81
115
|
['Type', '=', '访问菜单'],
|
|
82
116
|
['OR', 'Type', '=', '点击V8按钮']
|
|
83
|
-
]
|
|
84
|
-
|
|
117
|
+
],
|
|
118
|
+
_OrderBy: 'CreateTime',
|
|
119
|
+
_OrderByType: 'DESC',
|
|
120
|
+
_PageIndex: 1,
|
|
121
|
+
_PageSize: 20
|
|
122
|
+
});
|
|
85
123
|
```
|
|
86
124
|
|
|
87
125
|
## 实战模式
|
|
@@ -143,9 +181,11 @@ return { Code: 1, Data: result };
|
|
|
143
181
|
|
|
144
182
|
## 注意事项
|
|
145
183
|
|
|
146
|
-
- MongoDB 参数统一使用**对象格式**:`{ DbName, TableName, Id, _FormData, _Where }`
|
|
147
|
-
- `DbName` 是 MongoDB 数据库名,`TableName` 是集合名
|
|
148
|
-
- `_Where` 条件语法与 `V8.FormEngine` 一致
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
184
|
+
- MongoDB 参数统一使用**对象格式**:`{ DbName, TableName, Id, _FormData, _Where, _OrderBy, _OrderByType }`
|
|
185
|
+
- `DbName` 是 MongoDB 数据库名,`TableName` 是集合名
|
|
186
|
+
- `_Where` 条件语法与 `V8.FormEngine` 一致
|
|
187
|
+
- 批量写入仅接受安全字段名、已知操作符和非空条件;条件中的租户、用户和资源 Id 应来自 `V8.OsClient` / `V8.CurrentUser` / 权威回查,不得信任 `V8.Param` 中的同名身份字段
|
|
188
|
+
- 非主库 V8 运行时会把 MongoDB 操作绑定到当前租户;显式传入其它 `OsClient` 不能跨租户
|
|
189
|
+
- 适合存储日志、IoT 数据、大文档等非结构化 / 海量数据
|
|
190
|
+
- 建议按时间分库分表(如 `log_2024_01`),便于清理历史数据
|
|
191
|
+
- MongoDB 操作不参与 `V8.DbTrans` 事务。批量写入 `Code=1` 后即是已提交事实;后续 Hook/投递失败应返回警告或进入补偿,不得返回“未发生”导致盲目重试,重试必须有稳定业务 Id 幂等
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: v8-mq-mqtt
|
|
1
|
+
---
|
|
2
|
+
name: v8-mq-mqtt
|
|
3
3
|
description: Microi V8 消息队列与 MQTT 生产指南。用于 V8.MQ.SendMsg、RabbitMQ 消费与幂等,以及内嵌 MQTT Broker、SaaS 租户认证、Topic ACL、TLS、QoS/Retain、七类 V8.MQTT 事件、设备级接口引擎、服务端下行、IoT 数据分层和多节点部署验收。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
|
-
|
|
8
|
-
# Microi V8 消息队列与 MQTT
|
|
9
|
-
|
|
10
|
-
你正在开发 Microi 吾码平台的 V8 引擎代码,需要使用 RabbitMQ 消息队列或 MQTT 物联网协议。
|
|
11
|
-
|
|
12
|
-
<!-- microi-progressive:begin -->
|
|
13
|
-
<!-- microi-progressive:chunk id=v8-mq-mqtt-000 sha256=
|
|
14
|
-
## V8.MQ — RabbitMQ 消息队列
|
|
15
|
-
|
|
16
|
-
### 生产消息(后端)
|
|
17
|
-
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
|
+
|
|
8
|
+
# Microi V8 消息队列与 MQTT
|
|
9
|
+
|
|
10
|
+
你正在开发 Microi 吾码平台的 V8 引擎代码,需要使用 RabbitMQ 消息队列或 MQTT 物联网协议。
|
|
11
|
+
|
|
12
|
+
<!-- microi-progressive:begin -->
|
|
13
|
+
<!-- microi-progressive:chunk id=v8-mq-mqtt-000 sha256=4a6559aed1af036591a04002c6e3311d9f0957fbbabbcafdc4a6bad95dabd843 -->
|
|
14
|
+
## V8.MQ — RabbitMQ 消息队列
|
|
15
|
+
|
|
16
|
+
### 生产消息(后端)
|
|
17
|
+
|
|
18
18
|
```javascript
|
|
19
|
-
//
|
|
19
|
+
// 当前 V8EngineMQ 是同步包装,调用完成后直接返回 DosResult。
|
|
20
20
|
// 业务重试必须复用同一个 EventId,供消费者幂等去重。
|
|
21
|
-
var result =
|
|
21
|
+
var result = V8.MQ.SendMsg({
|
|
22
22
|
QueueName: 'order_process',
|
|
23
23
|
EventId: V8.Param.eventId || V8.Method.NewUlid(),
|
|
24
24
|
Message: {
|
|
@@ -31,45 +31,52 @@ if (result.Code !== 1) return result;
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
逻辑队列名由服务端规范为 `microi.{lowerOsClient}.{queueName}`。V8 上下文、登录 Token 与后台显式 `OsClient` 是权威租户,body 不能切换到其它租户队列。
|
|
34
|
-
|
|
35
|
-
### 生产消息(前端)
|
|
36
|
-
|
|
37
|
-
```javascript
|
|
38
|
-
V8.
|
|
34
|
+
|
|
35
|
+
### 生产消息(前端)
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
V8.ApiEngine.Run('platform-mq', {
|
|
39
|
+
Action: 'Send',
|
|
39
40
|
QueueName: 'queue_name',
|
|
40
41
|
EventId: stableEventId,
|
|
41
42
|
Message: { ProductId: '123', Count: 2 }
|
|
42
|
-
}
|
|
43
|
-
if (result.Code === 1) V8.Tips('消息已发送', true);
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
}).then(function(result) {
|
|
44
|
+
if (result.Code === 1) V8.Tips('消息已发送', true);
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`platform-mq` 是应用商城交付的 Managed 管理接口,只允许当前租户超级管理员,
|
|
49
|
+
并在可信 V8 原子层固定租户、队列名和 1 MB 消息上限。普通业务前端应调用
|
|
50
|
+
自己的受权接口引擎,再由业务接口引擎执行 `V8.MQ.SendMsg`。
|
|
51
|
+
|
|
52
|
+
### 消费消息
|
|
53
|
+
|
|
54
|
+
消费者是一个接口引擎,在 `diy_queue_receive` 表中配置队列名和接口引擎 Key 后,消息到达时自动调用。
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// 消费者接口引擎
|
|
58
|
+
var message = V8.Param.Message; // object 类型
|
|
54
59
|
// message.EventId — 稳定业务幂等 Id
|
|
55
60
|
// message.Id — EventId 的兼容别名
|
|
56
61
|
// message.OsClient — 消息所属租户
|
|
57
62
|
// message.Message — 消息内容
|
|
58
63
|
// message.CurrentUserId — 生产消息的用户 Id
|
|
59
|
-
|
|
60
|
-
// 处理业务逻辑
|
|
61
|
-
var data = message.Message;
|
|
62
|
-
V8.FormEngine.UptFormData('Product', {
|
|
63
|
-
Id: data.ProductId,
|
|
64
|
-
Stock: data.Count
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
|
|
65
|
+
// 处理业务逻辑
|
|
66
|
+
var data = message.Message;
|
|
67
|
+
V8.FormEngine.UptFormData('Product', {
|
|
68
|
+
Id: data.ProductId,
|
|
69
|
+
Stock: data.Count
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return { Code: 1, Data: { EventId: message.EventId } };
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 实战模式:异步处理耗时操作
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
// 接口引擎:接收请求后发送到队列,快速返回
|
|
79
|
+
var sendResult = V8.MQ.SendMsg({
|
|
73
80
|
QueueName: 'order_process',
|
|
74
81
|
EventId: V8.Param.eventId,
|
|
75
82
|
Message: {
|
|
@@ -78,63 +85,75 @@ await V8.MQ.SendMsg({
|
|
|
78
85
|
userId: V8.CurrentUser.Id
|
|
79
86
|
}
|
|
80
87
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
88
|
+
|
|
89
|
+
if (!sendResult || sendResult.Code !== 1) return sendResult;
|
|
90
|
+
|
|
91
|
+
return { Code: 1, Msg: '订单处理中,请稍候查看结果' };
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// 消费者接口引擎:异步处理订单
|
|
96
|
+
var msg = V8.Param.Message;
|
|
97
|
+
var data = msg.Message;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
// 耗时操作:调用第三方 ERP 接口
|
|
101
|
+
var erpResult = V8.Http.Post({
|
|
102
|
+
Url: 'https://erp.company.com/api/order',
|
|
103
|
+
PostParamString: JSON.stringify({ orderId: data.orderId }),
|
|
104
|
+
ParamType: 'json',
|
|
105
|
+
Timeout: 30
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
V8.FormEngine.UptFormData('Order', {
|
|
109
|
+
Id: data.orderId,
|
|
110
|
+
SyncStatus: 'success',
|
|
111
|
+
SyncTime: DateNow('yyyy-MM-dd HH:mm:ss')
|
|
112
|
+
});
|
|
113
|
+
return { Code: 1, Data: { EventId: msg.EventId } };
|
|
114
|
+
} catch (ex) {
|
|
115
|
+
V8.FormEngine.UptFormData('Order', {
|
|
116
|
+
Id: data.orderId,
|
|
117
|
+
SyncStatus: 'failed',
|
|
118
|
+
SyncError: ex.message
|
|
119
|
+
});
|
|
120
|
+
console.error('订单同步失败: ' + ex.message);
|
|
121
|
+
return { Code: 0, Msg: '订单同步失败' };
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### MQ 配置
|
|
126
|
+
|
|
127
|
+
每个租户都必须配置 `MQHost/MQPort/MQUserName/MQPassword/MQVitrualHost`,并在 RabbitMQ 中真实创建独立 user/vhost,把权限限制在自己的 vhost 与 `microi.{lowerOsClient}.*` 队列。缺少配置或与其它已加载租户共用 user/password/vhost 时失败关闭,不回退主租户管理员账号。可选 `MQUseTls/MQTlsServerName`;`MQHost` 可用逗号分隔多个端点。当前 `AddMicroiMQ()` 默认注册 `MicroiRabbitMQSingleConnection`,不会按 `MQType` 自动切换实现。
|
|
128
|
+
|
|
129
|
+
在 `diy_queue_receive` 表新增记录后,后台同步会创建或更新消费者:
|
|
130
|
+
|
|
131
|
+
| 字段 | 含义 |
|
|
132
|
+
|------|------|
|
|
133
|
+
| `Type` | `1`=接口引擎;`2`=仅默认主租户受控 DLL |
|
|
123
134
|
| `QueueName` | 逻辑队列名(与生产端 `SendMsg({ QueueName, ... })` 一致) |
|
|
124
|
-
| `ApiEngineKey` | 消费者接口引擎 Key |
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
| `ApiEngineKey` | 消费者接口引擎 Key |
|
|
136
|
+
| `FailToReject` | 值为“是”时失败后允许有限重入队 |
|
|
137
|
+
| `Count` | 最大重入队次数;`0` 表示首次失败后直接删除 |
|
|
138
|
+
|
|
139
|
+
新增、修改或删除队列处理配置会在下一同步周期生效。同步间隔默认最多 180 秒,
|
|
140
|
+
正数 `MQListenerTime` 可缩短但最低 15 秒;Host、端口、凭据、vhost 或 TLS 变更
|
|
141
|
+
不会重建已缓存连接,需要滚动重启对应节点。
|
|
129
142
|
|
|
130
143
|
多节点会对同一租户队列使用 RabbitMQ competing consumer,但“只有一个节点收到”不等于业务只执行一次。消息 envelope 的 `EventId` 是稳定幂等键,消费者必须配合数据库唯一约束、inbox/条件更新保证副作用仅一次;连接凭据轮换后当前版本需要重启节点重建连接。
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
144
|
+
|
|
145
|
+
消费者使用 `prefetch=1`、手动 Ack。接口引擎返回可解析的 `DosResult Code=1` 才
|
|
146
|
+
明确成功;为兼容旧处理器,`null` 或非 `DosResult` 返回也会 Ack,因此新代码必须
|
|
147
|
+
显式返回 `{Code:1}` 或 `{Code:0}`。失败且 `FailToReject=是` 时,Redis 以 EventId
|
|
148
|
+
记录 7 天重试次数并在未达到 `Count` 前立即 requeue;当前源码没有退避和自动 DLQ,
|
|
149
|
+
达到上限或未启用重入队时会 Reject 且不 requeue。
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
<!-- /microi-progressive:chunk -->
|
|
154
|
+
<!-- microi-progressive:chunk id=v8-mq-mqtt-001 sha256=f65fe365f1adca83c0eabe95f73a658d6d79f33b3d9a8d34c5b35ede841e7d73 -->
|
|
136
155
|
## 注意事项
|
|
137
|
-
|
|
156
|
+
|
|
138
157
|
- MQ 消费者接口引擎通过 `V8.Param.Message` 获取消息,包含 `EventId`、兼容 `Id`、`OsClient`、`Message`、`CurrentUserId`
|
|
139
158
|
- MQ 适合异步解耦、削峰填谷、耗时操作异步化
|
|
140
159
|
- MQTT 七类事件在同一个接口引擎中处理,通过 `V8.EventName` 区分
|
|
@@ -144,10 +163,12 @@ try {
|
|
|
144
163
|
- `ConnectedClients` 只代表当前 MQTT 节点的诊断快照,不是集群全局在线事实
|
|
145
164
|
- 内嵌 MQTT Broker 不具备跨节点共享会话/订阅/retained 的集群一致性;多 API 节点生产部署应使用支持集群的外部 Broker,或把内嵌 Broker 固定到独立节点并由负载入口路由,不能让每个 API 节点各自充当一套独立 Broker
|
|
146
165
|
- MQTT 生产配置、安全语义、事件字段可用性和上线清单以 [MQTT 生产参考](references/mqtt-production.md) 为准
|
|
147
|
-
<!-- /microi-progressive:chunk -->
|
|
148
|
-
## 详细参考路由(渐进披露)
|
|
149
|
-
|
|
150
|
-
仅在当前任务涉及对应主题时读取;下列文件合计保留了原 SKILL.md 的全部详细知识。
|
|
151
|
-
|
|
152
|
-
- [references/progressive-01-v8-mqtt-iot-物联网.md](references/progressive-01-v8-mqtt-iot-物联网.md):V8.MQTT — IoT 物联网
|
|
153
|
-
<!-- microi-progressive:end -->
|
|
166
|
+
<!-- /microi-progressive:chunk -->
|
|
167
|
+
## 详细参考路由(渐进披露)
|
|
168
|
+
|
|
169
|
+
仅在当前任务涉及对应主题时读取;下列文件合计保留了原 SKILL.md 的全部详细知识。
|
|
170
|
+
|
|
171
|
+
- [references/progressive-01-v8-mqtt-iot-物联网.md](references/progressive-01-v8-mqtt-iot-物联网.md):V8.MQTT — IoT 物联网
|
|
172
|
+
<!-- microi-progressive:end -->
|
|
173
|
+
|
|
174
|
+
平台级 MQTT 状态与下行发布统一调用 Managed 接口引擎 `platform-mqtt`;该入口只允许当前租户超级管理员,并由 `V8.Method.ManageMqtt` 固定租户边界。普通设备业务仍应创建自己的授权接口引擎。
|
|
@@ -32,8 +32,9 @@
|
|
|
32
32
|
配置、租户凭据、Topic 规范化和敏感字段边界。
|
|
33
33
|
4. `Microi.Server/Microi.Core/Interface/IMicroiMQTT.cs`:
|
|
34
34
|
`IMicroiMQTT.PublishAsync(osClient, ...)` 可信后端发布和节点状态接口。
|
|
35
|
-
5. `Microi.Server/Microi.
|
|
36
|
-
|
|
35
|
+
5. `Microi.Server/Microi.Core/V8Engine/Runtime/V8Method.PlatformPluginRuntimes.cs`
|
|
36
|
+
与 `Microi.Server/Microi.Upgrade/Resource/platform-mqtt.js`:平台管理员、当前节点
|
|
37
|
+
状态、租户 Topic 边界与接口引擎交付入口。
|
|
37
38
|
6. `microi.doc/docs/doc/system-engine/mqtt-engine.md`:面向用户的完整能力说明。
|
|
38
39
|
|
|
39
40
|
目标服务器可能落后于当前源码。编写事件代码前回读其 `sys_osclients` 字段和运行
|
|
@@ -14,7 +14,8 @@ const files = {
|
|
|
14
14
|
model: 'Microi.Server/Microi.Core/Model/MqttParam.cs',
|
|
15
15
|
mqttInterface: 'Microi.Server/Microi.Core/Interface/IMicroiMQTT.cs',
|
|
16
16
|
tenantSecurity: 'Microi.Server/Microi.Core/SaaSEngine/TenantConfigurationSecurity.cs',
|
|
17
|
-
|
|
17
|
+
platformRuntime: 'Microi.Server/Microi.Core/V8Engine/Runtime/V8Method.PlatformPluginRuntimes.cs',
|
|
18
|
+
platformApiEngine: 'Microi.Server/Microi.Upgrade/Resource/platform-mqtt.js'
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const content = {};
|
|
@@ -98,7 +99,7 @@ for (const propertyName of expectedProperties) {
|
|
|
98
99
|
|
|
99
100
|
const sourceEvents = [...runtimeEvents].sort();
|
|
100
101
|
const sourceProperties = [...mqttProperties].sort();
|
|
101
|
-
for (const targetName of ['
|
|
102
|
+
for (const targetName of ['reference', 'mqttDoc', 'v8Doc']) {
|
|
102
103
|
requireTokens(targetName, sourceEvents);
|
|
103
104
|
requireTokens(targetName, sourceProperties);
|
|
104
105
|
}
|
|
@@ -139,22 +140,20 @@ const operationsTokens = [
|
|
|
139
140
|
'独立 MQTT 节点'
|
|
140
141
|
];
|
|
141
142
|
|
|
142
|
-
requireTokens('
|
|
143
|
-
'
|
|
144
|
-
'
|
|
145
|
-
'
|
|
146
|
-
'
|
|
147
|
-
'
|
|
148
|
-
'MqttApiEngine',
|
|
149
|
-
'MqttAllowAnonymous',
|
|
150
|
-
'MqttTopicIsolation'
|
|
143
|
+
requireTokens('platformRuntime', [
|
|
144
|
+
'ManageMqtt',
|
|
145
|
+
'RequireCurrentTenantSuperAdmin',
|
|
146
|
+
'IMicroiMQTT',
|
|
147
|
+
'GetConnectedClients',
|
|
148
|
+
'PublishAsync'
|
|
151
149
|
]);
|
|
150
|
+
requireTokens('platformApiEngine', ['V8.Method.ManageMqtt']);
|
|
151
|
+
|
|
152
|
+
requireTokens('skill', ['references/mqtt-production.md', 'platform-mqtt']);
|
|
152
153
|
requireTokens('reference', configurationTokens);
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
requireTokens(targetName, operationsTokens);
|
|
157
|
-
}
|
|
155
|
+
requireTokens('reference', securityTokens);
|
|
156
|
+
requireTokens('reference', operationsTokens);
|
|
158
157
|
|
|
159
158
|
requireTokens(
|
|
160
159
|
'mqttDoc',
|
|
@@ -190,7 +189,6 @@ requireTokens('runtime', [
|
|
|
190
189
|
]);
|
|
191
190
|
requireTokens('mqttInterface', ['PublishAsync(string osClient', 'GetConnectedClients(string osClient)']);
|
|
192
191
|
requireTokens('tenantSecurity', ['NormalizeMqttTopic', 'HasTenantServiceCredentialCollision']);
|
|
193
|
-
requireTokens('controller', ['[PlatformAdminOnly]', 'StatusScope = "CurrentNode"']);
|
|
194
192
|
|
|
195
193
|
if (failures.length > 0) {
|
|
196
194
|
console.error('MQTT Skill 覆盖检查失败:');
|
|
@@ -54,8 +54,11 @@ V8.OsClientModel.AliOssPublicDomain // 可公开的文件域名
|
|
|
54
54
|
主库 `sys_osclients` 保存租户数据库/Redis/存储路由及影响整个 API 进程的平台级配置;目标租户建立上下文后,`sys_config` 和 `mci_system_setting` 从该租户自己的数据库读取。平台级配置必须遵守“主租户为准、子租户只能降额隔离”的规则:
|
|
55
55
|
|
|
56
56
|
- 所有可变业务逻辑默认必须由接口引擎编排,包括但不限于租户开通、开库、初始化、归属修复、官网个人中心、付费额度等 SaaS 业务流程。C# 后端只暴露原子 V8 能力,例如建库、导入空库模板、复制 `sys_config`、刷新 SaaS 缓存、补偿回滚、字段兜底等;不要把可变业务分支写死到 Controller 或 `TenantProvisioningService` 这类后端定制代码里。接口引擎缺少能力时,优先扩展 `V8.Method`/V8 引擎原子函数,再由接口引擎调用。
|
|
57
|
+
- 官方租户开通固定由 SaaS 包的 `platform-create-tenant` 编排:`platform-runtime-custom-hook` 的 Before 阶段可以阻断,`AuthorizeCurrentUserTenantProvisioning` / `ProvisionCurrentUserTenant` 只从可信主租户 DiyToken 派生所有者与密码材料;创建完成后的 Hook/审计失败只能返回 Warning,不能诱导重复创建。系统账号偏好/资料与租户系统设置分别由 `app.microi.sys_user`、`app.microi.sys-config` 单一拥有,SaaS/Store 不得复制这些 Managed ApiEngineKey。
|
|
58
|
+
- 旧版 UniApp 的 `microi-init` 是 SaaS 包继续托管的 Managed 兼容门面。匿名阶段只组合 `platform-os-client-by-domain` 与 `platform-sys-config` 的公开投影;原始 DiyToken 必须由 `GetCurrentToken` 和 `RefreshLoginUser(..., rawToken)` 在后端重新验证且绑定当前租户,菜单再由只允许该固定 Key 调用的 `GetLegacyInitMenuTree(rawToken, osClient?)` 复用 `SysMenuLogic` 权威角色过滤。禁止在匿名 V8 中直接通过 FormEngine 读取 `sys_menu`;域名解析到其它租户时返回目标 `OsClient` 并要求重试,禁止匿名跨租户读取配置或菜单。
|
|
57
59
|
- 主租户由运行环境决定:优先读取环境变量 `OsClient`,其次读取 `appsettings.json` 的 `AppSettings:OsClient`。只有这条主租户 `sys_osclients` 数据中的平台级字段会作为全局配置生效。
|
|
58
60
|
- API 启动配置只有十项白名单:`OsClient`、`OsClientType`、`OsClientNetwork`、`OsClientDbType`、`OsClientDbConn`、`OsClientRedisHost`、`OsClientRedisPort`、`OsClientRedisPwd`、`OsClientRedisDataBase`、`OsClientDbMongoConn`。除这十项外,部署/节点级运行参数与基础设施秘密从主控 `sys_osclients` 读取;允许子租户自行维护且需要浏览器判断的业务开关、入口显示和公开交互配置使用该租户 `sys_config` 实体字段,OAuth/第三方集成的凭据、RP/Origin/Issuer/Scope 与仅后端参数使用 `mci_system_setting`,未配置时使用代码安全默认值。官方 License 恢复次数/间隔与固定私钥挂载 `/app/microi_private.pem` 是信任链例外。禁止再增加 `MICROI_*`、`DOS_ORM_*`、自定义 `AppSettings` 节点或动态名称的环境变量读取。节点身份由平台自动生成。
|
|
61
|
+
- 存量畅捷通/微信 OAuth C# 协议网关有一组已发布到 SaaS 引擎的兼容字段:`OAuthReturnUrlOrigins`、`ChanjetOAuthState`、`ChanjetAesKey`、`ChanjetAppKey`、`WeChatTemplateAppId`、`WeChatTemplateAppSecret`、`WeChatTemplateId`、`WeChatMiniProgramAppId`。只能通过 Core 的租户绑定协议设置原子按请求权威 `OsClient` 读取,禁止使用主租户 `ConfigHelper` RuntimeConfigurationReader;整组字段不得复制给新租户,其中 OAuthState/AES Key/AppKey/AppSecret 不得进入 `V8.OsClientModel` 或前端投影且必须在审计中掩码。新集成不得继续扩展该兼容集合,仍使用当前租户 `mci_system_setting` + Managed ApiEngine。
|
|
59
62
|
- `ASPNETCORE_*`、`DOTNET_*` 仅用于 .NET 宿主;构建、安装、测试、MCP、发布脚本可使用自身进程变量,但 API 生产代码不得把它们当业务配置。新增 SaaS 运行字段必须配套独立或既有 Tab、幂等升级、缓存刷新、敏感字段脱敏、子租户不继承和源码扫描测试。
|
|
60
63
|
- 文件上传的租户业务开关与额度按“当前租户 `sys_osclients` → 代码默认值”解析;平台固定灾难保护、HTTP/Multipart/Form 和反向代理上限不可由租户覆盖,也不要求安装者维护额外上传环境变量。
|
|
61
64
|
- 类似 MQTT 端口、PressureGuard、V8Limits、OrmLimits、StartupLimits、SecurityGuard 这类影响整进程资源的配置,不能让每个子租户各自抬高全局上限。子租户同名隔离字段只能降低自己的并发、等待时间或资源额度,用于隔离弱租户、试用租户或异常租户。
|