@microi.net/cli 6.0.1 → 6.0.3
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 +81 -81
- package/scripts/microi-cli.js +60 -59
- package/scripts/microi-skills.meta.json +220 -220
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +64 -64
- package/skills/microi-client-frontend/SKILL.md +252 -244
- package/skills/microi-codex/SKILL.md +130 -116
- package/skills/microi-codex-installer/SKILL.md +4 -2
- package/skills/microi-deployment/SKILL.md +1 -1
- package/skills/microi-microservice/SKILL.md +5 -1
- package/skills/microi-system-delivery/SKILL.md +100 -98
- package/skills/microi-system-delivery/references/progressive-02-/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/345/277/205/351/241/273/350/246/206/347/233/226/347/232/204/345/235/221.md +2 -2
- package/skills/page-engine/SKILL.md +215 -211
- package/skills/playwright-e2e/SKILL.md +4 -2
- package/skills/v8-cache-pattern/SKILL.md +8 -4
- package/skills/v8-export-import/SKILL.md +5 -0
- package/skills/v8-file-upload/SKILL.md +4 -0
- package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +10 -2
- package/skills/workspace-conventions/SKILL.md +119 -102
- package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +4 -3
- package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +4 -4
- package/skills/workspace-conventions/references/progressive-04-/345/267/245/344/275/234/345/214/272/344/270/216/345/271/263/345/217/260/344/270/223/351/241/271/344/277/235/351/232/234.md +6 -4
|
@@ -1,222 +1,226 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: page-engine
|
|
3
|
-
description: 生成和审查 Microi 界面引擎 Page Engine 页面 JSON。用于创建仪表盘、图表、表格、地图、组件、页面布局,或校验 mic_page formData JSON。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
> **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
|
|
7
|
-
|
|
8
|
-
# Microi 界面引擎(Page Engine)页面 JSON 生成
|
|
9
|
-
|
|
10
|
-
MCP 的生成/保存入口包含 `microi_build_page_design` 与 `microi_save_page_design`;版本治理入口见下文。保存工具写入后仍需回读 `mic_page`,不能把生成成功当作持久化成功。
|
|
11
|
-
|
|
12
|
-
## 完整 AI 开发默认报表(强制)
|
|
13
|
-
|
|
14
|
-
完整系统开发必须按需求主动设计基于界面引擎的业务统计、老板经营与领导管理报表,
|
|
15
|
-
先把相应入口纳入菜单骨架,再接通真实数据并验收。详细范围与菜单切换流程读取
|
|
16
|
-
[菜单先行与经营报表增补要求](../microi-system-delivery/references/progressive-01-标准工作流.md#菜单先行与经营报表增补要求强制)。
|
|
17
|
-
局部修改只覆盖相关页面/指标,只读分析不创建页面。用户明确的范围优先。
|
|
18
|
-
|
|
19
|
-
- 每个报表服务明确角色与决策任务;业务运营覆盖状态、趋势、效率与异常,经营视角覆盖
|
|
20
|
-
有真实来源的规模、收支/利润、回款及风险,管理视角覆盖部门/人员、任务进展、待办/逾期、
|
|
21
|
-
利用与达成。不存在数据来源的指标标为不适用并说明,不能编造成本、利润或业绩。
|
|
22
|
-
- 每个指标定义来源、公式、单位/精度、状态、时间字段/时区/边界、比较基期、维度与角色。
|
|
23
|
-
当前租户真实数据通过服务端批量聚合,时间/维度筛选和用户数据权限必须在接口中生效。
|
|
24
|
-
禁止演示值、本页合计冒充全量或前端拉取全量明细再统计。
|
|
25
|
-
- 页面包含周期/维度筛选、加载、零/空、无权限和失败状态;请求失败不能显示为正常零值。
|
|
26
|
-
指标/图表穿透到同一口径的授权明细并对账。界面引擎负责看板和图表,分页查询/导出明细
|
|
27
|
-
配合 `report-engine`;列表汇总、Hero 和角标不能替代决策报表。
|
|
28
|
-
- 保存后回读 `mic_page`、菜单绑定、数据源/接口;以管理员、经营/管理角色、普通与无权限
|
|
29
|
-
角色验证时间边界、筛选、隔离、零/空/失败和明细对账,完成电脑/移动、明暗主题截图。
|
|
30
|
-
先从已授权候选入口独立预验收,再以回读后的真实页面 Id 切换同一菜单并按打开方式设置标记,
|
|
31
|
-
保留菜单身份与权限;从原 URL 最终验收,失败精确恢复本菜单原绑定并记录未完成。
|
|
32
|
-
|
|
33
|
-
## 紧凑报表与平台主题
|
|
34
|
-
|
|
35
|
-
需要“主指标 + 多行次指标 + 图表 + 排行表 + 资讯”布局时,可读取 [examples/compact-dashboard.json](examples/compact-dashboard.json) 作为原生组件示例。示例数值仅用于视觉排版,交付业务报表前必须替换为当前租户的真实数据源。
|
|
36
|
-
|
|
37
|
-
默认使用 `formConfig.themeMode="system"`、`density="compact"`,卡片背景使用 `var(--el-bg-color)`、页面背景使用 `var(--el-bg-color-page)`,正文使用平台文字变量。不要因为“驾驶舱/大屏”字样就强制深色。显式指定浅色或深色时才使用 `light/dark`。
|
|
38
|
-
|
|
39
|
-
运行容器默认 `wrapperOption.heightMode="content"`,`height` 只保留设计器布局尺寸。仅需固定图形画布时设为 `fixed`;图表自身可以设置 240–320px 高度。不要为一行指标、标题、三行表格预留 200–400px 空容器。紧凑布局的卡片间距建议 10–16px,面板内边距 8–16px;统计组件 `widgetParams[24].value` 支持 `summary`(图标和大数字)、`detail`(多行小指标)及兼容的 `cards`。
|
|
40
|
-
|
|
41
|
-
用户提供参考截图时,先拆出面板行列比例、指标主次、图表与表格高度、标题/筛选位置,再用原生 pannel/statistic/chart/tabel/list 组合配置。不能把所有指标替换成一排大色块,也不能用模板假数据声称已接通客户业务。必须用同一视口截图检查浅色和平台深色,检查页面缩放后的断行与纵向空白;布局接近度和真实数据绑定分别验收。
|
|
42
|
-
|
|
43
|
-
### 平台智能工作首页
|
|
44
|
-
|
|
45
|
-
保留 `aiengine` 原生 AI 创作中心,与 `homeoverview` 工作概览组成桌面首行 14/10 栅格;任务、月历与公告放在下一行,移动端自动切为 24 栅格。不能把任务的完整 15 行列表或未配置日程的大空白块直接堆在首屏。
|
|
46
|
-
|
|
47
|
-
- `homeoverview.widgetParams[0]` 为真实个人统计接口 `platform-home-overview`,`[1].value="compact"` 启用紧凑指标、常用应用与趋势;所有数据仍受当前用户权限过滤。
|
|
48
|
-
- `workcenter.widgetParams` 按索引依次为视图、工作菜单、流程菜单、公告菜单、日历菜单、分页条数;`[5].value=5` 用于首页任务,范围 5–20,不能影响独立“我的工作”页面。
|
|
49
|
-
- `diycalendar.widgetParams[0].value` 应绑定当前账号可访问、关联 `microi_calendar` 的真实模块 Id;`[1].value="compact"` 只压缩月历密度,仍须支持点击日期新增及事件编辑、删除。备注字段为 `Remark`。隐藏菜单也可以是授权入口,旧模块可能没有 `DiyTableName`,此时必须显式绑定实际模块,不把只读日期回退当作日程验收成功。无模块时展示本月日期并明确提示未配置,不伪造任务。
|
|
50
|
-
- `diytable.widgetParams[4].value="NoticeList"` 以只读公告列表展示真实授权公告;表、菜单仍在 `[0]/[1]`,模块缺失时使用 `referencePolicy.onMissing="RemoveWidget"` 移除可选组件及空容器。
|
|
51
|
-
- 修改已有首页前通过 MCP 读取页面及历史 `CurrentHash`,保存传 `expectedCurrentHash`,再回读 JSON 和历史,保留可恢复版本。
|
|
52
|
-
|
|
53
|
-
你正在为 Microi 吾码平台生成界面引擎页面的 JSON 数据。界面引擎页面由 `formData` 对象描述,用户导入 JSON 即可使用。
|
|
54
|
-
|
|
1
|
+
---
|
|
2
|
+
name: page-engine
|
|
3
|
+
description: 生成和审查 Microi 界面引擎 Page Engine 页面 JSON。用于创建仪表盘、图表、表格、地图、组件、页面布局,或校验 mic_page formData JSON。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
|
|
7
|
+
|
|
8
|
+
# Microi 界面引擎(Page Engine)页面 JSON 生成
|
|
9
|
+
|
|
10
|
+
MCP 的生成/保存入口包含 `microi_build_page_design` 与 `microi_save_page_design`;版本治理入口见下文。保存工具写入后仍需回读 `mic_page`,不能把生成成功当作持久化成功。
|
|
11
|
+
|
|
12
|
+
## 完整 AI 开发默认报表(强制)
|
|
13
|
+
|
|
14
|
+
完整系统开发必须按需求主动设计基于界面引擎的业务统计、老板经营与领导管理报表,
|
|
15
|
+
先把相应入口纳入菜单骨架,再接通真实数据并验收。详细范围与菜单切换流程读取
|
|
16
|
+
[菜单先行与经营报表增补要求](../microi-system-delivery/references/progressive-01-标准工作流.md#菜单先行与经营报表增补要求强制)。
|
|
17
|
+
局部修改只覆盖相关页面/指标,只读分析不创建页面。用户明确的范围优先。
|
|
18
|
+
|
|
19
|
+
- 每个报表服务明确角色与决策任务;业务运营覆盖状态、趋势、效率与异常,经营视角覆盖
|
|
20
|
+
有真实来源的规模、收支/利润、回款及风险,管理视角覆盖部门/人员、任务进展、待办/逾期、
|
|
21
|
+
利用与达成。不存在数据来源的指标标为不适用并说明,不能编造成本、利润或业绩。
|
|
22
|
+
- 每个指标定义来源、公式、单位/精度、状态、时间字段/时区/边界、比较基期、维度与角色。
|
|
23
|
+
当前租户真实数据通过服务端批量聚合,时间/维度筛选和用户数据权限必须在接口中生效。
|
|
24
|
+
禁止演示值、本页合计冒充全量或前端拉取全量明细再统计。
|
|
25
|
+
- 页面包含周期/维度筛选、加载、零/空、无权限和失败状态;请求失败不能显示为正常零值。
|
|
26
|
+
指标/图表穿透到同一口径的授权明细并对账。界面引擎负责看板和图表,分页查询/导出明细
|
|
27
|
+
配合 `report-engine`;列表汇总、Hero 和角标不能替代决策报表。
|
|
28
|
+
- 保存后回读 `mic_page`、菜单绑定、数据源/接口;以管理员、经营/管理角色、普通与无权限
|
|
29
|
+
角色验证时间边界、筛选、隔离、零/空/失败和明细对账,完成电脑/移动、明暗主题截图。
|
|
30
|
+
先从已授权候选入口独立预验收,再以回读后的真实页面 Id 切换同一菜单并按打开方式设置标记,
|
|
31
|
+
保留菜单身份与权限;从原 URL 最终验收,失败精确恢复本菜单原绑定并记录未完成。
|
|
32
|
+
|
|
33
|
+
## 紧凑报表与平台主题
|
|
34
|
+
|
|
35
|
+
需要“主指标 + 多行次指标 + 图表 + 排行表 + 资讯”布局时,可读取 [examples/compact-dashboard.json](examples/compact-dashboard.json) 作为原生组件示例。示例数值仅用于视觉排版,交付业务报表前必须替换为当前租户的真实数据源。
|
|
36
|
+
|
|
37
|
+
默认使用 `formConfig.themeMode="system"`、`density="compact"`,卡片背景使用 `var(--el-bg-color)`、页面背景使用 `var(--el-bg-color-page)`,正文使用平台文字变量。不要因为“驾驶舱/大屏”字样就强制深色。显式指定浅色或深色时才使用 `light/dark`。
|
|
38
|
+
|
|
39
|
+
运行容器默认 `wrapperOption.heightMode="content"`,`height` 只保留设计器布局尺寸。仅需固定图形画布时设为 `fixed`;图表自身可以设置 240–320px 高度。不要为一行指标、标题、三行表格预留 200–400px 空容器。紧凑布局的卡片间距建议 10–16px,面板内边距 8–16px;统计组件 `widgetParams[24].value` 支持 `summary`(图标和大数字)、`detail`(多行小指标)及兼容的 `cards`。
|
|
40
|
+
|
|
41
|
+
用户提供参考截图时,先拆出面板行列比例、指标主次、图表与表格高度、标题/筛选位置,再用原生 pannel/statistic/chart/tabel/list 组合配置。不能把所有指标替换成一排大色块,也不能用模板假数据声称已接通客户业务。必须用同一视口截图检查浅色和平台深色,检查页面缩放后的断行与纵向空白;布局接近度和真实数据绑定分别验收。
|
|
42
|
+
|
|
43
|
+
### 平台智能工作首页
|
|
44
|
+
|
|
45
|
+
保留 `aiengine` 原生 AI 创作中心,与 `homeoverview` 工作概览组成桌面首行 14/10 栅格;任务、月历与公告放在下一行,移动端自动切为 24 栅格。不能把任务的完整 15 行列表或未配置日程的大空白块直接堆在首屏。
|
|
46
|
+
|
|
47
|
+
- `homeoverview.widgetParams[0]` 为真实个人统计接口 `platform-home-overview`,`[1].value="compact"` 启用紧凑指标、常用应用与趋势;所有数据仍受当前用户权限过滤。
|
|
48
|
+
- `workcenter.widgetParams` 按索引依次为视图、工作菜单、流程菜单、公告菜单、日历菜单、分页条数;`[5].value=5` 用于首页任务,范围 5–20,不能影响独立“我的工作”页面。
|
|
49
|
+
- `diycalendar.widgetParams[0].value` 应绑定当前账号可访问、关联 `microi_calendar` 的真实模块 Id;`[1].value="compact"` 只压缩月历密度,仍须支持点击日期新增及事件编辑、删除。备注字段为 `Remark`。隐藏菜单也可以是授权入口,旧模块可能没有 `DiyTableName`,此时必须显式绑定实际模块,不把只读日期回退当作日程验收成功。无模块时展示本月日期并明确提示未配置,不伪造任务。
|
|
50
|
+
- `diytable.widgetParams[4].value="NoticeList"` 以只读公告列表展示真实授权公告;表、菜单仍在 `[0]/[1]`,模块缺失时使用 `referencePolicy.onMissing="RemoveWidget"` 移除可选组件及空容器。
|
|
51
|
+
- 修改已有首页前通过 MCP 读取页面及历史 `CurrentHash`,保存传 `expectedCurrentHash`,再回读 JSON 和历史,保留可恢复版本。
|
|
52
|
+
|
|
53
|
+
你正在为 Microi 吾码平台生成界面引擎页面的 JSON 数据。界面引擎页面由 `formData` 对象描述,用户导入 JSON 即可使用。
|
|
54
|
+
|
|
55
55
|
<!-- microi-progressive:begin -->
|
|
56
|
-
<!-- microi-progressive:chunk id=page-engine-000 sha256=
|
|
57
|
-
## 设计器源码事件
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
<!-- microi-progressive:chunk id=page-engine-000 sha256=44233416e71cc6ff3712347308dadd26e55b14938b81b8a2401478c6bc14921b -->
|
|
57
|
+
## 设计器源码事件
|
|
58
|
+
|
|
59
|
+
设计器选中事件只能在 `isDesignMode` 为真时执行 `preventDefault/stopPropagation` 和选中状态写入。运行和预览页面必须保留 HTML 链接、按钮与键盘激活的浏览器默认行为。链接使用实际授权菜单路由,保存后逐一点击验证,不以 JSON 中存在 href 代替跳转验收;回归入口为 `Microi.Client/tests/page-engine-runtime-click.spec.mjs`。
|
|
60
|
+
|
|
61
|
+
每个顶层运行路由必须创建并 provide 独立的 `PAGE_ENGINE_STORE_KEY`,其子组件通过 `usePageEngineStore` 使用当前渲染树的状态。不能让多个 KeepAlive 页面共用应用级 `formData`,否则迟到响应和页面激活会导致错页;验收需覆盖地图与看板往返、迟到响应、移动布局和主题隔离。
|
|
62
|
+
|
|
63
|
+
只有在扩展界面引擎设计器源码时才使用全局事件总线。用
|
|
64
|
+
`EventBus.on(eventName, handler)` 监听保存、日期选择或组件跳转等事件,并在
|
|
65
|
+
组件卸载时逐项调用 `EventBus.off(eventName)`。重复挂载不解绑会造成一次操作
|
|
66
|
+
触发多次;普通页面 JSON 生成不需要注册事件总线。
|
|
67
|
+
|
|
64
68
|
<!-- /microi-progressive:chunk -->
|
|
65
|
-
<!-- microi-progressive:chunk id=page-engine-001 sha256=
|
|
66
|
-
## 核心数据结构
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
formData(页面)
|
|
70
|
-
├── Id: string // 页面唯一ID(GUID)
|
|
71
|
-
├── Title: string // 页面标题
|
|
72
|
-
├── Number: string // 页面编号(如 PAGE1)
|
|
73
|
-
├── Desc: string // 页面描述
|
|
74
|
-
└── JsonObj
|
|
75
|
-
├── formConfig // 页面全局配置
|
|
76
|
-
└── wrapperList[] // 容器列表
|
|
77
|
-
├── wrapperOption // 容器配置
|
|
78
|
-
└── widgetList[] // 组件列表
|
|
79
|
-
├── widgetOption // 组件通用配置
|
|
80
|
-
└── widgetParams[] // 组件私有参数
|
|
81
|
-
```
|
|
82
|
-
|
|
69
|
+
<!-- microi-progressive:chunk id=page-engine-001 sha256=57717991f9c7de557b524ec36fc9bdd716080b71b682439f1b6662b0b9b2f45c -->
|
|
70
|
+
## 核心数据结构
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
formData(页面)
|
|
74
|
+
├── Id: string // 页面唯一ID(GUID)
|
|
75
|
+
├── Title: string // 页面标题
|
|
76
|
+
├── Number: string // 页面编号(如 PAGE1)
|
|
77
|
+
├── Desc: string // 页面描述
|
|
78
|
+
└── JsonObj
|
|
79
|
+
├── formConfig // 页面全局配置
|
|
80
|
+
└── wrapperList[] // 容器列表
|
|
81
|
+
├── wrapperOption // 容器配置
|
|
82
|
+
└── widgetList[] // 组件列表
|
|
83
|
+
├── widgetOption // 组件通用配置
|
|
84
|
+
└── widgetParams[] // 组件私有参数
|
|
85
|
+
```
|
|
86
|
+
|
|
83
87
|
<!-- /microi-progressive:chunk -->
|
|
84
|
-
<!-- microi-progressive:chunk id=page-engine-002 sha256=
|
|
85
|
-
## formConfig 页面全局配置
|
|
86
|
-
|
|
87
|
-
```json
|
|
88
|
-
{
|
|
89
|
-
"gutter": 0, "mask": true, "drag": true, "left": true,
|
|
90
|
-
"hover": true, "shadow": true, "link": false, "watermark": false,
|
|
91
|
-
"mobile": false, "dark": false, "autoRefresh": 0, "lastRefreshTime": "",
|
|
92
|
-
"watermarkStyle": {
|
|
93
|
-
"content": "Microi吾码",
|
|
94
|
-
"font": { "fontSize": 16, "color": "rgba(255, 0, 0, 0.15)" },
|
|
95
|
-
"rotate": -22
|
|
96
|
-
},
|
|
97
|
-
"dynamicStyle": { "padding": "4px", "backgroundColor": "", "opacity": 1 }
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
88
|
+
<!-- microi-progressive:chunk id=page-engine-002 sha256=6a1a85b40bcb5f8be694b0aad73a805083acd0d6fcd9cba36278dc9b51d24dcb -->
|
|
89
|
+
## formConfig 页面全局配置
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"gutter": 0, "mask": true, "drag": true, "left": true,
|
|
94
|
+
"hover": true, "shadow": true, "link": false, "watermark": false,
|
|
95
|
+
"mobile": false, "dark": false, "autoRefresh": 0, "lastRefreshTime": "",
|
|
96
|
+
"watermarkStyle": {
|
|
97
|
+
"content": "Microi吾码",
|
|
98
|
+
"font": { "fontSize": 16, "color": "rgba(255, 0, 0, 0.15)" },
|
|
99
|
+
"rotate": -22
|
|
100
|
+
},
|
|
101
|
+
"dynamicStyle": { "padding": "4px", "backgroundColor": "", "opacity": 1 }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
101
105
|
<!-- /microi-progressive:chunk -->
|
|
102
|
-
<!-- microi-progressive:chunk id=page-engine-003 sha256=
|
|
103
|
-
## 容器类型
|
|
104
|
-
|
|
105
|
-
| type | label | 说明 |
|
|
106
|
-
|------|-------|------|
|
|
107
|
-
| `pannel` | 卡片 | 标准容器,包含一个 `widgetList` |
|
|
108
|
-
| `tabs` | 选项卡 | 多标签页容器,使用 `tabWidgetMap` 存储每个 tab 的组件 |
|
|
109
|
-
|
|
110
|
-
### 卡片容器(pannel)关键字段
|
|
111
|
-
|
|
112
|
-
```json
|
|
113
|
-
{
|
|
114
|
-
"type": "pannel",
|
|
115
|
-
"label": "卡片",
|
|
116
|
-
"hidden": false,
|
|
117
|
-
"wrapperOption": {
|
|
118
|
-
"number": 10001, // 随机5位整数,页面内唯一
|
|
119
|
-
"span": 12, // 栅格宽度(1-24,24=满宽)
|
|
120
|
-
"height": 300, // 容器高度(px)
|
|
121
|
-
"margin": "0px 10px 10px 0px",
|
|
122
|
-
"dynamicStyle": { "padding": "10px", "backgroundColor": "" },
|
|
123
|
-
"titleOption": {
|
|
124
|
-
"hidden": true, // true=隐藏标题
|
|
125
|
-
"title": "未命名",
|
|
126
|
-
"dynamicStyle": { "textAlign": "left", "padding": "0px", "height": "20px", "lineHeight": "20px", "fontSize": "14px", "color": "" },
|
|
127
|
-
"moreOption": { "hidden": true, "icon": "More", "iconShow": false, "text": "更多", "linkurl": "/", "linktype": "router", "refresh": "0", "datetime": "0", "autotime": false, "autotimeval": 1, "dynamicStyle": { "color": "", "fontSize": "12px" } }
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
"widgetList": []
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### 选项卡容器(tabs)
|
|
135
|
-
|
|
136
|
-
组件放在 `tabWidgetMap[tabKey][]` 中,**不**放在 `widgetList` 中。
|
|
137
|
-
|
|
138
|
-
```json
|
|
139
|
-
{
|
|
140
|
-
"type": "tabs",
|
|
141
|
-
"wrapperOption": {
|
|
142
|
-
"number": 10002, "span": 24, "height": 400,
|
|
143
|
-
"tabType": "", // '' | 'card' | 'border-card'
|
|
144
|
-
"tabPosition": "top", // 'top' | 'right' | 'bottom' | 'left'
|
|
145
|
-
"tabs": [
|
|
146
|
-
{ "key": "tab_1", "label": "标签页1" },
|
|
147
|
-
{ "key": "tab_2", "label": "标签页2" }
|
|
148
|
-
],
|
|
149
|
-
"activeTab": "tab_1"
|
|
150
|
-
},
|
|
151
|
-
"tabWidgetMap": { "tab_1": [], "tab_2": [] },
|
|
152
|
-
"widgetList": []
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
106
|
+
<!-- microi-progressive:chunk id=page-engine-003 sha256=8afb75b0e4d85ab9f56a7ba018e1cc50f758d3e604683d8c38d5f0a45d53f135 -->
|
|
107
|
+
## 容器类型
|
|
108
|
+
|
|
109
|
+
| type | label | 说明 |
|
|
110
|
+
|------|-------|------|
|
|
111
|
+
| `pannel` | 卡片 | 标准容器,包含一个 `widgetList` |
|
|
112
|
+
| `tabs` | 选项卡 | 多标签页容器,使用 `tabWidgetMap` 存储每个 tab 的组件 |
|
|
113
|
+
|
|
114
|
+
### 卡片容器(pannel)关键字段
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"type": "pannel",
|
|
119
|
+
"label": "卡片",
|
|
120
|
+
"hidden": false,
|
|
121
|
+
"wrapperOption": {
|
|
122
|
+
"number": 10001, // 随机5位整数,页面内唯一
|
|
123
|
+
"span": 12, // 栅格宽度(1-24,24=满宽)
|
|
124
|
+
"height": 300, // 容器高度(px)
|
|
125
|
+
"margin": "0px 10px 10px 0px",
|
|
126
|
+
"dynamicStyle": { "padding": "10px", "backgroundColor": "" },
|
|
127
|
+
"titleOption": {
|
|
128
|
+
"hidden": true, // true=隐藏标题
|
|
129
|
+
"title": "未命名",
|
|
130
|
+
"dynamicStyle": { "textAlign": "left", "padding": "0px", "height": "20px", "lineHeight": "20px", "fontSize": "14px", "color": "" },
|
|
131
|
+
"moreOption": { "hidden": true, "icon": "More", "iconShow": false, "text": "更多", "linkurl": "/", "linktype": "router", "refresh": "0", "datetime": "0", "autotime": false, "autotimeval": 1, "dynamicStyle": { "color": "", "fontSize": "12px" } }
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"widgetList": []
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 选项卡容器(tabs)
|
|
139
|
+
|
|
140
|
+
组件放在 `tabWidgetMap[tabKey][]` 中,**不**放在 `widgetList` 中。
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"type": "tabs",
|
|
145
|
+
"wrapperOption": {
|
|
146
|
+
"number": 10002, "span": 24, "height": 400,
|
|
147
|
+
"tabType": "", // '' | 'card' | 'border-card'
|
|
148
|
+
"tabPosition": "top", // 'top' | 'right' | 'bottom' | 'left'
|
|
149
|
+
"tabs": [
|
|
150
|
+
{ "key": "tab_1", "label": "标签页1" },
|
|
151
|
+
{ "key": "tab_2", "label": "标签页2" }
|
|
152
|
+
],
|
|
153
|
+
"activeTab": "tab_1"
|
|
154
|
+
},
|
|
155
|
+
"tabWidgetMap": { "tab_1": [], "tab_2": [] },
|
|
156
|
+
"widgetList": []
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
156
160
|
<!-- /microi-progressive:chunk -->
|
|
157
|
-
<!-- microi-progressive:chunk id=page-engine-004 sha256=
|
|
158
|
-
## 组件通用结构
|
|
159
|
-
|
|
160
|
-
```json
|
|
161
|
-
{
|
|
162
|
-
"type": "bar",
|
|
163
|
-
"label": "柱状图",
|
|
164
|
-
"category": 0,
|
|
165
|
-
"show": 1,
|
|
166
|
-
"widgetOption": {
|
|
167
|
-
"number": 20001, // 随机5位整数,页面内唯一
|
|
168
|
-
"wrapperNumber": 10001, // 必须等于所在容器的 wrapperOption.number
|
|
169
|
-
"span": 24,
|
|
170
|
-
"height": 280,
|
|
171
|
-
"dynamicStyle": { "padding": "8px", "backgroundColor": "" }
|
|
172
|
-
},
|
|
173
|
-
"widgetParams": []
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
161
|
+
<!-- microi-progressive:chunk id=page-engine-004 sha256=9c7c85bb0d25267d07e45d9abeb5691c2f0ffd9da233351cf2f448a9b848cea5 -->
|
|
162
|
+
## 组件通用结构
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"type": "bar",
|
|
167
|
+
"label": "柱状图",
|
|
168
|
+
"category": 0,
|
|
169
|
+
"show": 1,
|
|
170
|
+
"widgetOption": {
|
|
171
|
+
"number": 20001, // 随机5位整数,页面内唯一
|
|
172
|
+
"wrapperNumber": 10001, // 必须等于所在容器的 wrapperOption.number
|
|
173
|
+
"span": 24,
|
|
174
|
+
"height": 280,
|
|
175
|
+
"dynamicStyle": { "padding": "8px", "backgroundColor": "" }
|
|
176
|
+
},
|
|
177
|
+
"widgetParams": []
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
177
181
|
<!-- /microi-progressive:chunk -->
|
|
178
|
-
<!-- microi-progressive:chunk id=page-engine-005 sha256=
|
|
179
|
-
## widgetParams 参数类型
|
|
180
|
-
|
|
181
|
-
| type | 说明 | value 类型 |
|
|
182
|
-
|------|------|-----------|
|
|
183
|
-
| `textarea` | 多行文本(数据来源) | `string` |
|
|
184
|
-
| `input` | 单行文本 | `string` |
|
|
185
|
-
| `number` | 数字 | `number` |
|
|
186
|
-
| `switch` | 开关 | `boolean` |
|
|
187
|
-
| `slider` | 滑块 | `number` |
|
|
188
|
-
| `color` | 颜色选择 | `string` |
|
|
189
|
-
| `select` | 下拉选择 | `string` |
|
|
190
|
-
| `radio` | 单选组 | `string` |
|
|
191
|
-
|
|
182
|
+
<!-- microi-progressive:chunk id=page-engine-005 sha256=c1e31dede775f14761182fc197cf428d494806c4503a622ffdad3d16d091d3e0 -->
|
|
183
|
+
## widgetParams 参数类型
|
|
184
|
+
|
|
185
|
+
| type | 说明 | value 类型 |
|
|
186
|
+
|------|------|-----------|
|
|
187
|
+
| `textarea` | 多行文本(数据来源) | `string` |
|
|
188
|
+
| `input` | 单行文本 | `string` |
|
|
189
|
+
| `number` | 数字 | `number` |
|
|
190
|
+
| `switch` | 开关 | `boolean` |
|
|
191
|
+
| `slider` | 滑块 | `number` |
|
|
192
|
+
| `color` | 颜色选择 | `string` |
|
|
193
|
+
| `select` | 下拉选择 | `string` |
|
|
194
|
+
| `radio` | 单选组 | `string` |
|
|
195
|
+
|
|
192
196
|
<!-- /microi-progressive:chunk -->
|
|
193
|
-
<!-- microi-progressive:chunk id=page-engine-006 sha256=
|
|
194
|
-
## 数据来源(widgetParams[0])
|
|
195
|
-
|
|
196
|
-
大多数组件的第一个参数(sort=0)是"数据来源":
|
|
197
|
-
- **静态数据**:`value` 为空,数据在 `typeOptions.dataJson` 中
|
|
198
|
-
- **动态接口**:`value` 设为接口地址,运行时请求替换 `dataJson`
|
|
199
|
-
|
|
200
|
-
**接口引擎地址格式:**
|
|
201
|
-
```
|
|
202
|
-
$ApiBase$/apiengine/{ApiEngineKey}
|
|
203
|
-
```
|
|
204
|
-
|
|
197
|
+
<!-- microi-progressive:chunk id=page-engine-006 sha256=2c021e43f4f0d730da1f5a5cf6a2d314589435e2bb9ff573148c787538a8cc86 -->
|
|
198
|
+
## 数据来源(widgetParams[0])
|
|
199
|
+
|
|
200
|
+
大多数组件的第一个参数(sort=0)是"数据来源":
|
|
201
|
+
- **静态数据**:`value` 为空,数据在 `typeOptions.dataJson` 中
|
|
202
|
+
- **动态接口**:`value` 设为接口地址,运行时请求替换 `dataJson`
|
|
203
|
+
|
|
204
|
+
**接口引擎地址格式:**
|
|
205
|
+
```
|
|
206
|
+
$ApiBase$/apiengine/{ApiEngineKey}
|
|
207
|
+
```
|
|
208
|
+
|
|
205
209
|
<!-- /microi-progressive:chunk -->
|
|
206
|
-
<!-- microi-progressive:chunk id=page-engine-007 sha256=
|
|
207
|
-
## 运行态布局与滚动规范
|
|
208
|
-
|
|
209
|
-
- 页面只保留一个主滚动容器:仪表盘、首页和嵌套界面优先继承最外层页面滚动;单个 `pannel`、`workcenter`、`diytable`、`diycalendar`、嵌套 `pageengine` 在运行态默认使用内容自适应高度和 `overflow: visible`,不得无条件设置 `overflow: auto`。
|
|
210
|
-
- 经典桌面主滚动容器为路由内容区,导航与 Tab 留在视口内;移动端继续保留外层文档滚动。图表在异步挂载后绑定尺寸观察器,切换导航、收缩侧栏及改变窗口宽度都应重新测量,禁止仅隐藏溢出而保留错误画布宽度。真实回归覆盖两种导航、两种桌面宽度,以及日程创建后刷新、备注编辑和删除。
|
|
211
|
-
- 固定高度只用于设计器拖拽预览、图表画布或确有虚拟滚动需求的组件。设计态可按 `widgetOption.height` 提供滚动,运行态必须先验证内容能完整展开;不能用双滚动条掩盖容器高度不足。
|
|
212
|
-
- 表格型容器通常按 15 条数据验收,表头、工具栏、全部数据行和分页区应同时可见;首页紧凑表格可通过 `diy-table` 的 `PageSizeList` 追加 `[10]`,并在 `sys_menu.DefaultPageSize` 为空时默认选择候选页码中的最小值。空间不足时先压缩统计卡、工具栏、行高和间距,再考虑增加外层页面长度,不隐藏分页、不在卡片内部滚动。
|
|
213
|
-
- 同一行的容器使用一致的左右边距、内边距、标题高度和顶部基线;同层卡片间距必须相同,左右边缘、标题、工具栏和内容区应形成稳定对齐线。推荐使用统一的 8px 基础间距和 8/12/16px 倍数。
|
|
214
|
-
- 成对容器应设置相同的最小高度;内容较少的组件通过内容居中、合理留白或自适应布局消化空间,不能在底部留下明显的大块无意义空白。内容较多时允许容器自然增高并由页面继续向下滚动。
|
|
215
|
-
- 日历和公告应完整展示工具栏、主体和底部操作区;日历优先使用 `height: auto` / `contentHeight: auto`,公告优先使用标准表单引擎分页与权限按钮。
|
|
216
|
-
- 运行态移动端必须按实际视口自动切为 24 栅格,不能只依赖保存 JSON 中的 `formConfig.mobile`;窗口旋转或宽度变化时也要同步。嵌入的 `diytable` 不得渲染独立列表页的固定返回栏和全局 FAB,新增、页面按钮、批量操作应留在当前容器工具栏内;容器标题操作区允许紧凑换行,但不能绝对定位覆盖标题或其它卡片。
|
|
217
|
-
- `aiengine` 在移动端嵌入首页时应使用内容自适应高度和外层页面滚动,指标卡与快捷入口优先两列紧凑排布,避免 4 个指标和全部快捷入口单列堆叠造成超长首屏;输入区和模型/推理设置必须保持可见且不横向溢出。
|
|
218
|
-
- 交付前必须在真实运行页检查:是否存在内部纵向/横向滚动条、15 条表格分页是否可见、同排容器是否对齐、四周边距是否一致、组件底部是否有异常空白。仅检查设计器画布不算验收完成。
|
|
219
|
-
|
|
210
|
+
<!-- microi-progressive:chunk id=page-engine-007 sha256=4170c6e274fc773427057d8606c4800a5bbd5fabd62d63e931aaa388262a51e9 -->
|
|
211
|
+
## 运行态布局与滚动规范
|
|
212
|
+
|
|
213
|
+
- 页面只保留一个主滚动容器:仪表盘、首页和嵌套界面优先继承最外层页面滚动;单个 `pannel`、`workcenter`、`diytable`、`diycalendar`、嵌套 `pageengine` 在运行态默认使用内容自适应高度和 `overflow: visible`,不得无条件设置 `overflow: auto`。
|
|
214
|
+
- 经典桌面主滚动容器为路由内容区,导航与 Tab 留在视口内;移动端继续保留外层文档滚动。图表在异步挂载后绑定尺寸观察器,切换导航、收缩侧栏及改变窗口宽度都应重新测量,禁止仅隐藏溢出而保留错误画布宽度。真实回归覆盖两种导航、两种桌面宽度,以及日程创建后刷新、备注编辑和删除。
|
|
215
|
+
- 固定高度只用于设计器拖拽预览、图表画布或确有虚拟滚动需求的组件。设计态可按 `widgetOption.height` 提供滚动,运行态必须先验证内容能完整展开;不能用双滚动条掩盖容器高度不足。
|
|
216
|
+
- 表格型容器通常按 15 条数据验收,表头、工具栏、全部数据行和分页区应同时可见;首页紧凑表格可通过 `diy-table` 的 `PageSizeList` 追加 `[10]`,并在 `sys_menu.DefaultPageSize` 为空时默认选择候选页码中的最小值。空间不足时先压缩统计卡、工具栏、行高和间距,再考虑增加外层页面长度,不隐藏分页、不在卡片内部滚动。
|
|
217
|
+
- 同一行的容器使用一致的左右边距、内边距、标题高度和顶部基线;同层卡片间距必须相同,左右边缘、标题、工具栏和内容区应形成稳定对齐线。推荐使用统一的 8px 基础间距和 8/12/16px 倍数。
|
|
218
|
+
- 成对容器应设置相同的最小高度;内容较少的组件通过内容居中、合理留白或自适应布局消化空间,不能在底部留下明显的大块无意义空白。内容较多时允许容器自然增高并由页面继续向下滚动。
|
|
219
|
+
- 日历和公告应完整展示工具栏、主体和底部操作区;日历优先使用 `height: auto` / `contentHeight: auto`,公告优先使用标准表单引擎分页与权限按钮。
|
|
220
|
+
- 运行态移动端必须按实际视口自动切为 24 栅格,不能只依赖保存 JSON 中的 `formConfig.mobile`;窗口旋转或宽度变化时也要同步。嵌入的 `diytable` 不得渲染独立列表页的固定返回栏和全局 FAB,新增、页面按钮、批量操作应留在当前容器工具栏内;容器标题操作区允许紧凑换行,但不能绝对定位覆盖标题或其它卡片。
|
|
221
|
+
- `aiengine` 在移动端嵌入首页时应使用内容自适应高度和外层页面滚动,指标卡与快捷入口优先两列紧凑排布,避免 4 个指标和全部快捷入口单列堆叠造成超长首屏;输入区和模型/推理设置必须保持可见且不横向溢出。
|
|
222
|
+
- 交付前必须在真实运行页检查:是否存在内部纵向/横向滚动条、15 条表格分页是否可见、同排容器是否对齐、四周边距是否一致、组件底部是否有异常空白。仅检查设计器画布不算验收完成。
|
|
223
|
+
|
|
220
224
|
<!-- /microi-progressive:chunk -->
|
|
221
225
|
## 详细参考路由(渐进披露)
|
|
222
226
|
|
|
@@ -7,6 +7,8 @@ description: 按 Microi 系统真实业务逻辑进行 Playwright 全自动化
|
|
|
7
7
|
|
|
8
8
|
# Microi 吾码 Playwright E2E 自动化测试
|
|
9
9
|
|
|
10
|
+
桌面人工补充或新建浏览器验收默认只选一个常规 CSS 视口:优先复现用户实际浏览器尺寸,未知时用 1920×1080。截图文件像素还受设备缩放影响,不能据此强制 4K;仅用户明确要求或问题涉及超宽屏时追加 4K,移动端或响应式改动按需补充相关窄屏。沿用已有有效验收,不为截图分辨率重复整套用例;正式脚本既有 Full 不在运行中删减。核心发布已冻结后,新增规范文案独立交付,不因此重启合格候选的构建、测试或上传。
|
|
11
|
+
|
|
10
12
|
每轮测试成功、失败、取消或中断退出时,在 `finally` / teardown 中关闭本任务 browser/context 和临时写入进程,再按 [测试临时文件随任务清理](../workspace-conventions/SKILL.md#测试临时文件必须随任务清理强制) 删除本任务 `.tmp` 下的一次性脚本、夹具及无用途的截图、trace、video、日志和编译副本。交付必需证据先最小归档并回读,失败排查只暂留有明确归属和清理时机的最小集合;报告和截图路径规则不构成永久保留许可,结束后必须单独验收清理结果。
|
|
11
13
|
|
|
12
14
|
> **2026-05 更新(重要)**:Microi.Agent 插件生成的E2E 配置、用例和帮助器保留在 `<projectRoot>/.microi-e2e/`;运行产物统一输出到 `<gitRoot>/.tmp/microi-e2e/<项目相对路径>/`,**不再**写入 `tests/e2e/`、根目录 `playwright.config.js` 或 `.env.e2e.example`。
|
|
@@ -144,14 +146,14 @@ PW_HOME_PATH=/#/pages/index/index
|
|
|
144
146
|
不要只说“代码已编译”或“需要用户自己重启后端”;除非用户明确要求不要中断当前服务,否则 AI 要主动完成重启。
|
|
145
147
|
|
|
146
148
|
<!-- /microi-progressive:chunk -->
|
|
147
|
-
<!-- microi-progressive:chunk id=playwright-e2e-007 sha256=
|
|
149
|
+
<!-- microi-progressive:chunk id=playwright-e2e-007 sha256=948194cce3a29cfec216546acab77314ccaa1c0afad3cbcfd6b426a94f36d4ac -->
|
|
148
150
|
## 服务自启动纪律(必做)
|
|
149
151
|
|
|
150
152
|
执行自动化测试、截图巡检、接口引擎回读、`/apiengine/{key}` 验收时,如果本地后端或前端不可达,不能把 `fetch failed`、`ECONNREFUSED`、`000 Failed to connect`、端口无人监听当作任务终点。必须先自动启动所需服务,再继续完整验证。
|
|
151
153
|
|
|
152
154
|
同一工作区有多个 AI 对话时,服务自启动还必须遵守共享生命周期:
|
|
153
155
|
|
|
154
|
-
- 先执行 `../workspace-conventions/SKILL.md`
|
|
156
|
+
- 先执行 `../workspace-conventions/SKILL.md` 的“本地测试程序与依赖生命周期”统一规则:默认复用当前 MCP 对应的 MySQL、Redis、MongoDB、MinIO/OSS,以及健康本地源码程序或隔离目录的编译产物;不按测试任务重复创建 Docker/Compose,不例行询问临时容器许可。需要双节点时启动两个本机程序进程连接同组依赖,不等于建立两套 Docker 环境。不得已创建的临时容器和纯测试资源每轮必须精确删除并回读。
|
|
155
157
|
- API 服务启动前执行 `node Microi.Server/tools/release-lock.mjs assert api <工作区根>`,PC 服务使用 `assert pc`。Playwright `webServer`、自愈脚本或 AI 只在对应范围发布时等待;官网/Agent 发布不阻塞 `61500/61501`。同时启动前后端时分别检查两项。旧锁仅在真实进程证明所属范围后缩小阻塞范围,未知锁继续保护,不得删除活跃锁。
|
|
156
158
|
- 端口健康且 PID/命令行属于当前工作区时直接复用,不得每个对话都无条件“先杀再启动”。
|
|
157
159
|
必须重载源码时才串行重启,并在状态播报中说明旧/新 PID。
|
|
@@ -55,20 +55,20 @@ V8.Cache.HashDelete(hashKey, 'Reserved');
|
|
|
55
55
|
平台 Redis 管理器固定路由为 `#/mci-redis-manager`:
|
|
56
56
|
|
|
57
57
|
- Redis 管理器接口要求已登录且当前用户 `Level >= 9999`。它只接受 `tenant`(当前租户默认 Redis)和 `saved`(主租户 `mci_redis_connection` 中保存的额外连接)两种模式;当前源码明确拒绝 `temporary`,也没有匿名临时连接模式。
|
|
58
|
+
- 从 v8.5.12 起,Redis 管理器由 `Microi.Cache` 强制实施租户边界:主租户身份以 API 宿主的 `OsClient` 配置为准,主租户超级管理员可管理当前受控连接中的全部 Key;子租户超级管理员只能扫描、读取和维护完整的 `Microi:{当前OsClient}:` 前缀,不能靠 `Level=9999`、请求参数或切换数据库取得主租户权限。精确读取、删除、覆盖、重命名的两端、TTL、批量删除和缓存失效均执行同一检查,批量请求中出现越权 Key 时整批拒绝。普通业务 `V8.Cache` 仍只允许当前租户。
|
|
59
|
+
- 子租户只能选择当前连接配置的数据库;MCP 省略 `database` 时使用后端配置,不默认传 `0`。页面以 `CanSelectDatabase` / `AllowedKeyPrefix` 为事实,不能从前端账号等级推断。`*` 与普通搜索词自动限定当前租户,显式其它租户前缀直接拒绝。子租户统计不返回共享实例 INFO、DBSIZE 或节点全局 L1 统计;扫描样本数量通过 `KeyCountIsEstimate` 标识。
|
|
58
60
|
- 保存连接记录必须按 `TenantOsClient` 隔离,密码只在后端保护并且不回传前端;调用方只传保存后的 `connectionId`。
|
|
59
61
|
- Key 列表必须使用 `SCAN` 游标分页,禁止在生产 Redis 上使用阻塞式 `KEYS *`。内容查看支持 String、Hash、List、Set、Sorted Set、Stream;集合内容要分页并限制单次条数。
|
|
60
62
|
- 写入 Hash/List/Set/Sorted Set 时先完整解析 JSON,再覆盖旧 Key;删除、覆盖、重命名和 TTL 变更属于破坏性操作,必须先展示目标连接、数据库与 Key 并要求明确确认。
|
|
61
63
|
- 管理接口不开放任意 Redis 命令、Lua、`FLUSHALL` 或 `FLUSHDB`;批量删除最多 500 个 Key,重命名不覆盖既有目标。
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
`/api/cache/invalidate`、模式失效 `/api/cache/invalidate-pattern`,以及 Redis 管理器
|
|
65
|
-
路由前缀 `/api/cache/redis/`。它们都属于受权管理能力,不是匿名业务 API。
|
|
65
|
+
当前统一入口为 Managed 接口引擎 `platform-cache-manager`,通过 `V8.Method.ManageCache` 调用可信后端;旧 `/api/cache/*` 路由由兼容入口处理。节点全局 `Statistics` 只开放主租户,子租户使用受限 `RedisStatistics`。这些都是受权管理能力,不是匿名业务 API。
|
|
66
66
|
|
|
67
67
|
MCP 默认操作当前 MCP `OsClient` 的租户 Redis;额外连接只传管理页保存后的 `connectionId`,禁止在 MCP 参数、日志或回答中传递 Redis 密码。
|
|
68
68
|
|
|
69
69
|
| MCP 工具 | 用途 | 确认规则 |
|
|
70
70
|
|------|------|------|
|
|
71
|
-
| `microi_redis_statistics` |
|
|
71
|
+
| `microi_redis_statistics` | 主租户服务器统计 / 子租户命名空间 Key 样本统计 | 只读 |
|
|
72
72
|
| `microi_redis_list_keys` | SCAN 分页检索 Key、类型、TTL、内存估算 | 只读 |
|
|
73
73
|
| `microi_redis_get_key` | 分页查看单个 Key 内容 | 只读 |
|
|
74
74
|
| `microi_redis_delete_keys` | 单个或批量删除,最多 500 个 | `confirmExecution="DELETE"` |
|
|
@@ -310,3 +310,7 @@ fire-and-forget;否则数千个 `SCAN/DEL/PUBLISH` 会同时进入同一个
|
|
|
310
310
|
- 每个租户可能使用不同 Redis,订阅初始化状态不得用一个全局 `static bool` 共享;
|
|
311
311
|
- 缓存实例必须保存创建时的准确 `OsClient`,按模式 `SCAN/DEL` 时直接使用该租户连接;禁止根据 Redis DB 编号反推连接,因为不同租户可能在不同服务器上使用相同 DB 编号;
|
|
312
312
|
- 等待发布只解决回压,业务写入和缓存失效仍需保持 `OsClient` 隔离及可重试幂等。
|
|
313
|
+
|
|
314
|
+
## Redis 管理隔离验收
|
|
315
|
+
|
|
316
|
+
使用已登记的隔离主/子租户,在同一 Redis 数据库创建有短 TTL 的唯一测试 Key,逐个验证两台 API:子租户正常维护自身 String/Hash/List/Set/Sorted Set;扫描不返回主租户或相似前缀;读取、混合批删、跨前缀重命名、TTL、覆盖和失效越权全部拒绝,拒绝后由主租户回读原值未变。验收后只清理登记的测试 Key。禁止用官方业务租户做可写 Full,也不能用物理分库替代 Key 授权测试。
|
|
@@ -217,3 +217,8 @@ return { Code: 1, Data: dataList, DataCount: dataList.length };
|
|
|
217
217
|
- 父子表边界:固定父记录外键优先于 Excel 同名列;接口引擎仍须核对目标表、项目/主记录存在性、业务状态和额度。需要整批钩子的模块禁止 `ContinueOnError`。
|
|
218
218
|
- 重复提交:客户端每次重新选择文件生成新的 `_ImportIdempotencyKey`,同一 HTTP 重试沿用该键;服务端按租户、表、菜单、用户和请求键保存状态,`Pending/Running/Succeeded` 均不得重复创建写入任务。该机制不等同于“相同文件作为新请求再次上传”的内容判重。
|
|
219
219
|
- 自动化检查:至少覆盖同批汇总超量、稳定行锁先于累计回查、整批回滚、父外键不可被 Excel 覆盖、同一请求重试只写一次,以及错误中包含行号和业务上限明细。
|
|
220
|
+
|
|
221
|
+
## 替换导入接口的上传与重试验收
|
|
222
|
+
|
|
223
|
+
- `ImportApi` 相对地址必须按当前 `ApiBase`(含路径前缀)解析,并携带当前登录与租户上下文;完整地址和 `$ApiBase$` 继续兼容。nginx HTTP 405 可能源于前端误向静态站点上传,先核对实际请求 URL,不能据错误页排除代码问题。
|
|
224
|
+
- 同步替换接口返回 `Code=1`,未配置 `ImportProgressApi` 且没有运行中状态时,直接展示最终结果与行数;不得查询它从未写入的标准导入进度。异步替换接口须配置进度协议。明确失败后重试必须把保留的原文件重新放入上传队列,同时保留映射与操作标识;HTTP 200 的 `Code=0` 也属于失败。超过 90 秒的未完成请求只提示等待并阻止重复提交,不能把等待时间当成失败、自动重放或宣称服务端已取消。自定义接口幂等仍需由业务实现。验收覆盖真实 multipart、同步成功、HTTP 405、同文件重试、迟到回调与标准异步兼容。
|
|
@@ -7,6 +7,10 @@ description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI
|
|
|
7
7
|
|
|
8
8
|
# Microi V8 文件上传下载
|
|
9
9
|
|
|
10
|
+
## 源码仓库资源隔离(默认强制)
|
|
11
|
+
|
|
12
|
+
截图、配图、PDF/PPT、音视频、压缩包、模型、安装包和其它大体积交付资源统一通过 MCP 上传 CDN,禁止提交到 Gitee/GitHub 源码历史,即使压缩到小于 1 MB 也不能把截图或附件改放源码仓库。官方资源使用已校验的 `microi_itdos`(`https://api.itdos.com + OsClient=iTdos`)公有桶与 `https://file.microi.net` 前缀;私有资料按租户实际权限保持私有。源码只存 URL、字节数、SHA-256、MIME 等轻量清单。上传后独立回读实际字节和类型,PDF 验证 Range;超过 Base64 协议容量使用流式/分片工具。暂存区及每个待推送提交都须排除资源本体,详细门禁遵循 [工作区默认 CDN 规范](../workspace-conventions/SKILL.md#截图与大体积资源必须走-cdn默认强制)。
|
|
13
|
+
|
|
10
14
|
你正在为 Microi 吾码平台编写文件上传/下载/返回相关代码。平台分布式存储(HDFS)支持阿里云OSS、MinIO、亚马逊S3,存储方案由 SaaS 引擎按租户配置。上传对象的 `Content-Type` 由框架按实际对象路径统一确定,覆盖 HTML、JS/MJS、CSS、JSON/MAP、SVG、字体与 WASM,未知后缀保留 `application/octet-stream`。MinIO、S3、OSS 普通与分片上传使用同一映射;该映射不替代上传权限、内容和配额校验。旧对象不会因后端升级自动改变元数据:先核对字节数和 SHA-256,再修正类型或按正式应用发布流程产生新版本。不要把源码与公有编译产物混在一起,也不要关闭 `nosniff` 掩盖类型错误。
|
|
11
15
|
|
|
12
16
|
文件与发布测试默认使用当前 MCP 连接下、当前租户实际生效的 HDFS Provider;先只读核实提供商与对象范围,沿已授权隔离前缀执行公私权限、原始字节、哈希、MIME 与写后回读断言。已确定当前环境后,不再为其它 OSS/MinIO/S3 反复询问或新建存储;仅明确的提供商缺陷、迁移或兼容任务才扩展矩阵,并逐项报告真实执行与未覆盖范围。提供商不可用或断言失败必须保留真实错误,不能换提供商或省略回读来冒充通过。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
4
|
|
|
5
|
-
<!-- microi-progressive:chunk id=v8-security-005 sha256=
|
|
5
|
+
<!-- microi-progressive:chunk id=v8-security-005 sha256=657db8189d96c594073735e9182af9d3e9a91f4f7dd111ecf2c7fe1eec6d7ede -->
|
|
6
6
|
## 2. 权限校验
|
|
7
7
|
|
|
8
8
|
### DiyToken 是平台会话与权限入口,不替换为 ASP.NET Identity
|
|
@@ -16,6 +16,14 @@ DiyToken 与 `sys_user`、`OsClient`、终端 `did`、共享 Redis 登录态、
|
|
|
16
16
|
|
|
17
17
|
开发新登录入口时只实现“验证身份 -> 获取仍启用的 `sys_user` -> 签发 DiyToken”,禁止并行创建第二套用户、角色、权限 Token 或会话有效期规则。
|
|
18
18
|
|
|
19
|
+
### MCP 角色开发授权
|
|
20
|
+
|
|
21
|
+
`Level >= 9999` 保留完整 MCP 权限及授权管理入口。其他账号须由超级管理员在开发工具连接窗口或角色编辑中明确授予 `McpDevelopment`,两处使用同一 `sys_rolelimit` 记录和并发版本;没有记录、损坏或重复配置一律拒绝。
|
|
22
|
+
|
|
23
|
+
每次请求从当前租户主库复核活动用户、有效角色、模块/表及操作开关。访问范围与创建/修改、删除必须由同一角色同时满足;Full 只读与 Role 写入不得合成 Full 写入。Role 范围只支持明确白名单的安全元数据编辑及保留行/字段授权的业务操作,不开放可执行脚本、SQL、数据源或权限配置。已验签的 MCP/VSCode 会话也不能改走普通 FormEngine/接口引擎 HTTP 绕过限制。
|
|
24
|
+
|
|
25
|
+
仅对获准的 MCP 调用,完整范围且同时具有创建/修改、删除的角色可作为可信开发者执行服务端开发;这类权限能够间接访问或修改数据,不得宣传为隔离的纯界面编辑。它不改变账号 Level,不放宽普通后台管理入口,不允许普通账号改写 MCP 授权。撤销须即时生效;受限角色、完整只读、写但不可删、多角色组合、普通 HTTP 绕过、跨租户、旧 Token 与并发授权保存必须纳入验收。
|
|
26
|
+
|
|
19
27
|
### 平台 FormEngine 授权边界
|
|
20
28
|
|
|
21
29
|
**Token 只完成身份认证,不是任意表的访问凭证。** 来自浏览器、UniApp、SDK 或其它外部客户端的通用 FormEngine 请求,必须由服务端完成以下授权,V8 或前端代码不得自行模拟、放宽:
|
|
@@ -39,7 +47,7 @@ DiyToken 与 `sys_user`、`OsClient`、终端 `did`、共享 Redis 登录态、
|
|
|
39
47
|
|
|
40
48
|
跨租户公共发现接口可以匿名,但返回值必须采用严格字段白名单,只返回公开标识和展示信息,禁止返回 `ApiKey`、上游 Endpoint、连接串或内部配置。例如官方 AI 中转模型清单应保持 `IsEnable=1`、`StopHttp=0`、`AllowAnonymous=1`,且只返回模型 Id、展示名等公开字段;将这类被其它租户服务器调用的发现接口误改为非匿名,会让所有消费者得到空模型列表或 `NoAuth`,属于破坏性配置变更。发布和升级后必须用无 Token 的真实 HTTP 请求验证返回字段与匿名状态。
|
|
41
49
|
|
|
42
|
-
> 后端接口引擎、后端表单 V8 和平台内部调用由服务端在构造参数时设置不可由 HTTP JSON 注入的 `_TrustedServerInvocation`,因此调用 `V8.FormEngine` 不要求 `_SysMenuId`。`_InvokeType:'Server'`
|
|
50
|
+
> 后端接口引擎、后端表单 V8 和平台内部调用由服务端在构造参数时设置不可由 HTTP JSON 注入的 `_TrustedServerInvocation`,因此调用 `V8.FormEngine` 不要求 `_SysMenuId`。`_InvokeType:'Server'` 只是事件调用语义,不是外部客户端可用的授权开关;普通用户即使在请求中伪造这两个字段也不能成为可信调用。能写 V8、接口引擎、任务或数据源的普通管理入口仍限制为 `Level >= 9999`。MCP 唯一例外是上节明确授权的 Full 范围且同时具有创建/修改、删除权限的可信开发角色,并须通过 MCP 专用逐请求鉴权;不能由菜单授权或请求参数推导此例外。
|
|
43
51
|
|
|
44
52
|
> 外层 `AddFormData` 的客户端菜单/`Add` 权限校验与事件内部执行权是两件事:前者防止无新增权限的用户进入事件,后者允许已经进入的 `SubmitBeforeServerV8` / `SubmitAfterServerV8` 像接口引擎一样在当前租户内执行复杂 SQL、跨表事务和其它表 CRUD。不要把客户端菜单范围再次套到服务器 V8 的嵌套调用。
|
|
45
53
|
|