@microi.net/cli 6.0.9 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codebuddy-plugin/marketplace.json +2 -2
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.workbuddy-plugin/marketplace.json +2 -2
- package/.workbuddy-plugin/plugin.json +1 -1
- package/assets/build-meta.json +5 -5
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/mcp-server.js +116 -116
- package/scripts/microi-cli.js +1 -1
- package/scripts/microi-skills.meta.json +312 -312
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +45 -45
- package/skills/README.md +1 -1
- package/skills/ai-engine/SKILL.md +2 -2
- package/skills/ai-engine/references/ai-employees.md +48 -48
- package/skills/ai-engine/references/self-hosted-digital-human.md +59 -59
- package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +2 -2
- package/skills/app-store/SKILL.md +531 -531
- package/skills/business-blueprint/SKILL.md +193 -193
- package/skills/datasource-engine/SKILL.md +93 -93
- package/skills/dos-orm/SKILL.md +105 -105
- package/skills/dos-orm/references/api-reference.md +229 -229
- package/skills/email-engine/SKILL.md +81 -81
- package/skills/email-engine/references/v8-email.md +34 -34
- package/skills/job-engine/SKILL.md +176 -176
- package/skills/message-notification/SKILL.md +156 -156
- package/skills/message-notification/references/contracts.md +102 -102
- package/skills/microi/SKILL.md +14 -14
- package/skills/microi-ai-app-auth.js +1 -1
- package/skills/microi-ai-application/SKILL.md +115 -115
- package/skills/microi-ai-application/references/frontend-baseline.md +164 -164
- package/skills/microi-client-frontend/references/progressive-01-3-/345/212/250/346/200/201/346/214/211/351/222/256/347/263/273/347/273/237.md +10 -10
- package/skills/microi-client-frontend/references/progressive-02-8-/350/277/220/350/241/214/346/227/266/351/253/230/351/242/221/345/235/221/345/244/215/347/233/230.md +10 -10
- package/skills/microi-client-frontend/references/progressive-03-vue3-/345/211/215/347/253/257/345/276/256/346/234/215/345/212/241/345/256/277/344/270/273/350/247/204/345/210/231.md +10 -10
- package/skills/microi-codex/SKILL.md +130 -130
- package/skills/microi-codex-installer/SKILL.md +1 -1
- package/skills/microi-datasource-mapping/SKILL.md +131 -122
- package/skills/microi-db-schema/SKILL.md +175 -175
- package/skills/microi-db-schema/references/core-tables.md +695 -695
- package/skills/microi-db-schema/references/form-component-options.md +256 -256
- package/skills/microi-db-schema/references/schema-overview.md +202 -202
- package/skills/microi-db-schema/references/schema.md +646 -646
- package/skills/microi-db-schema/references/table-catalog.md +1599 -1599
- package/skills/microi-deployment/SKILL.md +260 -260
- package/skills/microi-deployment/references/deployment-matrix.md +109 -109
- package/skills/microi-docs-coverage/SKILL.md +133 -133
- package/skills/microi-docs-coverage/references/capability-map.md +91 -91
- package/skills/microi-form-engine/SKILL.md +338 -338
- package/skills/microi-form-engine/references/component-catalog.md +218 -218
- package/skills/microi-form-engine/references/data-source-events.md +124 -124
- package/skills/microi-left-right-layout/SKILL.md +141 -141
- package/skills/microi-microservice/SKILL.md +6 -1
- package/skills/microi-microservice/references/runtime-delivery.md +282 -282
- package/skills/microi-solution-quotation/SKILL.md +78 -78
- package/skills/microi-sso/SKILL.md +102 -102
- package/skills/microi-sso/references/acceptance.md +1 -1
- package/skills/microi-sso/references/configuration-and-security.md +53 -53
- package/skills/microi-sso/references/inbound.md +53 -53
- package/skills/microi-sso/references/outbound.md +43 -43
- package/skills/microi-system-delivery/SKILL.md +102 -100
- package/skills/microi-system-delivery/references/context-quality.md +56 -56
- package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +11 -11
- 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 +12 -12
- package/skills/microi-system-delivery/references/progressive-03-/345/256/211/345/205/250/344/270/216/350/267/250/347/253/257/351/252/214/346/224/266.md +71 -71
- package/skills/module-engine/SKILL.md +297 -297
- package/skills/module-engine/references/module-config.md +204 -204
- package/skills/ocr-engine/SKILL.md +113 -113
- package/skills/page-engine/references/progressive-01-/346/211/200/346/234/211/347/273/204/344/273/266/347/261/273/345/236/213.md +10 -10
- package/skills/page-engine/references/progressive-02-/347/211/210/346/234/254/345/216/206/345/217/262-/345/271/266/345/217/221/344/277/235/345/255/230/344/270/216/345/233/236/346/273/232.md +12 -12
- package/skills/performance-testing/SKILL.md +227 -227
- package/skills/playwright-e2e/SKILL.md +2 -0
- package/skills/print-engine/SKILL.md +259 -259
- package/skills/production-readonly-audit/SKILL.md +41 -41
- package/skills/report-engine/SKILL.md +83 -83
- package/skills/search-engine/SKILL.md +75 -75
- package/skills/spider-engine/SKILL.md +190 -190
- package/skills/system-observability/SKILL.md +264 -264
- package/skills/system-observability/references/memory-incident-triage.md +77 -77
- package/skills/translate-engine/SKILL.md +140 -140
- package/skills/ui-design/assets/templates/MCI-DESIGN.md +206 -206
- package/skills/ui-design/references/design-pattern-library.md +184 -184
- package/skills/ui-design/references/mci-design-contract.md +163 -163
- package/skills/ui-design/references/motion-and-media.md +78 -78
- package/skills/ui-design/references/product-flow-recipes.md +94 -94
- package/skills/uniapp-mall-assets/SKILL.md +176 -176
- package/skills/unity-integration/SKILL.md +178 -178
- package/skills/unity-integration/references/ai-app-delivery.md +1 -1
- package/skills/unity-integration/references/sdk-api.md +82 -82
- package/skills/unity-integration/references/toolbox-migration.md +66 -66
- package/skills/unity-integration/references/webgl-hosting.md +57 -57
- package/skills/v8-api-config/SKILL.md +400 -400
- package/skills/v8-cache-pattern/SKILL.md +320 -320
- package/skills/v8-debugging/SKILL.md +284 -284
- package/skills/v8-explorer-tree/SKILL.md +228 -228
- package/skills/v8-file-upload/SKILL.md +1 -1
- package/skills/v8-file-upload/references/cdn-alias-repair.md +13 -13
- package/skills/v8-formengine-http/SKILL.md +1 -1
- package/skills/v8-frontend-events/references/bluetooth-print-api.md +135 -135
- package/skills/v8-frontend-events/references/bluetooth-print.md +258 -258
- package/skills/v8-image-processing/SKILL.md +190 -190
- package/skills/v8-image-processing/references/api-reference.md +623 -623
- package/skills/v8-mongodb/SKILL.md +201 -201
- package/skills/v8-mq-mqtt/references/mqtt-production.md +352 -352
- package/skills/v8-saas-multi-tenant/SKILL.md +4 -0
- package/skills/v8-security/SKILL.md +2 -0
- package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +9 -3
- package/skills/v8-sql-query/SKILL.md +302 -302
- package/skills/v8-tcp-integration/SKILL.md +69 -2
- package/skills/v8-template-engine/SKILL.md +167 -167
- package/skills/v8-utilities/SKILL.md +1 -0
- package/skills/v8-utilities/references/client-api-index.md +143 -143
- package/skills/v8-utilities/references/platform-http-routes.md +83 -83
- package/skills/v8-utilities/references/server-api-index.md +2 -1
- package/skills/v8-workflow/references/workflow-configuration.md +49 -49
- package/skills/vision-engine/SKILL.md +160 -160
- package/skills/vision-engine/references/architecture-and-acceptance.md +194 -194
- package/skills/workspace-conventions/SKILL.md +168 -162
- package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +2 -2
- 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 +34 -34
- 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 +6 -6
- 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 +188 -188
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
# Microi 产品流程配方
|
|
2
|
-
|
|
3
|
-
本文件用于登录、注册、订阅、支付、预约、权限、搜索、设置和敏感信息采集等真实产品流程。所有流程先保证任务闭环,再做视觉装饰。
|
|
4
|
-
|
|
5
|
-
## 目录
|
|
6
|
-
|
|
7
|
-
1. 登录与注册
|
|
8
|
-
2. 首次引导
|
|
9
|
-
3. 权限申请
|
|
10
|
-
4. 订阅与付费
|
|
11
|
-
5. 结算与支付
|
|
12
|
-
6. 搜索、筛选与列表
|
|
13
|
-
7. 创建与编辑
|
|
14
|
-
8. 设置与个人中心
|
|
15
|
-
9. 敏感资料采集
|
|
16
|
-
10. 通用验收
|
|
17
|
-
|
|
18
|
-
## 登录与注册
|
|
19
|
-
|
|
20
|
-
- 默认只突出一种主登录方式,其他方式收进次级入口,避免并排两个完整登录系统。
|
|
21
|
-
- 登录页保留返回首页、帮助和协议入口;输入错误时不清空账号,密码错误不暴露账号是否存在。
|
|
22
|
-
- 验证码必须同时有图片、刷新、过期状态和请求中状态;后端未开启验证码时不渲染占位框。
|
|
23
|
-
- “记住密码”明确作用范围;公共设备提供退出全部设备或不记住提示。
|
|
24
|
-
- 登录成功直接进入目标页,禁止先闪过中间成功页。
|
|
25
|
-
- 注册按账号 -> 验证 -> 必要资料 -> 完成分段;非必要资料延后到首次使用。
|
|
26
|
-
|
|
27
|
-
## 首次引导
|
|
28
|
-
|
|
29
|
-
- 开始前说明预计步骤、需要的资料、用途和能否稍后完成。
|
|
30
|
-
- 每一步只问同一类信息;显示当前步骤和总步骤,但不要伪造进度百分比。
|
|
31
|
-
- 支持保存草稿和恢复;关闭前说明哪些内容已保存。
|
|
32
|
-
- 完成页给出下一项具体任务,不只显示庆祝插画。
|
|
33
|
-
|
|
34
|
-
## 权限申请
|
|
35
|
-
|
|
36
|
-
- 在系统权限弹窗前展示预说明:为什么需要、将获得什么、拒绝后还能做什么。
|
|
37
|
-
- 一次只申请当前动作需要的权限;相机、定位、通知、通讯录不能在首屏批量索取。
|
|
38
|
-
- 拒绝后提供设置入口和不授权替代路径,不循环弹窗。
|
|
39
|
-
- 权限状态变化后立即刷新页面状态,不能要求用户重启应用。
|
|
40
|
-
|
|
41
|
-
## 订阅与付费
|
|
42
|
-
|
|
43
|
-
- 价值在价格之前;3-6 条图标化权益即可,避免长列表。
|
|
44
|
-
- 价格写清周期、续费、试用结束、税费和取消条件;这些信息放在 CTA 附近。
|
|
45
|
-
- 推荐方案用轻量边框/底色和说明强化,不用夸张倒计时或羞辱式拒绝按钮。
|
|
46
|
-
- 只有一个方案时不制造无意义选择;多个方案按典型用户目标命名。
|
|
47
|
-
- 购买后显示生效能力、订单入口、管理订阅和下一步。
|
|
48
|
-
|
|
49
|
-
## 结算与支付
|
|
50
|
-
|
|
51
|
-
- 结构顺序:商品/服务摘要 -> 地址/时间 -> 优惠 -> 支付方式 -> 金额明细 -> 提交。
|
|
52
|
-
- 总金额和主按钮固定可见,但不能遮挡最后一项;金额变化时说明原因。
|
|
53
|
-
- 提交后锁定重复支付并显示处理中;未知结果进入“正在确认”,不能直接当失败。
|
|
54
|
-
- 支付失败保留订单和输入,提供更换方式、重试和联系客服。
|
|
55
|
-
- 成功页展示订单号、核心信息、查看订单和继续使用。
|
|
56
|
-
|
|
57
|
-
## 搜索、筛选与列表
|
|
58
|
-
|
|
59
|
-
- 搜索框说明可搜字段;输入后提供清除、历史或建议,但不自动提交破坏输入。
|
|
60
|
-
- 高频筛选放在页面表层,低频筛选进入抽屉;已选条件用可移除标签展示。
|
|
61
|
-
- 排序和筛选结果数及时反馈;切换条件时保持列表容器高度并用骨架避免跳动。
|
|
62
|
-
- 每个卡片/行都要有对象身份、状态、关键元数据、时间和明确点击结果。
|
|
63
|
-
- 空结果区分“没有数据”和“筛选无匹配”,后者提供清除条件。
|
|
64
|
-
|
|
65
|
-
## 创建与编辑
|
|
66
|
-
|
|
67
|
-
- 字段按任务分组;必填项最少化,帮助文字靠近字段。
|
|
68
|
-
- 实时校验只用于格式和明确约束;服务端校验在提交后映射到对应字段和顶部摘要。
|
|
69
|
-
- 提交按钮具备默认、loading、成功、失败、disabled;失败不丢输入。
|
|
70
|
-
- 离开脏表单前提示;自动保存要显示最后保存时间和失败状态。
|
|
71
|
-
- 删除、归档、停用语义分开;不可逆操作二次确认并说明影响对象。
|
|
72
|
-
|
|
73
|
-
## 设置与个人中心
|
|
74
|
-
|
|
75
|
-
- 身份信息、账户安全、通知、隐私、外观、数据与帮助分组。
|
|
76
|
-
- 当前值比入口名更重要时在右侧展示,例如手机号尾号、语言、主题。
|
|
77
|
-
- 危险操作单独放在底部区域,不和普通设置混排。
|
|
78
|
-
- 修改手机号、密码、密钥和付款方式时重新验证身份。
|
|
79
|
-
- 注销前展示数据影响、冷静期、恢复方式和进度,不能只有一个红色按钮。
|
|
80
|
-
|
|
81
|
-
## 敏感资料采集
|
|
82
|
-
|
|
83
|
-
- 开始前说明采集内容、用途、存储方式、预计时间和所需环境。
|
|
84
|
-
- 先选择证件/资料类型,再打开相机或上传;提供手工录入备选。
|
|
85
|
-
- 相机界面使用深色背景、清晰取景框、实时成功边界和大触控按钮。
|
|
86
|
-
- 上传中显示进度、取消、失败重试;识别后让用户核对关键字段。
|
|
87
|
-
- 页面内嵌隐私和安全信号,但不使用无法验证的“绝对安全”宣传。
|
|
88
|
-
|
|
89
|
-
## 通用验收
|
|
90
|
-
|
|
91
|
-
- 键盘顺序、焦点环、错误关联、屏幕阅读标签和 44px 触摸目标可用。
|
|
92
|
-
- 刷新、后退、重复提交、弱网、超时、服务端 4xx/5xx 都有确定结果。
|
|
93
|
-
- 个人数据只在需要位置显示;日志、Toast 和错误信息不得泄露 token、SQL 或内部表名。
|
|
94
|
-
- 390px 移动端不横向滚动,键盘弹起不遮挡当前字段和主按钮。
|
|
1
|
+
# Microi 产品流程配方
|
|
2
|
+
|
|
3
|
+
本文件用于登录、注册、订阅、支付、预约、权限、搜索、设置和敏感信息采集等真实产品流程。所有流程先保证任务闭环,再做视觉装饰。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
1. 登录与注册
|
|
8
|
+
2. 首次引导
|
|
9
|
+
3. 权限申请
|
|
10
|
+
4. 订阅与付费
|
|
11
|
+
5. 结算与支付
|
|
12
|
+
6. 搜索、筛选与列表
|
|
13
|
+
7. 创建与编辑
|
|
14
|
+
8. 设置与个人中心
|
|
15
|
+
9. 敏感资料采集
|
|
16
|
+
10. 通用验收
|
|
17
|
+
|
|
18
|
+
## 登录与注册
|
|
19
|
+
|
|
20
|
+
- 默认只突出一种主登录方式,其他方式收进次级入口,避免并排两个完整登录系统。
|
|
21
|
+
- 登录页保留返回首页、帮助和协议入口;输入错误时不清空账号,密码错误不暴露账号是否存在。
|
|
22
|
+
- 验证码必须同时有图片、刷新、过期状态和请求中状态;后端未开启验证码时不渲染占位框。
|
|
23
|
+
- “记住密码”明确作用范围;公共设备提供退出全部设备或不记住提示。
|
|
24
|
+
- 登录成功直接进入目标页,禁止先闪过中间成功页。
|
|
25
|
+
- 注册按账号 -> 验证 -> 必要资料 -> 完成分段;非必要资料延后到首次使用。
|
|
26
|
+
|
|
27
|
+
## 首次引导
|
|
28
|
+
|
|
29
|
+
- 开始前说明预计步骤、需要的资料、用途和能否稍后完成。
|
|
30
|
+
- 每一步只问同一类信息;显示当前步骤和总步骤,但不要伪造进度百分比。
|
|
31
|
+
- 支持保存草稿和恢复;关闭前说明哪些内容已保存。
|
|
32
|
+
- 完成页给出下一项具体任务,不只显示庆祝插画。
|
|
33
|
+
|
|
34
|
+
## 权限申请
|
|
35
|
+
|
|
36
|
+
- 在系统权限弹窗前展示预说明:为什么需要、将获得什么、拒绝后还能做什么。
|
|
37
|
+
- 一次只申请当前动作需要的权限;相机、定位、通知、通讯录不能在首屏批量索取。
|
|
38
|
+
- 拒绝后提供设置入口和不授权替代路径,不循环弹窗。
|
|
39
|
+
- 权限状态变化后立即刷新页面状态,不能要求用户重启应用。
|
|
40
|
+
|
|
41
|
+
## 订阅与付费
|
|
42
|
+
|
|
43
|
+
- 价值在价格之前;3-6 条图标化权益即可,避免长列表。
|
|
44
|
+
- 价格写清周期、续费、试用结束、税费和取消条件;这些信息放在 CTA 附近。
|
|
45
|
+
- 推荐方案用轻量边框/底色和说明强化,不用夸张倒计时或羞辱式拒绝按钮。
|
|
46
|
+
- 只有一个方案时不制造无意义选择;多个方案按典型用户目标命名。
|
|
47
|
+
- 购买后显示生效能力、订单入口、管理订阅和下一步。
|
|
48
|
+
|
|
49
|
+
## 结算与支付
|
|
50
|
+
|
|
51
|
+
- 结构顺序:商品/服务摘要 -> 地址/时间 -> 优惠 -> 支付方式 -> 金额明细 -> 提交。
|
|
52
|
+
- 总金额和主按钮固定可见,但不能遮挡最后一项;金额变化时说明原因。
|
|
53
|
+
- 提交后锁定重复支付并显示处理中;未知结果进入“正在确认”,不能直接当失败。
|
|
54
|
+
- 支付失败保留订单和输入,提供更换方式、重试和联系客服。
|
|
55
|
+
- 成功页展示订单号、核心信息、查看订单和继续使用。
|
|
56
|
+
|
|
57
|
+
## 搜索、筛选与列表
|
|
58
|
+
|
|
59
|
+
- 搜索框说明可搜字段;输入后提供清除、历史或建议,但不自动提交破坏输入。
|
|
60
|
+
- 高频筛选放在页面表层,低频筛选进入抽屉;已选条件用可移除标签展示。
|
|
61
|
+
- 排序和筛选结果数及时反馈;切换条件时保持列表容器高度并用骨架避免跳动。
|
|
62
|
+
- 每个卡片/行都要有对象身份、状态、关键元数据、时间和明确点击结果。
|
|
63
|
+
- 空结果区分“没有数据”和“筛选无匹配”,后者提供清除条件。
|
|
64
|
+
|
|
65
|
+
## 创建与编辑
|
|
66
|
+
|
|
67
|
+
- 字段按任务分组;必填项最少化,帮助文字靠近字段。
|
|
68
|
+
- 实时校验只用于格式和明确约束;服务端校验在提交后映射到对应字段和顶部摘要。
|
|
69
|
+
- 提交按钮具备默认、loading、成功、失败、disabled;失败不丢输入。
|
|
70
|
+
- 离开脏表单前提示;自动保存要显示最后保存时间和失败状态。
|
|
71
|
+
- 删除、归档、停用语义分开;不可逆操作二次确认并说明影响对象。
|
|
72
|
+
|
|
73
|
+
## 设置与个人中心
|
|
74
|
+
|
|
75
|
+
- 身份信息、账户安全、通知、隐私、外观、数据与帮助分组。
|
|
76
|
+
- 当前值比入口名更重要时在右侧展示,例如手机号尾号、语言、主题。
|
|
77
|
+
- 危险操作单独放在底部区域,不和普通设置混排。
|
|
78
|
+
- 修改手机号、密码、密钥和付款方式时重新验证身份。
|
|
79
|
+
- 注销前展示数据影响、冷静期、恢复方式和进度,不能只有一个红色按钮。
|
|
80
|
+
|
|
81
|
+
## 敏感资料采集
|
|
82
|
+
|
|
83
|
+
- 开始前说明采集内容、用途、存储方式、预计时间和所需环境。
|
|
84
|
+
- 先选择证件/资料类型,再打开相机或上传;提供手工录入备选。
|
|
85
|
+
- 相机界面使用深色背景、清晰取景框、实时成功边界和大触控按钮。
|
|
86
|
+
- 上传中显示进度、取消、失败重试;识别后让用户核对关键字段。
|
|
87
|
+
- 页面内嵌隐私和安全信号,但不使用无法验证的“绝对安全”宣传。
|
|
88
|
+
|
|
89
|
+
## 通用验收
|
|
90
|
+
|
|
91
|
+
- 键盘顺序、焦点环、错误关联、屏幕阅读标签和 44px 触摸目标可用。
|
|
92
|
+
- 刷新、后退、重复提交、弱网、超时、服务端 4xx/5xx 都有确定结果。
|
|
93
|
+
- 个人数据只在需要位置显示;日志、Toast 和错误信息不得泄露 token、SQL 或内部表名。
|
|
94
|
+
- 390px 移动端不横向滚动,键盘弹起不遮挡当前字段和主按钮。
|
|
@@ -1,176 +1,176 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uniapp-mall-assets
|
|
3
|
-
description: Microi UniApp、H5、商城与 PC 页面中的上传图片、附件、FileServer、私有文件临时 URL 和富文本资源安全规范。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
> **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
|
|
7
|
-
|
|
8
|
-
# Microi UniApp / 商城资源路径规范
|
|
9
|
-
|
|
10
|
-
本 Skill 只描述平台通用资源规则。不得写入客户名称、真实 `OsClient`、客户应用路径、客户接口 Key 或特定商城结算规则。
|
|
11
|
-
|
|
12
|
-
## 先识别资源类型
|
|
13
|
-
|
|
14
|
-
Microi 上传字段存在四种必须长期兼容的存储形态,不能假设字段一定是字符串:
|
|
15
|
-
|
|
16
|
-
| 存储形态 | 示例 | 兼容要求 |
|
|
17
|
-
|---|---|---|
|
|
18
|
-
| 旧版绝对地址字符串 | `https://cdn.example.com/a.png` | 校验协议和允许域名后原样使用,禁止再次拼接 `FileServer` |
|
|
19
|
-
| 旧版相对路径字符串 | `/tenant/a.png` | 仅此类纯相对对象 Key 拼接当前租户 `FileServer` |
|
|
20
|
-
| 新版单图对象 | `{ FilePathName:'/tenant/a.png', Name:'a.png' }` | 提取路径字段;保留对象的名称、大小、Id 等元数据,不得把整个对象转成字符串 |
|
|
21
|
-
| 新版多图数组 | `[{ FilePathName:'/tenant/a.png' }, ...]` | 逐项解析并保持原顺序;单图展示取第一个有效路径,多图展示返回全部有效项 |
|
|
22
|
-
|
|
23
|
-
数据库或接口还可能返回上述对象/数组的 JSON 字符串,解析器也必须兼容。标准路径键按以下顺序识别:`Url`、`FileUrl`、`FileURL`、`PreviewUrl`、`PreviewURL`、`FullUrl`、`Path`、`FilePathName`、`FilePath`、`FullPath`、`Src`、`Href`,并兼容相应 camelCase 键。
|
|
24
|
-
|
|
25
|
-
页面不能把相对路径直接交给 `<image>` / `<img>`,也不能一律拼接 API Base。项目已使用 `microi.v8.js` 时,优先直接复用 `V8.extractUploadPath`、返回路径数组的 `V8.normalizeUploadValue`、保留 `{ item, path }` 元数据的 `V8.normalizeUploadEntries`、`V8.assetUrl` / `V8.resolveAssetUrl` 和 `V8.resolveFileUrl`,不得在业务页面另造一套不完整的解析规则。
|
|
26
|
-
|
|
27
|
-
| 类型 | 处理方式 |
|
|
28
|
-
|---|---|
|
|
29
|
-
| 已允许的 `https://` 绝对地址 | 校验协议和允许域名后使用 |
|
|
30
|
-
| 公有对象存储相对路径 | 拼接当前租户 `FileServer` |
|
|
31
|
-
| `/file/...` 平台本地文件路由 | 拼接当前 API Base |
|
|
32
|
-
| `/micro-app/v3/...` 应用稳定解析路由 | 拼接当前 API Base;这是动态指针路由,不是 HDFS 对象 Key,禁止拼接 `FileServer` |
|
|
33
|
-
| 私有对象 | 后端鉴权后签发短期 URL,前端只使用临时 URL |
|
|
34
|
-
| `blob:` | 仅用于本页创建且能及时 `revokeObjectURL` 的预览 |
|
|
35
|
-
| `data:` | 仅允许经过大小和 MIME 校验的图片预览;禁止用于富文本任意 HTML |
|
|
36
|
-
|
|
37
|
-
`FileServer` 是对象存储/CDN 地址,不等同于 API 服务。租户切换时必须使用当前 `OsClient` 的运行期配置,禁止在源码中写死域名或租户目录。
|
|
38
|
-
|
|
39
|
-
### 跨端上传字段的形态与桶属性
|
|
40
|
-
|
|
41
|
-
- 控件读取嵌套 `Config.ImgUpload/FileUpload.Multiple` 与 `MaxCount`;`Multiple=false` 保存单图对象,不能因数量上限或默认九图值而保存数组。已有单元素数组在单图控件中按第一项读取,不在查看时回写业务数据。
|
|
42
|
-
- 上传时透传字段授权上下文与配置的 `Limit`,后端仍独立校验字段策略。上传响应中的实际 `Limit` 属于可持久化元数据,必须保留;临时 `Url/PreviewUrl` 与本地预览地址不得入库。
|
|
43
|
-
- 历史对象缺少 `Limit` 时,以字段配置作为取址依据。已有对象明确 `Limit=true/1` 时,即使字段当前为公有,也必须签名;私有字段上下文与实际私有标记优先于公有提示。签发失败保持空地址,禁止尝试把私有路径拼到公有 CDN。
|
|
44
|
-
|
|
45
|
-
## 统一资源解析函数
|
|
46
|
-
|
|
47
|
-
未使用 `microi.v8.js` 的项目,应在共享请求/资源模块中实现等价的“提取上传路径 + 解析资源 URL”两层函数,所有页面复用同一逻辑:
|
|
48
|
-
|
|
49
|
-
```js
|
|
50
|
-
const uploadPathKeys = [
|
|
51
|
-
'Url', 'FileUrl', 'FileURL', 'PreviewUrl', 'PreviewURL', 'FullUrl',
|
|
52
|
-
'Path', 'FilePathName', 'FilePath', 'FullPath', 'Src', 'Href',
|
|
53
|
-
'url', 'fileUrl', 'previewUrl', 'fullUrl', 'path', 'filePathName', 'filePath', 'fullPath', 'src', 'href'
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
export function extractUploadPath(raw, depth = 0) {
|
|
57
|
-
if (depth > 4 || raw === null || raw === undefined) return '';
|
|
58
|
-
if (Array.isArray(raw)) {
|
|
59
|
-
for (const item of raw) {
|
|
60
|
-
const path = extractUploadPath(item, depth + 1);
|
|
61
|
-
if (path) return path;
|
|
62
|
-
}
|
|
63
|
-
return '';
|
|
64
|
-
}
|
|
65
|
-
if (typeof raw === 'object') {
|
|
66
|
-
for (const key of uploadPathKeys) {
|
|
67
|
-
const path = extractUploadPath(raw[key], depth + 1);
|
|
68
|
-
if (path) return path;
|
|
69
|
-
}
|
|
70
|
-
return '';
|
|
71
|
-
}
|
|
72
|
-
const value = String(raw).trim();
|
|
73
|
-
if (!value) return '';
|
|
74
|
-
if (/^[{[]/.test(value)) {
|
|
75
|
-
try { return extractUploadPath(JSON.parse(value), depth + 1); } catch (_) {}
|
|
76
|
-
}
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function normalizeUploadValue(raw) {
|
|
81
|
-
let value = raw;
|
|
82
|
-
if (typeof value === 'string' && /^[{[]/.test(value.trim())) {
|
|
83
|
-
try { value = JSON.parse(value); } catch (_) {}
|
|
84
|
-
}
|
|
85
|
-
const items = Array.isArray(value) ? value : [value];
|
|
86
|
-
return items.map(item => ({ item, path: extractUploadPath(item) })).filter(entry => entry.path);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function resolveAssetUrl(raw, {
|
|
90
|
-
apiBase,
|
|
91
|
-
fileServer,
|
|
92
|
-
allowedHosts = []
|
|
93
|
-
}) {
|
|
94
|
-
const value = extractUploadPath(raw);
|
|
95
|
-
if (!value) return '';
|
|
96
|
-
|
|
97
|
-
if (/^https:\/\//i.test(value)) {
|
|
98
|
-
const host = new URL(value).hostname.toLowerCase();
|
|
99
|
-
return allowedHosts.includes(host) ? value : '';
|
|
100
|
-
}
|
|
101
|
-
if (/^blob:/i.test(value)) return value;
|
|
102
|
-
if (/^data:image\/(png|jpeg|gif|webp);base64,/i.test(value)) return value;
|
|
103
|
-
if (value.startsWith('/file/')) return `${apiBase.replace(/\/$/, '')}${value}`;
|
|
104
|
-
if (/^\/?micro-app\/v3(?:\/|$)/i.test(value)) {
|
|
105
|
-
return `${apiBase.replace(/\/$/, '')}/${value.replace(/^\/+/, '')}`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const relative = value.replace(/^\/+/, '');
|
|
109
|
-
return `${fileServer.replace(/\/$/, '')}/${relative}`;
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
示例:
|
|
114
|
-
|
|
115
|
-
```vue
|
|
116
|
-
<script setup>
|
|
117
|
-
import { resolveAssetUrl } from '@/utils/assets.js';
|
|
118
|
-
|
|
119
|
-
function imageUrl(path) {
|
|
120
|
-
return resolveAssetUrl(path, {
|
|
121
|
-
apiBase: runtimeConfig.apiBase,
|
|
122
|
-
fileServer: runtimeConfig.fileServer,
|
|
123
|
-
allowedHosts: runtimeConfig.assetAllowedHosts
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
</script>
|
|
127
|
-
|
|
128
|
-
<template>
|
|
129
|
-
<image v-if="row.MainImg" :src="imageUrl(row.MainImg)" mode="aspectFill" />
|
|
130
|
-
</template>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
不要让业务页面各自拼 `${API_BASE}/${path}` 或 `${FILE_SERVER}/${path}`;这会造成租户切换、私有桶、绝对 URL 和本地文件路由行为不一致。
|
|
134
|
-
|
|
135
|
-
## 大资源与小程序包体门禁
|
|
136
|
-
|
|
137
|
-
- Hero、Banner、资讯封面、视频、音频、字体和大型占位图等公开资源,先按真实显示尺寸压缩,再上传当前 `OsClient` 的公有 HDFS;数据库和配置只保存相对 `Path`,客户端运行时读取 `SysConfig.FileServer` 组成 CDN 地址。
|
|
138
|
-
- 发布前扫描真实小程序构建目录。单个非离线关键资源超过 `256 KB` 时默认不得进入主包;主包静态资源达到 `1.5 MB` 或距平台硬上限不足 `300 KB` 时必须中止上传并输出最大文件清单。
|
|
139
|
-
- 上传完成必须执行 CDN 匿名 GET 回读,验证 `200`、媒体类型、字节数,重要资源比对 SHA-256;同时回读业务字段确认已由 `/static/...` 改为 HDFS 相对路径。只有上传成功、数据库回读、CDN 回读和构建产物瘦身全部通过,才能关闭问题。
|
|
140
|
-
- 原始设计素材移到不会参与构建的资料目录;主包只保留轻量失败占位和离线关键图标。远程资源加载失败时显示该占位,禁止回退为同一张大图的本地副本。
|
|
141
|
-
|
|
142
|
-
## 私有文件
|
|
143
|
-
|
|
144
|
-
- 私有对象 Key 不能直接转换成可长期访问的公网地址。
|
|
145
|
-
- 前端通过受保护接口请求临时 URL;后端使用 `V8.Method.GetPrivateFileUrl({ FilePathName })` 或等价受控能力。
|
|
146
|
-
- 后端必须验证当前用户、`OsClient`、记录范围和字段绑定关系,不能只凭文件路径签名。
|
|
147
|
-
- 临时 URL 设置较短有效期,不写入数据库、不进入长期缓存、不记录完整签名参数。
|
|
148
|
-
- 下载响应使用安全的 `Content-Disposition`、MIME 白名单与文件名清洗。
|
|
149
|
-
|
|
150
|
-
## 富文本与 CSS 资源
|
|
151
|
-
|
|
152
|
-
- 富文本 HTML 先做标签、属性和协议白名单清洗,再改写允许的 `src` / `href`。
|
|
153
|
-
- 禁止 `javascript:`、任意 `data:text/html`、事件属性和未知 iframe。
|
|
154
|
-
- `background-image`、Markdown 图片、视频封面和头像与普通 `<image>` 使用同一资源解析策略。
|
|
155
|
-
- 商品详情等富文本可让图片自适应宽度,但文本容器仍需留出安全边距;不得在文件名或页面文案暴露成本、导入批次、内部目录或生成规则。
|
|
156
|
-
|
|
157
|
-
## 上传与预览边界
|
|
158
|
-
|
|
159
|
-
- 前端扩展名、大小提示只是体验校验;服务端仍须验证单文件大小、请求总量、租户配额、真实 MIME、魔数和文件名。
|
|
160
|
-
- 对象 Key 必须由服务端生成并包含租户隔离前缀,不能信任客户端提交的完整路径。
|
|
161
|
-
- SVG、HTML、脚本、压缩包和办公文档按风险策略处理;可执行内容不在同源页面直接渲染。
|
|
162
|
-
- 预览失败时显示受控占位,不回退到未经校验的原始 URL。
|
|
163
|
-
|
|
164
|
-
## 验收清单
|
|
165
|
-
|
|
166
|
-
- [ ] 公有对象、本地文件、`/micro-app/v3` 动态路由、私有对象、绝对 URL 的路径分支均有测试
|
|
167
|
-
- [ ] 绝对地址字符串、相对路径字符串、单图对象、多图数组及其 JSON 字符串均有测试
|
|
168
|
-
- [ ] 单图从数组取第一个有效路径,多图保持原顺序;解析 URL 时不丢失或改写上传对象元数据
|
|
169
|
-
- [ ] 切换两个 `OsClient` 后使用各自 `FileServer`,无跨租户路径或缓存复用
|
|
170
|
-
- [ ] 私有 URL 越权、过期、篡改签名和复制到其它账号均失败
|
|
171
|
-
- [ ] 网络面板中没有硬编码客户域名、真实租户或永久签名 URL
|
|
172
|
-
- [ ] 关键列表、详情、上传、预览页面有截图和真实资源加载断言
|
|
173
|
-
- [ ] `blob:` URL 及时释放,大文件预览不会持续占用内存
|
|
174
|
-
- [ ] 富文本协议/标签白名单可阻止 XSS
|
|
175
|
-
|
|
176
|
-
参见 `microi.skills/v8-file-upload/SKILL.md`、`microi.skills/microi-uniapp-frontend/SKILL.md` 和 `microi.skills/playwright-e2e/SKILL.md`。
|
|
1
|
+
---
|
|
2
|
+
name: uniapp-mall-assets
|
|
3
|
+
description: Microi UniApp、H5、商城与 PC 页面中的上传图片、附件、FileServer、私有文件临时 URL 和富文本资源安全规范。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
|
|
7
|
+
|
|
8
|
+
# Microi UniApp / 商城资源路径规范
|
|
9
|
+
|
|
10
|
+
本 Skill 只描述平台通用资源规则。不得写入客户名称、真实 `OsClient`、客户应用路径、客户接口 Key 或特定商城结算规则。
|
|
11
|
+
|
|
12
|
+
## 先识别资源类型
|
|
13
|
+
|
|
14
|
+
Microi 上传字段存在四种必须长期兼容的存储形态,不能假设字段一定是字符串:
|
|
15
|
+
|
|
16
|
+
| 存储形态 | 示例 | 兼容要求 |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| 旧版绝对地址字符串 | `https://cdn.example.com/a.png` | 校验协议和允许域名后原样使用,禁止再次拼接 `FileServer` |
|
|
19
|
+
| 旧版相对路径字符串 | `/tenant/a.png` | 仅此类纯相对对象 Key 拼接当前租户 `FileServer` |
|
|
20
|
+
| 新版单图对象 | `{ FilePathName:'/tenant/a.png', Name:'a.png' }` | 提取路径字段;保留对象的名称、大小、Id 等元数据,不得把整个对象转成字符串 |
|
|
21
|
+
| 新版多图数组 | `[{ FilePathName:'/tenant/a.png' }, ...]` | 逐项解析并保持原顺序;单图展示取第一个有效路径,多图展示返回全部有效项 |
|
|
22
|
+
|
|
23
|
+
数据库或接口还可能返回上述对象/数组的 JSON 字符串,解析器也必须兼容。标准路径键按以下顺序识别:`Url`、`FileUrl`、`FileURL`、`PreviewUrl`、`PreviewURL`、`FullUrl`、`Path`、`FilePathName`、`FilePath`、`FullPath`、`Src`、`Href`,并兼容相应 camelCase 键。
|
|
24
|
+
|
|
25
|
+
页面不能把相对路径直接交给 `<image>` / `<img>`,也不能一律拼接 API Base。项目已使用 `microi.v8.js` 时,优先直接复用 `V8.extractUploadPath`、返回路径数组的 `V8.normalizeUploadValue`、保留 `{ item, path }` 元数据的 `V8.normalizeUploadEntries`、`V8.assetUrl` / `V8.resolveAssetUrl` 和 `V8.resolveFileUrl`,不得在业务页面另造一套不完整的解析规则。
|
|
26
|
+
|
|
27
|
+
| 类型 | 处理方式 |
|
|
28
|
+
|---|---|
|
|
29
|
+
| 已允许的 `https://` 绝对地址 | 校验协议和允许域名后使用 |
|
|
30
|
+
| 公有对象存储相对路径 | 拼接当前租户 `FileServer` |
|
|
31
|
+
| `/file/...` 平台本地文件路由 | 拼接当前 API Base |
|
|
32
|
+
| `/micro-app/v3/...` 应用稳定解析路由 | 拼接当前 API Base;这是动态指针路由,不是 HDFS 对象 Key,禁止拼接 `FileServer` |
|
|
33
|
+
| 私有对象 | 后端鉴权后签发短期 URL,前端只使用临时 URL |
|
|
34
|
+
| `blob:` | 仅用于本页创建且能及时 `revokeObjectURL` 的预览 |
|
|
35
|
+
| `data:` | 仅允许经过大小和 MIME 校验的图片预览;禁止用于富文本任意 HTML |
|
|
36
|
+
|
|
37
|
+
`FileServer` 是对象存储/CDN 地址,不等同于 API 服务。租户切换时必须使用当前 `OsClient` 的运行期配置,禁止在源码中写死域名或租户目录。
|
|
38
|
+
|
|
39
|
+
### 跨端上传字段的形态与桶属性
|
|
40
|
+
|
|
41
|
+
- 控件读取嵌套 `Config.ImgUpload/FileUpload.Multiple` 与 `MaxCount`;`Multiple=false` 保存单图对象,不能因数量上限或默认九图值而保存数组。已有单元素数组在单图控件中按第一项读取,不在查看时回写业务数据。
|
|
42
|
+
- 上传时透传字段授权上下文与配置的 `Limit`,后端仍独立校验字段策略。上传响应中的实际 `Limit` 属于可持久化元数据,必须保留;临时 `Url/PreviewUrl` 与本地预览地址不得入库。
|
|
43
|
+
- 历史对象缺少 `Limit` 时,以字段配置作为取址依据。已有对象明确 `Limit=true/1` 时,即使字段当前为公有,也必须签名;私有字段上下文与实际私有标记优先于公有提示。签发失败保持空地址,禁止尝试把私有路径拼到公有 CDN。
|
|
44
|
+
|
|
45
|
+
## 统一资源解析函数
|
|
46
|
+
|
|
47
|
+
未使用 `microi.v8.js` 的项目,应在共享请求/资源模块中实现等价的“提取上传路径 + 解析资源 URL”两层函数,所有页面复用同一逻辑:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const uploadPathKeys = [
|
|
51
|
+
'Url', 'FileUrl', 'FileURL', 'PreviewUrl', 'PreviewURL', 'FullUrl',
|
|
52
|
+
'Path', 'FilePathName', 'FilePath', 'FullPath', 'Src', 'Href',
|
|
53
|
+
'url', 'fileUrl', 'previewUrl', 'fullUrl', 'path', 'filePathName', 'filePath', 'fullPath', 'src', 'href'
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export function extractUploadPath(raw, depth = 0) {
|
|
57
|
+
if (depth > 4 || raw === null || raw === undefined) return '';
|
|
58
|
+
if (Array.isArray(raw)) {
|
|
59
|
+
for (const item of raw) {
|
|
60
|
+
const path = extractUploadPath(item, depth + 1);
|
|
61
|
+
if (path) return path;
|
|
62
|
+
}
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
if (typeof raw === 'object') {
|
|
66
|
+
for (const key of uploadPathKeys) {
|
|
67
|
+
const path = extractUploadPath(raw[key], depth + 1);
|
|
68
|
+
if (path) return path;
|
|
69
|
+
}
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
const value = String(raw).trim();
|
|
73
|
+
if (!value) return '';
|
|
74
|
+
if (/^[{[]/.test(value)) {
|
|
75
|
+
try { return extractUploadPath(JSON.parse(value), depth + 1); } catch (_) {}
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function normalizeUploadValue(raw) {
|
|
81
|
+
let value = raw;
|
|
82
|
+
if (typeof value === 'string' && /^[{[]/.test(value.trim())) {
|
|
83
|
+
try { value = JSON.parse(value); } catch (_) {}
|
|
84
|
+
}
|
|
85
|
+
const items = Array.isArray(value) ? value : [value];
|
|
86
|
+
return items.map(item => ({ item, path: extractUploadPath(item) })).filter(entry => entry.path);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function resolveAssetUrl(raw, {
|
|
90
|
+
apiBase,
|
|
91
|
+
fileServer,
|
|
92
|
+
allowedHosts = []
|
|
93
|
+
}) {
|
|
94
|
+
const value = extractUploadPath(raw);
|
|
95
|
+
if (!value) return '';
|
|
96
|
+
|
|
97
|
+
if (/^https:\/\//i.test(value)) {
|
|
98
|
+
const host = new URL(value).hostname.toLowerCase();
|
|
99
|
+
return allowedHosts.includes(host) ? value : '';
|
|
100
|
+
}
|
|
101
|
+
if (/^blob:/i.test(value)) return value;
|
|
102
|
+
if (/^data:image\/(png|jpeg|gif|webp);base64,/i.test(value)) return value;
|
|
103
|
+
if (value.startsWith('/file/')) return `${apiBase.replace(/\/$/, '')}${value}`;
|
|
104
|
+
if (/^\/?micro-app\/v3(?:\/|$)/i.test(value)) {
|
|
105
|
+
return `${apiBase.replace(/\/$/, '')}/${value.replace(/^\/+/, '')}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const relative = value.replace(/^\/+/, '');
|
|
109
|
+
return `${fileServer.replace(/\/$/, '')}/${relative}`;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
示例:
|
|
114
|
+
|
|
115
|
+
```vue
|
|
116
|
+
<script setup>
|
|
117
|
+
import { resolveAssetUrl } from '@/utils/assets.js';
|
|
118
|
+
|
|
119
|
+
function imageUrl(path) {
|
|
120
|
+
return resolveAssetUrl(path, {
|
|
121
|
+
apiBase: runtimeConfig.apiBase,
|
|
122
|
+
fileServer: runtimeConfig.fileServer,
|
|
123
|
+
allowedHosts: runtimeConfig.assetAllowedHosts
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<template>
|
|
129
|
+
<image v-if="row.MainImg" :src="imageUrl(row.MainImg)" mode="aspectFill" />
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
不要让业务页面各自拼 `${API_BASE}/${path}` 或 `${FILE_SERVER}/${path}`;这会造成租户切换、私有桶、绝对 URL 和本地文件路由行为不一致。
|
|
134
|
+
|
|
135
|
+
## 大资源与小程序包体门禁
|
|
136
|
+
|
|
137
|
+
- Hero、Banner、资讯封面、视频、音频、字体和大型占位图等公开资源,先按真实显示尺寸压缩,再上传当前 `OsClient` 的公有 HDFS;数据库和配置只保存相对 `Path`,客户端运行时读取 `SysConfig.FileServer` 组成 CDN 地址。
|
|
138
|
+
- 发布前扫描真实小程序构建目录。单个非离线关键资源超过 `256 KB` 时默认不得进入主包;主包静态资源达到 `1.5 MB` 或距平台硬上限不足 `300 KB` 时必须中止上传并输出最大文件清单。
|
|
139
|
+
- 上传完成必须执行 CDN 匿名 GET 回读,验证 `200`、媒体类型、字节数,重要资源比对 SHA-256;同时回读业务字段确认已由 `/static/...` 改为 HDFS 相对路径。只有上传成功、数据库回读、CDN 回读和构建产物瘦身全部通过,才能关闭问题。
|
|
140
|
+
- 原始设计素材移到不会参与构建的资料目录;主包只保留轻量失败占位和离线关键图标。远程资源加载失败时显示该占位,禁止回退为同一张大图的本地副本。
|
|
141
|
+
|
|
142
|
+
## 私有文件
|
|
143
|
+
|
|
144
|
+
- 私有对象 Key 不能直接转换成可长期访问的公网地址。
|
|
145
|
+
- 前端通过受保护接口请求临时 URL;后端使用 `V8.Method.GetPrivateFileUrl({ FilePathName })` 或等价受控能力。
|
|
146
|
+
- 后端必须验证当前用户、`OsClient`、记录范围和字段绑定关系,不能只凭文件路径签名。
|
|
147
|
+
- 临时 URL 设置较短有效期,不写入数据库、不进入长期缓存、不记录完整签名参数。
|
|
148
|
+
- 下载响应使用安全的 `Content-Disposition`、MIME 白名单与文件名清洗。
|
|
149
|
+
|
|
150
|
+
## 富文本与 CSS 资源
|
|
151
|
+
|
|
152
|
+
- 富文本 HTML 先做标签、属性和协议白名单清洗,再改写允许的 `src` / `href`。
|
|
153
|
+
- 禁止 `javascript:`、任意 `data:text/html`、事件属性和未知 iframe。
|
|
154
|
+
- `background-image`、Markdown 图片、视频封面和头像与普通 `<image>` 使用同一资源解析策略。
|
|
155
|
+
- 商品详情等富文本可让图片自适应宽度,但文本容器仍需留出安全边距;不得在文件名或页面文案暴露成本、导入批次、内部目录或生成规则。
|
|
156
|
+
|
|
157
|
+
## 上传与预览边界
|
|
158
|
+
|
|
159
|
+
- 前端扩展名、大小提示只是体验校验;服务端仍须验证单文件大小、请求总量、租户配额、真实 MIME、魔数和文件名。
|
|
160
|
+
- 对象 Key 必须由服务端生成并包含租户隔离前缀,不能信任客户端提交的完整路径。
|
|
161
|
+
- SVG、HTML、脚本、压缩包和办公文档按风险策略处理;可执行内容不在同源页面直接渲染。
|
|
162
|
+
- 预览失败时显示受控占位,不回退到未经校验的原始 URL。
|
|
163
|
+
|
|
164
|
+
## 验收清单
|
|
165
|
+
|
|
166
|
+
- [ ] 公有对象、本地文件、`/micro-app/v3` 动态路由、私有对象、绝对 URL 的路径分支均有测试
|
|
167
|
+
- [ ] 绝对地址字符串、相对路径字符串、单图对象、多图数组及其 JSON 字符串均有测试
|
|
168
|
+
- [ ] 单图从数组取第一个有效路径,多图保持原顺序;解析 URL 时不丢失或改写上传对象元数据
|
|
169
|
+
- [ ] 切换两个 `OsClient` 后使用各自 `FileServer`,无跨租户路径或缓存复用
|
|
170
|
+
- [ ] 私有 URL 越权、过期、篡改签名和复制到其它账号均失败
|
|
171
|
+
- [ ] 网络面板中没有硬编码客户域名、真实租户或永久签名 URL
|
|
172
|
+
- [ ] 关键列表、详情、上传、预览页面有截图和真实资源加载断言
|
|
173
|
+
- [ ] `blob:` URL 及时释放,大文件预览不会持续占用内存
|
|
174
|
+
- [ ] 富文本协议/标签白名单可阻止 XSS
|
|
175
|
+
|
|
176
|
+
参见 `microi.skills/v8-file-upload/SKILL.md`、`microi.skills/microi-uniapp-frontend/SKILL.md` 和 `microi.skills/playwright-e2e/SKILL.md`。
|