# 广寒基地时态拓扑页面适配审计报告 日期:2026-07-09 测试目标: ```text http://localhost:8080/guanghan_topology_demo.html?mission=GHC-01 ``` 审计范围: - 页面初始加载和默认快照; - 全部 9 个任务选项; - 明确时间查询、任务与时间并存、基地建立前时间点; - 无效时间、不存在任务、API 异常呈现; - 浏览器回退与 URL 状态一致性; - GHC-05 全部 14 个可视节点的详情弹窗; - 鼠标、键盘、关闭按钮、遮罩和 Escape 交互; - 1440×1000 桌面视图与 390×844 窄屏视图; - API 响应、数据库任务事件、布局数据; - 既有自动化回归测试; - 页面、API、种子数据和近期改动的根因检查。 本轮只检查、截图和出具报告,没有修改页面、API、数据库或测试代码。 ## 1. 总结结论 当前版本不建议确认完成或发布。 它已经具备可工作的基本链路:页面能从 API 取得数据,9 个任务均可查询,GHC-05 最终拓扑的空间骨架基本正确,14 个可视节点均能打开详情弹窗,弹窗在桌面和窄屏下都可用。 但适配仍存在发布阻断问题: 1. 页面并未真正由数据库拓扑和布局驱动,仍是固定 SVG 加少量显隐逻辑; 2. 历史任务会显示不存在的悬空连接线,散热器在基地不存在时仍出现; 3. 5 个任务的“最后一条任务记录时间”与 Timeline 不一致; 4. GHC-05 图中文字大面积越界,严重影响工程总图可读性; 5. 页面写死 `localhost:5000`,部署到真正 Wiki 后,其他访问者会请求自己电脑的 localhost; 6. 无效时间导致 API 返回 HTML 500,页面显示 JSON 解析错误; 7. 自动化回归测试 8 项中有 4 项失败; 8. 后端配置文件包含已提交的明文默认数据库凭据。 ## 2. 测试结果概览 | 测试项 | 结果 | 说明 | |---|---|---| | API 健康检查 | 通过 | `/health` 返回 200 | | 无参数默认快照 | 部分通过 | 页面能显示最新数据,但数据库“最新事件”本身不完整 | | 9 个任务逐项查询 | 接口通过、呈现部分失败 | 全部返回数据并截图;早期任务拓扑线和散热器错误 | | 明确时间优先 | 部分通过 | 时间生效,但任务上下文从 API 请求和副标题中丢失 | | 基地建立前时间 | 部分通过 | 组件和连接为空,但 4 个散热器仍显示 | | 无效时间 | 失败 | API 500 HTML;页面显示 `Unexpected token '<'` | | 不存在任务 | 基本通过 | 404 能显示,但下拉框丢失请求值,错误文案为英文 | | 浏览器回退 | 失败 | URL 回到 GHC-01,页面仍显示 GHC-05 | | 14 个节点点击弹窗 | 通过 | 全部能打开和关闭 | | Enter / Space 打开弹窗 | 通过 | 两种键盘操作均有效 | | × / 遮罩 / Escape 关闭 | 通过 | 三种方式均有效 | | 关闭后焦点恢复 | 失败 | 焦点落到 `BODY`,未返回触发节点 | | 390×844 页面布局 | 部分通过 | 页面本身无横向溢出,地图可横向滚动;图中文字仍越界 | | 390×844 弹窗 | 通过 | 弹窗完整落在视口内 | | 既有自动化测试 | 失败 | 8 项中 4 项失败 | ## 3. 全部任务测试 所有任务都通过页面下拉框和“查询”按钮实际执行,并分别截图。 | 任务 | API 快照时间(页面显示,UTC+8) | 组件数 | API 连接数 | 页面结果 | 截图 | |---|---:|---:|---:|---|---| | BL-01 | 2024-04-20 06:58 | 1 | 0 | 失败:仍显示一条供电/燃料线;4 个散热器始终存在 | [截图](../demo/topology_audit/mission_bl_01.png) | | GHC-01 | 2024-06-16 15:23 | 2 | 0 | 失败:核心舱四条结构线悬空,冰轮线悬空 | [截图](../demo/topology_audit/mission_ghc_01.png) | | JC-01 | 2024-06-17 16:19 | 2 | 0 | 失败:与 GHC-01 相同 | [截图](../demo/topology_audit/mission_jc_01.png) | | BL-02 | 2024-06-23 13:23 | 3 | 0 | 失败:显示 6 条并不存在的固定线 | [截图](../demo/topology_audit/mission_bl_02.png) | | GHC-02 | 2024-07-01 18:37 | 3 | 1 | 失败:页面固定线不能表达 API 中实际的冰轮连接 | [截图](../demo/topology_audit/mission_ghc_02.png) | | GHC-03 | 2024-08-19 08:00 | 9 | 7 | 失败:显示核心舱未来安装口悬空线和不存在的电力东侧线 | [截图](../demo/topology_audit/mission_ghc_03.png) | | BL-03 | 2024-09-24 03:20 | 10 | 7 | 失败:固定线仍多于该时刻实际拓扑 | [截图](../demo/topology_audit/mission_bl_03.png) | | GHC-04 | 2024-11-08 08:00 | 12 | 14 | 部分通过:主要电力拓扑可见,但固定图不能忠实反映连接记录 | [截图](../demo/topology_audit/mission_ghc_04.png) | | GHC-05 | 2025-01-02 08:00 | 17 | 19 | 空间骨架基本正确;文字越界、信息缺失和时间锚点错误 | [截图](../demo/topology_audit/mission_ghc_05.png) | 说明:17 个数据库组件被合并成 14 个可视节点,电力模块东、西两组分别使用组合节点。这种组合可以保留,但组合关系目前硬编码在页面中。 ## 4. 发布阻断问题 ### P0-1 页面没有真正使用数据库拓扑和布局 表现: - API 返回 `layout`,页面没有读取 `currentData.layout`; - SVG 节点、连接线和散热器全部写死在 HTML 中; - 任务列表也由 `loadMissions()` 返回固定数组; - 数据库新增模块、连接或未来十字结构后,页面不会自动出现; - 数据库 17 个组件只能依赖页面中的 `GROUP_MEMBERS` 硬编码压成 14 个节点。 证据: - `data/wiki/guanghan_topology_demo.html:258` 写死 API 地址; - `data/wiki/guanghan_topology_demo.html:282` 写死任务列表; - `data/wiki/guanghan_topology_demo.html:301` 仅对固定节点执行显隐; - 页面没有任何 `currentData.layout` 使用点; - `topology_api/queries/snapshot_queries.py` 已返回布局,但前端丢弃。 根因: 这次改动保留了原始静态 SVG,只将部分文字和显隐状态接到了 API。它不是“从数据库渲染拓扑”,而是“数据库数据覆盖固定模板”。 影响: - 无法满足未来扩建; - 无法保证任意历史快照准确; - 数据库和页面会持续形成两套事实来源。 ### P0-2 正式 Wiki 部署后 API 地址不可用 页面写死: ```js const API_BASE = 'http://localhost:5000'; ``` 在当前开发机上可以工作,但真正的 Wiki 访问者打开页面后,浏览器会请求访问者自己电脑的 `localhost:5000`,而不是服务器。 根因: 缺少同源反向代理路径或部署时可注入的 API 基址。 影响: 除开发机外,页面大概率完全无法加载。 ### P0-3 后端源码包含明文默认数据库凭据 `topology_api/config.py` 在环境变量缺失时使用硬编码用户名和密码,并已进入 Git 历史。 报告中不重复凭据内容。 影响: - 凭据泄露; - 环境变量遗漏时会静默连接真实数据库; - 后续即使删除源码中的字符串,Git 历史仍保留旧值。 需要在修复阶段先轮换凭据,再移除默认密码并检查 Git 历史处置方案。 ## 5. 高优先级功能问题 ### P1-1 历史拓扑连接线不可信 页面没有根据 `currentData.connections` 画线,而是给固定线配置粗粒度的组件依赖: ```js const connTypes = { 'core-structural': ['core_cabin'], ... }; ``` 随后使用“任意一个依赖组件存在即可显示”: ```js needed.some(k => existing.has(k)) ``` 因此: - GHC-01 只有核心舱和冰轮一号、API 连接数为 0,但页面显示核心舱四条悬空线和冰轮线; - 基地建立前 0 组件时,4 个散热器仍显示; - GHC-03 电力东部模块尚不存在,东侧结构线仍显示。 相关截图: - [GHC-01 桌面图](../demo/topology_audit/04_ghc01_desktop.png) - [基地建立前](../demo/topology_audit/time_before_base.png) ### P1-2 任务默认时间不符合已确认规则 已确认规则是:仅传任务编号时,采用该任务最后一条记录的时间。 数据库目前只录入少量拓扑变化事件,导致 5 个任务的最大事件时间早于 Timeline 中最后一条任务记录。 下表均以 Timeline 原始时间(UTC)比较: | 任务 | 数据库最后事件 | Timeline 最后记录 | 结果 | |---|---:|---:|---| | BL-01 | 2024-04-19 22:58 | 2024-05-17 23:01 | 错误 | | GHC-01 | 2024-06-16 07:23 | 2024-06-16 07:23 | 正确 | | JC-01 | 2024-06-17 08:19 | 2024-06-17 08:19 | 正确 | | BL-02 | 2024-06-23 05:23 | 2024-06-23 05:23 | 正确 | | GHC-02 | 2024-07-01 10:37 | 2024-07-15 03:16 | 错误 | | GHC-03 | 2024-08-19 00:00 | 2024-08-30 12:45 | 错误 | | BL-03 | 2024-09-23 19:20 | 2024-09-23 19:20 | 正确 | | GHC-04 | 2024-11-08 00:00 | 2024-11-20 13:57 | 错误 | | GHC-05 | 2025-01-02 00:00 | 2025-01-22 10:52 | 错误 | 根因: 种子数据将 `mission_events` 当成“拓扑变化事件”,但查询规则将其当成“完整任务时间线”。两种语义冲突。 ### P1-3 GHC-05 图中文字严重越界 `updateNodeLabels()` 将数据库中的长摘要直接写入固定大小 SVG ``,没有截断、换行或 `foreignObject` 布局。 同时它把原本的安装位置说明替换成泛化类型,如“舱段模块”“设备模块”,使工程图信息反而减少。 核心舱和北部模块仍残留“加载中...”占位文本。 截图: - [上半区](../demo/topology_audit/ghc05_slice_top.png) - [电力模块区](../demo/topology_audit/ghc05_slice_mid.png) - [底部区](../demo/topology_audit/ghc05_slice_bottom.png) ### P1-4 同时提供任务和时间时,页面丢失任务上下文 页面 URL 正确保留: ```text ?mission=GHC-01&at=2025-01-02T08:00:00+08:00 ``` 但 `fetchSnapshot()` 使用: ```js if (at) ... else if (mission) ... ``` 所以 API 请求只携带 `at`,没有携带 `mission`。最终副标题显示“(最新)”,而不是保留 GHC-01 上下文。 API 本身在同时收到两个参数时行为正确,缺陷位于页面请求组装。 截图:[任务与时间并存](../demo/topology_audit/explicit_time_with_mission.png) ### P1-5 浏览器回退后 URL 与页面状态不一致 页面查询时调用 `history.pushState()`,但没有监听 `popstate`。 复现: 1. 查询 GHC-01; 2. 查询 GHC-05; 3. 浏览器后退; 4. URL 已回到 `mission=GHC-01`; 5. 下拉框、副标题和拓扑仍是 GHC-05。 截图:[浏览器回退错位](../demo/topology_audit/browser_back_mismatch.png) ### P1-6 无效时间产生 500 和技术性错误文案 输入 `not-a-time` 后: - API 将原字符串直接送入 PostgreSQL `timestamptz` 强制转换; - PostgreSQL 抛错; - Flask 返回 HTML 500; - 页面对错误响应强制执行 `res.json()`; - 用户看到 `Unexpected token '<'`; - 旧副标题仍保留,形成新旧状态混杂。 截图:[无效时间错误](../demo/topology_audit/invalid_time_error.png) ### P1-7 “任务节点”字段并不是任务信息 弹窗中的“任务节点”实际显示 `detailUrl`: ```text 详见 Wiki: /home/Spacecraft/... ``` 没有链接时显示“资料未记录”。 API 没有返回组件引入任务、最后状态任务或生效事件,因此页面无法正确展示任务节点。 ### P1-8 模块尺寸和详情数据不完整 GHC-05 的 17 个组件中: - 只有居住舱一号、居住舱二号、温室舱和实验舱具有完整几何尺寸; - 核心舱只有质量,没有长宽高; - 其余 12 个组件的 `dimensions` 为空; - 多个电力设备只有一句摘要,没有描述和规格; - 电力东、西组合弹窗以第一个成员作为标题、位置和状态,其他成员只被拼进“关键参数”。 全部 14 个可视节点的弹窗操作都成功,但信息完整性未达到“模块状态、模块信息等均从数据库读取并完整呈现”的目标。 代表截图: - [核心舱弹窗](../demo/topology_audit/modal_core_cabin.png) - [西部组合弹窗](../demo/topology_audit/modal_power_west_group.png) - [吴刚二号弹窗](../demo/topology_audit/modal_wugang_2.png) ### P1-9 冰轮连接端口数据与空间位置冲突 视觉图将冰轮一号、二号、三号分别放在电力模块西、南、东。 但数据库中的 6 条燃料/供电连接全部指向: ```text power_module_hub.west ``` 这使弹窗连接数据与工程图位置矛盾。至少冰轮二号、三号应分别核对南、东端口语义。 ## 6. 中优先级问题 ### P2-1 任务下拉框不是数据库数据 `loadMissions()` 返回固定任务数组。数据库新增任务后页面不会出现,删除或更名也不会同步。 需要任务列表 API,或在快照 API 中返回可选任务。 ### P2-2 自动化测试与适配代码不同步 执行: ```text python -m unittest tests.test_guanghan_topology_demo tests.test_guanghan_topology_e2e -v ``` 结果: ```text Ran 8 tests FAILED (failures=4) ``` 失败项: 1. 加压走道连接契约; 2. 每个可点击模块的尺寸字段契约; 3. 未来扩展锚点注释; 4. 渲染和弹窗 E2E。 部分失败是适配后没有同步测试,部分对应真实回归: - 原来要求保留的 `FUTURE_EXPANSION_ANCHOR:north/east/west` 注释已被删除; - 新 E2E 没有启动或模拟拓扑 API; - 测试仍检查旧 `data-connection` 契约。 此外,当前没有 API 查询、时间优先级、数据库约束和 9 个任务快照的自动化测试。 ### P2-3 详情弹窗关闭后不恢复焦点 Enter 和 Space 可以打开弹窗,Escape 可以关闭;但关闭后焦点落在 `BODY`,没有返回原节点。 这会影响键盘连续浏览和无障碍体验。 窄屏证据: - [390×844 页面](../demo/topology_audit/mobile_390_top.png) - [390×844 弹窗](../demo/topology_audit/mobile_modal.png) ### P2-4 时间显示依赖访问者本地时区 页面使用 `toLocaleString('zh-CN')`,没有固定时区或显示 UTC 偏移。不同地区的访问者可能看到不同日期和时间。 ### P2-5 页面显示内部枚举值 页面直接显示: ```text mission_latest_event latest_recorded_state explicit_time ``` 这些是 API 内部值,不适合作为最终 Wiki 文案。 ### P2-6 数据库内容进入 `innerHTML` 时未转义 尺寸、关键参数和连接关系通过模板字符串写入 `innerHTML`。当前种子数据可信,但未来如果后台编辑内容含 HTML,会形成存储型脚本注入风险。 ## 7. 已确认通过的部分 以下部分不应在修复时无故推翻: - GHC-05 核心舱十字结构与电力模块十字结构的整体空间关系; - 两条加压走道为绿色实线; - 四组散热器的视觉样式和文字标注; - 14 个可视节点均可点击; - 弹窗 ×、遮罩和 Escape 关闭; - 节点 Enter / Space 键打开弹窗; - 窄屏弹窗尺寸和内部信息布局; - API 的明确时间优先规则; - API 的未知基地 404 和未知任务 404; - 无参数查询采用数据库内已发生任务的最大事件时间这一查询逻辑。 最后一项的查询代码逻辑正确,但种子事件不完整,所以实际得到的“最新时间”仍错误。 ## 8. 建议修复边界 如果本报告获确认,建议按以下顺序实施: 1. 先处理凭据轮换与部署 API 地址; 2. 为页面/API/数据库补充失败测试,固定已确认语义; 3. 补齐任务末条事件或拆分“拓扑事件”和“任务时间锚点”; 4. 用 API 的组件、连接和布局数据真正生成图,不再维护固定拓扑事实; 5. 修复历史快照连接、散热器和组合节点; 6. 修复图中文字布局和残留占位符; 7. 修复时间校验、任务上下文、回退导航和错误状态; 8. 补齐任务列表、模块引入任务、尺寸与详情数据; 9. 完成 9 个任务、明确时间、异常输入、桌面和窄屏全量回归并重新截图。 修复过程中应保留目前已通过的 GHC-05 空间骨架、绿色加压走道、散热器样式和弹窗视觉风格。 ## 9. 截图目录 全部测试截图位于: ```text demo/topology_audit/ ``` 其中包括: - 9 个任务各 1 张完整页面截图; - GHC-05 上、中、下三段人工视觉检查; - 3 个代表性详情弹窗; - 明确时间、无效时间、基地建立前、不存在任务和浏览器回退; - 390×844 页面和弹窗。 其他异常截图: - [不存在的任务](../demo/topology_audit/missing_mission.png)