chore: add reference docs, scripts, tests, demo, prototypes

- Reference docs: known issues, user journey, location redesign, mission merge plan
- Scripts: fill_location_state, merge_missions, migrate_location, migrate_state
- Tests: e2e test suite, asset entries unit tests
- Demo: location board, hifi prototypes, test screenshots
- Data backup: asset log entries and state nodes
- Update .gitignore

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 15:29:47 +08:00
co-authored by Claude Opus 4.7
parent f4909765af
commit e1d601a774
190 changed files with 11464 additions and 0 deletions
@@ -0,0 +1,272 @@
# 时间线页面复测报告
日期:2026-05-31
## 结论
其他团队的修改有部分推进,但时间线页面还不能算完成。已改善的点包括:`scale=day` 时下拉框能正确显示 Day、segment 外层宽度开始使用 `track_width_pct`、asset 名称已具备链接、`_partial=1` 从 HTML 改成了 JSON,单次响应体从上一轮约 315 KB 降到当前约 128 KB。
但复测发现新的关键回归:页面加载后客户端 JS 会直接替换 `#tl-canvas.innerHTML`,把时间刻度行清掉;滚轮/按钮缩放没有可靠生效;Auto scale 在手动范围下仍然错误;事件点仍然只是无文字小点;资产筛选仍不是需求里的“type/location 筛选后资产多选默认全选”。
## 测试环境
- 本地服务:`http://127.0.0.1:9000`
- 页面:`/mission-preview/timeline`
- 自动化:Codex in-app Browser + 现有 `tests/test_e2e.py`
- 当前工作区包含大量未提交改动,本报告只做复测与记录,不修改业务代码。
## 执行的检查
### HTTP / 路由检查
1. `GET /mission-preview/timeline`
- HTTP 200
- 响应体约 512 KB
- 包含 `tl-viewport` / `tl-canvas`
2. `GET /mission-preview/timeline?_partial=1`
- HTTP 200
- `Content-Type: application/json`
- 响应体约 128 KB
- `scale=year`
- `bucket_count=15`
- `rows=14`
3. `GET /mission-preview/timeline?scale=auto&start=2057-04-24&end=2057-07-21&range_mode=manual&_partial=1`
- HTTP 200
- `range_start=2057-04-24T00:00`
- `range_end=2057-07-21T00:00`
- 实际 `scale=year`
- 实际 `bucket_count=1`
- 这是错误结果。88 天范围应该自动进入 Day 或至少 Month。
4. `GET /mission-preview/timeline?scale=day&start=2057-04-24&end=2057-07-21&range_mode=manual&_partial=1`
- HTTP 200
- `scale=day`
- `bucket_count=89`
- 说明显式 Day 路径可工作。
### 浏览器 DOM 检查
初始打开 `/mission-preview/timeline` 后,等待 JS 执行完成:
- `#tl-canvas.children.length = 14`
- DOM 中 asset 行数 = 14
- segment 数 = 116
- event 数 = 238
- `timeline_json.scale = year`
- `timeline_json.bucket_count = 15`
- `select[name=scale].value = year`
- `window.tlZoom = undefined`
- `window.tlResetView = undefined`
重要现象:客户端渲染后,`#tl-canvas` 的第一个子元素已经是 `Europa Outpost` 的资产行,不再是时间刻度行。也就是说,服务端首屏渲染出来的刻度行被 JS 清掉了。
### 现有 E2E
执行命令:
```powershell
python tests\test_e2e.py
```
结果:
- 总计 36 项
- 通过 34 项
- 失败 2 项
- 失败项均为时间线:
- `8.1 渲染`
- `8.2 数据 -- 0 rows`
说明:现有 E2E 仍在找旧 DOM 选择器 `#tls` / `.tl-r`,已经与当前实现失配。这个失败不等价于页面完全不可用,但说明测试套件没有跟随时间线重构更新,无法作为时间线验收依据。
## 逐项复测结果
### 1. 滚轮缩放卡顿
状态:部分改善,但未通过。
改善点:
- `_partial=1` 改成 JSON 后,单次 partial 数据量下降到约 128 KB。
- 新实现意图是在客户端用 `timeline_json` 直接重绘,理论上可以避免上一版每次 wheel 都请求后端。
仍存在的问题:
- 实测滚轮操作没有改变 hidden `start/end`,也没有改变 segment 位置。
- `Zoom +` / `Zoom -` / `Reset View` 按钮仍然存在,但模板里没有定义 `tlZoom()``tlResetView()`
- 客户端 zoom 逻辑只调整 `bucket_count`,没有按新的 `range_start/range_end` 重新计算每个 segment/event 相对位置。即使 wheel 触发成功,也会出现“缩放但不按新窗口重定位”的问题。
证据:
- `window.tlZoom``undefined`
- `window.tlResetView``undefined`
- 点击按钮对应的 inline handler 是 `tlZoom(1.3)` / `tlResetView()`
### 2. 滚轮缩放丢失日志/资产
状态:旧丢参问题缓解,但新实现仍有风险。
已改善:
- 客户端缩放不再发 AJAX,因此上一版 wheel AJAX 丢 `asset_ids` / `record_scope` 的问题不再以同样形式出现。
- URL 手动传 2 个 `asset_ids` 时,DOM 确实只渲染这 2 个 asset。
仍存在的问题:
- 默认资产多选框没有任何选中项,但页面渲染 14 个 asset。UI 表达仍然是“未选择”,实际语义却是“全部显示”。
- 客户端缩放数据来自初始 `timeline_json`,如果后续改为 partial 更新,需要重新验证所有 filter 参数是否同步。
证据:
- 默认打开时:asset options = 14selected count = 0timeline rows = 14。
-`asset_ids=A&B` 打开时:selected count = 2timeline rows = 2。
### 3. 缩放时自动切换年/月/日
状态:未通过。
显式 `scale=day` 已经可以显示 Day,并且 JSON 返回 `bucket_count=89`。但 `scale=auto` 或不传 `scale` 且传入 88 天手动范围时,后端仍返回 `scale=year``bucket_count=1`
根因判断:
- `mission_timeline_preview()` 在解析 `range_start/range_end` 之前先调用 `_resolve_timeline_scale(request.args.get("scale"), initial_range_start, initial_range_end)`
- 这导致 auto scale 使用的是默认全量时间范围,而不是用户当前手动范围。
- 此外 `<option value="auto">` 永远不会保持 Auto selected,因为后端最后传给模板的是解析后的 `year/month/day`,不是原始 scale mode。
### 4. Type/location 筛选后资产多选默认全选
状态:未通过。
当前行为:
- Type 和 Location 仍是普通单选下拉。
- Assets 仍是原生 `<select multiple>`
- 默认 selected count = 0,但页面显示全部资产。
- 没有 checkbox dropdown、Select All、Clear、已选数量展示。
- 没有 State 筛选。
需求目标是“先根据 type 和 location 进行筛选,筛选结果进入下拉多选框,默认全选,仅展示选中的 asset”。当前仍是“空 asset_ids 表示全部”,没有做到视觉和语义一致。
### 5. Event point 显示效果
状态:未通过。
当前 DOM
- `.tl-evt` 数量 = 238
- 有可见文本的 `.tl-evt` 数量 = 0
- connector 数量 = 0
- state node 与普通 event 没有稳定的 class/data 区分
后端仍然计算了事件 label 宽度、label side、track、connector height,但当前客户端渲染没有使用这些字段。页面仍然只是 7px 小圆点,不能扫描事件内容。
### 6. 点击 asset 跳转到 asset 页面
状态:基本通过,但还需要人工点击验收。
当前 asset label 已渲染为链接:
```text
/assets/fba95050-ca89-4e67-b796-66b7e1c40764?sim_time=2060-08-11T10:00
```
这比上一版 plain div 有明显改善。需要注意的是当前 `simulation_time` 来自 DB/session,复测时已变成 `2060-08-11T10:00`,不是原本默认的 `2060-03-12T09:00`。这不是时间线链接本身的问题,但全局 simulation time 被写 DB 后会影响所有页面和测试基线,建议测试报告里固定 sim_time。
## 新发现的问题
### A. 时间刻度行被客户端渲染清除
模板服务端先渲染了刻度行:
```html
<div id="tl-canvas">
<div>scale labels...</div>
...
</div>
```
但脚本执行:
```js
if (tlCanvas) tlCanvas.innerHTML = buildHTML(tlData);
```
`buildHTML()` 只生成 asset rows,不生成 scale labels。因此用户最终看到的 DOM 没有时间刻度行。
### B. 缩放按钮是坏控件
页面底部仍显示:
- `Zoom +`
- `Zoom -`
- `Reset View`
但没有定义对应函数:
- `window.tlZoom === undefined`
- `window.tlResetView === undefined`
这些按钮应删除、接入同一套 zoom controller,或改成有效按钮。
### C. 客户端缩放算法不完整
当前 wheel handler 更新:
```js
tlData.range_start = fmtDate(newFrom)
tlData.range_end = fmtDate(newTo)
tlData.bucket_count = Math.round(scale)
```
但 segment/event 的 `start_position``display_end_position``position` 都是后端基于旧 range 计算出的 bucket position。只改 `bucket_count` 无法正确表达新 range。缩放后必须重新按时间戳计算位置,或向后端请求基于新 range 的 JSON。
### D. `_partial=1` 合约已变,但旧 partial 模板仍在仓库
当前路由 `_partial=1` 返回 JSON,不再使用 `mission_timeline_partial.html`。但该模板仍存在且是旧 HTML 片段。建议清理或明确废弃,避免后续维护者误用。
### E. 现有 E2E 已过期
`tests/test_e2e.py` 仍验证旧选择器:
- `#tls`
- `.tl-r`
当前实现已经没有这些节点,所以时间线测试失败。需要更新测试,让它验证当前关键行为,而不是旧 DOM 名称。
## 建议修复优先级
### P0
1. 恢复最终 DOM 的时间刻度行,或让客户端 `buildHTML()` 同时渲染刻度。
2. 修复 auto scale:先确定手动 range,再基于最终 range 解析 scale。
3. 删除或实现 `tlZoom()` / `tlResetView()`
4. 让 wheel zoom 要么真正按时间戳重新计算位置,要么改回请求 `_partial=1` JSON 并完整替换数据。
5. 更新 E2E 时间线选择器和断言。
### P1
1. 实现资产 checkbox dropdown,默认全选筛选后的候选资产。
2. 增加 State 筛选。
3. 让 selected count、URL 参数、实际 rows 三者一致。
4. 渲染 event labels、state-node/event 区分和 connector。
### P2
1. 清理废弃的 `mission_timeline_partial.html`
2. 固定测试用 `sim_time`,避免 DB 全局 simulation time 污染测试基线。
3. 给 timeline JSON 增加 schema/契约测试。
## 建议验收用例
1. 打开 `/mission-preview/timeline?sim_time=2060-03-12T09:00`,确认有时间刻度行,首行不是 asset 文本。
2. 默认打开后,asset dropdown 显示所有候选 asset 已选,rows 数等于 selected count。
3. 选择 type/location 后,asset dropdown 只包含筛选结果,并默认全选。
4. 取消一个 asset,确认该 asset row 立即消失或提交后消失。
5.`start=2057-04-24&end=2057-07-21&range_mode=manual&scale=auto`,确认 scale 解析为 Day 或预期精度。
6. 使用滚轮缩放,确认 hidden start/end、刻度标签、segment/event 位置一起变化。
7. 点击 `Zoom +` / `Zoom -` / `Reset View`,确认没有 JS error 且视图变化符合按钮含义。
8. 点击 asset 名称,确认跳到 `/assets/<id>?sim_time=...`
9. Hover event point,确认 tooltip 有 title/time;肉眼可见的事件标签或聚合提示存在。
10. 执行更新后的 E2E,时间线模块不再依赖旧 `#tls` / `.tl-r`