diff --git a/docs/PRODUCTION_MOBILE_DEPLOYMENT_RUNBOOK.md b/docs/PRODUCTION_MOBILE_DEPLOYMENT_RUNBOOK.md index 34d4ef1..734c774 100644 --- a/docs/PRODUCTION_MOBILE_DEPLOYMENT_RUNBOOK.md +++ b/docs/PRODUCTION_MOBILE_DEPLOYMENT_RUNBOOK.md @@ -1,20 +1,23 @@ # frontend-miniapp 正式环境部署手册 -最后更新:2026-07-24(同步五节点 `17083` 缓存策略修复及逐节点验收) +最后更新:2026-07-28(同步 CDN 源站域名、五节点缓存策略、讲解音频兼容路由及 CDN 验收/回滚流程) ## 适用范围 -本文档适用于将本地仓库 `F:\深圳自然博物馆\frontend-miniapp` 的 H5 产物部署到深圳自然博物馆正式环境。 +本文档适用于将本地仓库 `E:\深圳自然博物馆\workplace\frontend-miniapp` 的 H5 产物部署到深圳自然博物馆正式环境。 - 正式域名:`https://guide.sznhmuseum.org.cn` +- CDN 源站域名:`https://guide-real.sznhmuseum.org.cn`(只供 CDN 回源和源站排障,不作为游客入口) +- 源站公网 IP:`218.17.240.244` - 主节点:`172.20.14.21` - 部署目录:`/data/nginx/html/mobile` - Nginx 容器:`sgs-nginx`(`nginx/1.30.3`,Docker host 网络) - Nginx 生效配置目录:`/data/nginx/conf.d`(容器内 `/etc/nginx/conf.d`,只读挂载) - 同步脚本:`/data/scripts/sync-frontend-nodes.sh` -- Nginx 配置变更记录:2026-07-24,五台 `17083` 已统一 assets/字体缓存及静态资源 404 防 SPA 回退规则 +- Nginx 配置变更记录:2026-07-28,`.21` 公网入口及五台 `17083` 已完成 CDN 源站、分层缓存、静态资源 404 防 SPA 回退和音频兼容路由适配 - 同步节点:`172.20.14.23`、`172.20.14.26`、`172.20.14.27`、`172.20.14.33` -- 远程仓库:`http://192.168.0.93:3333/lyf/frontend-miniapp.git` +- 远程仓库:`https://git.whaoyue.com/lyf/frontend-miniapp.git` +- 本次文档同步基线:分支 `codex/import-sgs-frontend-mobile-20260727`,提交 `fc93b8d7d32a208a037be576ce3b9da2db3c1fe3`(仅表示仓库同步基线,不表示该提交已部署到生产) 不要把服务器密码、证书私钥、token、真实后台账号、腾讯地图正式 key 写入仓库文档或提交记录。 @@ -23,7 +26,7 @@ 进入项目目录: ```powershell -cd 'F:\深圳自然博物馆\frontend-miniapp' +cd 'E:\深圳自然博物馆\workplace\frontend-miniapp' ``` 检查分支、远程仓库和本地改动: @@ -80,11 +83,13 @@ Saved working directory and index state On master: pre-deploy-prod-local-fixes-Y STASHED=pre-deploy-prod-local-fixes-YYYYMMDDHHmmss ``` -拉取远程最新代码: +拉取目标发布分支。发布前必须由变更单或版本记录明确分支名,不得默认把 `master` 当作本次目标: ```powershell +$branch = 'codex/import-sgs-frontend-mobile-20260727' git fetch origin -git pull --ff-only origin master +git switch $branch +git pull --ff-only origin $branch git log -1 --pretty=format:'%h %s' ``` @@ -103,15 +108,26 @@ git status --short --branch --untracked-files=all 恢复后重点检查 `.env.production`、`static/guide-data/manifest.json` 和 `src/utils/publicUrl.ts`,确认正式环境仍指向 `172.20.14.21` 和 `https://guide.sznhmuseum.org.cn`。 -## Nginx 当前生产配置(2026-07-24) +## Nginx 与 CDN 当前生产配置(2026-07-28) 本节以 `172.20.14.21` 上 `docker exec sgs-nginx nginx -T` 的实际生效结果为准。主 Nginx 使用 Docker host 网络,公共域名不直接读取宿主静态目录,而是通过 upstream `sgs_mobile_pool` 转发到五台 `17083` 工作节点: ```text -guide.sznhmuseum.org.cn:80 -> sgs_mobile_pool -> 172.20.14.21/.23/.26/.27/.33:17083 - guide.sznhmuseum.org.cn:443 -> sgs_mobile_pool -> 172.20.14.21/.23/.26/.27/.33:17083 +游客 HTTPS -> guide.sznhmuseum.org.cn CDN -> guide-real.sznhmuseum.org.cn:443 + -> 218.17.240.244 / 172.20.14.21 sgs-nginx + -> sgs_mobile_pool -> 172.20.14.21/.23/.26/.27/.33:17083 ``` +`.21:/data/nginx/conf.d/guide-public.conf` 的 HTTP/HTTPS `server_name` 均包含 `guide.sznhmuseum.org.cn` 和 `guide-real.sznhmuseum.org.cn`。通配符证书 `*.sznhmuseum.org.cn` 覆盖两者,有效期至 2027-01-14。80 端口只保留 ACME challenge 直出和 `/_analytics/` 统一 404,其余请求 301 到正式 HTTPS 域名。游客 HTTP 到 HTTPS 的跳转仍应在 CDN 边缘启用;CDN 使用 HTTPS 回源时,不能依赖源站 80 端口完成游客侧跳转。 + +CDN 控制台应使用以下回源基线: + +- 回源地址、Host 和 SNI:`guide-real.sznhmuseum.org.cn`; +- 回源协议/端口:HTTPS `443`; +- 启用源站证书校验; +- 游客域名始终为 `guide.sznhmuseum.org.cn`; +- 不把 `guide-real` 写入前端构建变量或对外链接。 + 当前移动端工作节点 `17083` 的关键规则如下: ```nginx @@ -130,48 +146,30 @@ server { try_files $uri =404; } - # 模型、manifest、GLB/GLTF 等:明确 MIME,缺失时返回 404 - location ^~ /static/nav-assets/ { - types { - application/json json; - model/gltf-binary glb; - model/gltf+json gltf; - text/csv csv; - text/markdown md; - } - default_type application/octet-stream; - expires 30d; - add_header Cache-Control "public"; - try_files $uri =404; - } - - # 字体:30 天缓存;其他 static 资源不得回退 index.html - location ^~ /static/Fonts/ { - add_header Cache-Control "public, max-age=2592000"; - try_files $uri =404; - } - location ^~ /static/ { - try_files $uri =404; - } - - # 页面路由才允许 SPA fallback - location / { - try_files $uri $uri/ /index.html; - } + # 实际配置还按可变性拆分以下资源;变更时以 nginx -T 为准: + # - index.html、SPA fallback、/engine/index.html、guide-data、固定名 SDK 入口:no-store + # - engine chunks、字体、模型及普通静态文件:30 天 + # - 缺失静态资源:404,且不附加长期缓存头 + # - 页面路由:仅页面请求允许回退 /index.html,回退响应为 no-store } ``` 注意: - `/assets/`、`/static/nav-assets/`、`/static/Fonts/` 和其他 `/static/` 路径必须优先 `try_files $uri =404`,不能把缺失的 JSON、GLB、字体返回为 `index.html`。 -- `index.html` 仍应保持不缓存或短缓存;带 hash 的 `/assets/` 才使用长期不可变缓存。 -- `guide-public.conf` 的 80/443 入口包含 ACME challenge 的直接静态规则,其余请求代理到 `sgs_mobile_pool`;不要把公共域名改回单节点静态 root。 +- `index.html`、SPA fallback、`/engine/index.html`、`/static/guide-data/` 和固定文件名 SDK 入口必须为 `Cache-Control: no-store`;带 hash 的 `/assets/` 才使用一年不可变缓存。 +- engine chunk、字体、模型和普通静态文件使用 30 天缓存;缺失资源返回 404,不能带长期缓存头。 +- `guide-public.conf` 的 80/443 入口包含 ACME challenge 和 `/_analytics/` 404 规则;不要把公共域名改回单节点静态 root。 - 当前 upstream 使用 `least_conn`,单节点由 `max_fails=3 fail_timeout=10s` 被动摘除。 +- 所有 `/_analytics/` 路径必须保持 404;不得恢复 Tracker、Matomo 注入、匿名访问通知、`visitor.js` 或 `privacy.html`。 -2026-07-24 逐节点直连 `17083` 复核时发现配置漂移:`.21` 缓存策略正确,`.23/.26/.27/.33` 缺少 `/assets/` 长期 immutable 缓存头和 `/static/Fonts/` 专用规则。四台异常节点已分别备份配置、补齐对应 `17083` location,并在 `nginx -t` 通过后平滑 reload。修复后五台均满足: +2026-07-24 逐节点直连 `17083` 复核时发现配置漂移:`.21` 缓存策略正确,`.23/.26/.27/.33` 缺少 `/assets/` 长期 immutable 缓存头和 `/static/Fonts/` 专用规则。四台异常节点已分别备份配置、补齐对应 `17083` location,并在 `nginx -t` 通过后平滑 reload。2026-07-27 至 28 日又完成 CDN 分层缓存适配,并同步更新 `.21:/data/scripts/sync-frontend-nodes.sh` 的 `write_worker_conf()` 模板。当前五台均满足: - 字体返回 `Cache-Control: public, max-age=2592000`; - 带 hash 的 assets 返回 `Cache-Control: public, max-age=31536000, immutable`; +- HTML、SPA fallback、guide-data 和固定名 SDK 入口返回 `Cache-Control: no-store`; +- engine chunk、字体、模型及普通静态文件返回 30 天缓存; +- 缺失静态资源返回 HTTP `404` 且不附加长期缓存头; - 字体 ETag 条件请求返回 HTTP `304`,且 `304` 响应仍保留 30 天缓存头; - 20 个 `17080-17083 /healthz` 端点全部正常,五台 `sgs-nginx` 容器均未重启。 @@ -342,15 +340,15 @@ $script | ssh -o StrictHostKeyChecking=no root@172.20.14.21 'bash -s' ssh -o StrictHostKeyChecking=no root@172.20.14.21 'bash /data/scripts/sync-frontend-nodes.sh' ``` -执行前必须注意:该脚本不仅同步 `/data/nginx/html`,还会通过 `write_worker_conf()` 重写五台工作节点的 `/data/nginx/conf.d/sgs-worker.conf`。2026-07-24 已修复 `.23/.26/.27/.33` 的 `17083` 缓存配置漂移,当前五台生效配置均包含 `/assets/` 一年 immutable 缓存、`/static/Fonts/` 30 天缓存以及静态资源 404 规则;但同步脚本文件本身仍需要单独复核,不能用脚本内容推断当前生产配置。**普通移动端静态发布不得在未复核脚本内嵌配置前直接运行该脚本。** +执行前必须注意:该脚本不仅同步 `/data/nginx/html`,还会通过 `write_worker_conf()` 重写五台工作节点的 `/data/nginx/conf.d/sgs-worker.conf`。脚本模板已于 2026-07-28 同步当前 `17083` CDN 分层缓存策略,但每次使用前仍必须复核脚本与现网配置。**普通移动端静态发布不得在未复核脚本内嵌配置前直接运行该脚本。** 继续使用同步脚本前先检查它是否已经包含本次规则: ```powershell -ssh -o StrictHostKeyChecking=no root@172.20.14.21 'grep -n -E "static/Fonts|31536000|try_files \\$uri =404" /data/scripts/sync-frontend-nodes.sh' +ssh -o StrictHostKeyChecking=no root@172.20.14.21 'grep -n -E "no-store|static/guide-data|engine/index.html|31536000|2592000|try_files \\$uri =404" /data/scripts/sync-frontend-nodes.sh' ``` -如果检查没有同时看到 `static/Fonts` 和 `31536000`,应先在维护窗口审核并更新 `write_worker_conf()`,再执行 `bash -n /data/scripts/sync-frontend-nodes.sh`、逐节点 `nginx -t` 和 `/healthz` 验证。不要用 `.21` 的整份 `sgs-worker.conf` 覆盖其他节点,因为 `.21` 的 `17080` 还包含本节点专用 CSP 配置;只同步审核后的 `17083` 移动端配置块。 +如果检查未同时覆盖 no-store、30 天缓存、一年 immutable 和静态 404 规则,应先在维护窗口审核并更新 `write_worker_conf()`,再执行 `bash -n /data/scripts/sync-frontend-nodes.sh`、逐节点 `nginx -t` 和 `/healthz` 验证。不要用 `.21` 的整份 `sgs-worker.conf` 覆盖其他节点,因为 `.21` 的 `17080` 还包含本节点专用 CSP 配置;只同步审核后的 `17083` 移动端配置块。 如果本次仅发布 H5 静态文件、没有修改 Nginx 配置,推荐采用“仅同步 `/data/nginx/html/mobile`”的流程,不要触发同步脚本中的 `write_worker_conf()`、`/data/apps/sgs-map` 同步和 PM2 重启。静态文件替换完成后不需要 Nginx reload;只有配置变更才执行 `nginx -t` 后 reload。 @@ -413,6 +411,37 @@ foreach ($node in $nodes) { 此流程不修改 `/data/nginx/conf.d`,不执行 `nginx -s reload`,也不操作 `/data/apps/sgs-map` 或 PM2。只有 Nginx 配置变更才需要先 `nginx -t` 再 reload。 +## 讲解音频与 CDN 兼容 + +### 当前链路 + +讲解音频的正确生产链路为: + +```text +/app-api/gis/guide/stop/info 或 /app-api/gis/guide/audio/play-info + -> 返回 https://guide.sznhmuseum.org.cn/minio/museum-assets/...mp3 + -> CDN + -> .21 /minio/ 代理 + -> sgs_minio / museum-assets 对象 +``` + +`.21:/data/nginx/conf.d/guide-public.conf` 当前还保留以下兼容措施: + +- `/app-api/gis/sdk/minio/museum-assets/` 直接代理到 `sgs_minio/museum-assets/`,绕过会返回拒绝访问 HTML 的 Java SDK 媒体代理; +- 对 `/app-api/gis/guide/stop/info` 和 `/app-api/gis/guide/audio/play-info` 精确启用 no-store 响应改写,将相对 `/minio/museum-assets/...` 地址改为正式域名绝对 URL; +- `/minio/museum-assets/tts-audio/audio/` 保留 Range 请求能力,正常响应应为 `audio/mpeg` 和 HTTP `206`。 + +上述 Nginx 兼容层属于生产止血措施。长期方案应在以下两项中择一实施并完成回归测试: + +1. 前端修正 `src/utils/publicUrl.ts`,不再把 `/minio/` 强制转换为 `/app-api/gis/sdk/minio/`; +2. Java SDK 媒体代理修复权限、状态码、Content-Type 和 Range 转发。 + +在长期方案上线、CDN 污染对象完成清理且原 SDK 媒体 URL 验证正常前,不得删除当前兼容路由或 API URL 改写。 + +### 故障识别 + +已确认的错误特征为:请求 MP3 时收到 HTTP `206`,但 `Content-Type` 为 `text/html;charset=utf-8`、响应约 726 字节,正文提示页面被管理员禁止。该响应来自 Java SDK 媒体代理的拒绝访问页;CDN 曾缓存该错误对象。只看 HTTP 状态码会误判成功,音频验收必须同时检查 MIME、Range、长度和文件头。 + ## 线上验证 公网验证: @@ -437,9 +466,10 @@ curl.exe -sI "$base/static/__not_exists__.json" | Select-String 'HTTP/|Content-T 预期: - `/assets/.js`:HTTP `200`,`Cache-Control` 含 `max-age=31536000, immutable`; +- `/`、`/index.html`、`/engine/index.html` 和 `/static/guide-data/*.json`:HTTP `200`,`Cache-Control` 为 no-store; - manifest:HTTP `200`,`Content-Type: application/json`; - 真实 GLB:HTTP `200`,`Content-Type: model/gltf-binary`; -- `/static/__not_exists__.json`:HTTP `404`,不得返回 `200 text/html`。 +- `/static/__not_exists__.json`:HTTP `404`,不得返回 `200 text/html`,也不得附加长期缓存头。 入口 JS 验证: @@ -513,6 +543,62 @@ foreach ($node in $nodes) { 注意:`.21` 的整份 `sgs-worker.conf` 哈希可以与其余节点不同,因为它还承载额外的 `17080` CSP 配置;验收目标是五台 `17083` 移动端配置块和响应行为一致。不要用 `.21` 的整份配置覆盖其他节点。 +### 源站、音频与安全负向验证 + +使用 `--resolve` 绕过 CDN 直连源站,确认 Host/SNI、入口缓存和音频 Range 行为: + +```powershell +$origin = '218.17.240.244' +$originHost = 'guide-real.sznhmuseum.org.cn' +$public = 'https://guide.sznhmuseum.org.cn' +$audio = '/minio/museum-assets/tts-audio/audio/2026/07/17/cdb263222e7749009db1768b49477283.mp3' + +curl.exe --resolve "${originHost}:443:${origin}" -sI "https://${originHost}/" +curl.exe --resolve "${originHost}:443:${origin}" -sI "https://${originHost}/engine/index.html" +curl.exe --resolve "${originHost}:443:${origin}" -sI "https://${originHost}/static/guide-data/manifest.json" +curl.exe --resolve "${originHost}:443:${origin}" -sS -D - -o NUL -H 'Range: bytes=0-31' "https://${originHost}${audio}" +curl.exe -sS -D - -o NUL -H 'Range: bytes=0-31' "${public}${audio}" +``` + +音频源站和 CDN 响应均应满足:HTTP `206`、`Content-Type: audio/mpeg`、有效 `Content-Range`,且不得出现约 726 字节的 `text/html`。随后在 390 x 844 移动浏览器中打开英文展项 `865546638960193536`,确认播放器时间从 `0:00` 推进、可完整播放并正常结束。 + +安全负向检查: + +```powershell +@('/_analytics/visitor.js','/_analytics/privacy.html','/_analytics/matomo.js','/_analytics/matomo.php') | + ForEach-Object { curl.exe -s -o NUL -w "%{http_code} $_`n" "$public$_" } +``` + +四项必须全部返回 HTTP `404`。 + +### CDN 控制台发布后检查 + +CDN 控制台操作不能由源站部署脚本代替。每次部署涉及 HTML、guide-data、engine 固定入口或音频路由时,至少刷新: + +```text +https://guide.sznhmuseum.org.cn/ +https://guide.sznhmuseum.org.cn/index.html +https://guide.sznhmuseum.org.cn/engine/index.html +https://guide.sznhmuseum.org.cn/static/guide-data/ +``` + +若处理过讲解音频代理,还需刷新: + +```text +https://guide.sznhmuseum.org.cn/app-api/gis/sdk/minio/museum-assets/ +``` + +CDN 规则应保持: + +- `/assets/*`:遵循源站一年 immutable; +- `/minio/museum-assets/tts-audio/audio/*`:30 天;仅在确认文件名内容不可变时才可提升为一年 immutable; +- `/app-api/gis/guide/audio/*`、`/app-api/gis/guide/stop/*`:TTL 0; +- `/app-api/gis/sdk/minio/museum-assets/*`:污染对象清理和直源验证完成前 TTL 0; +- HTML、SPA fallback、`/engine/index.html`、guide-data 和固定名 SDK 入口:TTL 0 或严格遵循源站 no-store; +- 媒体缓存必须校验成功状态和正确 Content-Type,禁止把 `text/html` 当作音频缓存。 + +同时在 CDN 边缘启用游客 HTTP 到 HTTPS 的 301。控制台变更后重新执行公网与 `--resolve` 对照检查,以区分 CDN 和源站响应。 + ## 回滚 主节点部署脚本会在 `/data/nginx/html/_backups` 下生成备份,例如: @@ -540,6 +626,14 @@ $script | ssh -o StrictHostKeyChecking=no root@172.20.14.21 'bash -s' 回滚后应使用前述“仅同步 mobile 静态目录”流程把回滚版本同步到四个工作节点,并重新验证五节点入口 JS 的 SHA256。只有已确认 `/data/scripts/sync-frontend-nodes.sh` 内嵌 Nginx 配置与生产一致时,才允许用全量脚本同步。 +如需回滚 CDN/Nginx 适配,使用对应变更备份,不能用旧整目录覆盖现网: + +- CDN 源站集群适配:`/data/nginx/_backups/cdn-origin-cluster-20260727223921`; +- SDK MinIO 音频绕过:`/data/nginx/_backups/audio-sdk-minio-bypass-20260727232518`; +- 音频 API URL 改写:`/data/nginx/_backups/audio-api-url-rewrite-20260727233006`。 + +回滚顺序固定为:确认影响范围,备份当前配置,只恢复对应文件或 location,逐节点执行 `docker exec sgs-nginx nginx -t`,平滑 reload,再验证五节点 `/healthz`、正式域名、源站域名、音频 Range 和 `/_analytics/` 404。若 CDN 已缓存回滚前对象,还需在控制台刷新对应 URL;仅回滚源站不会自动清除 CDN 对象。 + ## 已遇到的问题与处理 ### 1. terser 压缩阶段内存不足 @@ -643,7 +737,30 @@ current: { node: 'v20.19.5', npm: '10.8.2' } - 验收时关注最终 PM2 状态是否 `online`。 - 如未来只需同步 mobile,可拆分或新增专用同步脚本,避免不必要地重启 map 服务。 -### 8. 页面返回 200 但浏览器空白 +### 8. CDN 后讲解音频返回 HTML + +现象: + +```text +HTTP 206 +Content-Type: text/html;charset=utf-8 +Content-Length: 726 +``` + +根因: + +- 前端把 `/minio/...mp3` 转换成 `/app-api/gis/sdk/minio/...mp3`; +- Java SDK 媒体代理返回拒绝访问 HTML,但状态仍为 200; +- CDN 缓存后以 206 响应 Range 请求,造成播放器无法解码。 + +处理: + +- `.21` 兼容路由将 `/app-api/gis/sdk/minio/museum-assets/` 直接代理到 MinIO; +- 两个讲解 API 的响应 URL 改写为正式域名 `/minio/museum-assets/...`; +- CDN 清除污染前,SDK media 路径 TTL 设为 0; +- 验收同时检查状态、`audio/mpeg`、`Content-Range`、文件头和浏览器播放进度。 + +### 9. 页面返回 200 但浏览器空白 现象: @@ -679,3 +796,6 @@ https://guide.sznhmuseum.org.cn/ HTTP 200 - 2026-07-20:拉取远程最新代码前已执行 `git stash push -u -m pre-deploy-prod-local-fixes-20260720232941`,确认“本地补丁已 stash”;随后快进到 `735c5cd`,执行 `git stash pop` 恢复正式环境补丁,无冲突。构建入口为 `assets/index-CsFOrYfU.js`,主节点备份为 `/data/nginx/html/_backups/mobile-before-deploy-20260720233343.tar.gz`,同步脚本最终输出 `All frontend worker nodes synced.`。 - 2026-07-23:移动端 `17083` 目标配置新增 `/assets/` 一年 immutable 缓存、`/static/Fonts/` 30 天缓存、`/static/nav-assets/` MIME/30 天缓存及全部 `/static/` 缺失资源返回 404。普通 H5 发布改为优先仅同步 mobile 静态目录,不再无条件重写 `sgs-worker.conf`、同步地图应用或重启 PM2。 - 2026-07-24:逐节点直连复核发现 `.23/.26/.27/.33` 存在 `17083` 缓存配置漂移,仅 `.21` 正确;四台异常节点已分别备份并补齐字体和 hash assets 缓存规则,`nginx -t` 后平滑 reload。修复后字体缓存头、hash asset immutable 缓存头及字体 ETag/304 均为 5/5 一致,20 个工作端点和六类对外入口正常,五台 Nginx 容器均未重启。部署验收新增五节点直连缓存头与 ETag/304 检查,避免公网 `least_conn` 抽样漏检单节点漂移。 +- 2026-07-27:从分支 `codex/import-sgs-frontend-mobile-20260727` 部署提交 `f1047414bbf00a254e1c0cf99755556e45a18e6f`,生产入口 `assets/index-DvpqYP-p.js`,SHA256 `b9f099e2d30d71ca691cc64f20ca6808af834b12dce1c9af8838a6aef1eb893d`;五节点一致,`/engine/` 运行时和移动端导览/讲解流程验证通过。 +- 2026-07-27 至 28 日:新增 `guide-real.sznhmuseum.org.cn` CDN 源站域名并完成五节点 `17083` 分层缓存适配;同步脚本模板已更新。随后修复 CDN 后英文讲解音频失败:绕过错误 Java SDK 媒体代理,并把讲解 API 的 `/minio/` URL 改写为正式域名绝对路径。五组语言/声线音频均验证为 HTTP 206、`audio/mpeg` 和正确 MPEG 文件头,展项 `865546638960193536` 英文音频可完整播放至 48.96 秒。CDN 控制台刷新、边缘 HTTP 到 HTTPS 和 Host/SNI 规则仍需按本文清单人工维护。 +- 2026-07-28:目标分支已快进同步到 `fc93b8d7d32a208a037be576ce3b9da2db3c1fe3`,包含讲解列表、宿主环境判断、展厅卡片图片及相关测试更新。本条只记录文档编制时的代码基线;生产仍以最近一次明确部署记录 `f1047414bbf00a254e1c0cf99755556e45a18e6f` 为准,后续上线 `fc93b8d` 时必须另行补充构建产物、五节点哈希和验收记录。