@microi.net/cli 5.4.9 → 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.
@@ -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
- - 该链路只允许通过能力鉴权的当前租户超级管理员,并继续受 `FileUploadEnabled` 总开关控制;它不是普通用户上传或任意 HDFS 路径写入接口。
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
- | `FileUploadEnabled` | `true` | — |
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` 六个字段全部可空;空值、无效值或老数据库缺列时使用代码默认值,不会因升级自动停用上传。`FileUploadMaxRequestMB` 指一次上传所有文件的业务合计大小,不等于 Kestrel HTTP 请求正文上限。
141
+ - `sys_osclients` 六个现行字段全部可空;`DisableFileUpload` 空值、无效值或 `0/false` 均表示允许上传,只有 `1/true` 才禁止。旧数据库尚未创建新物理字段时才回退读取 `FileUploadEnabled`,升级后旧正向字段即使为 `0` 也不得覆盖新字段的默认允许语义。`FileUploadMaxRequestMB` 指一次上传所有文件的业务合计大小,不等于 Kestrel HTTP 请求正文上限。
142
142
  - 固定灾难保护和 HTTP/Multipart/Form 解析上限不属于安装配置;租户值即使更大也会被这些边界截断。最终单次总量还不能超过帐号或租户的有效日额度,单文件不能超过最终单次总量。
143
- - `FileUploadEnabled=0` 表示禁用当前租户上传,也会阻止 AI 应用资产断点续传;不能把内部发布协议当成绕过开关的后门。普通单请求继续受全局大小硬上限,v3 只移除产品级字节上限;租户配置刷新应走现有 SaaS 引擎重载和共享 Redis 发布订阅,不能依赖单节点内存。
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.FileUploadEnabled` 被明确设置为 `0/false`。缺列、空值、无效值和新租户都按默认 `true`,不要先把问题归因于 MinIO/HDFS。
153
- - 新版响应同时返回 `DataAppend.ErrorType=TenantFileUploadDisabled`、`OsClient`、`ConfigField=FileUploadEnabled` 和文档地址;客户端必须保留后端 `Msg/DataAppend`,不能只显示“上传失败”。
154
- - 处理时先读取当前 API 进程的 `OsClient + OsClientType + OsClientNetwork`,再精确回读同三元组的启用记录并把 `FileUploadEnabled` 改为 `1`。不能仅按租户名批量覆盖其它网络或环境记录。
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` 和六个 `FileUpload*` 配额字段。
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`。可修改字段为 `FileUploadEnabled`、`FileUploadMaxFileMB`、`FileUploadMaxRequestMB`、`FileUploadMaxCount`、`FileUploadDailyUserQuotaMB`、`FileUploadDailyTenantQuotaMB`。
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=dd53500e57af81daec15456a538ac36b2c965faca61ba99de46c7ba0e3b4bbaa -->
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=84f41fd0ea02234d3d883ba73e894393a4f2a57c5f4fc51a985145f658dd3a8c -->
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=e2d7fb49de3af1ef244df5027086674b10d18ec830e765adbe252d094aa14490 -->
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` → 代码默认值”解析;平台固定灾难保护、HTTP/Multipart/Form 和反向代理上限不可由租户覆盖,也不要求安装者维护额外上传环境变量。
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
- ```javascript
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
 
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: vision-engine
3
+ description: 设计、实现、配置、扩展和验收视觉引擎能力。处理 Microi.Vision、V8.Vision、ONNX Runtime、图片或视频帧、商品或物体样本库优先匹配、平台 AI 异步回退、人脸特征与关注人员合规、platform-vision-runtime、microi-vision 前端微服务和应用商城交付时使用。
4
+ ---
5
+
6
+ > **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
7
+
8
+ # 视觉引擎
9
+
10
+ ## 先读取相关规范
11
+
12
+ - 涉及完整表、字段、菜单和权限交付时,读取 `../microi-system-delivery/SKILL.md`。
13
+ - 涉及接口引擎业务逻辑时,读取 `../v8-crud-api/SKILL.md` 与 `../v8-api-config/SKILL.md`。
14
+ - 涉及图片上传、私有 HDFS 或保留清理时,读取 `../v8-file-upload/SKILL.md`。
15
+ - 涉及 AI 回退时,读取 `../ai-engine/SKILL.md`;涉及持久异步任务时读取 `../job-engine/SKILL.md`。
16
+ - 涉及人脸、关注人员、日志、租户或特征向量时,读取 `../v8-security/SKILL.md` 与 `../v8-saas-multi-tenant/SKILL.md`。
17
+ - 涉及全新工作台时,读取 `../microi-microservice/SKILL.md`、`../microi-frontend-sdk/SKILL.md`、`../ui-design/SKILL.md` 与 `../playwright-e2e/SKILL.md`。
18
+ - 涉及官方应用包时,读取 `../app-store/SKILL.md`。
19
+
20
+ ## 固定架构
21
+
22
+ 用户可见的应用、菜单、页面和文档标题统一使用中性名称“视觉引擎”;`Microi.Vision`、`V8.Vision` 和 `microi-vision` 仅作为程序集、API 与 AppKey 等技术标识,不得把“Microi 视觉识别”作为租户界面的固定品牌名称。
23
+
24
+ Microi.Vision 是可信 .NET 宿主中的最小视觉原子层,只负责:
25
+
26
+ 1. 有界 JPEG/PNG/WebP 解码;
27
+ 2. 只从受控模型目录按 `ModelKey` 加载 `model.json + *.onnx`;
28
+ 3. 校验模型路径、大小、任务类型、输入参数和 SHA-256;
29
+ 4. 用 ONNX Runtime 执行检测、分割、Embedding/FaceEmbedding,并按已安装运行库选择 CPU、CUDA、TensorRT、OpenVINO 或 DirectML;
30
+ 5. 对检测框做人脸五点对齐或对象裁剪,归一化向量并执行 HNSW 近邻检索;
31
+ 6. 提供 IoU 目标跟踪、连续帧投票和有界帧流入口。
32
+
33
+ 商品/人员表、样本查询、阈值、状态机、AI 回退、业务日志和租户 Hook 必须留在接口引擎。不能把这些逻辑搬入 C# Controller/Service,也不能让 V8 指定模型路径、执行提供程序、Endpoint、API Key、摄像头凭据或其它 OsClient。
34
+
35
+ 固定组件:
36
+
37
+ | 组件 | 策略 | 职责 |
38
+ |---|---|---|
39
+ | `platform-vision-runtime` | `Managed` | Recognize/Enroll/Result/Recent/Bootstrap/Capabilities |
40
+ | `platform-vision-ai-worker` | `Managed` | 持久任务消费、Microi.AI 视觉调用、回写与敏感图片清理 |
41
+ | `platform-vision-category-options` | `Managed` | 当前租户启用分类选项 |
42
+ | `platform-vision-subject-options` | `Managed` | 当前租户启用对象选项 |
43
+ | `platform-vision-custom-hook` | `CreateIfMissing` | 租户自己的低敏业务扩展,官方升级永不覆盖 |
44
+ | `microi-vision/workbench` | MicroService | 图片、摄像头帧、连续模式、来源状态、样本录入 |
45
+
46
+ 详细数据、状态、协议和验收规范见 [architecture-and-acceptance.md](references/architecture-and-acceptance.md)。
47
+
48
+ ## 数据库优先,AI 异步回退
49
+
50
+ 识别顺序不可颠倒:
51
+
52
+ 1. 生成稳定 `RequestId`,先按 `RequestNo` 做幂等读取;
53
+ 2. 用当前策略的 `PipelineKey/ModelKey` 调用 `V8.Vision.Analyze`,先检测/分割,再对每个目标生成特征;
54
+ 3. 只查询当前租户 `Status=Ready`、`Mode` 和 `ModelKey` 一致的样本;
55
+ 4. 解保护候选向量,调用 `V8.Vision.Search` 执行 HNSW 近邻检索;
56
+ 5. 连续视频调用 `V8.Vision.Stabilize` 做会话级投票;达阈值返回 `Status=LocalMatched, MatchSource=Database`;
57
+ 6. 未达阈值且 AI 关闭,返回 `Status=Unmatched, MatchSource=None`;
58
+ 7. AI 开启时先保存私有输入并创建共享后台任务,立即返回 `Status=AiPending`;
59
+ 8. Worker 收敛到 `AiMatched` 或 `Failed`,前端用同一 `RequestNo` 轮询。
60
+
61
+ 不得同步阻塞前端等待大模型,不得把 AI 结果自动写入可信对象主数据,也不得把本地相似度与 AI 置信度混成一个分数。
62
+
63
+ ## V8 原子函数
64
+
65
+ ```js
66
+ var analyzed = await V8.Vision.Analyze({
67
+ FileByteBase64: V8.FilesByteBase64['frame'],
68
+ FileName: 'frame.jpg',
69
+ PipelineKey: 'general-yolox-dinov2-v1',
70
+ Mode: 'General',
71
+ FrameId: 'camera-01-000123',
72
+ StreamSessionId: 'scale-01-session-42',
73
+ FrameSequence: 123
74
+ });
75
+
76
+ var ranked = await V8.Vision.Search({
77
+ QueryEmbeddingBase64: analyzed.Data.Detections[0].EmbeddingBase64,
78
+ Candidates: candidates,
79
+ Threshold: 0.82,
80
+ TopK: 5,
81
+ EfSearch: 80,
82
+ IndexKey: 'ready-general-samples'
83
+ });
84
+
85
+ var voted = await V8.Vision.Stabilize({
86
+ StreamSessionId: 'scale-01-session-42',
87
+ FrameId: 'camera-01-000123',
88
+ Key: ranked.Data.Matches.length ? ranked.Data.Matches[0].Key : '__unmatched__',
89
+ Confidence: ranked.Data.Matches.length ? ranked.Data.Matches[0].Similarity : 0,
90
+ WindowSize: 5,
91
+ MinimumVotes: 3
92
+ });
93
+ ```
94
+
95
+ 主流流水线方法:`V8.Vision.Analyze`、`V8.Vision.Search`、`V8.Vision.Stabilize`;兼容原子方法:`Extract`、`ExtractBatch`、`Compare`、`CompareBatch`、`GetCapabilities`。业务入口优先使用 `V8.ApiEngine.Run('platform-vision-runtime', ...)`,不要把向量返回给普通浏览器或日志。
96
+
97
+ ## 模型与准确率门禁
98
+
99
+ - 默认 `builtin-visual-fingerprint-v1` 只是相同/近似样图指纹,不能称为语义分类器或人脸模型。
100
+ - 内核已经实现 YOLOv8/YOLOX/RT-DETR/SCRFD 常见 ONNX 输出适配、DINOv2/DINOv3/SigLIP2 类嵌入、ArcFace 类人脸嵌入、HNSW、IoU 跟踪和连续帧投票;生产结果取决于实际安装的受控模型包与门店数据,不得把“已兼容”写成“模型准确率已通过”。
101
+ - 模型代码许可证、权重许可证、训练数据限制和商用授权必须分别核对;`model.json` 的 `License` 字段不是法律结论。
102
+ - 每个模型包固定版本、输入尺寸、RGB/BGR、Mean/Std、池化、来源、许可证和 SHA-256;不允许浮动 URL 在线下载后直接加载。
103
+ - 模型必须在目标 CPU/GPU、真实镜头、光照、角度和包装变化下建立标注集,测 Top-1/Top-K、误接受率、误拒绝率、P50/P95/P99、内存和人工复核率。
104
+ - 没有真实模型包与标注结果时,只能说“平台接入完成/指纹联调通过”,不能说“商品或人脸识别准确率通过”。
105
+ - ONNX Runtime 是主流部署运行时,但运行时主流不等于所装模型第一梯队;未测真实标注集与目标硬件 P50/P95/P99 前,不得宣称准确率或速度第一梯队。
106
+
107
+ ## 人脸与关注人员强制边界
108
+
109
+ - 录入、识别、留存和共享生物特征前必须有合法依据、告知/同意、用途限制、访问控制和删除流程。
110
+ - 工作台 Face 模式必须要求本次明确同意;前端门禁不能替代后端校验。
111
+ - 人脸模板和请求按当前租户隔离;向量使用绑定租户和接口引擎的保护能力保存,列表/Hook/普通日志不返回正文。
112
+ - 默认 `RetainFaceInput=0`;AI Worker 无论成功失败都清理临时图片。
113
+ - 数据库未命中时,大模型只能描述中性可见事实,不得猜姓名、罪犯身份、族群、健康、政治等敏感属性。
114
+ - “罪犯/关注人员”命中仅是人工复核线索,不能单独触发抓捕、处罚、解雇、拒绝服务或身份认证。
115
+ - 身份认证仍使用 DiyToken、Passkey/严格人脸一次性票据和原有权限体系;视觉相似度不能替代登录或强身份验证。
116
+
117
+ ## 视频入口
118
+
119
+ 前端使用 `getUserMedia` 并按 `FrameIntervalMs` 取帧,必须提供开启、暂停、继续、再次识别和关闭操作;每次打开摄像头生成新的 `StreamSessionId`,每帧递增 `FrameSequence`。可信边缘宿主将 RTSP/WebRTC 解码为 `IAsyncEnumerable<MicroiVisionExtractParam>` 后调用 `RecognizeFramesAsync`。Microi.Vision 不直接连接调用方提交的 RTSP URL,不保存摄像头账号,不承担流媒体服务器职责。
120
+
121
+ 连续帧必须有:最大图片大小、最小间隔、单客户端串行、取消/停止、RequestId 幂等、网络退避和后台任务去重。不能每个视频帧都无界调用大模型。接口返回 `AiPending` 后,前端必须进入 single-flight 状态:暂停提交新帧,只轮询当前 `RequestId`,直到 `AiMatched/AiFailed/Unmatched`、超时或用户取消后才允许下一帧;禁止并行创建多个 AI 任务。
122
+
123
+ 跨版本接口字段必须在协议边界归一化。连续稳定结果至少兼容 `.NET` 原子常用的 `VoteCount/WindowCount/AverageConfidence` 与历史接口的 `Votes/WindowSize/Confidence`,前端只读取归一化 DTO,任何真实响应都不得展示 `undefined/undefined`。
124
+
125
+ 手机收银/巡检工作台属于高频全屏操作面:在宿主提供的可用高度内同时露出取景区、主要识别按钮、当前状态和核心结果,不依赖纵向滚动才能完成一次识别;竖屏上下分区、短横屏左右分区,且不得改变已验收的桌面布局。相机权限拒绝、AI 等待、终态结果和重试操作都要在当前视口可见。
126
+
127
+ ## 交付顺序
128
+
129
+ 1. 更新并测试 Microi.Vision、V8 注入与 API DI;
130
+ 2. 用 MCP `microi_plan_system` 做 dry run,再生成并 `microi_validate_system`;
131
+ 3. 同步私有微服务源码,构建后按 V3 stage/finalize 发布公开产物并回读;
132
+ 4. 写入默认分类/策略,按业务 Key 先查后写;
133
+ 5. 发布框架 NuGet/Docker,使目标运行时实际包含 `V8.Vision`;
134
+ 6. 通过官方应用发布引擎生成商城包,明确 `Managed/CreateIfMissing`,回读包 SHA 和不可变版本;
135
+ 7. 在目标租户安装/更新后做真实浏览器、接口、后台任务和模型测试;
136
+ 8. 分别报告源码、包、制品、部署、浏览器、模型准确率和物理摄像头边界。
137
+
138
+ `Microi.Vision` 必须作为独立闭源仓维护:`Microi.Anderson.sln` 使用 `ProjectReference`,公开 `Microi.net.sln` 只能使用同版本 NuGet;一键发布必须像 `Microi.AI`、`Microi.WorkFlow` 一样对 DLL 执行 Obfuscar、回写 nupkg,并以加密前后哈希变化及 `.microi-encrypted` 清单为门禁。模型权重继续独立分发,不进入 NuGet。
139
+
140
+ ## 禁止事项
141
+
142
+ - 禁止用 HTTP 200、构建成功、Mock UI、同一图片命中代替真实商品/人脸准确率。
143
+ - 禁止把大模型标签伪装成数据库命中或权威身份。
144
+ - 禁止在接口参数中接受模型目录、任意本地路径、任意网络地址或执行提供程序。
145
+ - 禁止把图片 Base64、原始人脸、向量、提示词、AI Key 写入 Hook 或普通日志。
146
+ - 禁止用进程内队列、`static` 字典或本地文件作为 AI 任务完成事实源。
147
+ - 禁止在应用升级时覆盖 `platform-vision-custom-hook`。
148
+ - 禁止只发布商城包却宣称旧平台运行时已经具备 `V8.Vision`。
149
+
150
+ ## 验收输出
151
+
152
+ 最终报告至少分开列出:源码测试、5 表/字段/索引、7 菜单、5 引擎/4 事件、微服务源码与构建哈希、商城包与版本、框架制品、目标节点版本、真实浏览器、真实图片素材来源、数据库命中、AI 回退、人脸同意、模型准确率和未执行的物理设备测试。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "视觉引擎"
3
+ short_description: "构建与验收数据库优先、AI回退的通用视觉识别"
4
+ default_prompt: "Use $vision-engine to build and validate tenant-isolated Microi.Vision workflows."