@microi.net/cli 4.6.2
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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/mcp-codex-stdio-adapter.js +189 -0
- package/dist/mcp-server.js +972 -0
- package/dist/mcp-trae-windows-launcher.cmd +21 -0
- package/dist/microi-cli-mcp.js +7 -0
- package/dist/microi-cli.js +1645 -0
- package/dist/microi-skills.meta.json +335 -0
- package/dist/microi.skills/.microi-skills-version.json +6 -0
- package/dist/microi.skills/README.md +276 -0
- package/dist/microi.skills/ai-engine/SKILL.md +140 -0
- package/dist/microi.skills/ai-engine/agents/openai.yaml +4 -0
- package/dist/microi.skills/app-store/SKILL.md +105 -0
- package/dist/microi.skills/app-store/agents/openai.yaml +4 -0
- package/dist/microi.skills/business-blueprint/SKILL.md +184 -0
- package/dist/microi.skills/datasource-engine/SKILL.md +89 -0
- package/dist/microi.skills/datasource-engine/agents/openai.yaml +4 -0
- package/dist/microi.skills/dos-orm/SKILL.md +76 -0
- package/dist/microi.skills/dos-orm/references/api-reference.md +229 -0
- package/dist/microi.skills/job-engine/SKILL.md +141 -0
- package/dist/microi.skills/job-engine/agents/openai.yaml +4 -0
- package/dist/microi.skills/message-notification/SKILL.md +113 -0
- package/dist/microi.skills/message-notification/agents/openai.yaml +6 -0
- package/dist/microi.skills/message-notification/references/contracts.md +99 -0
- package/dist/microi.skills/microi-ai-app-auth.js +651 -0
- package/dist/microi.skills/microi-ai-application/SKILL.md +80 -0
- package/dist/microi.skills/microi-ai-application/agents/openai.yaml +4 -0
- package/dist/microi.skills/microi-ai-application/references/frontend-baseline.md +164 -0
- package/dist/microi.skills/microi-client-frontend/SKILL.md +562 -0
- package/dist/microi.skills/microi-datasource-mapping/SKILL.md +108 -0
- package/dist/microi.skills/microi-db-schema/SKILL.md +170 -0
- package/dist/microi.skills/microi-db-schema/agents/openai.yaml +4 -0
- package/dist/microi.skills/microi-db-schema/references/core-tables.md +695 -0
- package/dist/microi.skills/microi-db-schema/references/form-component-options.md +256 -0
- package/dist/microi.skills/microi-db-schema/references/schema-overview.md +203 -0
- package/dist/microi.skills/microi-db-schema/references/schema.md +647 -0
- package/dist/microi.skills/microi-db-schema/references/table-catalog.md +1607 -0
- package/dist/microi.skills/microi-deployment/SKILL.md +117 -0
- package/dist/microi.skills/microi-deployment/references/deployment-matrix.md +94 -0
- package/dist/microi.skills/microi-docs-coverage/SKILL.md +91 -0
- package/dist/microi.skills/microi-docs-coverage/references/capability-map.md +65 -0
- package/dist/microi.skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +887 -0
- package/dist/microi.skills/microi-form-engine/SKILL.md +159 -0
- package/dist/microi.skills/microi-form-engine/references/component-catalog.md +116 -0
- package/dist/microi.skills/microi-form-engine/references/data-source-events.md +117 -0
- package/dist/microi.skills/microi-form-layout/SKILL.md +373 -0
- package/dist/microi.skills/microi-frontend-sdk/SKILL.md +304 -0
- package/dist/microi.skills/microi-left-right-layout/SKILL.md +132 -0
- package/dist/microi.skills/microi-microservice/SKILL.md +115 -0
- package/dist/microi.skills/microi-microservice/references/runtime-delivery.md +145 -0
- package/dist/microi.skills/microi-mobile-app-quality/SKILL.md +436 -0
- package/dist/microi.skills/microi-solution-quotation/SKILL.md +76 -0
- package/dist/microi.skills/microi-solution-quotation/agents/openai.yaml +4 -0
- package/dist/microi.skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -0
- package/dist/microi.skills/microi-system-delivery/SKILL.md +446 -0
- package/dist/microi.skills/microi-ui/SKILL.md +321 -0
- package/dist/microi.skills/microi-uniapp-frontend/SKILL.md +483 -0
- package/dist/microi.skills/microi.v8.js +1758 -0
- package/dist/microi.skills/module-engine/SKILL.md +131 -0
- package/dist/microi.skills/module-engine/references/module-config.md +174 -0
- package/dist/microi.skills/page-engine/SKILL.md +397 -0
- package/dist/microi.skills/performance-testing/SKILL.md +207 -0
- package/dist/microi.skills/playwright-e2e/SKILL.md +769 -0
- package/dist/microi.skills/print-engine/SKILL.md +237 -0
- package/dist/microi.skills/production-readonly-audit/SKILL.md +39 -0
- package/dist/microi.skills/report-engine/SKILL.md +69 -0
- package/dist/microi.skills/report-engine/agents/openai.yaml +4 -0
- package/dist/microi.skills/search-engine/SKILL.md +73 -0
- package/dist/microi.skills/search-engine/agents/openai.yaml +4 -0
- package/dist/microi.skills/spider-engine/SKILL.md +188 -0
- package/dist/microi.skills/translate-engine/SKILL.md +91 -0
- package/dist/microi.skills/translate-engine/agents/openai.yaml +4 -0
- package/dist/microi.skills/ui-design/SKILL.md +1575 -0
- package/dist/microi.skills/ui-design/assets/pattern-showcase/app.js +54 -0
- package/dist/microi.skills/ui-design/assets/pattern-showcase/index.html +163 -0
- package/dist/microi.skills/ui-design/assets/pattern-showcase/styles.css +311 -0
- package/dist/microi.skills/ui-design/assets/templates/MCI-DESIGN.md +98 -0
- package/dist/microi.skills/ui-design/references/design-pattern-library.md +171 -0
- package/dist/microi.skills/ui-design/references/mci-design-contract.md +84 -0
- package/dist/microi.skills/ui-design/references/motion-and-media.md +71 -0
- package/dist/microi.skills/ui-design/references/product-flow-recipes.md +94 -0
- package/dist/microi.skills/uniapp-mall-assets/SKILL.md +105 -0
- package/dist/microi.skills/v8-api-config/SKILL.md +272 -0
- package/dist/microi.skills/v8-cache-pattern/SKILL.md +286 -0
- package/dist/microi.skills/v8-crud-api/SKILL.md +398 -0
- package/dist/microi.skills/v8-debugging/SKILL.md +279 -0
- package/dist/microi.skills/v8-explorer-tree/SKILL.md +224 -0
- package/dist/microi.skills/v8-export-import/SKILL.md +590 -0
- package/dist/microi.skills/v8-file-upload/SKILL.md +497 -0
- package/dist/microi.skills/v8-formengine-http/SKILL.md +218 -0
- package/dist/microi.skills/v8-frontend-events/SKILL.md +349 -0
- package/dist/microi.skills/v8-frontend-events/references/bluetooth-print-api.md +107 -0
- package/dist/microi.skills/v8-frontend-events/references/bluetooth-print.md +185 -0
- package/dist/microi.skills/v8-http-integration/SKILL.md +379 -0
- package/dist/microi.skills/v8-image-processing/SKILL.md +187 -0
- package/dist/microi.skills/v8-image-processing/agents/openai.yaml +4 -0
- package/dist/microi.skills/v8-image-processing/references/api-reference.md +620 -0
- package/dist/microi.skills/v8-menu-buttons/SKILL.md +661 -0
- package/dist/microi.skills/v8-mongodb/SKILL.md +149 -0
- package/dist/microi.skills/v8-mq-mqtt/SKILL.md +227 -0
- package/dist/microi.skills/v8-saas-multi-tenant/SKILL.md +193 -0
- package/dist/microi.skills/v8-security/SKILL.md +417 -0
- package/dist/microi.skills/v8-sql-query/SKILL.md +290 -0
- package/dist/microi.skills/v8-table-event/SKILL.md +385 -0
- package/dist/microi.skills/v8-template-engine/SKILL.md +165 -0
- package/dist/microi.skills/v8-utilities/SKILL.md +79 -0
- package/dist/microi.skills/v8-utilities/references/client-api-index.md +136 -0
- package/dist/microi.skills/v8-utilities/references/platform-http-routes.md +80 -0
- package/dist/microi.skills/v8-utilities/references/server-api-index.md +129 -0
- package/dist/microi.skills/v8-workflow/SKILL.md +322 -0
- package/dist/microi.skills/workspace-conventions/SKILL.md +479 -0
- package/package.json +40 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: page-engine
|
|
3
|
+
description: 生成和审查 Microi 界面引擎 Page Engine 页面 JSON。用于创建仪表盘、图表、表格、地图、组件、页面布局,或校验 mic_page formData JSON。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Microi 界面引擎(Page Engine)页面 JSON 生成
|
|
7
|
+
|
|
8
|
+
你正在为 Microi 吾码平台生成界面引擎页面的 JSON 数据。界面引擎页面由 `formData` 对象描述,用户导入 JSON 即可使用。
|
|
9
|
+
|
|
10
|
+
## 设计器源码事件
|
|
11
|
+
|
|
12
|
+
只有在扩展界面引擎设计器源码时才使用全局事件总线。用
|
|
13
|
+
`EventBus.on(eventName, handler)` 监听保存、日期选择或组件跳转等事件,并在
|
|
14
|
+
组件卸载时逐项调用 `EventBus.off(eventName)`。重复挂载不解绑会造成一次操作
|
|
15
|
+
触发多次;普通页面 JSON 生成不需要注册事件总线。
|
|
16
|
+
|
|
17
|
+
## 核心数据结构
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
formData(页面)
|
|
21
|
+
├── Id: string // 页面唯一ID(GUID)
|
|
22
|
+
├── Title: string // 页面标题
|
|
23
|
+
├── Number: string // 页面编号(如 PAGE1)
|
|
24
|
+
├── Desc: string // 页面描述
|
|
25
|
+
└── JsonObj
|
|
26
|
+
├── formConfig // 页面全局配置
|
|
27
|
+
└── wrapperList[] // 容器列表
|
|
28
|
+
├── wrapperOption // 容器配置
|
|
29
|
+
└── widgetList[] // 组件列表
|
|
30
|
+
├── widgetOption // 组件通用配置
|
|
31
|
+
└── widgetParams[] // 组件私有参数
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## formConfig 页面全局配置
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"gutter": 0, "mask": true, "drag": true, "left": true,
|
|
39
|
+
"hover": true, "shadow": true, "link": false, "watermark": false,
|
|
40
|
+
"mobile": false, "dark": false, "autoRefresh": 0, "lastRefreshTime": "",
|
|
41
|
+
"watermarkStyle": {
|
|
42
|
+
"content": "Microi吾码",
|
|
43
|
+
"font": { "fontSize": 16, "color": "rgba(255, 0, 0, 0.15)" },
|
|
44
|
+
"rotate": -22
|
|
45
|
+
},
|
|
46
|
+
"dynamicStyle": { "padding": "4px", "backgroundColor": "", "opacity": 1 }
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 容器类型
|
|
51
|
+
|
|
52
|
+
| type | label | 说明 |
|
|
53
|
+
|------|-------|------|
|
|
54
|
+
| `pannel` | 卡片 | 标准容器,包含一个 `widgetList` |
|
|
55
|
+
| `tabs` | 选项卡 | 多标签页容器,使用 `tabWidgetMap` 存储每个 tab 的组件 |
|
|
56
|
+
|
|
57
|
+
### 卡片容器(pannel)关键字段
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"type": "pannel",
|
|
62
|
+
"label": "卡片",
|
|
63
|
+
"hidden": false,
|
|
64
|
+
"wrapperOption": {
|
|
65
|
+
"number": 10001, // 随机5位整数,页面内唯一
|
|
66
|
+
"span": 12, // 栅格宽度(1-24,24=满宽)
|
|
67
|
+
"height": 300, // 容器高度(px)
|
|
68
|
+
"margin": "0px 10px 10px 0px",
|
|
69
|
+
"dynamicStyle": { "padding": "10px", "backgroundColor": "" },
|
|
70
|
+
"titleOption": {
|
|
71
|
+
"hidden": true, // true=隐藏标题
|
|
72
|
+
"title": "未命名",
|
|
73
|
+
"dynamicStyle": { "textAlign": "left", "padding": "0px", "height": "20px", "lineHeight": "20px", "fontSize": "14px", "color": "" },
|
|
74
|
+
"moreOption": { "hidden": true, "icon": "More", "iconShow": false, "text": "更多", "linkurl": "/", "linktype": "router", "refresh": "0", "datetime": "0", "autotime": false, "autotimeval": 1, "dynamicStyle": { "color": "", "fontSize": "12px" } }
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"widgetList": []
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 选项卡容器(tabs)
|
|
82
|
+
|
|
83
|
+
组件放在 `tabWidgetMap[tabKey][]` 中,**不**放在 `widgetList` 中。
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"type": "tabs",
|
|
88
|
+
"wrapperOption": {
|
|
89
|
+
"number": 10002, "span": 24, "height": 400,
|
|
90
|
+
"tabType": "", // '' | 'card' | 'border-card'
|
|
91
|
+
"tabPosition": "top", // 'top' | 'right' | 'bottom' | 'left'
|
|
92
|
+
"tabs": [
|
|
93
|
+
{ "key": "tab_1", "label": "标签页1" },
|
|
94
|
+
{ "key": "tab_2", "label": "标签页2" }
|
|
95
|
+
],
|
|
96
|
+
"activeTab": "tab_1"
|
|
97
|
+
},
|
|
98
|
+
"tabWidgetMap": { "tab_1": [], "tab_2": [] },
|
|
99
|
+
"widgetList": []
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 组件通用结构
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"type": "bar",
|
|
108
|
+
"label": "柱状图",
|
|
109
|
+
"category": 0,
|
|
110
|
+
"show": 1,
|
|
111
|
+
"widgetOption": {
|
|
112
|
+
"number": 20001, // 随机5位整数,页面内唯一
|
|
113
|
+
"wrapperNumber": 10001, // 必须等于所在容器的 wrapperOption.number
|
|
114
|
+
"span": 24,
|
|
115
|
+
"height": 280,
|
|
116
|
+
"dynamicStyle": { "padding": "8px", "backgroundColor": "" }
|
|
117
|
+
},
|
|
118
|
+
"widgetParams": []
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## widgetParams 参数类型
|
|
123
|
+
|
|
124
|
+
| type | 说明 | value 类型 |
|
|
125
|
+
|------|------|-----------|
|
|
126
|
+
| `textarea` | 多行文本(数据来源) | `string` |
|
|
127
|
+
| `input` | 单行文本 | `string` |
|
|
128
|
+
| `number` | 数字 | `number` |
|
|
129
|
+
| `switch` | 开关 | `boolean` |
|
|
130
|
+
| `slider` | 滑块 | `number` |
|
|
131
|
+
| `color` | 颜色选择 | `string` |
|
|
132
|
+
| `select` | 下拉选择 | `string` |
|
|
133
|
+
| `radio` | 单选组 | `string` |
|
|
134
|
+
|
|
135
|
+
## 数据来源(widgetParams[0])
|
|
136
|
+
|
|
137
|
+
大多数组件的第一个参数(sort=0)是"数据来源":
|
|
138
|
+
- **静态数据**:`value` 为空,数据在 `typeOptions.dataJson` 中
|
|
139
|
+
- **动态接口**:`value` 设为接口地址,运行时请求替换 `dataJson`
|
|
140
|
+
|
|
141
|
+
**接口引擎地址格式:**
|
|
142
|
+
```
|
|
143
|
+
$ApiBase$/apiengine/{ApiEngineKey}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 所有组件类型
|
|
147
|
+
|
|
148
|
+
### statistic — 统计面板
|
|
149
|
+
```json
|
|
150
|
+
{ "data": [{ "name": "指标名", "value": 100000, "icon": "Top", "bgColor": "", "bgImage": "linear-gradient(...)", "linkUrl": "/" }], "searchData": [] }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### progress — 进度
|
|
154
|
+
```json
|
|
155
|
+
{ "data": [{ "title": "标题", "value": "¥1,000", "subTitle": "目标", "percentage": 60, "color": "#409EFF" }], "searchData": [] }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### links — 快捷导航
|
|
159
|
+
```json
|
|
160
|
+
[{ "title": "导航名", "iconUrl": "图标URL", "linkUrl": "/路径" }]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### carousel — 轮播图
|
|
164
|
+
```json
|
|
165
|
+
[{ "url": "图片URL" }]
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### tabel — 表格
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"headerData": [{ "prop": "字段名", "label": "列标题", "width": "", "align": "center" }],
|
|
172
|
+
"bodyData": [{ "字段名": "值" }],
|
|
173
|
+
"total": 2,
|
|
174
|
+
"searchData": []
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
特殊列标记:`progress_ui`(进度条), `chart_ui`(迷你图), `rate_ui`(评分), `status_ui`(状态标签), `children`(多级表头)
|
|
178
|
+
|
|
179
|
+
### line / bar — 折线图 / 柱状图
|
|
180
|
+
```json
|
|
181
|
+
{ "xAxis": ["Mon", "Tue", "Wed"], "series": [{ "name": "系列", "data": [420, 132, 101] }], "searchData": [] }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### pie — 饼图
|
|
185
|
+
```json
|
|
186
|
+
{ "data": [{ "value": 1048, "name": "搜索引擎" }], "searchData": [] }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### funnel — 漏斗图
|
|
190
|
+
```json
|
|
191
|
+
{ "data": [{ "value": 100, "name": "展示" }], "searchData": [] }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### linebar — 折柱混合
|
|
195
|
+
```json
|
|
196
|
+
{ "xAxis": ["周一"], "series": [{ "name": "蒸发量", "type": "bar", "unit": "ml", "data": [2.0] }, { "name": "温度", "type": "line", "unit": "°C", "data": [2.0] }], "searchData": [] }
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### map — 高德地图
|
|
200
|
+
```json
|
|
201
|
+
[{ "id": "1", "title": "标记名", "position": "经度,纬度", "icon": "", "content": "" }]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### areamap — 区域地图
|
|
205
|
+
```json
|
|
206
|
+
[{ "name": "地区名", "value": 74, "path": "/路径" }]
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### gantt — 甘特图
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"tasks": [{ "id": 10, "text": "任务名", "type": "project", "progress": 0.1, "open": true }],
|
|
213
|
+
"links": [{ "id": 10, "source": 12, "target": 13, "type": 1 }],
|
|
214
|
+
"columns": [{ "name": "text", "label": "任务名称", "width": 220, "tree": true }]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### fullcalendar — 日历看板
|
|
219
|
+
```json
|
|
220
|
+
[{ "id": "event_01", "title": "事件名", "start": "2025-05-12", "end": "2025-05-13", "allDay": true }]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### html — 超文本
|
|
224
|
+
```json
|
|
225
|
+
{ "dataJson": { "col21": "替换值" }, "dataHtml": "<!DOCTYPE html>...<td>${col21}</td>..." }
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### descriptions — 描述列表
|
|
229
|
+
```json
|
|
230
|
+
[{ "label": "字段名", "value": "值", "span": 1, "align": "center" }]
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### 其他组件
|
|
234
|
+
| 组件 | dataJson |
|
|
235
|
+
|------|----------|
|
|
236
|
+
| workbench | `{ "icon": "URL", "title": "欢迎", "subTitle": "副标题" }` |
|
|
237
|
+
| calendar | `[{ "date": "2024-12-01", "content": "事件" }]` |
|
|
238
|
+
| collapse | `[{ "title": "标题", "content": "HTML内容" }]` |
|
|
239
|
+
| steps | `{ "activeIndex": 0, "stepArr": [{ "title": "步骤1", "description": "描述" }] }` |
|
|
240
|
+
| timeline | `[{ "date": "2024-05-01", "title": "标题", "content": "内容" }]` |
|
|
241
|
+
| fish | `[{ "label": "类别", "children": [{ "label": "子项" }] }]` |
|
|
242
|
+
| webgl | `{ "gltfPath": "模型URL", "hdrPath": "HDR URL" }` |
|
|
243
|
+
| office | `{ "filePath": "文件URL" }` |
|
|
244
|
+
| image | widgetParams[0] 为 `input` 类型,value 为图片URL |
|
|
245
|
+
| video | widgetParams[0] 为 `input` 类型,value 为视频URL |
|
|
246
|
+
| browser | widgetParams[0] 为 `input` 类型,value 为网址 |
|
|
247
|
+
| diytable | 传入模块ID和菜单ID,嵌入低代码表格 |
|
|
248
|
+
| diyform | 传入表ID和记录ID,嵌入低代码表单 |
|
|
249
|
+
|
|
250
|
+
### 平台内置业务组件
|
|
251
|
+
|
|
252
|
+
| type | label | 关键参数 | 说明 |
|
|
253
|
+
|------|-------|----------|------|
|
|
254
|
+
| `aiengine` | AI引擎 | 无 | 嵌入 `Microi.Client/src/views/ai-engine/index.vue`;运行态使用紧凑嵌入模式,缩小英雄区、统计卡和快捷入口 |
|
|
255
|
+
| `workcenter` | 工作中心 | `[0]` 内容;`[1]` 待办模块;`[2]` 流程模块 | 展示“我的工作”时可用两个隐藏 `sys_menu` 模块让 `diy-table` 承载待办与流程列表;日历/公告为兼容模式 |
|
|
256
|
+
| `pageengine` | 界面引擎 | `widgetParams[0].type = "pageengine"`,`value` 为 `mic_page.Id` | 嵌入另一个界面引擎页面,设计器提供图形化页面下拉选择 |
|
|
257
|
+
|
|
258
|
+
界面引擎嵌套必须使用 `pageengine-widget.vue` 直接加载 `form-renderer` 组件,不得使用 iframe。每个嵌套页面由 `PAGE_ENGINE_STORE_KEY` 注入独立的 Pinia store,避免子页面覆盖父页面 `formData`;递归页面 Id 通过 `PAGE_ENGINE_RENDER_CONTEXT_KEY` 检测并阻止循环嵌套。父容器和组件高度设为 `0` 表示自动高度,由最外层页面统一滚动。
|
|
259
|
+
|
|
260
|
+
界面引擎渲染页会为 `_IsAdmin` 或 `Level >= 9999` 的用户提供“界面设计”入口,跳转 `/mic/autopage?Id={mic_page.Id}`。入口优先放入后台 TagsView 页签右键菜单,并在页面第一个容器标题栏右侧提供紧凑快捷入口,与 `moreOption` 等操作使用同一个 flex 操作区垂直居中、右对齐;不得额外占用页面高度、增加顶部空白或覆盖标题,非管理员不显示。嵌套 `pageengine` 也必须显示其自身页面的设计入口,并由父页面直接打开对应子页面设计器。
|
|
261
|
+
|
|
262
|
+
首页编排可以组合 `aiengine`、`workcenter`、`diycalendar`、`diytable` 和一个占大区域的 `pageengine`。公告应优先通过绑定 `diy_notice` 的 `diytable` 渲染,使增删改权限继续由 `sys_menu + _RoleLimits` 控制;统计子页面由客户独立替换时,只需修改被嵌入的 `mic_page`,无需重做首页布局。
|
|
263
|
+
|
|
264
|
+
## 运行态布局与滚动规范
|
|
265
|
+
|
|
266
|
+
- 页面只保留一个主滚动容器:仪表盘、首页和嵌套界面优先继承最外层页面滚动;单个 `pannel`、`workcenter`、`diytable`、`diycalendar`、嵌套 `pageengine` 在运行态默认使用内容自适应高度和 `overflow: visible`,不得无条件设置 `overflow: auto`。
|
|
267
|
+
- 固定高度只用于设计器拖拽预览、图表画布或确有虚拟滚动需求的组件。设计态可按 `widgetOption.height` 提供滚动,运行态必须先验证内容能完整展开;不能用双滚动条掩盖容器高度不足。
|
|
268
|
+
- 表格型容器通常按 15 条数据验收,表头、工具栏、全部数据行和分页区应同时可见;首页紧凑表格可通过 `diy-table` 的 `PageSizeList` 追加 `[10]`,并在 `sys_menu.DefaultPageSize` 为空时默认选择候选页码中的最小值。空间不足时先压缩统计卡、工具栏、行高和间距,再考虑增加外层页面长度,不隐藏分页、不在卡片内部滚动。
|
|
269
|
+
- 同一行的容器使用一致的左右边距、内边距、标题高度和顶部基线;同层卡片间距必须相同,左右边缘、标题、工具栏和内容区应形成稳定对齐线。推荐使用统一的 8px 基础间距和 8/12/16px 倍数。
|
|
270
|
+
- 成对容器应设置相同的最小高度;内容较少的组件通过内容居中、合理留白或自适应布局消化空间,不能在底部留下明显的大块无意义空白。内容较多时允许容器自然增高并由页面继续向下滚动。
|
|
271
|
+
- 日历和公告应完整展示工具栏、主体和底部操作区;日历优先使用 `height: auto` / `contentHeight: auto`,公告优先使用标准表单引擎分页与权限按钮。
|
|
272
|
+
- 运行态移动端必须按实际视口自动切为 24 栅格,不能只依赖保存 JSON 中的 `formConfig.mobile`;窗口旋转或宽度变化时也要同步。嵌入的 `diytable` 不得渲染独立列表页的固定返回栏和全局 FAB,新增、页面按钮、批量操作应留在当前容器工具栏内;容器标题操作区允许紧凑换行,但不能绝对定位覆盖标题或其它卡片。
|
|
273
|
+
- `aiengine` 在移动端嵌入首页时应使用内容自适应高度和外层页面滚动,指标卡与快捷入口优先两列紧凑排布,避免 4 个指标和全部快捷入口单列堆叠造成超长首屏;输入区和模型/推理设置必须保持可见且不横向溢出。
|
|
274
|
+
- 交付前必须在真实运行页检查:是否存在内部纵向/横向滚动条、15 条表格分页是否可见、同排容器是否对齐、四周边距是否一致、组件底部是否有异常空白。仅检查设计器画布不算验收完成。
|
|
275
|
+
|
|
276
|
+
## Office/PDF 在线预览自然语言生成规则
|
|
277
|
+
|
|
278
|
+
当用户用自然语言要求“界面引擎预览 PDF/Word/Excel/PPT”“接口引擎返回 PDF 文件”“打开时跳到第 N 页”“按角色显示不同页码”“每 5 秒/10 秒轮询,但只有文件变化才刷新”时,优先生成 `office` 组件,而不是 `iframe/html/browser` 拼接。
|
|
279
|
+
|
|
280
|
+
`office` 组件图形化参数必须完整:
|
|
281
|
+
|
|
282
|
+
```json
|
|
283
|
+
{
|
|
284
|
+
"type": "office",
|
|
285
|
+
"label": "Office/PDF预览",
|
|
286
|
+
"widgetOption": { "span": 24, "height": 720 },
|
|
287
|
+
"widgetParams": [
|
|
288
|
+
{ "sort": 0, "label": "接口引擎地址", "type": "textarea", "value": "$ApiBase$/apiengine/{ApiEngineKey}" },
|
|
289
|
+
{ "sort": 1, "label": "静态文件地址", "type": "input", "value": "" },
|
|
290
|
+
{ "sort": 2, "label": "文件类型", "type": "select", "value": "pdf" },
|
|
291
|
+
{ "sort": 3, "label": "初始页码", "type": "number", "value": 1 },
|
|
292
|
+
{ "sort": 4, "label": "轮询接口秒数", "type": "number", "value": 0 }
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
接口引擎返回契约:
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
return {
|
|
301
|
+
Code: 1,
|
|
302
|
+
Data: {
|
|
303
|
+
FileName: 'report.pdf',
|
|
304
|
+
ContentType: 'application/pdf',
|
|
305
|
+
FileByteBase64: base64,
|
|
306
|
+
PageNumber: 2,
|
|
307
|
+
InitialPage: 2,
|
|
308
|
+
FileKey: activeFileKey + ':p2',
|
|
309
|
+
RefreshSeconds: 5
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
轮询但不刷新时,不要重复返回文件内容;返回下面任一写法即可,前端会保持当前预览不重建:
|
|
315
|
+
|
|
316
|
+
```javascript
|
|
317
|
+
return { Code: 1, Data: { NeedRefresh: false, FileKey: currentFileKey, PageNumber: currentPage } };
|
|
318
|
+
return { Code: 1, Data: { NotModified: true, FileKey: currentFileKey } };
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
接口引擎应读取前端轮询参数:`V8.Param.CurrentFileKey`、`V8.Param.CurrentPageNumber`、`V8.Param.PageNumber`、`V8.Param.WidgetNumber`。当 Redis/缓存中的活动文件、版本号、角色页码没有变化时返回 `NeedRefresh:false`;当文件或页码变化时返回 `FileByteBase64` 或 `FileUrl`,同时返回新的 `FileKey` 和 `PageNumber/InitialPage`。
|
|
322
|
+
|
|
323
|
+
角色页码建议在接口引擎中根据 `V8.CurrentUser.Level`、`RoleName`、`RoleIds` 判断,例如管理员第 2 页、财务第 3 页;不要把角色判断写死在前端 Page JSON。缓存 Key 使用 `Microi:${V8.OsClient}:PageEnginePdfPreview:{业务Key}`。
|
|
324
|
+
|
|
325
|
+
### Office/PDF 接口返回字段细则
|
|
326
|
+
|
|
327
|
+
- `FileName`:文件名,例如 `report.pdf`。
|
|
328
|
+
- `ContentType`:文件类型,PDF 必须用 `application/pdf`;Word/Excel/PPT 可用对应 Office MIME。
|
|
329
|
+
- `FileByteBase64`:文件字节 Base64;适合接口引擎动态生成或转发 PDF。
|
|
330
|
+
- `FileUrl`:文件 URL;适合文件已在 HDFS/OSS/公网可访问时返回。
|
|
331
|
+
- `PageNumber` / `InitialPage`:PDF 打开后跳转页码。角色控制页码必须在接口引擎中根据 `V8.CurrentUser` 判断,不要写死在页面 JSON。
|
|
332
|
+
- `FileKey` / `CacheKey`:文件版本标识,建议包含业务 Key、缓存版本号、角色页码,例如 `activePdf:v3:page2`。
|
|
333
|
+
- `NeedRefresh:false` / `NotModified:true`:轮询时文件和页码未变化,前端保持当前预览并重新按当前页码定位,不重新下载文件。
|
|
334
|
+
- `RefreshSeconds`:接口可返回建议轮询秒数,但页面图形化配置仍是主要控制项;没有自动刷新需求时配置为 `0`。
|
|
335
|
+
|
|
336
|
+
接口引擎要显式读取这些前端参数:
|
|
337
|
+
|
|
338
|
+
- `V8.Param.PageNumber`:组件配置的初始页码。
|
|
339
|
+
- `V8.Param.CurrentPageNumber`:前端当前页码。
|
|
340
|
+
- `V8.Param.CurrentFileKey`:前端当前文件 Key。
|
|
341
|
+
- `V8.Param.CurrentFileUrl`:前端当前文件地址。
|
|
342
|
+
- `V8.Param.WidgetNumber`:当前 office 组件编号。
|
|
343
|
+
|
|
344
|
+
生成示例接口时必须写清楚中文注释:每个参数的含义、Redis/缓存 Key 的作用、什么情况下返回 `NeedRefresh:false`、什么情况下返回新的 `FileByteBase64/FileUrl` 和 `PageNumber`。
|
|
345
|
+
|
|
346
|
+
## searchData 查询条件通用结构
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
[
|
|
350
|
+
{ "prop": "period", "value": "month", "defaultValue": "month", "label": "统计周期", "type": "select", "remote": false, "options": [{ "label": "本日", "value": "today" }, { "label": "本周", "value": "week" }, { "label": "本月", "value": "month" }, { "label": "本季", "value": "quarter" }, { "label": "本年", "value": "year" }, { "label": "去年", "value": "lastYear" }] },
|
|
351
|
+
{ "prop": "department", "value": "", "label": "部门", "type": "select", "remote": false, "optionUrl": "", "options": [{ "label": "全部", "value": "" }] },
|
|
352
|
+
{ "prop": "keyword", "value": "", "label": "关键词", "type": "input" }
|
|
353
|
+
]
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### 统计周期与更多筛选
|
|
357
|
+
|
|
358
|
+
- 统计类组件 `statistic`、`progress`、`bar`、`line`、`linebar`、`pie`、`funnel`、`tabel` 默认必须支持周期筛选。
|
|
359
|
+
- 周期按钮固定包含:本日 `today`、本周 `week`、本月 `month`、本季 `quarter`、本年 `year`、去年 `lastYear`。
|
|
360
|
+
- 同时开启组件的显示查询和日期筛选开关:`statistic` 为 `widgetParams[18]/[19]`,`progress` 为 `[26]/[27]`,`bar/line` 为 `[1]/[16]`,`linebar` 为 `[1]/[18]`,`pie` 为 `[1]/[19]`,`funnel/tabel` 为 `[1]/[13]`。
|
|
361
|
+
- 接口引擎会收到 `period`、`_period`、`start`、`end`、`startDate`、`endDate`。当 `period` 是 `today/week/month/quarter/year/lastYear` 时,必须优先按 `period` 计算时间范围、标题前缀和图表粒度;只有 `period=custom` 或没有 `period` 但有 `start/end` 时,才按自定义时间范围处理。
|
|
362
|
+
- “更多”筛选里放业务条件,例如 `keyword`、`ownerId`、`customerType`、`status`、`department` 等;日期范围作为自定义时间范围保留在更多筛选中。
|
|
363
|
+
|
|
364
|
+
### 交付类首页例外与乱码验收
|
|
365
|
+
|
|
366
|
+
- 如果用户明确说明首页是“项目交付看板、全量采集看板、客户交付状态看板”,或统计的是表单数、模块数、接口引擎数、用户数等平台全量资源,并明确不要本日/本周/本月/本年筛选,则统计组件的 `searchData` 必须保持 `[]`,相关显示查询开关必须为 `false`,不要套用经营看板的周期筛选默认值。
|
|
367
|
+
- 交付类首页优先使用 `statistic`、`progress`、`pie`、`bar`、`linebar`、`html` 等图形化组件,不要为了凑数据把明细表格放到首页;明细应放在低代码表单菜单里查看。
|
|
368
|
+
- 使用脚本或 FormEngine 写入 `mic_page.JsonObj` 时,中文 JSON 必须做编码安全处理:可将最终 JSON 字符串中的非 ASCII 字符转为 `\uXXXX` 后写入,避免数据库或中间层把标题写成 `????`。
|
|
369
|
+
- 写入界面引擎后必须回读 `mic_page.JsonObj` 并检查:不包含 `????`;用户要求无周期筛选时,不包含 `period`、`本日`、`本周`、`本月`、`本年`;`JSON.parse(JsonObj)` 后标题能还原为中文。
|
|
370
|
+
- 如果使用 MCP 或自动页面生成工具后发现它注入了默认周期筛选,必须在最终写入前移除这些筛选,并再次回读验证。
|
|
371
|
+
- 交付类首页必须做可读性验收:彩色统计卡片、深色渐变、图表标签、HTML 组件中的文字必须显式设置高对比文字色。深色/饱和背景使用白色或接近白色文字;浅色背景使用 `#0f172a/#334155` 等深色文字。不得只设置背景色而遗漏标题、数值、图标颜色。
|
|
372
|
+
- 写入后必须检查页面 JSON 和接口返回内容不包含直接暴露的 ECharts 模板占位符:`{a}`、`{b}`、`{c}`、`{d}`、`{value}`。这些只能作为图表 formatter 内部配置使用,不能出现在用户可见标题、饼图中心标题、HTML 文案或默认加载文本中。
|
|
373
|
+
- 容器标题栏的 `moreOption.hidden` 语义必须按“true=隐藏、false=显示”处理。交付首页、统计驾驶舱如无明确跳转需求,容器标题可直接留空并在 HTML/组件内部渲染标题,避免右上角出现默认 `More/更多`。
|
|
374
|
+
- 如果 Page Engine 前端标准图表组件会因为远程数据源自动显示周期筛选,而用户明确要求不要筛选条,可优先使用 `html` 组件承载实时接口返回的图形化驾驶舱;接口返回扁平字段如 `{ html: "<div>...</div>" }`,页面 `dataHtml` 使用 `${html}`。写入后回读验证无周期词、无 `More/更多`、无占位符、无低对比色。
|
|
375
|
+
- 生成首页前必须判断组件沉淀层级:如果某个能力是平台高频标准能力(如指标卡、进度列表、状态分布、排行榜、时间线、描述列表、Office 预览),优先使用或补充 Page Engine 标准组件源码;如果只是当前项目的强业务驾驶舱、组合排版、一次性说明区块,可以用 `html` 组件定制。不要把明显可复用的标准能力长期塞进项目 HTML,也不要为了单个业务驾驶舱新增一堆低复用组件。
|
|
376
|
+
- `html` 组件承载长文本、失败原因、来源清单、备注说明、交付结论时,必须设置 `white-space:normal`、`overflow-wrap:anywhere` 或使用逐条列表/卡片渲染;禁止把多条内容用 `;`、`,` 拼成一整行导致底部说明挤在一起。写入后必须检查长文本在 1366/1440 宽度下能自然换行且不横向溢出。
|
|
377
|
+
- 交付类首页如果使用一个远程 `html` 组件承载整页驾驶舱,运行态应由外层框架滚动,不要给容器和组件写死 1000px 这类固定高度。页面 JSON 可将 `wrapperOption.height` 与 `widgetOption.height` 设为 `0`(或运行态支持的 `auto`),前端运行态必须按内容自适应高度;设计器模式再使用可编辑的默认高度。
|
|
378
|
+
- 首页写入口径说明时,必须区分“用户口头期望数量”“原始资料条目数量”“按业务主体合并后的数量”“后台项目/规则/别名数量”。不要只显示一个“未交付 N 个”,应同时列出部分交付、待执行、阻塞/需业务人员配合的清单和原因。
|
|
379
|
+
|
|
380
|
+
## 生成 JSON 注意事项
|
|
381
|
+
|
|
382
|
+
1. **编号唯一**:`wrapperOption.number` 和 `widgetOption.number` 页面内唯一(随机5位整数)
|
|
383
|
+
2. **关联一致**:`widgetOption.wrapperNumber` 必须等于所在容器的 `wrapperOption.number`
|
|
384
|
+
3. **高度合理**:容器高度 >= 内部组件高度之和
|
|
385
|
+
4. **widgetParams 完整**:必须包含该组件定义的所有参数,不能遗漏
|
|
386
|
+
5. **栅格布局**:span 总和 24 为一行,如 span=12 的两个容器为两列布局
|
|
387
|
+
6. **数据来源**:接口引擎 value 使用稳定路径 `$ApiBase$/apiengine/{Key}`,由运行时通过 `osclient` Header 传入 `$OsClient$`;只有无法设置 Header/Form/Query 的 GET/HEAD 或第三方回调才使用特殊租户路径
|
|
388
|
+
7. **formConfig 完整**:所有字段都应包含,不能省略
|
|
389
|
+
8. **选项卡容器**:组件放在 `tabWidgetMap[tabKey][]` 中,不放在 `widgetList` 中
|
|
390
|
+
## 经营看板周期筛选与布局规则
|
|
391
|
+
|
|
392
|
+
- 老板驾驶舱、经营看板、CRM/订单/售后统计页面,所有统计类组件默认都要提供统一周期筛选:本日、本周、本月、本季、本年、去年,以及“更多”里的自定义时间范围和业务条件。
|
|
393
|
+
- 指标名称不要写死为“月订单金额、月新增客户、月跟进活跃”等固定月份口径。优先使用“订单金额、新增客户、跟进活跃”等中性名称;如业务需要展示周期前缀,运行态会根据当前 `period` 自动显示为“本日/本周/本月/本季/本年/去年”。
|
|
394
|
+
- `statistic`、`progress` 这类内容型组件在运行态必须允许按内容自适应高度。生成 JSON 时也要按数据条数预留高度:统计卡片按每行列数计算行数,避免首次打开时出现卡片底部被遮挡或容器内部滚动条。
|
|
395
|
+
- 有远程数据源的图表/表格/统计组件,点击周期按钮必须真实触发接口请求,并把 `period`、`_period`、`startDate`、`endDate` 等查询条件传给接口。接口返回数据时不要清空组件已有的 `searchData`,除非明确返回新的完整筛选配置。
|
|
396
|
+
- 接口返回的指标名、图例名、表格列名禁止固定写成“月新增客户、月订单金额”等,必须根据最终生效周期输出“本日/本周/本月/本季/本年/去年/自定义”或保持中性名称;否则切换周期后文案会和数据口径冲突。
|
|
397
|
+
- `/mic/autopage/:Id` 面向最终用户运行态展示,应渲染 `formRenderer`;设计器入口才渲染 `formDesigner`,避免导航、组件面板、引导遮罩干扰看板访问和自动化截图。
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: performance-testing
|
|
3
|
+
description: Microi 高并发、性能压力测试规范。用于对 ApiEngine、V8 事件、FormEngine CRUD、VS Code 插件性能页、压力/尖峰/长稳测试、报告、并发、吞吐、延迟分位和瓶颈诊断做压测。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Microi 高并发性能压力测试
|
|
7
|
+
|
|
8
|
+
> 排查长任务时,严禁把“等待或执行超过 1 分钟”直接判定为失败;接口引擎、V8、表单写入、导入导出、批处理等合法长任务默认应支持 10 分钟以上,同步链路推荐 10 分钟,排队窗口推荐 30 分钟。
|
|
9
|
+
|
|
10
|
+
本 Skill 用于设计、实现和执行 Microi 吾码性能测试,覆盖接口引擎、V8 事件、FormEngine 表 CRUD、前端工作流与 VS Code 插件性能测试页。
|
|
11
|
+
|
|
12
|
+
## 总原则
|
|
13
|
+
|
|
14
|
+
1. **真实链路优先**:接口引擎压测默认走真实 HTTP `/apiengine/{ApiEngineKey}`,不要用调试执行接口替代线上链路。
|
|
15
|
+
2. **事件隔离与真实触发分开**:单独测 V8 事件代码可用 `ExecuteV8Event`;要验证表单事件真实成本,必须通过 FormEngine Add/Upt/Del 触发服务端事件。
|
|
16
|
+
3. **读写分级**:先跑只读基线,再跑写入压力;写入压测必须使用测试表、测试租户或自动清理策略。
|
|
17
|
+
4. **渐进升压**:不要一上来极限并发。标准顺序是 smoke -> baseline -> load -> stress -> spike -> soak。
|
|
18
|
+
5. **报告必须可读**:报告至少包含并发数、总请求、成功/失败、RPS、平均耗时、P50/P90/P95/P99、错误 Top、每秒趋势和测试参数。
|
|
19
|
+
6. **通用坑必须回写 Skill**:修复过程中发现可复用的平台、插件、MCP、前端、V8、FormEngine 坑时,不要只写到本地 memory;必须更新对应 `microi.skills/*/SKILL.md`,必要时新增 Skill,让 VS Code 插件打包后其他用户也能受益。
|
|
20
|
+
|
|
21
|
+
## 测试类型
|
|
22
|
+
|
|
23
|
+
| 类型 | 目的 | 建议配置 |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| Smoke | 确认目标可用 | 并发 1-2,总请求 3-10 |
|
|
26
|
+
| Baseline | 单机基线 | 并发 1,总请求 50-200 |
|
|
27
|
+
| Load | 常规高并发 | 目标业务并发,持续 3-10 分钟 |
|
|
28
|
+
| Stress | 找瓶颈 | 阶梯增加并发直到错误率或 P95 不可接受 |
|
|
29
|
+
| Spike | 突增流量 | 短时间从低并发升到高并发,再回落 |
|
|
30
|
+
| Soak | 长稳态 | 30 分钟到数小时,重点看内存、连接池、缓存和慢查询 |
|
|
31
|
+
|
|
32
|
+
## 接口引擎压测
|
|
33
|
+
|
|
34
|
+
默认使用真实入口:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
POST /apiengine/{ApiEngineKey}
|
|
38
|
+
Body: { "OsClient": "xxx", ...业务参数 }
|
|
39
|
+
Header: Authorization / Token 复用当前登录态
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
规则:
|
|
43
|
+
|
|
44
|
+
- 除非明确在调试 V8 代码,否则不要用 `/api/V8Debug/ExecuteApiEngine` 作为性能结论。
|
|
45
|
+
- 测试参数要覆盖真实业务分支,包括分页、关键过滤条件、权限上下文、缓存命中/未命中场景。
|
|
46
|
+
- 返回必须按 DosResult 判断业务成功:`Code === 1` 才算成功;HTTP 200 但 `Code !== 1` 要计入失败。
|
|
47
|
+
- 报告中隐藏 token、密码、API Key 等敏感参数。
|
|
48
|
+
|
|
49
|
+
## V8 事件压测
|
|
50
|
+
|
|
51
|
+
两种模式要区分清楚:
|
|
52
|
+
|
|
53
|
+
1. **隔离执行**:使用 `/api/V8Debug/ExecuteV8Event`,传 `EventType`、`V8Code`、`Form`,用于判断单段 V8 代码在当前服务器上的解释执行成本。
|
|
54
|
+
2. **真实触发**:通过 `/api/formengine/AddFormData`、`/api/formengine/UptFormData`、`/api/formengine/DelFormData` 触发 `SubmitBeforeServerV8`、`SubmitAfterServerV8`、`DataFilterV8`,用于判断真实表单保存/查询成本。
|
|
55
|
+
|
|
56
|
+
常见结论不能混用:隔离执行快,不代表真实保存快;真实保存慢也可能是 SQL、索引、事件、外部 HTTP、缓存或权限链路导致。
|
|
57
|
+
|
|
58
|
+
## FormEngine CRUD 压测
|
|
59
|
+
|
|
60
|
+
推荐标准 Controller 路由:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
POST /api/formengine/GetFormData
|
|
64
|
+
POST /api/formengine/GetTableData
|
|
65
|
+
POST /api/formengine/AddFormData
|
|
66
|
+
POST /api/formengine/UptFormData
|
|
67
|
+
POST /api/formengine/DelFormData
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
请求体必须包含:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{ "OsClient": "xxx", "FormEngineKey": "表名", "Id": "测试行Id", "Name": "测试数据" }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
安全规则:
|
|
77
|
+
|
|
78
|
+
- CRUD 压测默认创建带 `perf_` 前缀的测试 Id,结束后删除本次创建的数据。
|
|
79
|
+
- 不要对生产业务表直接跑删除压力,除非用户明确确认并指定可清理条件。
|
|
80
|
+
- Add/Upt payload 必须由用户或测试清单明确给出,避免写不存在字段导致大量业务失败。
|
|
81
|
+
- 查询压测要区分 `GetFormData` 单行查询和 `GetTableData` 列表查询;列表查询必须带分页。
|
|
82
|
+
|
|
83
|
+
## VS Code 插件性能测试页实现规范
|
|
84
|
+
|
|
85
|
+
插件配置页的“性能测试”功能应遵守:
|
|
86
|
+
|
|
87
|
+
- Webview 只负责收参、进度和报告展示;请求由扩展宿主执行,复用 `ApiClient` 登录态,避免 CORS 和 token 泄露。
|
|
88
|
+
- 支持三类目标:接口引擎、V8 事件、表 CRUD。
|
|
89
|
+
- 支持手动 JSON 参数、并发数、总迭代次数、持续时间、渐进升压、单请求超时。
|
|
90
|
+
- 支持停止测试:停止发起新请求,等待已发出的请求返回后生成部分报告。
|
|
91
|
+
- 支持保存 HTML 报告到 `.microi-performance/`,并可从 VS Code 打开。
|
|
92
|
+
- 报告至少展示:完成、成功、失败、RPS、平均耗时、P95/P99、错误率、延迟分布、每秒趋势、错误 Top。
|
|
93
|
+
|
|
94
|
+
## 结果判定
|
|
95
|
+
|
|
96
|
+
性能测试不是只看 RPS。结论必须同时看:
|
|
97
|
+
|
|
98
|
+
- 错误率是否为 0 或低于目标阈值。
|
|
99
|
+
- P95/P99 是否满足业务 SLA。
|
|
100
|
+
- 是否出现连接超时、身份过期、数据库死锁、缓存穿透、外部接口超时。
|
|
101
|
+
- 写入场景是否确实触发了 V8 事件并完成清理。
|
|
102
|
+
- 长时间测试后内存、线程、连接池、Redis、数据库连接是否稳定。
|
|
103
|
+
|
|
104
|
+
## 用户行为日志队列压测
|
|
105
|
+
|
|
106
|
+
行为审计不能只测 MongoDB 写入速度,必须分开验证请求线程入队、后台批量持久化、故障 spool 和重放幂等:
|
|
107
|
+
|
|
108
|
+
- 使用本地 Fake Mongo/测试库注入故障,不得为了验证日志队列让生产 MongoDB 停机。
|
|
109
|
+
- 至少覆盖 10 万事件、100 以上并发生产者,报告入队吞吐和入队 P95/P99;入队阶段不得等待 MongoDB。
|
|
110
|
+
- 模拟前若干批次写 MongoDB 失败,确认事件先进入 spool,恢复后按 `EventId` 幂等重放,最终唯一事件数必须等于入队成功数。
|
|
111
|
+
- 在未满批次的 150ms 聚合窗口主动触发正常停机,确认已从 Channel 取出的局部批次仍会写 spool,重启后不丢失。
|
|
112
|
+
- 验收同时检查 spool 剩余文件数、重复 EventId、队列内存计数、失败批次数和最后持久化时间;只看接口 HTTP 200 不算通过。
|
|
113
|
+
- “有界 Channel + 无界 ConcurrentQueue 溢出区”仍然是无界队列,禁止作为保护方案。主队列和内存重试区都必须有硬容量;两者满时要同步写持久化 spool/WAL 形成回压,并断言 `EmergencySpooled` 可观测、`Dropped=0`。
|
|
114
|
+
- 进程被强制结束、宿主机掉电等场景若要求绝对零丢失,必须采用外部持久消息队列或同步 WAL;内存 Channel 加异步 spool 只能保证 Mongo 故障和正常停机,不得宣称覆盖尚未落盘的强杀窗口。
|
|
115
|
+
|
|
116
|
+
### 多节点与滚动重启压测
|
|
117
|
+
|
|
118
|
+
- 至少启动两个 API/Worker 实例连接同一 Redis、业务数据库和 MongoDB,通过同一负载均衡入口并发施压;禁止用单进程内开两个对象冒充分布式验收。
|
|
119
|
+
- 同一 `Microi.Job`/定时任务同时到点时,只允许一个节点取得带 TTL、持有者令牌和续租能力的租约;同时验证锁过期后接管、旧持有者不得释放新锁,以及业务幂等约束可阻止锁超时造成的重复副作用。
|
|
120
|
+
- 对同一请求、消息和日志 `EventId` 做跨节点重复投递,最终数据库业务结果和审计事件都只能有一份;节点级本机去重不算通过。
|
|
121
|
+
- 在队列有未完成工作、锁已取得、写库成功但响应未返回等时间点分别终止一个节点,再启动连接同一持久卷和共享存储的替代节点,验证 spool/outbox 自动恢复、共享状态不损坏、其余节点持续服务;节点身份由平台自动管理。
|
|
122
|
+
- 做滚动升级时让新旧版本同时接流量,验证数据库、Redis 值、消息和 API 合约双向兼容;readiness 关闭后节点不得继续接收新工作,宽限期结束前应完成排空或可靠移交。
|
|
123
|
+
|
|
124
|
+
## 后台批处理与初始化任务防护
|
|
125
|
+
|
|
126
|
+
启动初始化、缓存预热、多语言同步、批量翻译、批量建索引、批量修复元数据这类后台任务,不能按普通接口逻辑无界并发执行。即使每个单次 SQL 都很快,几千条元数据逐条 `Get/Add/Upt`、多个租户同时 `Task.Run`、外部翻译超时堆积,也会把数据库连接池、MySQL `max_connections`、`max_connect_errors` 和线程池一起打满。
|
|
127
|
+
|
|
128
|
+
强制要求:
|
|
129
|
+
|
|
130
|
+
- 全量后台任务必须有全局并发上限;多租户初始化默认按租户串行或小并发队列执行,不允许启动时对所有租户同时打满数据库。
|
|
131
|
+
- 单租户内的后台 DB 写入要有租户级限流,不能和正常用户请求抢满连接池。
|
|
132
|
+
- 批处理遇到 `too many connections`、`blocked because of many connection errors`、连接超时等数据库连接压力错误时,必须熔断退避一段时间,停止继续重试撞库。
|
|
133
|
+
- 外部 HTTP/翻译/AI 调用必须有超时、并发阀门和失败降级,超时任务不能无限制堆到线程池。
|
|
134
|
+
- 能预加载或缓存的元数据不要在循环里重复查库,例如树形根节点、字段结构、已有词条字典等。
|
|
135
|
+
- 运行时缓存预热必须先做行数预算检查,只选择必要列并按稳定主键游标固定小分页,同时设置原始行数、字符/字节总预算和单 SQL 超时;只有全部读取成功后才能原子替换旧缓存。多语言缓存只加载租户实际启用的语言列;禁止 `SELECT * ... ToList()` 后再复制为字典,也不能在迁移或依赖检查失败后继续执行无界缓存加载。
|
|
136
|
+
- 进度日志应按批次写入,避免每条数据都写日志表。
|
|
137
|
+
|
|
138
|
+
### API 进程内存熔断验收
|
|
139
|
+
|
|
140
|
+
- 进程必须同时有软阈值和硬阈值。软阈值进入 readiness 失败并拒绝新业务请求;硬阈值连续命中后先有界停机,失控任务不响应取消时允许在宽限期后强制退出,不能等宿主机 OOM。
|
|
141
|
+
- liveness 与 readiness 必须分开验证:内存压力下 liveness 仍可用于判断进程存活,readiness 必须让负载均衡摘除当前节点。
|
|
142
|
+
- 启动 API、Node、浏览器自动化和长稳压测前后都要记录进程树 PID;每 15-30 秒采集 Working Set、Private Bytes、托管堆和整机可用内存,达到全机 95% 时立即终止本次测试进程树。
|
|
143
|
+
- 进程内存熔断必须使用实际驻留内存(Windows Working Set / Linux RSS)作为压力指标。Linux 的 `PrivateMemorySize64` 可能包含 .NET GC 预留的巨大虚拟地址空间,只能用于辅助诊断,禁止与 Working Set 取较大值后作为拒绝请求或退出进程的依据。
|
|
144
|
+
- 内存阈值不能固定为与机器规格无关的小常量。平台按 cgroup 容器限额或宿主机物理内存动态计算,软阈值为 95%、硬阈值为 98%,不再增加环境变量或 appsettings 参数;多节点或数据库共用宿主机时必须给每个容器设置独立 memory limit,防止各节点重复使用整机额度。
|
|
145
|
+
- 内存修复不能只用“限制进程最大内存”代替根因治理。报告必须指出造成增长的具体对象/查询/队列,并分别验证根因边界和进程最后防线。
|
|
146
|
+
|
|
147
|
+
## 数据库连接压力防护
|
|
148
|
+
|
|
149
|
+
高并发问题不能只靠调大 MySQL `max_connections`。平台后端和 ORM 层必须把数据库连接视为受保护资源,所有普通接口、接口引擎、FormEngine、V8 事件、后台任务和批量导入都应该走统一连接打开入口。
|
|
150
|
+
|
|
151
|
+
强制要求:
|
|
152
|
+
|
|
153
|
+
- ORM 层需要按连接串限制并发 `Open/OpenAsync`,避免瞬时 1 万个请求同时抢连接池或打爆 MySQL 握手。
|
|
154
|
+
- 遇到 `Host ... is blocked because of many connection errors`、`too many connections`、连接池超时、连接超时等错误时,应短时间熔断退避,快速失败并停止继续撞库。
|
|
155
|
+
- API 层需要对租户、用户、IP、重接口和写接口配置限流/并发阀门;压力超过阈值时返回明确的“系统繁忙/稍后重试”,不要无限排队。
|
|
156
|
+
- 连接串的 `Max Pool Size` 不能所有租户默认写很大;要按单进程、租户数、读写库和 MySQL `max_connections` 统一核算。
|
|
157
|
+
- 事务、`DbDataReader`、批量导入、后台任务必须确保连接按 `using/Dispose/Close` 释放;压测后要观察连接池、MySQL 当前连接数和等待线程是否回落。
|
|
158
|
+
|
|
159
|
+
## V8 / 接口引擎失控保护
|
|
160
|
+
|
|
161
|
+
接口引擎和表单 V8 事件属于用户可编程能力,必须默认假设会出现误写死循环、循环套循环查库、未分页大查询、外部接口长时间超时等问题。平台要在运行时给出硬保护,而不能只靠代码审查。
|
|
162
|
+
|
|
163
|
+
强制要求:
|
|
164
|
+
|
|
165
|
+
- V8/Jint 默认超时、最大语句数、内存、递归深度必须保守,并提供环境变量或配置项给私有部署按机器规格放宽。
|
|
166
|
+
- V8Engine.Run 入口必须有全局、租户级、接口/事件级并发阀门;过载时返回 `Code=0` 和“系统繁忙,请稍后重试”,不要继续进入事务和数据库。
|
|
167
|
+
- HTTP 入口应在进入 Controller 前做全局、租户、路由、接口引擎级背压;过载时直接返回 DosResult 风格 JSON,保证前端能弹出明确提示。
|
|
168
|
+
- 接口引擎配置里的 Timeout、MaxStatements、LimitMemory、LimitRecursion 不能无限放大,必须被平台级最大值夹住。
|
|
169
|
+
- 对外部 HTTP、AI、翻译、短信、第三方 ERP 这类慢操作,V8 扩展层必须设置超时和并发限制,不能把同步等待堆满线程池。
|
|
170
|
+
|
|
171
|
+
## 复盘写回规则
|
|
172
|
+
|
|
173
|
+
每次压测暴露通用问题后,把经验写回最贴近的 Skill:
|
|
174
|
+
|
|
175
|
+
- FormEngine 路由/CRUD 坑 -> `v8-formengine-http/SKILL.md` 或本 Skill。
|
|
176
|
+
- V8 代码性能/缓存/SQL -> `v8-sql-query`、`v8-cache-pattern`、`v8-debugging`。
|
|
177
|
+
- 前端弹层、主题、表格搜索等 UI 运行时坑 -> `microi-client-frontend/SKILL.md`。
|
|
178
|
+
- 插件/MCP 能力缺口 -> 本 Skill、`playwright-e2e` 或 `microi-system-delivery`。
|
|
179
|
+
|
|
180
|
+
本地 memory 只能作为临时笔记;对用户和其他安装插件的人有价值的规则,必须进入 `microi.skills`。
|
|
181
|
+
|
|
182
|
+
## 长任务限流原则
|
|
183
|
+
|
|
184
|
+
吾码经常承载大型业务系统,接口引擎、初始化任务、批量导入、批量修复、ERP/第三方同步等场景可能需要处理上万条数据,正常执行时间可能超过 5 分钟。平台保护不能简单把“执行时间长”判定为异常,也不能默认短等待后快速失败。
|
|
185
|
+
|
|
186
|
+
强制要求:
|
|
187
|
+
- 长任务保护的核心是“并发阀门 + 排队限流 + 可配置超时”,不是粗暴拒绝。
|
|
188
|
+
- 接口引擎/V8 默认执行窗口应能覆盖常见长任务,默认建议不少于 10 分钟;私有部署可通过环境变量继续放宽。
|
|
189
|
+
- 入口限流对接口引擎/V8 应使用长排队窗口;只有排队窗口耗尽、请求被客户端取消、或平台资源已进入保护熔断时,才返回“系统繁忙/正在排队,请稍后重试”。
|
|
190
|
+
- 不允许为了保护数据库而误杀合法批处理。真正需要治理的是无界并发、循环套循环查库、未分页大查询、外部接口无限等待和连接泄漏。
|
|
191
|
+
- 对确实超过 HTTP/网关可承受时间的任务,应改造为后台任务/MQ/进度日志模式;后台任务进度优先通过吾码标准 WebSocket/SignalR 推送,不要让前端频繁轮询接口。但这属于交互形态升级,不应影响同步接口引擎的兼容性。
|
|
192
|
+
- 应用商城安装、初始化多语言、批量导入、批量修复、跨系统同步等用户明确需要等待进度的操作,优先做成后台任务。前端按钮使用 `DiyCommon.ApiEngine.RunBackground` 或菜单按钮后台任务字段,后端接口引擎通过 `V8.Method.UpdateBackgroundTask({ Progress, Message, Total, Current })` 持续上报。
|
|
193
|
+
- 后台任务必须有任务标题、状态、进度、耗时、错误摘要和清理/取消能力。取消只表示“请求停止后续步骤”,不能强行中断已经写入中的数据库事务。
|
|
194
|
+
|
|
195
|
+
## 恶意访问与雪崩防护
|
|
196
|
+
|
|
197
|
+
高并发保护要区分“合法排队”和“恶意攻击”。一个合法接口执行 5 分钟不代表攻击;一个 IP 在很短时间内疯狂请求、扫描不存在接口、反复触发 4xx/5xx,才应进入安全防护。
|
|
198
|
+
|
|
199
|
+
强制要求:
|
|
200
|
+
- HTTP 层应提供全局、租户、路由、接口引擎/V8 等并发阀门,用于排队和保护资源;IP 层安全防护只用于识别高频恶意请求,不要替代业务并发治理。
|
|
201
|
+
- 恶意访问记录不应每次请求都同步写数据库。最近访问明细可先保存在内存或专用轻量存储,真正封禁、解封、攻击识别事件才写系统日志和 `mci_` 安全表;必要的访问明细也要异步、采样或只记录可疑/拦截请求。
|
|
202
|
+
- 自动封禁必须有白名单、自动解封时间、手动解封接口和系统日志记录;封禁响应要返回 DosResult 风格 JSON,让前端能明确提示。
|
|
203
|
+
- 部署在 Nginx/网关后时,必须明确是否信任 `X-Forwarded-For`;公网直连且无法保证 Header 可信时,应使用真实 `RemoteIpAddress`。
|
|
204
|
+
- 安全阈值默认要保守,避免误伤同一公司出口 NAT 下的正常用户;需要更激进的策略时应交给网关/WAF 或私有部署配置调整。
|
|
205
|
+
- 系统级安全防护表必须使用 `mci_` 前缀,例如 `mci_security_attack_event`、`mci_security_ip_block`、`mci_security_access_log`。业务系统表不要使用 `mci_` 前缀。
|
|
206
|
+
- 攻击事件落库必须做原因去重和时间窗合并,不要把同一个 IP、同一个原因、同一个时间窗的失败原因重复写成上万条记录。
|
|
207
|
+
- 不能把“接口执行时间长”“排队时间长”“后台任务运行 5 分钟以上”单独作为恶意攻击依据。恶意判断主要看短时间高频请求、异常状态码爆发、扫描不存在路径、命中封禁后继续请求等行为。
|