@microi.net/cli 5.2.3 → 5.2.5
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 +4 -4
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/mcp-server.js +56 -56
- package/scripts/microi-skills.meta.json +193 -193
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +6 -6
- package/skills/app-store/SKILL.md +43 -5
- package/skills/message-notification/references/contracts.md +2 -0
- package/skills/microi-ai-application/SKILL.md +4 -3
- package/skills/microi-db-schema/references/form-component-options.md +1 -1
- package/skills/microi-form-engine/references/component-catalog.md +1 -1
- package/skills/microi-microservice/SKILL.md +1 -0
- package/skills/module-engine/SKILL.md +16 -4
- package/skills/v8-export-import/SKILL.md +22 -11
- package/skills/v8-saas-multi-tenant/SKILL.md +8 -7
- package/skills/v8-security/SKILL.md +3 -1
|
@@ -89,9 +89,18 @@ description: Microi 应用商城开发、打包、安装和升级规范。用于
|
|
|
89
89
|
- 商城详情、来源管理等 Teleport 弹层必须使用宿主级固定遮罩,毛玻璃覆盖完整可视区域;标题栏拖动按弹层真实尺寸限制四边,窗口缩放后重新限制,不能依赖固定像素最大位移或允许内容越出视口。
|
|
90
90
|
- “我发布的应用”只读取当前登录用户拥有的记录,可以包含草稿和构建失败项;普通商城列表只读取已发布项。两者必须在服务端按 Owner 和发布状态过滤,不能依赖浏览器过滤后再分页。
|
|
91
91
|
|
|
92
|
-
## 发布版本与资源选择
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
## 发布版本与资源选择
|
|
93
|
+
|
|
94
|
+
### 每个应用版本必须有更新日志(强制)
|
|
95
|
+
|
|
96
|
+
- 本规则覆盖 `Platform / Regular / Web / UniApp / MicroService` 以及所有 AI 应用。任何创建、修改、升级、重新构建或重新发布,只要产生新的应用版本,就必须先在 `sys_microistore_changelog` 写入一条更新日志;不允许先发布后补写,也不允许因为只改了元数据、V8、路由、提示文案或内置运行产物而省略。
|
|
97
|
+
- 更新日志通过 `StoreId` 关联 `sys_microistore.Id`,`Version` 必须是规范化后的精确 `AppVersion`,且同一应用同一版本只能有一条。`Title`、`ChangeType`、`Content`、`ReleaseTime` 都是必填项;`Content` 要写清用户可感知的新增、优化、修复、兼容或安全影响,禁止只写“优化体验”“修复问题”等无法审计的空泛文字。
|
|
98
|
+
- 正确顺序固定为:确定目标版本 → 保存商城应用 → 写入或审阅该精确版本日志 → 同步源码与构建 → 制包/发布 → 回读商城行、安装包和日志。发布器发现日志缺失、软删除、版本不一致或任一必填项为空时必须失败关闭。
|
|
99
|
+
- `microi_publish_application_directory_stream`、V3 发布或其它支持变更摘要的工具必须显式传 `changeSummary`,且其含义与更新日志一致;`mci_ai_app_version.ChangeSummary`、`AppPakcet.PackageInfo.ChangeLog` 和商城子表不得互相矛盾。
|
|
100
|
+
- 应用商城包升级必须同时交付子表、真实 `StoreId` 外键字段、唯一约束/索引、隐藏子菜单、主表 `TableChild` 字段、详情时间线和发布门禁。历史商城源没有该能力时前端应明确显示“当前来源暂未提供更新日志”,不得伪造空日志为“暂无更新”。
|
|
101
|
+
- 验收至少回读“商城当前版本 + 对应日志 + 包内版本/ChangeLog”,再在真实商城详情弹层检查版本、类型、标题、时间和正文;重复执行相同版本写入应命中唯一记录而不产生重复行。
|
|
102
|
+
|
|
103
|
+
- 发布器必须把用户请求的精确版本传给资产准备器,并断言 `RequestedVersion == Prepared.PackageVersion == AppPakcet.PackageInfo.Version`;版本不存在就失败,禁止静默改用最新版本。
|
|
95
104
|
- `SelectMenu`、`SelectTable`、`SelectApiEngine` 必须从当前发布包的显式选择持久化;不能读取商城行上一次保存的选择来决定本次包内容。
|
|
96
105
|
- 发布完成后同时回读商城版本行、包正文和构建资产,逐项核对版本、数量与 SHA-256;任一处漂移即发布失败。
|
|
97
106
|
- 官方内置前端应用只能有一个发布源码根。默认租户工作副本与受审计的独立 Git 源码仓库不能同时成为事实源;若使用独立仓库,必须提交一个发布契约并让构建、跨工程测试、所有程序集内置包共同读取它,正式发布还要证明源码根来自无未提交修改的 Git 提交。前端主包只提供通用宿主、诊断和恢复入口,不复制完整业务源码。HDFS/CDN 运行镜像必须回读到同一 `RuntimeManifestHash` 后才可启用,不能用较新的镜像静默覆盖较旧数据库包,也不能用较新的数据库包静默覆盖未验签镜像。
|
|
@@ -99,8 +108,8 @@ description: Microi 应用商城开发、打包、安装和升级规范。用于
|
|
|
99
108
|
|
|
100
109
|
## MCP 工作流
|
|
101
110
|
|
|
102
|
-
1. 安装/更新现成商城应用时,先调用 `microi_install_store_application` / `microi_update_store_application` 且不传 `confirmExecution`,核对目标租户、商城源、StoreId 和幂等请求 Id
|
|
103
|
-
2. 用户明确确认后,把 `confirmExecution` 精确设为 `StoreId`,提交真实持久化后台任务;只传 StoreId、版本和商城源定位信息,不通过 MCP/HTTP 传完整 `AppPakcet`。
|
|
111
|
+
1. 安装/更新现成商城应用时,先调用 `microi_install_store_application` / `microi_update_store_application` 且不传 `confirmExecution`,核对目标租户、商城源、StoreId 和幂等请求 Id 的预检结果。恢复被商城升版打断的旧任务时,额外传入已回读且仍含完整包正文的 `storeVersionId`;不同快照必须使用新的 `requestId`。
|
|
112
|
+
2. 用户明确确认后,把 `confirmExecution` 精确设为 `StoreId`,提交真实持久化后台任务;只传 StoreId、可选不可变 StoreVersionId、版本和商城源定位信息,不通过 MCP/HTTP 传完整 `AppPakcet`。
|
|
104
113
|
3. 自建应用或低代码系统先用 `microi_list_applications` / `microi_get_application_context` 盘点源码,再用 `microi_get_manifest_schema`、`microi_plan_system` 与 `microi_generate_system(dryRun:true)` 干跑。
|
|
105
114
|
4. 安装任务必须回读至 `Succeeded`,再执行 `microi_validate_system`、远端资源回读和真实 UI 验收;仅返回 TaskId 不代表安装成功。
|
|
106
115
|
|
|
@@ -241,3 +250,32 @@ description: Microi 应用商城开发、打包、安装和升级规范。用于
|
|
|
241
250
|
- 根因证据:两次托管转储的调用链都停在 `V8.ApiEngine.Run -> GetApiEngineModelInternal -> MicroiTwoLevelCache.SetAsync -> PublishInvalidateAsync -> PublishWithRetryAsync`。权威 Redis 写入已经成功,随后用于通知其它节点清理一级缓存的 Pub/Sub 广播没有完成时限,因而把业务调用永久挂住;与此同时,单字段元数据更新即使只变更 Label、Remark、布局或 V8,也会重复执行物理列 DDL,放大旧租户升级成本。
|
|
242
251
|
- 通用规则:Redis 权威值写入成功后,跨节点一级缓存失效广播只能是有界、尽力而为的附属动作。队列等待和实际发布都必须有独立短超时,超时后观察迟到异常并开启短暂熔断冷却,防止悬挂发布任务无界堆积;不得因此回滚已经成功的权威写入。`UptDiyField` 只有列名或物理类型变化时才允许调用 `ChangeColumn`,显示名称、说明、布局和事件代码等纯元数据变化禁止触发 DDL。
|
|
243
252
|
- 自动化与真实验收:缓存测试必须覆盖发布永久不返回时的有界退出、迟到异常观察和冷却期抑制;字段测试必须断言只有 Name/Type 变化才进入物理 DDL。应用商城资源契约测试、后台任务与 SaaS 回归测试通过后,还必须在真实主租户完成 8/8 子租户收敛并立即执行一轮 8/8 无操作重跑。验收期间若官方应用发布了新版本,应以新的不可变 `StoreVersionId` 启动独立追赶批次,禁止混入已经开始的旧版本分片。
|
|
253
|
+
|
|
254
|
+
## 复盘:最新版后端仍放行未锁快照的旧商城工作器
|
|
255
|
+
|
|
256
|
+
- 触发场景:目标租户已经部署最新版前后端,但数据库中的 `import-microi-store-package v2.2.9` 与 `bulk-import-microi-store-packages v1.2.4` 被启动完整性检查判为合格;平台应用批量任务开始后,官方商城恰好升版,首个应用在下一分片仍从易变当前行取包并失败。
|
|
257
|
+
- 根因:后端自举门槛只校验早期版本和历史标记,没有把不可变快照、禁止自适应单片、前置物理列分片检查点及商城自举优先级纳入能力契约;MCP 单应用恢复工具也只能传 `StoreId`,无法显式锁定已经回读的 `mic_data_version` 快照。
|
|
258
|
+
- 通用规则:后端启动必须同时校验导入器最低版本与固定快照/有界分片/前置列检查点标记、批量工作器最低版本与 `StoreVersionId + BulkAdaptiveSingleSlice:false`、商城列表的自举优先级;任一缺失都必须从受信升级资源刷新,不能仅看后端二进制版本。MCP 安装/更新允许传受校验的可选 `storeVersionId`,审计同样记录该 Id;不同快照使用不同幂等请求 Id,禁止把旧任务静默改指新包。
|
|
259
|
+
- 自动化与真实验收:单元测试同时拒绝旧版本和缺标记的伪新版工作器;在真实旧租户先用固定快照完成应用商城自举,再执行全部平台应用更新并立即做零更新复跑。验收期间出现的新官方版本只进入下一独立批次,历史失败任务保留作审计,不删除或篡改。
|
|
260
|
+
|
|
261
|
+
## 复盘:本租户自助批量被误判为主租户跨租户任务
|
|
262
|
+
|
|
263
|
+
- 触发场景:普通租户在自己的应用商城点击“全部安装/更新”,后台执行器仅按固定批量工作器 Key 触发跨租户自愈,把 `owner=当前租户、target=当前租户` 当成主→子任务;旧导入器只能从自己复制自己,随后被新能力门禁拒绝,并错误提示“先更新主租户应用商城”。
|
|
264
|
+
- 根因:跨租户自愈没有校验由服务端控制面持久化的目标租户保留标记,也没有要求任务拥有者与执行租户不同;把同租户自助批量和主租户代子租户执行混成了同一作用域。
|
|
265
|
+
- 通用规则:运行前强制自愈只允许用于固定批量工作器、非空服务端目标标记、`owner != execution` 且标记精确等于执行租户的主→子任务。普通租户自助批量必须跳过跨租户复制,让持久计划按官方列表顺序优先安装/更新应用商城自身,再由新版导入器继续后续应用;不能要求官方发布源安装自己。
|
|
266
|
+
- 自动化与真实验收:单元测试覆盖同租户无标记、同租户伪标记、跨租户正确标记、标记漂移和错误工作器 Key;真实旧租户先保留旧导入器发起自助批量,断言不再出现“子租户商城工作器执行前自愈失败”,计划第一项为应用商城,全部任务成功后立即执行零更新复跑。
|
|
267
|
+
|
|
268
|
+
## 复盘:平台包跨旧租户安装时同时暴露结构闭包、元数据回读与目录脆弱性
|
|
269
|
+
|
|
270
|
+
- 触发场景:同一轮主租户批量任务在不同子租户分别报“数据集目标表尚未创建”“接口引擎写入后 HTTP 状态不一致”“既有菜单 Url 唯一冲突”或“未找到 OsClient”;父任务若在目录阶段对全部目标做有副作用自举,还会让一个坏租户阻断其余已可执行租户。
|
|
271
|
+
- 包结构闭包:任何 `DataSets.TableName` 都必须由同一包的 `DiyTables + DiyFields + DDLStatements + PhysicalColumns` 完整创建或由显式依赖声明保证先安装,不能只因为官方母库已有该表就省略。契约测试要逐个数据集反向验证目标表、主键、字段和物理 DDL,并在真实缺表旧租户执行首次安装与重复安装。
|
|
272
|
+
- 受管接口状态:旧库 `diy_field` 元数据可能落后于 `sys_apiengine` 物理列,导致 FormEngine 返回成功却忽略 `IsEnable/StopHttp`。仅对受信官方 `Managed` 接口的固定布尔控制列,允许在 FormEngine 写后不一致时按稳定 Id 参数化校准物理列、清理租户缓存并严格二次回读;源码、匿名权限、ApiAddress 和租户扩展不得借此绕过三方冲突或所有权保护,二次回读仍不一致必须整包失败。
|
|
273
|
+
- 菜单与目录隔离:更新既有菜单发生 Url 唯一冲突时,优先保留目标租户当前唯一路由;确需新路由时使用有界、可回读的稳定后缀,不得字符串拼 SQL。子租户目录发现必须是无副作用读取;运行时缺失在单目标投递前按 `sys_osclients` 受控热加载并只记录该租户失败,已经排队的子任务继续监控到终态,禁止一个 `未找到OsClient` 提前终止整批。
|
|
274
|
+
- 百租户并发边界:父协调任务继续使用集群级防重键;子安装任务必须使用固定商城工作器 `ConcurrencyKey` 在当前运行环境内串行。不能根据 `OsClient` 推断物理库隔离,因为多个子租户可能共享同一数据库;只有未来能由服务端权威连接指纹证明互不共享物理库并建立分组锁时,才允许在不同物理库组间并行。
|
|
275
|
+
- 兼容与验收:V8 协调器需要兼容尚未部署新 C# 原子的节点,在目录或单目标投递返回明确的缺失 OsClient 时最多热加载并重试一次,重复同一缺失或超过安全上限立即失败关闭。验收顺序固定为先更新主租户应用商城导入器,再用不可变 `StoreVersionId` 更新 SaaS 协调器,完成全部子租户父子任务收敛,最后立即执行 `Planned=0` 的无操作重跑;同时保留每个失败租户的独立任务和通知中心证据。
|
|
276
|
+
|
|
277
|
+
## 复盘:旧对象存储节点缺少 MoveObject 导致编译资源无限重传
|
|
278
|
+
|
|
279
|
+
- 触发场景:平台微服务包只有少量 `BuildAssets`,但后台检查点长期停在 `ApplicationAssets / Build / AssetIndex=1`,`ApplicationAssetUploaded` 却持续超过包内资源总数。目标节点能够上传并回读公有对象,但不支持或拒绝 `MoveObject`;导入器每一片都重新上传同一文件,再因移动失败写回临时路径,下一片继续重复。
|
|
280
|
+
- 通用规则:新上传文件移动到租户稳定路径失败时,若上传结果已通过摘要和大小校验,应保留这个真实可读路径并写入显式兼容 scope(当前为 `PrivateSource+PublicBuildMoveFallback`)。后续分片只允许在同时命中 AppId、文件路径、摘要、大小、真实 HDFS 路径和该 scope 时直接复用,禁止再次调用 `MoveObject` 或上传;没有兼容标记的历史坏路径仍只允许一次有界重传修复,不能把任意旧元数据误当成有效对象。
|
|
281
|
+
- 自举与验收:主租户投递每个子任务前必须先复制最新版应用商城导入器和批量工作器,已经排队的任务不得假定会动态取得主租户新代码。回归测试要模拟“上传成功、MoveObject 不可用、下一分片恢复”,断言上传次数保持 1 且资源索引继续前进;真实旧租户验收必须观察 4 个编译资源在有界片数内完成,并在全租户成功后立即做 `Planned=0` 复跑。
|
|
@@ -73,6 +73,8 @@
|
|
|
73
73
|
- 仅返回 `V8.CurrentUser.Id` 的 `ChannelType=平台内部` 记录。
|
|
74
74
|
- 支持 `PageIndex/PageSize`,`PageSize` 最大 100。
|
|
75
75
|
- `DataAppend.UnreadCount` 返回同一用户权威未读数。
|
|
76
|
+
- 每条记录统一投影 `SenderUserId/Account=AI`、`SenderName=AI助手`、`ReadOnly=false`;聊天侧只能固定一个 AI 助手联系人,并将通知权威历史与 AI 聊天历史按时间合并。
|
|
77
|
+
- 兼容旧 `MICROI_PLATFORM_ADMIN/admin` 元数据时只改显示投影,不批量删除或改写 `mic_msg_event_log` 历史事实。
|
|
76
78
|
|
|
77
79
|
### `msg_internal_mark_read`
|
|
78
80
|
|
|
@@ -80,9 +80,10 @@ UniApp 使用 Vue 3 + TypeScript 的官方 Vite 工具链,并同时遵守 `mic
|
|
|
80
80
|
2. 依次执行类型检查、单元测试、生产构建和产物静态扫描。
|
|
81
81
|
3. 检查 `dist/build` 不含源码、Token、密钥、localhost、source map 或陈旧 chunk。
|
|
82
82
|
4. 同步私有源码,再流式发布公有构建目录;源码同步失败不得继续发布。发布前回读并冻结应用的 `CurrentVersion` 与 `AppVersion`,stage 只上传不可变版本资产,finalize 必须同时提交 `ExpectedCurrentVersion` 与 `ExpectedAppVersion` 做 compare-and-set;缺一项、状态漂移或回读不一致都停止,不能自动覆盖较新发布。
|
|
83
|
-
5.
|
|
84
|
-
6.
|
|
85
|
-
7. `SharedPublicRuntime.EntryUrl`
|
|
83
|
+
5. 每次创建、修改、升级或重新发布 AI 应用,必须在任何源码同步、stage、finalize 或商城制包之前,为目标精确 `AppVersion` 写入 `sys_microistore_changelog`。日志的 `StoreId / Version / Title / ChangeType / Content / ReleaseTime` 必须完整;发布工具显式传入含义一致且非空的 `changeSummary`,发布后同时回读商城子表与 `mci_ai_app_version.ChangeSummary`。缺日志或版本不一致必须停止发布。
|
|
84
|
+
6. Web/UniApp 使用 `/{OsClient}/ai-app-publish/{AppKey}/index.html`;MicroService 使用 `/micro-app/{OsClient}/{AppKey}/index.html`。不要因技术栈相同而混淆运行类型。
|
|
85
|
+
7. 官网、二维码、分享链接和商城“立即体验”只能使用不含 `/releases/`、`/requests/`、`/versions/` 与语义版本号的稳定当前入口;不得使用 `SharedPublicRuntime.EntryUrl` 或发布结果中的不可变版本 URL。固定入口必须以代理或全屏加载壳保持浏览器地址不变,不能用 30x、`meta refresh` 或 `location.replace` 把地址栏跳到版本产物。
|
|
86
|
+
8. `SharedPublicRuntime.EntryUrl` 和 `/versions/{Version}/index.html` 仅用于历史记录、回滚、摘要校验与审计。回读应用、版本、active 文件清单和 SHA-256;旧清单文件只能可逆归档,不能删除。再分别直接请求稳定当前入口、不可变版本入口及主要 JS/CSS,并断言前者完成加载后地址栏仍不含版本段。
|
|
86
87
|
|
|
87
88
|
## 完成定义
|
|
88
89
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
| 组件 | 推荐类型 | 配置项 |
|
|
79
79
|
| --- | --- | --- |
|
|
80
80
|
| `RichText` | `mediumtext` | 富文本内容,图片上传遵循平台上传配置。 |
|
|
81
|
-
| `CodeEditor` | `mediumtext` | `CodeEditor.Height`。 |
|
|
81
|
+
| `CodeEditor` | `mediumtext` | `CodeEditor.Height`;`CodeEditor.DisplayMode=Inline/Dialog`,配置项很多或同页多个代码字段时优先 `Dialog`。 |
|
|
82
82
|
| `JsonTable` | `mediumtext` | JSON 表格展示/编辑,保存结构化 JSON;配置必须写在 `Config.JsonTable`。 |
|
|
83
83
|
| `ImgUpload` | `mediumtext` | `ImgUpload.Limit`、`Multiple`、`Tips`、`MaxCount`、`ShowFileList`、`Preview`、`MaxSize`。 |
|
|
84
84
|
| `FileUpload` | `mediumtext` | `FileUpload.Limit`、`Multiple`、`Tips`、`MaxCount`、`ShowFileList`、`MaxSize`。 |
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
| `StaticText` | 静态文本 | 不承载业务状态 |
|
|
37
37
|
| `Html` | 可信 HTML 展示 | 必须净化,不拼接不可信内容 |
|
|
38
38
|
| `RichText` | 富文本 | `mediumtext`;输出需净化 |
|
|
39
|
-
| `CodeEditor` | 代码/JSON/SQL 编辑 | `mediumtext`;通常 `FormWidth=24` |
|
|
39
|
+
| `CodeEditor` | 代码/JSON/SQL 编辑 | `mediumtext`;通常 `FormWidth=24`;长配置表单使用 `Config.CodeEditor.DisplayMode=Dialog`,默认只渲染“编辑代码(N字)”按钮;代码工作台可用 `Inline` |
|
|
40
40
|
| `JsonTable` | JSON 表格 | `mediumtext`;定义结构与大小上限 |
|
|
41
41
|
|
|
42
42
|
## 文件与高级输入
|
|
@@ -104,6 +104,7 @@ AppKey 稳定且只含安全字符。`microi.routes.json` 是页面事实源,
|
|
|
104
104
|
- 创建/更新元数据:`microi_create_microservice`。
|
|
105
105
|
- 同步私有源码:`microi_sync_microservice_source`;本地工程必须优先传 `directory`,不构造 Base64 文件数组。
|
|
106
106
|
- 真实编译目录优先 `microi_publish_application_directory_stream` 流式发布。
|
|
107
|
+
- 每次创建、修改、升级或重新发布微服务,必须先为目标精确 `AppVersion` 在 `sys_microistore_changelog` 写入完整日志,再同步源码、stage、finalize 或制作商城包。日志必须关联真实 `StoreId`,包含 `Title / ChangeType / Content / ReleaseTime`;流式发布显式传入与日志含义一致的非空 `changeSummary`。发布后同时回读商城日志、`mci_ai_app_version.ChangeSummary` 与包内 `PackageInfo.ChangeLog`;缺失或版本不一致必须失败关闭。
|
|
107
108
|
- 发布动作必须明确区分两种模式:默认“源码+编译产物”先把完整工程同步到私有桶并逐文件回读 SHA-256,再把 `dist` 流式发布到公有桶;显式“仅编译产物”只更新公有桶,必须在界面中告知其他用户仍会拉取上一次私有源码,禁止暗示源码已同步。
|
|
108
109
|
- 私有源码同步使用 `ReplacePrivateSourceOnly` 精确清理过期源码;兼容调用可以继续接受 `replace`,但实现不得用旧式全表 `Replace=true` 删除同一应用的公有运行产物元数据。
|
|
109
110
|
- `mci_ai_app_file` 同时存在私有源码和公有编译产物。源码拉取/差异比较必须优先按 `StorageScope=PublicBuildStream|PublicBuildStreamArchived|PublicBuildOnly` 排除公有产物,并保留旧数据中 `HdfsPath == PublishHdfsPath` 的兼容判断;不能仅靠两个路径相等识别,否则版本路径与稳定别名不同的流式产物会被误读成私有源码。
|
|
@@ -112,10 +112,22 @@ Iframe 不把长期 Token、密码或连接串放 URL。第三方单点登录使
|
|
|
112
112
|
- 查询接口替换、导入/导出替换和跨表动作属于复杂逻辑时,使用接口引擎。
|
|
113
113
|
- 前端按钮只做确认、收集少量参数、调用接口和刷新;事务与最终校验在后端。
|
|
114
114
|
- 预计超过 2 分钟、500 条、1000 个扇出或 100 次外部调用时使用真实后台任务。
|
|
115
|
-
- 不复制官网旧“Redis 文本进度 + 长事务循环”导入示例作为新实现;必须有稳定
|
|
116
|
-
幂等键、业务任务状态、真实 Current/Total、失败恢复和必要的 checkpoint 分片。
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
- 不复制官网旧“Redis 文本进度 + 长事务循环”导入示例作为新实现;必须有稳定
|
|
116
|
+
幂等键、业务任务状态、真实 Current/Total、失败恢复和必要的 checkpoint 分片。
|
|
117
|
+
|
|
118
|
+
### 菜单启动查询与字段元数据兼容
|
|
119
|
+
|
|
120
|
+
- 菜单树是登录后的启动控制面。读取 `sys_menu` 时,不能把浏览器传入的
|
|
121
|
+
`_SelectFields` 直接交给依赖 `diy_field` 的通用查询投影:旧库、空库或升级后缓存
|
|
122
|
+
未同步时,物理列仍存在但元数据可能不完整,查询结果会退化成只含固定字段 `Id`。
|
|
123
|
+
- 服务端应先在已完成登录、租户与角色菜单范围校验的可信边界读取物理菜单行,再在内存中
|
|
124
|
+
按请求字段投影;构建树所需的 `Id/ParentId/Sort` 必须保留。可信标记不得由浏览器 JSON
|
|
125
|
+
绑定,不能借此绕过菜单、角色或数据权限。
|
|
126
|
+
- 底层菜单查询失败必须原样返回失败,禁止把失败结果转换成 `Code=1` 的空菜单。回归测试至少
|
|
127
|
+
覆盖“不向表单引擎下传显式投影”“字段名大小写兼容”“投影仍保留树字段”“未指定字段时
|
|
128
|
+
保留物理行”。
|
|
129
|
+
|
|
130
|
+
## 跨端 ViewSchema
|
|
119
131
|
|
|
120
132
|
顶层 PC 数据列表默认使用紧凑的新模块标题样式;即使未启用自定义表单视图,也不能退回无标题的旧外观。无指标头部固定 `44px`、含指标头部固定 `62px`,连同间距总纵向占用约 `50px / 68px`。子表、关联表、嵌入表不重复显示,移动端由固定导航栏承载标题。`Scene=List/Card` 的个性化标题、指标、复合列和卡片配置存在时必须直接生效;`EnableViewSchema` 只控制 Detail/Edit 自定义表单视图。
|
|
121
133
|
|
|
@@ -150,8 +150,8 @@ return {
|
|
|
150
150
|
| `NumberFormat/HeaderStyle/Style` | 数字格式与列级样式 |
|
|
151
151
|
|
|
152
152
|
<!-- /microi-progressive:chunk -->
|
|
153
|
-
<!-- microi-progressive:chunk id=v8-export-import-003 sha256=
|
|
154
|
-
## 解析上传的 Excel(导入)
|
|
153
|
+
<!-- microi-progressive:chunk id=v8-export-import-003 sha256=51a6c3e7a44ca35dc17b5536545ac7a1d47a8915045283841d304a04c04cd219 -->
|
|
154
|
+
## 解析上传的 Excel / CSV(导入)
|
|
155
155
|
|
|
156
156
|
```javascript
|
|
157
157
|
// 接口引擎接收 V8.FilesByteBase64
|
|
@@ -160,24 +160,35 @@ if (!filesByteBase64) return { Code: 0, Msg: '请上传 Excel 文件' };
|
|
|
160
160
|
|
|
161
161
|
var base64 = Object.values(filesByteBase64)[0];
|
|
162
162
|
|
|
163
|
-
//
|
|
164
|
-
var parsed = V8.Office.ExcelToList({
|
|
165
|
-
FileByteBase64: base64,
|
|
166
|
-
SheetIndex: 0
|
|
163
|
+
// 可直接解析旧的首行表头模板
|
|
164
|
+
var parsed = V8.Office.ExcelToList({
|
|
165
|
+
FileByteBase64: base64,
|
|
166
|
+
SheetIndex: 0
|
|
167
167
|
});
|
|
168
168
|
if (parsed.Code !== 1) return parsed;
|
|
169
169
|
|
|
170
|
-
var dataList = parsed.Data; // [{ 列标题: 值, ... }, ...]
|
|
170
|
+
var dataList = parsed.Data; // [{ 列标题: 值, ... }, ...]
|
|
171
171
|
return { Code: 1, Data: dataList, DataCount: dataList.length };
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
+
`ExcelToList` 的增强参数为 `FileType/FileName/Encoding/Delimiter/HeaderStartRow/HeaderEndRow/DataStartRow/DataEndRow/Columns/MaxDataRows/MaxColumns`。CSV 传 `FileType:'csv'` 或 `.csv` 文件名,编码和分隔符通常省略,由服务端自动识别 UTF-8/GBK 与逗号、制表符、分号、竖线;结果通过 `DataAppend.Encoding/Delimiter` 回传实际识别值。除 `SheetIndex` 和 `Columns[].ColumnIndex` 从 `0` 开始外,行号都从 `1` 开始;传增强范围后每行带 `_ExcelRow`。参数全省略时继续兼容“首行表头、第二行开始数据”。
|
|
175
|
+
|
|
174
176
|
### 固定版式模板与后台自定义导入
|
|
175
177
|
|
|
176
|
-
|
|
178
|
+
通用【导入】和 `V8.OpenImportDialog` 共用智能导入弹层:默认宽度 `80%`,支持 `.xls/.xlsx/.csv`,选择文件后自动识别工作表、单行/多级合并表头、首条与末条数据行和字段映射;先按每页 `15` 条预览,用户确认后才写入。数据预览必须独立保留全部源列、源行,不能因为零字段匹配而显示空白;未匹配列头以红色和悬停原因标记。低可信度时必须允许用户人工指定表头/数据起止行和逐列映射。模板顶部图片、标题、说明文字以及 A 列为空的数据行都不能破坏识别。
|
|
179
|
+
|
|
180
|
+
【列映射】后的【原始工作簿】页签必须不依赖解析可信度,直接显示完整工作簿/CSV,包括所有工作表、合并表头、图片、说明、样式和原始数据;该视图只用于核对,不能绕过目标字段映射和服务端校验。
|
|
181
|
+
|
|
182
|
+
`.xlsx` 原始预览必须兼容 OpenPyXL 等生成器使用等价 DrawingML 默认命名空间的锚定图片。只允许在内存中的预览副本规范化 `xdr` 命名空间和关系目标;正式上传、接口引擎和服务端复核始终使用未经改写的原始文件。
|
|
177
183
|
|
|
178
|
-
- 页面 V8
|
|
179
|
-
-
|
|
180
|
-
-
|
|
184
|
+
- 页面 V8 可只声明 `ApiEngineKey`;`Workbook.Cells/Columns/HeaderStartRow/HeaderEndRow/DataStartRow/DataEndRow/KeyField` 均为模板提示或固定约束,不传时自动识别。不得拼上传 DOM、传完整工作簿 Base64 或自行轮询。
|
|
185
|
+
- 弹层必须让用户在 `RollbackAll`(默认,任一错误整批回滚)和 `ContinueOnError`(逐行提交/跳过错误行)之间明确选择;未传时保持旧版 `RollbackAll`。最终值同时写入 `_ImportErrorPolicy` 与 `_ImportMetaJson.ErrorPolicy`,不能由服务端静默改成另一策略。
|
|
186
|
+
- 当前表的唯一配置必须在上传前可读展示:每个 `Unique=1 + Config.Unique.Type=Alone` 字段各自是一条规则,全部 `Type=All` 字段共同组成一条组合规则;无唯一规则时明确警告“只能新增,重复导入可能产生重复数据”。
|
|
187
|
+
- 标准导入的服务端必须从权威 `diy_field` 重新计算唯一规则,不能信任前端快照。任一完整规则命中同一 Id 则按 Id 修改,均未命中则新增;不同规则命中不同 Id、或一条规则命中多条脏数据时按行报冲突,禁止任意选记录或按宽泛条件更新多行。
|
|
188
|
+
- `V8.OpenImportDialog` 后台接口引擎从 `V8.Param._ImportRowsJson`、`_ImportMetaJson`、`_ImportErrorPolicy` 和 `_ImportUniqueRulesJson` 取值;必须重做模板、权限、字段、唯一性和状态校验。v2.2 元数据中的 `UniqueRules` 只用于说明和诊断。
|
|
189
|
+
- 菜单【导入接口替换】仍接收原始文件 `V8.FilesByteBase64`,并接收上述策略/规则元数据。接口引擎必须把元数据里的 `FileType/SheetIndex/HeaderStartRow/HeaderEndRow/DataStartRow/DataEndRow/Columns` 传给 `V8.Office.ExcelToList`,由服务端按同一范围重读原文件;CSV 应省略固定 `Encoding/Delimiter` 让服务端独立识别,再用 `DataAppend` 与元数据交叉复核。不能只信任浏览器预览,也不能固定读取第一行。
|
|
190
|
+
- `RollbackAll` 在首个行错误时返回 `Code != 1`,依靠平台事务整体回滚;`ContinueOnError` 捕获并记录行错误、继续下一行,最后返回 `Code=1` 提交成功行。两种模式都禁止手动 Commit/Rollback;若底层异常使事务不可继续,必须先做整批预校验或使用平台允许的独立幂等行操作。
|
|
191
|
+
- 结果与进度必须分别统计 `Added/Updated/Failed/Errors`;整批回滚后成功、新增、修改数必须归零,不能把已回滚行计作成功。
|
|
181
192
|
- 用 `V8.Method.UpdateBackgroundTask({Current,Total,Msg,Log})` 上报真实校验/写入工作量;未知总量保持不确定进度,不伪造百分比。
|
|
182
193
|
- 业务幂等键使用后台任务 Id 或明确的导入操作 Id;重试前回读批次,避免重复写入。
|
|
183
194
|
|
|
@@ -32,7 +32,7 @@ V8.ClientModel // OsClientModel 的兼容别名,同样是脱敏副本
|
|
|
32
32
|
V8.SysConfig // 当前租户系统配置根对象;不存在 PublicSettings 属性
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
前端 V8 的 `V8.SysConfig` 是匿名 `GetSysConfig`
|
|
35
|
+
前端 V8 的 `V8.SysConfig` 是匿名 `GetSysConfig` 的独立脱敏 `sys_config` 副本;`mci_system_setting` 的任何记录都不会进入浏览器。后端接口引擎与后端 V8 事件的 `V8.SysConfig` 包含当前租户完整、独立的 `sys_config`,后端私密设置统一位于 `V8.SysConfig.ServerPrivateSettings`,其中 Secret 由可信后端解密。两端都不存在 `PublicSettings` 包装层。后端可使用 Secret,但严禁返回前端、写日志或写审计。子租户显式传其它 `OsClient` 仍会被强制改回当前租户。
|
|
36
36
|
|
|
37
37
|
### V8.OsClientModel 常用字段
|
|
38
38
|
|
|
@@ -41,7 +41,7 @@ V8.OsClientModel.SysTitle // 租户系统标题
|
|
|
41
41
|
V8.OsClientModel.DbType // 非敏感数据库类型
|
|
42
42
|
V8.OsClientModel.HDFS // 'Aliyun' / 'MinIO' / 'S3'
|
|
43
43
|
V8.OsClientModel.AliOssPublicDomain // 可公开的文件域名
|
|
44
|
-
//
|
|
44
|
+
// 当前租户自行扩展的业务字段只作存量兼容;新增公开配置使用 sys_config,后端私密参数使用 mci_system_setting
|
|
45
45
|
|
|
46
46
|
// 以下基础设施字段不会注入 V8:
|
|
47
47
|
// DbConn / DbReadConn / AuthSecret
|
|
@@ -55,7 +55,7 @@ V8.OsClientModel.AliOssPublicDomain // 可公开的文件域名
|
|
|
55
55
|
|
|
56
56
|
- 所有可变业务逻辑默认必须由接口引擎编排,包括但不限于租户开通、开库、初始化、归属修复、官网个人中心、付费额度等 SaaS 业务流程。C# 后端只暴露原子 V8 能力,例如建库、导入空库模板、复制 `sys_config`、刷新 SaaS 缓存、补偿回滚、字段兜底等;不要把可变业务分支写死到 Controller 或 `TenantProvisioningService` 这类后端定制代码里。接口引擎缺少能力时,优先扩展 `V8.Method`/V8 引擎原子函数,再由接口引擎调用。
|
|
57
57
|
- 主租户由运行环境决定:优先读取环境变量 `OsClient`,其次读取 `appsettings.json` 的 `AppSettings:OsClient`。只有这条主租户 `sys_osclients` 数据中的平台级字段会作为全局配置生效。
|
|
58
|
-
- API 启动配置只有十项白名单:`OsClient`、`OsClientType`、`OsClientNetwork`、`OsClientDbType`、`OsClientDbConn`、`OsClientRedisHost`、`OsClientRedisPort`、`OsClientRedisPwd`、`OsClientRedisDataBase`、`OsClientDbMongoConn`。除这十项外,部署/节点级运行参数与基础设施秘密从主控 `sys_osclients`
|
|
58
|
+
- 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` 节点或动态名称的环境变量读取。节点身份由平台自动生成。
|
|
59
59
|
- `ASPNETCORE_*`、`DOTNET_*` 仅用于 .NET 宿主;构建、安装、测试、MCP、发布脚本可使用自身进程变量,但 API 生产代码不得把它们当业务配置。新增 SaaS 运行字段必须配套独立或既有 Tab、幂等升级、缓存刷新、敏感字段脱敏、子租户不继承和源码扫描测试。
|
|
60
60
|
- 文件上传的租户业务开关与额度按“当前租户 `sys_osclients` → 代码默认值”解析;平台固定灾难保护、HTTP/Multipart/Form 和反向代理上限不可由租户覆盖,也不要求安装者维护额外上传环境变量。
|
|
61
61
|
- 类似 MQTT 端口、PressureGuard、V8Limits、OrmLimits、StartupLimits、SecurityGuard 这类影响整进程资源的配置,不能让每个子租户各自抬高全局上限。子租户同名隔离字段只能降低自己的并发、等待时间或资源额度,用于隔离弱租户、试用租户或异常租户。
|
|
@@ -147,15 +147,16 @@ var erpUrl = (V8.OsClientNetwork === 'Intranet')
|
|
|
147
147
|
// ❌ 危险:密钥写在代码里,所有租户共用,无法独立轮换
|
|
148
148
|
var ak = 'AKIDxxxxxxxx';
|
|
149
149
|
|
|
150
|
-
// ✅
|
|
151
|
-
var
|
|
150
|
+
// ✅ 浏览器需要判断的开关:使用 sys_config 实体字段
|
|
151
|
+
var giteeEnabled = V8.SysConfig.GiteeLoginEnabled === 1;
|
|
152
152
|
|
|
153
153
|
// ✅ 后端 V8 可读取当前租户 Secret 并直接调用供应商
|
|
154
|
-
var
|
|
154
|
+
var privateSettings = V8.SysConfig.ServerPrivateSettings || {};
|
|
155
|
+
var secret = privateSettings['Login.Gitee.ClientSecret'];
|
|
155
156
|
// 禁止 return secret、console.log(secret) 或写入前端可读字段。
|
|
156
157
|
```
|
|
157
158
|
|
|
158
|
-
> `mci_system_setting`
|
|
159
|
+
> `mci_system_setting` 位于每个租户自己的数据库,只保存不能公开或仅供后端执行的配置;普通值与 Secret 都不会下发浏览器。Secret 保存认证密文,只在后端 V8 的当前租户 `ServerPrivateSettings` 中解密使用。能力开关和入口显示必须建成 `sys_config` 实体字段,禁止通过 `IsPublic` 或其它运行时勾选把私密记录公开。前端 V8、普通 FormEngine HTTP、匿名/访问密钥会话不能读取私密设置,后端 V8 也不能获得通用解密器。`sys_osclients` 自定义业务字段只保留存量兼容;共享基础设施字段由服务端强制移除,不能用自定义同义字段绕过安全代理。
|
|
159
160
|
|
|
160
161
|
## 用户扩展字段访问(同理)
|
|
161
162
|
|
|
@@ -15,7 +15,9 @@ description: Microi V8 安全指南。用于审查 DiyToken 与权限、可逆
|
|
|
15
15
|
<!-- microi-progressive:chunk id=v8-security-000 sha256=0346bd9ca3fe98dd2b589d1777aa1e55cd24cf7dedc69ee65d4ca91461db7dee -->
|
|
16
16
|
## 0. 租户动态系统设置与密钥边界
|
|
17
17
|
|
|
18
|
-
第三方密钥(微信、支付宝、OpenAI、阿里云、ERP、SMTP)**禁止**硬编码在 V8 代码或前端。公开的租户配置必须建成当前租户 `sys_config` 的实体字段;敏感或仅供后端使用的租户业务配置保存到 `mci_system_setting`。数据库、Redis、MongoDB、MinIO、MQ 等部署控制面仍由主库 `sys_osclients` 托管,子租户不能修改。
|
|
18
|
+
第三方密钥(微信、支付宝、OpenAI、阿里云、ERP、SMTP)**禁止**硬编码在 V8 代码或前端。公开的租户配置必须建成当前租户 `sys_config` 的实体字段;敏感或仅供后端使用的租户业务配置保存到 `mci_system_setting`。数据库、Redis、MongoDB、MinIO、MQ 等部署控制面仍由主库 `sys_osclients` 托管,子租户不能修改。
|
|
19
|
+
|
|
20
|
+
能力是否启用、入口是否显示、公开交互模式等 Bool/Enum 配置即使属于登录或第三方集成,也必须放在 `sys_config`;API Key、ClientSecret、RP ID、Origin、Issuer、Scope、供应商地址等后端参数才放在 `mci_system_setting`。禁止给同一个新配置双写两张表。迁移旧开关时采用“新 `sys_config` 显式值 → 旧私密 Key → 存量安全默认”的只读回退,并从私密设置的列表、保存和删除入口移除旧 Key。
|
|
19
21
|
|
|
20
22
|
```javascript
|
|
21
23
|
// ✅ 浏览器/前端 V8 只读取 sys_config 的浏览器安全投影
|