@microi.net/cli 5.5.0 → 5.5.1
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 +3 -3
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/microi-skills.meta.json +201 -201
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +7 -7
- package/skills/README.md +103 -103
- package/skills/ai-engine/SKILL.md +90 -90
- package/skills/microi-db-schema/references/core-tables.md +1 -1
- package/skills/microi-db-schema/references/table-catalog.md +1 -1
- package/skills/microi-docs-coverage/references/capability-map.md +2 -2
- package/skills/microi-microservice/SKILL.md +292 -290
- package/skills/v8-api-config/SKILL.md +275 -263
- package/skills/v8-cache-pattern/SKILL.md +3 -1
- package/skills/v8-file-upload/SKILL.md +9 -9
- 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 +26 -22
- package/skills/v8-saas-multi-tenant/SKILL.md +113 -97
- package/skills/vision-engine/SKILL.md +152 -148
- package/skills/vision-engine/agents/openai.yaml +4 -4
- package/skills/vision-engine/references/architecture-and-acceptance.md +172 -172
|
@@ -115,7 +115,7 @@ Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进
|
|
|
115
115
|
- 服务端逐片写入 HDFS 后重新流式回读校验;完成时按顺序合并、再次核对整文件 SHA-256,再生成不可变版本完整性标记。
|
|
116
116
|
- 会话 Id 由租户、应用、版本、路径和文件摘要确定。网络中断或进程重启后先读取远端状态,只续传缺失分片;已成功的相同摘要请求直接幂等返回。
|
|
117
117
|
- 吾码不为协议 v3 设置业务文件/目录字节上限,状态中的 `ApplicationAssetResumableProductSizeLimitBytes=0` 表示没有产品配置上限。每个对象仍受协议技术边界(最多 10000 片、单片最多 1 GiB)、JavaScript 安全整数、对象存储、磁盘、网关和网络条件约束。
|
|
118
|
-
- 该链路只允许通过能力鉴权的当前租户超级管理员,并继续受 `
|
|
118
|
+
- 该链路只允许通过能力鉴权的当前租户超级管理员,并继续受 `DisableFileUpload` 负向总开关控制;它不是普通用户上传或任意 HDFS 路径写入接口。
|
|
119
119
|
- 每个会话都在 `mci_ai_app_file` 保留审计记录,`StorageScope=ApplicationAssetMultipartSession`。管理员在 **系统引擎 → 超大文件上传记录** 查看状态、阶段、已传字节/分片、进度、心跳、错误和恢复建议;成功、失败和取消记录都不静默删除。
|
|
120
120
|
|
|
121
121
|
### 普通业务上传的分层限制
|
|
@@ -131,16 +131,16 @@ Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进
|
|
|
131
131
|
|
|
132
132
|
| `sys_osclients` 字段 | 代码默认值 | 平台固定边界 |
|
|
133
133
|
|---|---:|---:|
|
|
134
|
-
| `
|
|
134
|
+
| `DisableFileUpload` | `false`(关闭,即允许上传) | — |
|
|
135
135
|
| `FileUploadMaxFileMB` | 100 MB | 1024 MB |
|
|
136
136
|
| `FileUploadMaxRequestMB` | 200 MB | 2048 MB |
|
|
137
137
|
| `FileUploadMaxCount` | 10 | 100 |
|
|
138
138
|
| `FileUploadDailyUserQuotaMB` | 2048 MB | 10 TB |
|
|
139
139
|
| `FileUploadDailyTenantQuotaMB` | 20480 MB | 10 TB |
|
|
140
140
|
|
|
141
|
-
- `sys_osclients`
|
|
141
|
+
- `sys_osclients` 六个现行字段全部可空;`DisableFileUpload` 空值、无效值或 `0/false` 均表示允许上传,只有 `1/true` 才禁止。旧数据库尚未创建新物理字段时才回退读取 `FileUploadEnabled`,升级后旧正向字段即使为 `0` 也不得覆盖新字段的默认允许语义。`FileUploadMaxRequestMB` 指一次上传所有文件的业务合计大小,不等于 Kestrel HTTP 请求正文上限。
|
|
142
142
|
- 固定灾难保护和 HTTP/Multipart/Form 解析上限不属于安装配置;租户值即使更大也会被这些边界截断。最终单次总量还不能超过帐号或租户的有效日额度,单文件不能超过最终单次总量。
|
|
143
|
-
- `
|
|
143
|
+
- `DisableFileUpload=1` 表示禁用当前租户上传,也会阻止 AI 应用资产断点续传;不能把内部发布协议当成绕过开关的后门。普通单请求继续受全局大小硬上限,v3 只移除产品级字节上限;租户配置刷新应走现有 SaaS 引擎重载和共享 Redis 发布订阅,不能依赖单节点内存。
|
|
144
144
|
- 帐号与租户每日额度在共享 Redis 中用单次原子脚本预留,支持多节点;Redis 不可用时失败关闭,不能降级成无限上传。
|
|
145
145
|
- 额度按 UTC 日期统计。为防并发重试绕过限制,后续对象存储失败也不退还已经预留的额度。
|
|
146
146
|
- 每日额度只阻断短期滥用;对象存储必须另外配置租户/桶总容量、账单告警、生命周期与实际用量对账。Redis 计数不能作为长期容量事实源。
|
|
@@ -149,9 +149,9 @@ Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进
|
|
|
149
149
|
|
|
150
150
|
### 复盘:“当前租户已停用文件上传”
|
|
151
151
|
|
|
152
|
-
- 该提示只表示当前运行环境命中的 `sys_osclients.
|
|
153
|
-
- 新版响应同时返回 `DataAppend.ErrorType=TenantFileUploadDisabled`、`OsClient`、`ConfigField=
|
|
154
|
-
- 处理时先读取当前 API 进程的 `OsClient + OsClientType + OsClientNetwork`,再精确回读同三元组的启用记录并把 `
|
|
152
|
+
- 该提示只表示当前运行环境命中的 `sys_osclients.DisableFileUpload` 被明确设置为 `1/true`(或仍在旧库兼容期且 `FileUploadEnabled=0`)。新字段缺列以外的空值、无效值和新租户都按默认允许,不能先把问题归因于 MinIO/HDFS。
|
|
153
|
+
- 新版响应同时返回 `DataAppend.ErrorType=TenantFileUploadDisabled`、`OsClient`、`ConfigField=DisableFileUpload`、`ExpectedValue=0` 和文档地址;客户端必须保留后端 `Msg/DataAppend`,不能只显示“上传失败”。
|
|
154
|
+
- 处理时先读取当前 API 进程的 `OsClient + OsClientType + OsClientNetwork`,再精确回读同三元组的启用记录并把 `DisableFileUpload` 关闭为 `0`。不能仅按租户名批量覆盖其它网络或环境记录。
|
|
155
155
|
- 保存后等待 SaaS 共享配置重载,再分别验证一个小公有图片和一个小私有文件。只有错误转为 endpoint、bucket、签名或 `Invalid URI` 后,才进入对象存储配置排查。
|
|
156
156
|
- 不要通过删除 Redis 日额度 Key、扩大文件大小上限或改成公有桶来解除租户停用;这些动作与开关无关,还会扩大安全风险。
|
|
157
157
|
|
|
@@ -159,9 +159,9 @@ Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进
|
|
|
159
159
|
|
|
160
160
|
用户明确授权修改某个租户的上传额度时,AI 可以直接使用标准 MCP 完成,不要把应用层提示误判成阿里云 OSS、MinIO 或 S3 的存储配额,也不要先清 Redis:
|
|
161
161
|
|
|
162
|
-
1. `microi_get_table_data(tableName: "sys_osclients")` 按 `OsClient`、`IsEnable=1` 查询,选择 `Id/OsClientType/OsClientNetwork`
|
|
162
|
+
1. `microi_get_table_data(tableName: "sys_osclients")` 按 `OsClient`、`IsEnable=1` 查询,选择 `Id/OsClientType/OsClientNetwork`、`DisableFileUpload` 和五个现行 `FileUpload*` 配额字段;`FileUploadEnabled` 只用于诊断未升级旧节点。
|
|
163
163
|
2. 先以当前服务器的 `OsClientType + OsClientNetwork` 收窄到实际生效记录;只有用户明确要求多个环境保持一致时才扩展范围。逐条调用 `microi_update_form_data`,`row` 必须包含 `Id`,并传 `confirmExecution: "sys_osclients"`。
|
|
164
|
-
3. MB 是存储单位:`20 GB = 20480 MB`。可修改字段为 `
|
|
164
|
+
3. MB 是存储单位:`20 GB = 20480 MB`。可修改字段为 `DisableFileUpload`、`FileUploadMaxFileMB`、`FileUploadMaxRequestMB`、`FileUploadMaxCount`、`FileUploadDailyUserQuotaMB`、`FileUploadDailyTenantQuotaMB`;不要再写旧 `FileUploadEnabled`。
|
|
165
165
|
4. 保存后逐条远程回读;FormEngine 会排队重载 SaaS 运行配置,再用真实小文件上传做生效冒烟。只看到 MCP 返回“更新成功”不算验收。
|
|
166
166
|
5. 提高每日配额保留当天已用计数,剩余额度为新上限减已用量。计数按 UTC 日期,失败上传不退款;除非用户明确授权事故处置,不得删除 Redis 配额 Key。
|
|
167
167
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# v8-menu-buttons 详细参考 2
|
|
2
|
-
|
|
3
|
-
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
|
-
|
|
5
|
-
<!-- microi-progressive:chunk id=v8-menu-buttons-008 sha256=
|
|
1
|
+
# v8-menu-buttons 详细参考 2
|
|
2
|
+
|
|
3
|
+
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
|
+
|
|
5
|
+
<!-- microi-progressive:chunk id=v8-menu-buttons-008 sha256=495161c95d321f2937486db3fecc4e27d2e38c6d7a21587b5028d04ea6491b90 -->
|
|
6
6
|
## 8. 模式 F:后台任务按钮(长任务)
|
|
7
7
|
|
|
8
8
|
应用安装、初始化多语言、批量导入、批量修复、跨系统同步等可能超过浏览器或网关等待时间的操作,必须优先设计为后台任务。判断阈值:预计超过 2 分钟、500 条以上、1000 个以上扇出子操作、100 次以上外部调用,或总量未知且可能持续运行。前端按钮只负责提交任务,后台任务列表通过 WebSocket/SignalR 推送并以轮询兜底。
|
|
@@ -100,6 +100,10 @@ reportProgress(2, 5, '正在写入表结构');
|
|
|
100
100
|
|
|
101
101
|
预计超过 10 分钟的任务必须分页/分片。每片只处理可在较短事务内提交的一批,仍有后续时返回 `Data.BackgroundTask.HasMore=true + Checkpoint + Current/Total`,平台持久化检查点并重新入队;最后一片返回普通 `Code:1`。重试副作用必须用稳定幂等键、数据库唯一约束和 `_BackgroundTaskFencingToken` 条件写入,不能依赖锁本身。
|
|
102
102
|
|
|
103
|
+
后台接口同时启用 `LockKey` 时,平台可信持久后台上下文会把接口 `Timeout` 作为单次 Redis 租期并按唯一持有者令牌自动续租;当前默认最长续租边界为 12 小时,显式配置得更长的单次租期不会被缩短。客户端自行传入 `_BackgroundTaskId`、`_TrustedServerInvocation` 或用户对象不能取得该能力。自动续租只防止正常长任务因短 TTL 自然过期,不能取消本节的检查点、幂等和 fencing token 要求。
|
|
104
|
+
|
|
105
|
+
持有者令牌不匹配、锁已过期、Redis 所有权/续租确认失败或达到最长租约时,任务必须失败关闭并保留最后真实进度与原始错误。V8 代码不得捕获“分布式锁租约已丢失”后返回 `Code:1`;旧执行者后续的业务条件写入必须因 `_BackgroundTaskFencingToken` 过期而被拒绝。完整配置语义见 `../../v8-api-config/SKILL.md`。
|
|
106
|
+
|
|
103
107
|
### 复盘:主租户默认值误清空子租户后台任务身份
|
|
104
108
|
|
|
105
109
|
- 触发场景:同一套前后端中,独立部署的主租户可正常执行后台安装,挂在平台主库下的子租户却间歇性提示“只有超级管理员才能安装”;数据库中的 admin 实际已经是最高等级。
|
|
@@ -109,12 +113,12 @@ reportProgress(2, 5, '正在写入表结构');
|
|
|
109
113
|
|
|
110
114
|
---
|
|
111
115
|
|
|
112
|
-
<!-- /microi-progressive:chunk -->
|
|
113
|
-
<!-- microi-progressive:chunk id=v8-menu-buttons-009 sha256=
|
|
116
|
+
<!-- /microi-progressive:chunk -->
|
|
117
|
+
<!-- microi-progressive:chunk id=v8-menu-buttons-009 sha256=8cb7f4d399068549be662cc47bd4add8b3d3bbdb6e7a74832e4c63b7768e136b -->
|
|
114
118
|
## 9. 通过 MCP 创建菜单 + 按钮(一次到位)
|
|
115
|
-
|
|
116
|
-
> AI 在 `microi_create_module` 调用时,把按钮 JSON **作为字符串** 传入对应字段。
|
|
117
|
-
|
|
119
|
+
|
|
120
|
+
> AI 在 `microi_create_module` 调用时,把按钮 JSON **作为字符串** 传入对应字段。
|
|
121
|
+
|
|
118
122
|
```js
|
|
119
123
|
var moreBtns = [
|
|
120
124
|
{
|
|
@@ -152,8 +156,8 @@ var modulePayload = {
|
|
|
152
156
|
|
|
153
157
|
---
|
|
154
158
|
|
|
155
|
-
<!-- /microi-progressive:chunk -->
|
|
156
|
-
<!-- microi-progressive:chunk id=v8-menu-buttons-010 sha256=0398a9fc35520d4b17b990efc565ba1d9f6050303e122dc128207adf08434aee -->
|
|
159
|
+
<!-- /microi-progressive:chunk -->
|
|
160
|
+
<!-- microi-progressive:chunk id=v8-menu-buttons-010 sha256=0398a9fc35520d4b17b990efc565ba1d9f6050303e122dc128207adf08434aee -->
|
|
157
161
|
## 前端 FormEngine 权限与兼容
|
|
158
162
|
|
|
159
163
|
- 当前按钮所在菜单绑定表会由 scoped facade 自动补真实 `_SysMenuId`,历史按钮不需要逐个修改。
|
|
@@ -166,23 +170,23 @@ var modulePayload = {
|
|
|
166
170
|
|
|
167
171
|
---
|
|
168
172
|
|
|
169
|
-
<!-- /microi-progressive:chunk -->
|
|
170
|
-
<!-- microi-progressive:chunk id=v8-menu-buttons-011 sha256=
|
|
173
|
+
<!-- /microi-progressive:chunk -->
|
|
174
|
+
<!-- microi-progressive:chunk id=v8-menu-buttons-011 sha256=16414969be47116a51bab1c6a7e1482bb987b278430e0fb8daaf7d6ecf41bf94 -->
|
|
171
175
|
## 10. 与接口引擎配套的工作流
|
|
172
176
|
|
|
173
177
|
业务按钮通常与接口引擎配套:
|
|
174
|
-
|
|
175
|
-
1. **microi_create_engine** 先创建 ApiEngineKey(如 `aftersale_assign`),写好后端事务/校验/通知逻辑
|
|
176
|
-
2. **microi_create_module** 创建菜单时把按钮的 `V8Code` 设为 `V8.ApiEngine.Run({ApiEngineKey:'aftersale_assign',...})`
|
|
177
|
-
3. AI 在生成菜单时**应主动**:
|
|
178
|
-
- 识别业务动作(指派/接单/审核/完成/驳回/导出/批量处理/状态推进)
|
|
178
|
+
|
|
179
|
+
1. **microi_create_engine** 先创建 ApiEngineKey(如 `aftersale_assign`),写好后端事务/校验/通知逻辑
|
|
180
|
+
2. **microi_create_module** 创建菜单时把按钮的 `V8Code` 设为 `V8.ApiEngine.Run({ApiEngineKey:'aftersale_assign',...})`
|
|
181
|
+
3. AI 在生成菜单时**应主动**:
|
|
182
|
+
- 识别业务动作(指派/接单/审核/完成/驳回/导出/批量处理/状态推进)
|
|
179
183
|
- 为每个动作创建一个接口引擎
|
|
180
184
|
- 在 `moreBtns` / `formBtns` 中绑定按钮 → 接口
|
|
181
185
|
|
|
182
186
|
---
|
|
183
187
|
|
|
184
|
-
<!-- /microi-progressive:chunk -->
|
|
185
|
-
<!-- microi-progressive:chunk id=v8-menu-buttons-012 sha256=086e02210a949cc0a2c343e7f50c54c48079667999e9264ea5282c9caa951222 -->
|
|
188
|
+
<!-- /microi-progressive:chunk -->
|
|
189
|
+
<!-- microi-progressive:chunk id=v8-menu-buttons-012 sha256=086e02210a949cc0a2c343e7f50c54c48079667999e9264ea5282c9caa951222 -->
|
|
186
190
|
## 10.1 后台直充/调账类行按钮
|
|
187
191
|
|
|
188
192
|
会员积分、余额、库存、额度等会改动资产的后台行按钮,必须采用“前端按钮只收参数,后端接口负责事务”的模式。
|
|
@@ -221,4 +225,4 @@ V8.OpenAppDialog({
|
|
|
221
225
|
|
|
222
226
|
---
|
|
223
227
|
|
|
224
|
-
<!-- /microi-progressive:chunk -->
|
|
228
|
+
<!-- /microi-progressive:chunk -->
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: v8-saas-multi-tenant
|
|
1
|
+
---
|
|
2
|
+
name: v8-saas-multi-tenant
|
|
3
3
|
description: Microi V8 SaaS 多租户指南。用于处理 OsClient、OsClientType、OsClientNetwork、租户配置、V8.OsClientModel、隔离和租户感知代码。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
|
-
|
|
8
|
-
# Microi V8 SaaS 多租户引擎
|
|
9
|
-
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
|
+
|
|
8
|
+
# Microi V8 SaaS 多租户引擎
|
|
9
|
+
|
|
10
10
|
你正在为 Microi 吾码平台编写多租户(SaaS)相关代码。每个租户使用独立数据库账号;Redis、对象存储、RabbitMQ、MQTT 和搜索集群可以共享基础设施,但必须使用租户命名空间与独立服务凭据,**所有 V8 代码都在租户上下文中运行**。
|
|
11
|
-
|
|
12
|
-
## 核心概念
|
|
13
|
-
|
|
14
|
-
Microi 多租户 = **`OsClient` + `OsClientType` + `OsClientNetwork`** 三参数模型:
|
|
15
|
-
|
|
16
|
-
| 参数 | 说明 | 示例 |
|
|
17
|
-
|------|------|------|
|
|
11
|
+
|
|
12
|
+
## 核心概念
|
|
13
|
+
|
|
14
|
+
Microi 多租户 = **`OsClient` + `OsClientType` + `OsClientNetwork`** 三参数模型:
|
|
15
|
+
|
|
16
|
+
| 参数 | 说明 | 示例 |
|
|
17
|
+
|------|------|------|
|
|
18
18
|
| `OsClient` | 租户标识(系统Key) | `tenant_a`, `tenant_demo` |
|
|
19
|
-
| `OsClientType` | 租户类型 | `Normal` / `App` / `Wechat` |
|
|
20
|
-
| `OsClientNetwork` | 网络环境 | `Intranet`(内网)/`Outernet`(外网) |
|
|
21
|
-
|
|
19
|
+
| `OsClientType` | 租户类型 | `Normal` / `App` / `Wechat` |
|
|
20
|
+
| `OsClientNetwork` | 网络环境 | `Intranet`(内网)/`Outernet`(外网) |
|
|
21
|
+
|
|
22
22
|
主租户不是固定字符串 `master`,而是由当前部署的环境变量 `OsClient` 或 `AppSettings:OsClient` 决定。租户记录存放在受保护的 `sys_osclients` 表中;普通业务角色和普通 V8 不得直接查询、复制或修改该表。
|
|
23
|
-
|
|
24
|
-
## 上下文变量
|
|
25
|
-
|
|
26
|
-
```javascript
|
|
23
|
+
|
|
24
|
+
## 上下文变量
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
27
|
V8.OsClient // 当前租户标识,如 'tenant_demo'
|
|
28
|
-
V8.OsClientType // 'Normal' / 'App' / 'Wechat'
|
|
29
|
-
V8.OsClientNetwork // 'Intranet' / 'Outernet'
|
|
28
|
+
V8.OsClientType // 'Normal' / 'App' / 'Wechat'
|
|
29
|
+
V8.OsClientNetwork // 'Intranet' / 'Outernet'
|
|
30
30
|
V8.OsClientModel // 当前租户 SaaS 配置的脱敏副本
|
|
31
31
|
V8.ClientModel // OsClientModel 的兼容别名,同样是脱敏副本
|
|
32
32
|
V8.SysConfig // 当前租户系统配置根对象;不存在 PublicSettings 属性
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
前端 V8 的 `V8.SysConfig` 是匿名 `GetSysConfig` 的独立脱敏 `sys_config` 副本;`mci_system_setting` 的任何记录都不会进入浏览器。后端接口引擎与后端 V8 事件的 `V8.SysConfig` 包含当前租户完整、独立的 `sys_config`,后端私密设置统一位于 `V8.SysConfig.ServerPrivateSettings`,其中 Secret 由可信后端解密。两端都不存在 `PublicSettings` 包装层。后端可使用 Secret,但严禁返回前端、写日志或写审计。子租户显式传其它 `OsClient` 仍会被强制改回当前租户。
|
|
36
|
-
|
|
37
|
-
### V8.OsClientModel 常用字段
|
|
38
|
-
|
|
39
|
-
```javascript
|
|
36
|
+
|
|
37
|
+
### V8.OsClientModel 常用字段
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
40
|
V8.OsClientModel.SysTitle // 租户系统标题
|
|
41
41
|
V8.OsClientModel.DbType // 非敏感数据库类型
|
|
42
42
|
V8.OsClientModel.HDFS // 'Aliyun' / 'MinIO' / 'S3'
|
|
@@ -62,7 +62,7 @@ V8.OsClientModel.AliOssPublicDomain // 可公开的文件域名
|
|
|
62
62
|
- 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` 节点或动态名称的环境变量读取。节点身份由平台自动生成。
|
|
63
63
|
- 存量畅捷通/微信 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。
|
|
64
64
|
- `ASPNETCORE_*`、`DOTNET_*` 仅用于 .NET 宿主;构建、安装、测试、MCP、发布脚本可使用自身进程变量,但 API 生产代码不得把它们当业务配置。新增 SaaS 运行字段必须配套独立或既有 Tab、幂等升级、缓存刷新、敏感字段脱敏、子租户不继承和源码扫描测试。
|
|
65
|
-
- 文件上传的租户业务开关与额度按“当前租户 `sys_osclients` →
|
|
65
|
+
- 文件上传的租户业务开关与额度按“当前租户 `sys_osclients` → 代码默认值”解析;现行负向字段 `DisableFileUpload` 默认关闭/空值即允许上传,只有 `1/true` 才禁止。旧 `FileUploadEnabled` 只在新物理字段尚不存在的滚动升级节点回退读取,并须在新版 SaaS 表单隐藏。平台固定灾难保护、HTTP/Multipart/Form 和反向代理上限不可由租户覆盖,也不要求安装者维护额外上传环境变量。
|
|
66
66
|
- 类似 MQTT 端口、PressureGuard、V8Limits、OrmLimits、StartupLimits、SecurityGuard 这类影响整进程资源的配置,不能让每个子租户各自抬高全局上限。子租户同名隔离字段只能降低自己的并发、等待时间或资源额度,用于隔离弱租户、试用租户或异常租户。
|
|
67
67
|
- 修改 `sys_osclients` 的表、字段、数据源或配置值后,必须刷新 SaaS 引擎运行缓存,并回读验证字段 `Component`、`Data`、`Config`、实际数据值和前端真实消费结果。不要只看 MCP 写入成功。
|
|
68
68
|
- SaaS 配置只在启动、管理员保存 `sys_osclients` 或显式租户刷新时发布到共享 Redis。初始化数据库会话、创建 `V8.Dbs` 运行态对象、普通 FormEngine 请求和表单设计器保存不得冒充配置变更反复发布。
|
|
@@ -72,33 +72,33 @@ V8.OsClientModel.AliOssPublicDomain // 可公开的文件域名
|
|
|
72
72
|
- 新租户记录不得复制主租户的数据库、鉴权、Redis/对象存储密钥或 MQ/MQTT/Search 凭据。共享基础设施地址与管理密钥只在服务端运行时解析,不持久化到子租户记录,也不进入 V8 投影。
|
|
73
73
|
- RabbitMQ 子租户必须使用独立 user/vhost/ACL,MQTT 必须使用独立账号密码,Search 必须使用限制到 `{osClient}_*` 的 API Key/用户。外部资源尚未真实创建时保持空凭据并失败关闭,禁止把主租户管理员凭据复制过去冒充完成。
|
|
74
74
|
- `V8.Cache`、`V8.HDFS`、RabbitMQ、MQTT、Search 分别强制 `Microi:{OsClient}:*`、`/{osClient}/...`、`microi.{osClient}.*`、`tenant/{osClient}/...`、`{osClient}_*` 命名空间。body/query 中的 `OsClient` 不能覆盖登录 Token 或 V8 上下文。
|
|
75
|
-
|
|
76
|
-
## 接口调用区分租户的三种方式
|
|
77
|
-
|
|
78
|
-
### 方式 1:Token 自动识别(最常用)
|
|
79
|
-
|
|
80
|
-
请求头携带 `Token`,平台自动识别用户所属租户。
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
GET /apiengine/get-products
|
|
84
|
-
Token: xxx-token-xxx
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 方式 2:URL 参数
|
|
88
|
-
|
|
89
|
-
```bash
|
|
75
|
+
|
|
76
|
+
## 接口调用区分租户的三种方式
|
|
77
|
+
|
|
78
|
+
### 方式 1:Token 自动识别(最常用)
|
|
79
|
+
|
|
80
|
+
请求头携带 `Token`,平台自动识别用户所属租户。
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
GET /apiengine/get-products
|
|
84
|
+
Token: xxx-token-xxx
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 方式 2:URL 参数
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
90
|
GET /apiengine/get-products?OsClient=tenant_demo
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### 方式 3:特殊 URL 格式(无 Token、无参数)
|
|
94
|
-
|
|
95
|
-
```bash
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 方式 3:特殊 URL 格式(无 Token、无参数)
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
96
|
GET /apiengine/get-products--OsClient--tenant_demo--
|
|
97
97
|
GET /apiengine/get-products--OsClient--tenant_demo--OsClientType--App--
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
> 适用于第三方回调(无法添加 Header)、支付/微信回调等场景。
|
|
101
|
-
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
> 适用于第三方回调(无法添加 Header)、支付/微信回调等场景。
|
|
101
|
+
|
|
102
102
|
## 跨租户操作(仅可信控制面)
|
|
103
103
|
|
|
104
104
|
普通 V8 的 `FormEngine`、`DataSourceEngine`、`TranslateEngine`、`WFEngine`、`Cache`、`HDFS`、MQ/MQTT、Search 和 `Dbs` 都必须绑定当前 Token/V8 上下文中的 `OsClient`。请求 body/query 里传入其它租户不能切换连接,也不能读取其它租户凭据。
|
|
@@ -110,10 +110,24 @@ GET /apiengine/get-products--OsClient--tenant_demo--OsClientType--App--
|
|
|
110
110
|
- 不把数据库、认证、Redis、存储、MQ/MQTT、搜索等连接与密钥投影进 V8;
|
|
111
111
|
- 每次操作写安全审计、幂等键和补偿状态,并对目标租户回读验收;
|
|
112
112
|
- 多节点部署使用共享租约和业务幂等,不能依赖进程静态锁。
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
|
|
114
|
+
### SaaS 数据库 ZIP 开库的进度与租约
|
|
115
|
+
|
|
116
|
+
通过 SaaS 引擎上传数据库 ZIP 创建租户时,必须把上传和还原建模为两个独立阶段:
|
|
117
|
+
|
|
118
|
+
- 上传使用分片会话并显示真实字节进度;暂停或网络中断后复用同一文件指纹续传,不能把上传百分比冒充数据库导入进度。
|
|
119
|
+
- 上传完成后提交平台持久后台任务。租户开通进度总数使用后端共享契约的固定 13 步,接口引擎、可信宿主原子和前端不得各自维护不同分母。
|
|
120
|
+
- 第 2 步是数据库 ZIP 校验与还原。该阶段应按真实 SQL 读取量推进总百分比,并显示已读取/总字节、SQL 百分比、已执行语句数、当前批次与语句类型、平均吞吐和动态预计剩余时间;不能长时间只显示静态的 `2 / 13`。
|
|
121
|
+
- 进度状态仅在距上次更新约 2 秒或新增约 16MB 数据时刷新;执行日志只记录阶段里程碑和 SQL 每跨过约 5% 的进度,前端只渲染最近 200 条。禁止按 SQL 行或每条语句推送日志,避免几十万条数据把共享任务表、SignalR 或浏览器 DOM 拖垮。
|
|
122
|
+
- 页面同时显示服务端心跳。单个百分比短时不变不等于卡死,应综合心跳、字节、语句数、批次和消息判断;失败、取消或中断必须保留最后真实进度和错误原因,只有成功终态才算开库完成。
|
|
123
|
+
|
|
124
|
+
租户开库使用平台可信后台上下文中的可续租共享锁:`Timeout` 是单次 Redis 租期,当前开库最长保护边界为 12 小时。普通 HTTP/V8 请求传入 `_BackgroundTaskId`、`_TrustedServerInvocation` 或伪造用户对象不能开启续租。持有者令牌不匹配、锁过期、Redis 所有权/续租确认失败或达到最长租约都必须失败关闭并停止推进;不能为了避免报错而把真实锁丢失降级成成功或 Warning。
|
|
125
|
+
|
|
126
|
+
自动续租只解决“正常长任务不因短 TTL 自然过期”,不能代替业务幂等、检查点、补偿状态和 fencing token。大库导入完成但域名绑定未完成时,应保留已经成功创建的租户和数据库,进入明确的域名补偿状态;不能因数据面域名失败重新导入整个数据库。接口引擎的通用锁配置读取 `../v8-api-config/SKILL.md`,长任务按钮和检查点读取 `../v8-menu-buttons/references/progressive-02-8-模式-f-后台任务按钮-长任务.md`。
|
|
127
|
+
|
|
128
|
+
## 缓存按租户隔离
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
117
131
|
// 推荐传逻辑 Key;服务端自动添加当前租户前缀
|
|
118
132
|
var key = 'Product:' + V8.Param.id;
|
|
119
133
|
V8.Cache.Set(key, value, 600);
|
|
@@ -122,36 +136,36 @@ V8.Cache.Set(key, value, 600);
|
|
|
122
136
|
var fullKey = 'Microi:' + V8.OsClient + ':Product:' + V8.Param.id;
|
|
123
137
|
|
|
124
138
|
// 其它租户的 Microi: 前缀会被服务端拒绝
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## 接口引擎中针对不同租户走不同逻辑
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## 接口引擎中针对不同租户走不同逻辑
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
130
144
|
// 租户差异应来自当前租户的非敏感业务配置,不要硬编码某个“主租户”字符串
|
|
131
145
|
if (V8.OsClientModel.OrderApprovalMode === 'Direct') {
|
|
132
146
|
V8.FormEngine.UptFormData('Order', { Id: id, Status: 'Approved' });
|
|
133
147
|
} else {
|
|
134
148
|
await V8.WFEngine.StartWork({ FlowDesignId: 'order-flow', TableRowId: id });
|
|
135
149
|
}
|
|
136
|
-
|
|
137
|
-
// App 端 vs PC 端不同返回
|
|
138
|
-
if (V8.OsClientType === 'App') {
|
|
139
|
-
return { Code: 1, Data: simplifiedList };
|
|
140
|
-
}
|
|
141
|
-
return { Code: 1, Data: fullList };
|
|
142
|
-
|
|
143
|
-
// 内网外网走不同 ERP 网关
|
|
144
|
-
var erpUrl = (V8.OsClientNetwork === 'Intranet')
|
|
145
|
-
? 'http://192.168.1.10/erp/api'
|
|
146
|
-
: 'https://erp.public.com/api';
|
|
147
|
-
```
|
|
148
|
-
|
|
150
|
+
|
|
151
|
+
// App 端 vs PC 端不同返回
|
|
152
|
+
if (V8.OsClientType === 'App') {
|
|
153
|
+
return { Code: 1, Data: simplifiedList };
|
|
154
|
+
}
|
|
155
|
+
return { Code: 1, Data: fullList };
|
|
156
|
+
|
|
157
|
+
// 内网外网走不同 ERP 网关
|
|
158
|
+
var erpUrl = (V8.OsClientNetwork === 'Intranet')
|
|
159
|
+
? 'http://192.168.1.10/erp/api'
|
|
160
|
+
: 'https://erp.public.com/api';
|
|
161
|
+
```
|
|
162
|
+
|
|
149
163
|
## 第三方密钥放租户动态设置(不要硬编码)
|
|
150
|
-
|
|
151
|
-
```javascript
|
|
152
|
-
// ❌ 危险:密钥写在代码里,所有租户共用,无法独立轮换
|
|
153
|
-
var ak = 'AKIDxxxxxxxx';
|
|
154
|
-
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
// ❌ 危险:密钥写在代码里,所有租户共用,无法独立轮换
|
|
167
|
+
var ak = 'AKIDxxxxxxxx';
|
|
168
|
+
|
|
155
169
|
// ✅ 浏览器需要判断的开关:使用 sys_config 实体字段
|
|
156
170
|
var giteeEnabled = V8.SysConfig.GiteeLoginEnabled === 1;
|
|
157
171
|
|
|
@@ -162,21 +176,21 @@ var secret = privateSettings['Login.Gitee.ClientSecret'];
|
|
|
162
176
|
```
|
|
163
177
|
|
|
164
178
|
> `mci_system_setting` 位于每个租户自己的数据库,只保存不能公开或仅供后端执行的配置;普通值与 Secret 都不会下发浏览器。Secret 保存认证密文,只在后端 V8 的当前租户 `ServerPrivateSettings` 中解密使用。能力开关和入口显示必须建成 `sys_config` 实体字段,禁止通过 `IsPublic` 或其它运行时勾选把私密记录公开。前端 V8、普通 FormEngine HTTP、匿名/访问密钥会话不能读取私密设置,后端 V8 也不能获得通用解密器。`sys_osclients` 自定义业务字段只保留存量兼容;共享基础设施字段由服务端强制移除,不能用自定义同义字段绕过安全代理。
|
|
165
|
-
|
|
166
|
-
## 用户扩展字段访问(同理)
|
|
167
|
-
|
|
168
|
-
平台 `sys_user` 也由表单引擎驱动。如给 `sys_user` 添加 `Wife` 字段:
|
|
169
|
-
|
|
170
|
-
```javascript
|
|
171
|
-
// V8 代码中可访问
|
|
172
|
-
V8.CurrentUser.Wife;
|
|
173
|
-
|
|
174
|
-
// SQL 数据源中可访问
|
|
175
|
-
SELECT * FROM Contact WHERE OwnerId = $CurrentUser.Id$ AND Spouse = $CurrentUser.Wife$
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## 常见错误
|
|
179
|
-
|
|
179
|
+
|
|
180
|
+
## 用户扩展字段访问(同理)
|
|
181
|
+
|
|
182
|
+
平台 `sys_user` 也由表单引擎驱动。如给 `sys_user` 添加 `Wife` 字段:
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
// V8 代码中可访问
|
|
186
|
+
V8.CurrentUser.Wife;
|
|
187
|
+
|
|
188
|
+
// SQL 数据源中可访问
|
|
189
|
+
SELECT * FROM Contact WHERE OwnerId = $CurrentUser.Id$ AND Spouse = $CurrentUser.Wife$
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 常见错误
|
|
193
|
+
|
|
180
194
|
❌ 绕开 `V8.Cache` 使用底层 Redis → 租户数据串号(V8 已不再暴露底层句柄)
|
|
181
195
|
❌ MongoDB DbName 不带 OsClient → 数据混淆
|
|
182
196
|
❌ 把 OsClientModel 字段直接返回给前端 → 密钥泄漏
|
|
@@ -184,9 +198,9 @@ SELECT * FROM Contact WHERE OwnerId = $CurrentUser.Id$ AND Spouse = $CurrentUser
|
|
|
184
198
|
❌ 在前端硬编码 OsClient → 一改全改,应通过 token/URL 自动识别
|
|
185
199
|
❌ 跨租户操作不验证当前用户权限 → 越权风险
|
|
186
200
|
❌ 子租户缺少 MQ/MQTT/Search 独立凭据时回退主账号 → 全平台越权
|
|
187
|
-
|
|
201
|
+
|
|
188
202
|
## 检查清单
|
|
189
|
-
|
|
203
|
+
|
|
190
204
|
- [ ] 缓存只通过 `V8.Cache` 使用逻辑 Key 或当前租户完整 Key
|
|
191
205
|
- [ ] 所有 MongoDB DbName 都包含 `V8.OsClient`
|
|
192
206
|
- [ ] 当前租户自有的业务集成密钥可放 `V8.OsClientModel`,共享 Redis/存储/MQ/MQTT/Search 凭据只能由服务端托管
|
|
@@ -195,6 +209,8 @@ SELECT * FROM Contact WHERE OwnerId = $CurrentUser.Id$ AND Spouse = $CurrentUser
|
|
|
195
209
|
- [ ] 子租户数据库账号只授权本租户库,MQ/MQTT/Search 独立凭据已真实创建
|
|
196
210
|
- [ ] 文件、队列、Topic、索引均由服务端规范为当前租户命名空间
|
|
197
211
|
- [ ] 无扩展库租户重复执行 V8 时不会重复刷新 SaaS 配置;真实 `sys_osclients` 保存后各节点能按租户失效并回源
|
|
212
|
+
- [ ] ZIP 开库区分上传与还原进度,使用固定 13 步、低频明细、服务端心跳和有界日志
|
|
213
|
+
- [ ] 开库锁只由可信持久后台任务自动续租;真实租约丢失失败关闭,所有副作用仍具备幂等、检查点和 fencing token
|
|
198
214
|
|
|
199
215
|
## Microi.AI 中转站租户凭据
|
|
200
216
|
|