功能说明

功能迭代直接读取 worklog 时间线,保持实现、验证和结论的回溯一致。

迭代时间线 84
来源 docs/worklog.md

迭代时间线

数据直接来自 docs/worklog.md 的时间线记录。

来源
v0.84.0 2026-04-09 11:42 CST
调整 App Store / Google Play 的媒体字段差异判断:媒体内容相同但 URL 变化时更新数据库,不产出变更事件,也不进入时间线可见差异。
改动内容
改动预览 3/5
  • 修改 `common/models/app.py`、`common/storage/schema.sql`、`common/storage/init_db.py`、`common/storage/repositories/app_repository.py`,为应用记录新增 `icon_md5`、`header_image_md5`、`screenshot_md5s` 三类隐藏媒体哈希字段,并补齐 SQLite 轻量迁移和仓储层读写。
  • 新增 `common/utils/media_hash.py`,封装媒体资源下载、MD5 计算、有限重试和同 URL 哈希缓存;在 `google_play_scrap/workflow.py`、`app_store_scrap/workflow.py`、`app_store_scrap/backfill.py` 中接入,先补齐当前/上一版应用的媒体哈希,再做差异判断。
  • 修改 `common/diff/app_diff.py`,让 `icon_url`、`header_image_url`、`screenshot_urls` 在比较时优先使用媒体 MD5 指纹,只有没有哈希时才回退到 URL 归一化比较;事件里仍保留真实 URL 值,确保真正变化时可读。
展开完整改动
  • 修改 `common/models/app.py`、`common/storage/schema.sql`、`common/storage/init_db.py`、`common/storage/repositories/app_repository.py`,为应用记录新增 `icon_md5`、`header_image_md5`、`screenshot_md5s` 三类隐藏媒体哈希字段,并补齐 SQLite 轻量迁移和仓储层读写。
  • 新增 `common/utils/media_hash.py`,封装媒体资源下载、MD5 计算、有限重试和同 URL 哈希缓存;在 `google_play_scrap/workflow.py`、`app_store_scrap/workflow.py`、`app_store_scrap/backfill.py` 中接入,先补齐当前/上一版应用的媒体哈希,再做差异判断。
  • 修改 `common/diff/app_diff.py`,让 `icon_url`、`header_image_url`、`screenshot_urls` 在比较时优先使用媒体 MD5 指纹,只有没有哈希时才回退到 URL 归一化比较;事件里仍保留真实 URL 值,确保真正变化时可读。
  • 修改 `orchestrator/admin_service/presentation.py`,让快照时间线对媒体字段也按 MD5 指纹判断差异,并忽略新增的哈希字段本身,避免“媒体内容没变、仅 URL 变了”继续出现在时间线里。
  • 修改 `common/tests/test_app_diff.py` 与 `orchestrator/tests/test_admin_service.py`,新增“媒体 URL 变但 MD5 相同不触发差异”和“时间线忽略同内容媒体变化”的回归测试。
验证结果
验证预览 2/9
  • `./.venv/bin/python -m py_compile common/diff/app_diff.py common/utils/media_hash.py common/storage/repositories/app_repository.py common/storage/init_db.py google_play_scrap/workflow.py app_store_scrap/workflow.py app_store_scrap/backfill.py orchestrator/admin_service/presentation.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python -m py_compile common/diff/app_diff.py common/utils/media_hash.py common/storage/repositories/app_repository.py common/storage/init_db.py google_play_scrap/workflow.py app_store_scrap/workflow.py app_store_scrap/backfill.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 关键模块编译通过。
  • `./.venv/bin/pytest -q common/tests/test_app_diff.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `28 passed in 1.00s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `86 passed in 21.06s`
发现与结论
  • 仅修改 diff 规则不够,时间线仍会把快照里的 URL 变化渲染出来;必须同时改“事件 diff”和“快照可见 diff”两套判断。
  • 想满足“数据库值更新但不认为有变化”,关键是把“存储值”和“比较指纹”彻底分离:数据库继续存当前 URL,比较链路只认 MD5 指纹。
  • 这次实现对旧快照没有追溯补哈希,因此旧快照与新快照之间第一次过渡仍可能受历史数据完整性影响;从新版本开始生成的快照口径已经统一。
下一步
  • 如果后面要把历史时间线也完全清洗干净,下一步应该补一个“历史快照媒体哈希回填”工具,专门给旧 `app_snapshots` 补齐媒体指纹。
v0.83.0 2026-04-09 11:05 CST
排查 Google Play `Candy-Mobile` 工作室只能抓到一款应用的问题,并补齐发现链路的失败重试与数据校验。
改动内容
改动预览 3/4
  • 修改 `google_play_scrap/collector/studio_collector.py`,将 `developer_name` 类型的发现策略从“优先走通用搜索页”改为“优先访问开发者主页 `/store/apps/developer?id=...`,拿不到有效结果时才回退到搜索页”。同时补充 `_is_retryable_fetch_error()`,仅对超时、传输错误、`429` 与 `5xx` 做有限退避重试,避免对不可恢复错误无限重试。
  • 修改 `google_play_scrap/tests/test_studio_collector.py`,新增“优先使用开发者主页”和“开发者主页失败后回退搜索页”的回归测试。
  • 修改 `config/app.yaml`,为 Google Play / App Store 的详情抓取与评论抓取补齐可配置的重试次数和退避秒数默认值,统一失败重试口径。
展开完整改动
  • 修改 `google_play_scrap/collector/studio_collector.py`,将 `developer_name` 类型的发现策略从“优先走通用搜索页”改为“优先访问开发者主页 `/store/apps/developer?id=...`,拿不到有效结果时才回退到搜索页”。同时补充 `_is_retryable_fetch_error()`,仅对超时、传输错误、`429` 与 `5xx` 做有限退避重试,避免对不可恢复错误无限重试。
  • 修改 `google_play_scrap/tests/test_studio_collector.py`,新增“优先使用开发者主页”和“开发者主页失败后回退搜索页”的回归测试。
  • 修改 `config/app.yaml`,为 Google Play / App Store 的详情抓取与评论抓取补齐可配置的重试次数和退避秒数默认值,统一失败重试口径。
  • 修改 `orchestrator/admin_service/presentation.py`,补上 `rating_count` 的主翻译词条,恢复管理后台全量测试稳定性。
验证结果
验证预览 2/12
  • `./.venv/bin/python - <<'PY' ... GooglePlayStudioCollector(Candy-Mobile) ... PY`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python - <<'PY' ... GooglePlayStudioCollector(Candy-Mobile) ... PY`
  • Result: PASS
  • Detail: 实抓返回 `15` 个应用,`app_id` 包括 `com.Zgame.BlockPuzzle`、`com.dxm.nopuzzle`、`com.wordsmobile.gunship` 等,已不再只抓到单一应用。
  • `sqlite3 data/scrap.db "select ... where platform='google_play' and lower(studio_name) like '%candy%'"`
  • Result: PASS
  • Detail: 本地库里当前 `Candy-Mobile` 为 `15` 条 Google Play 应用,`developer_name` 均为 `Candy Mobile`。
  • `./.venv/bin/pytest -q google_play_scrap/tests/test_studio_collector.py google_play_scrap/tests/test_google_play_app_detail_collector.py google_play_scrap/tests/test_google_play_comment_collector.py orchestrator/tests/test_google_play_workflow.py`
  • Result: PASS
  • Detail: `11 passed in 0.38s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `82 passed in 1.49s`
发现与结论
  • 问题根因不是 Google Play 评论或详情解析,而是工作室发现入口用错了页面:通用搜索页只能给出关键词命中结果,既可能漏抓,也会混入不属于该工作室的应用。
  • 对 `developer_name` 类型工作室,Google Play 的开发者主页才是主发现入口;搜索页只能作为兜底方案。
  • 失败重试需要做“可恢复错误”筛选,盲目对所有异常重试只会放大限流和错误流量。
下一步
  • 如果后面继续提升稳定性,下一步应该把“工作室归属异常巡检”结果接入后台只读视图,直接展示疑似错归属应用与预期工作室候选。
v0.82.0 2026-04-05 00:08 CST
修复时间线里截图字段在响应式断点下被压成单列的问题,恢复为前后双列加中轴箭头。
改动内容
改动预览 2/2
  • 修改 `orchestrator/admin_service/static/admin.css`,将媒体字段从通用“单列堆叠”断点规则中剥离出来:桌面与移动端都保持 `旧值 / 箭头 / 新值` 三列结构,仅文本字段在极窄宽度下才允许单列堆叠。
  • 同时把移动端下 `snapshot-media-grid` 恢复为两列缩略图栅格,避免截图组在每一侧内部再次退化成单列。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `10 passed in 0.95s`
  • `./.venv/bin/python - <<'PY' ... playwright media compare probe ... PY`
  • Result: PASS
  • Detail: 实测时间线截图字段桌面端网格为 `480px 60px 480px`,移动端 `430px` 视口下为 `123px 52px 123px`,两端均保留中间箭头。
发现与结论
  • 之前截图字段“变成一列”不是模板结构丢了,而是响应式 CSS 把 `snapshot-media-compare` 和 `snapshot-media-grid` 一起压成了单列。
  • 文本字段和媒体字段在移动端不能共用同一套堆叠规则,否则时间线会再次退化。
下一步
  • 如果后面还要继续强化时间线,下一步可以给媒体字段补一个小型可视化标签,标明“新增 / 删除 / 替换”的变化类型。
v0.81.0 2026-04-04 03:31 CST
修复时间线字段对比里“中轴不统一”和“短值仍然用大框展示”的问题,并给所有字段补上明确箭头。
改动内容
改动预览 2/2
  • 修改 `orchestrator/admin_service/templates/partials/timeline_panel.html`,让媒体字段也走统一的前后对比结构,在中间插入和文本字段一致的箭头轴线;同时给文本字段补充一组明确的紧凑字段名单,让 `下载量 / 版本 / 链接 / 应用名 / 评分` 这类中短值优先进入紧凑展示分支,而不是继续落入大卡片容器。
  • 修改 `orchestrator/admin_service/static/admin.css`,将 `snapshot-media-compare` 收口到和文本对比一致的三列中轴布局,给 `after` 侧媒体与文本值增加更明确的高亮层次,并收紧普通值容器和 URL 值容器的最大宽度,避免短内容出现过大的空白框。
验证结果
验证预览 2/9
  • `./.venv/bin/python - <<'PY' ... playwright timeline probe ... PY`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python - <<'PY' ... playwright timeline probe ... PY`
  • Result: PASS
  • Detail: 实测时间线前 6 个对比块都带箭头,且箭头中轴 `x=1003` 保持一致;`评分 / 评分数` 这类短值盒子已收敛到 `103~110px` 宽、`44px` 高,不再是大空框。
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `10 passed in 1.53s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `80 passed in 1.97s`
发现与结论
  • 之前“很少的内容用了很大的展示框”并不只是 padding 问题,真正根因是很多业务字段没有命中紧凑展示分支,和长文本共用了同一套普通容器。
  • 把中轴箭头统一到文本字段和媒体字段两类组件后,视觉基准线才真正固定下来;仅仅给文本字段加箭头是不够的。
下一步
  • 如果后面还要继续提升时间线可读性,下一步应该把媒体字段的“新值”高亮进一步做成更明显的主状态卡片,并补一个针对时间线布局的前端回归断言。
v0.80.0 2026-04-04 02:18 CST
修复时间线页查询条件区域的布局错乱和文案露出 key 的问题。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/timeline.html`,把时间线查询区重构成更明确的两段布局:左侧是关键字搜索与候选列表,右侧是唯一目标应用卡片;底部再单独放每页数量和“查看时间线”操作,避免原来一个大栅格里混合四种职责导致的空洞和错位。
  • 同时将时间线页前端脚本里的动态文案改成从表单 `data-*` 属性读取,而不是在脚本内部重复拼接静态文案,避免因缓存或局部模板错位导致原始 key 被直接渲染到页面。
  • 修改 `orchestrator/admin_service/static/admin.css`,补齐 `timeline-picker-layout / timeline-picker-actions` 的桌面和响应式布局规则,保证查询区在桌面与移动端都不会出现大面积空白或控件对不齐的问题。
验证结果
验证预览 2/12
  • `./.venv/bin/python - <<'PY' ... timeline page render probe ... PY`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python - <<'PY' ... timeline page render probe ... PY`
  • Result: PASS
  • Detail: 确认页面 HTML 中不再出现 `TIMELINE_SELECTED_TARGET / timeline_clear_target / TIMELINE_CANDIDATE_RESULTS` 这类原始 key,且中文文案正常渲染。
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.12s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台入口、查询层与时间线页模板引用正常。
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `78 passed in 1.15s`
发现与结论
  • 这次问题核心不是单个控件错位,而是查询区把“关键字过滤 / 唯一目标 / 分页大小 / 执行操作”四类职责塞进了一条网格,导致布局和心智模型都混乱。
  • 原始 key 出现在页面上,虽然当前测试环境里无法稳定复现,但把动态文案统一改成服务端注入的 `data-*` 属性后,这类问题的来源就被收掉了。
下一步
  • 如果继续推进,下一步建议把时间线候选应用列表补上键盘上下选择和回车确认,让这套唯一目标选择器的交互完整闭环。
v0.79.0 2026-04-04 02:05 CST
优化时间线页字段改动对比的视觉表达,避免少量文本占用过大的空框,并强化“变更后”状态。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/partials/timeline_panel.html`,将文本字段对比从简单双列改成 `变更前 -> 变更后` 的显式对比结构,中间新增箭头标识;同时放宽紧凑对比阈值,让较短的名称、日期、版本等字段优先走紧凑模式。
  • 修改 `orchestrator/admin_service/static/admin.css`,将文本字段对比改成三列布局:左侧旧值、中央箭头、右侧新值;新增 `snapshot-value-box.is-after` 的强调样式,让“变更后”在边框、背景和层次上更突出。
  • 同时保留媒体字段对比为两列布局,避免把图片对比错误地拉成三列。
验证结果
验证预览 2/9
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台入口、查询层和时间线模板引用未出现编译问题。
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.92s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `78 passed in 1.62s`
发现与结论
  • 之前“内容很少却占很大框”的根因不是单纯 padding 过大,而是很多字段没有命中紧凑对比分支,仍然沿用普通大卡片容器。把名称类字段的紧凑阈值放宽后,这类问题会明显减少。
  • 视觉上真正需要强调的是“新状态”,不是旧状态与新状态完全对等。把箭头和高亮都集中在 `after` 侧,阅读路径会更清晰。
下一步
  • 如果继续推进,下一步建议把时间线里的媒体字段也做成同样的“主次分明”对比布局,让新增截图和旧截图之间的层级更统一。
v0.78.0 2026-04-04 01:46 CST
收口时间线页查询交互、jobs/comments 移动端布局,以及 App Store / Google Play 抓取失败重试策略。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/templates/timeline.html`,将时间线页查询改为“关键字即时联想 + 唯一 `app_key` 提交”的两段式交互:关键字输入只用于异步过滤候选应用,真正提交结果仍只依赖隐藏的 `app_key` 字段。
  • 修改 `orchestrator/admin_service/static/admin.css`,补齐时间线联想选择器样式,并新增 `desktop-table-only / mobile-kv-list / mobile-kv-card` 等样式,把 `comments` 与 `jobs` 页在移动端改造成更紧凑的键值对卡片,而不是沿用桌面表格堆叠语义。
  • 修改 `orchestrator/admin_service/templates/comments.html` 与 `orchestrator/admin_service/templates/jobs.html`,保留桌面表格,同时增加移动端卡片视图,收口应用、任务、执行记录与评论监控在窄屏下的信息密度。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/timeline.html`,将时间线页查询改为“关键字即时联想 + 唯一 `app_key` 提交”的两段式交互:关键字输入只用于异步过滤候选应用,真正提交结果仍只依赖隐藏的 `app_key` 字段。
  • 修改 `orchestrator/admin_service/static/admin.css`,补齐时间线联想选择器样式,并新增 `desktop-table-only / mobile-kv-list / mobile-kv-card` 等样式,把 `comments` 与 `jobs` 页在移动端改造成更紧凑的键值对卡片,而不是沿用桌面表格堆叠语义。
  • 修改 `orchestrator/admin_service/templates/comments.html` 与 `orchestrator/admin_service/templates/jobs.html`,保留桌面表格,同时增加移动端卡片视图,收口应用、任务、执行记录与评论监控在窄屏下的信息密度。
  • 修改 `app_store_scrap/collector/app_detail_collector.py` 与 `google_play_scrap/collector/app_detail_collector.py`,为详情页 HTTP 抓取增加有限重试,仅对超时、传输错误以及 `429/5xx` 做保守退避重试,避免把解析错误和业务错误误判成可重试问题。
  • 修改 `google_play_scrap/comment_collector/comment_collector.py`,为 Google Play 评论抓取增加有限重试;同步修改 `app_store_scrap/workflow.py`、`google_play_scrap/workflow.py` 与 `google_play_scrap/comment_workflow.py`,让工作流支持新的重试配置项并落到 collector 层。
  • 新增 `google_play_scrap/tests/test_google_play_app_detail_collector.py`,并扩充 `app_store_scrap/tests/test_app_detail_collector.py`、`google_play_scrap/tests/test_google_play_comment_collector.py` 与 `orchestrator/tests/test_admin_service.py`,覆盖“首轮失败、次轮成功”的重试行为,以及时间线联想控件、jobs/comments 移动端卡片结构断言。
验证结果
验证预览 2/9
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py google_play_scrap/comment_collector/comment_collector.py google_play_scrap/collector/app_detail_collector.py app_store_scrap/collector/app_detail_collector.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py google_play_scrap/comment_collector/comment_collector.py google_play_scrap/collector/app_detail_collector.py app_store_scrap/collector/app_detail_collector.py`
  • Result: PASS
  • Detail: 后台入口、查询层和新 collector 重试代码均能正常编译。
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py app_store_scrap/tests/test_app_detail_collector.py google_play_scrap/tests/test_google_play_app_detail_collector.py google_play_scrap/tests/test_google_play_comment_collector.py`
  • Result: PASS
  • Detail: `19 passed in 2.10s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `78 passed in 2.57s`
发现与结论
  • 时间线页真正需要澄清的是“关键字”和“目标应用”是两件不同的事。关键字只能缩小候选范围,不能直接决定结果;实际查询必须继续由 `platform:app_id` 这种唯一标识驱动,否则结果口径会重新回到模糊匹配。
  • 移动端上的 `jobs/comments` 问题不是简单的换行,而是继续拿桌面表格做语义降级会导致阅读路径很差。改成键值对卡片后,信息优先级和操作位置都更稳定。
  • 抓取失败重试需要有限且保守。现在只对明确的瞬时网络错误和 `429/5xx` 做退避,不对解析失败、字段缺失或业务过滤重试,避免扩大抓取风险或放大脏数据写入。
下一步
  • 如果继续推进,下一步建议把时间线页的候选应用选择改成键盘可导航的列表,并把 `detail_max_retries/comment_max_retries` 这些配置项补进文档,避免线上配置与代码能力脱节。
v0.77.0 2026-04-04 01:12 CST
过滤时间线中与上一份快照相比没有可见字段变化的记录。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/timeline.html`,移除对旧 `history.snapshots` 变量的依赖,统一使用路由传入的 `timeline_entries`。
  • 修改 `orchestrator/tests/test_admin_service.py`,新增一份仅 `raw_html_hash/raw_json_hash` 不同的重复快照样本,验证这类无可见变化的快照不会出现在时间线里。
  • 同步更新历史接口断言,确认原始快照仍完整保留 3 条,而时间线页面只展示 2 条可见变化记录。
验证结果
验证预览 2/2
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • `./.venv/bin/pytest -q`
发现与结论
  • 之前过滤逻辑虽然已经在 `snapshot_timeline()` 内部存在,但时间线模板仍直接引用旧的 `history.snapshots`,导致页面没有真正用上过滤后的结果。
  • 这类问题单看 Python 层很容易误判为“逻辑已完成”,实际必须把模板和测试一起收口,才能保证 UI 口径和数据口径一致。
下一步
  • 如果继续推进,下一步建议给时间线页的大批量媒体变更加默认折叠,避免单条记录过长。
v0.76.0 2026-04-04 00:46 CST
把最近两轮对时间线页、应用列表页和应用详情页的结构改动统一收口到移动端布局,避免桌面样式残留导致的按钮组挤压和横向溢出。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/static/admin.css`,为 `timeline-app-summary-actions` 和 `app-jump-actions` 增加统一的弹性/网格降级逻辑:在 `<=960px` 下先改为可拉伸按钮组,在 `<=720px` 下改成单列全宽按钮,确保“查看时间线 / 打开应用详情 / 打开商店页”在窄屏下稳定落成一列。
  • 同步为 `.filters input/select/button` 增加 `width: 100%`、`min-width: 0`、`max-width: 100%` 与 `box-sizing: border-box`,修复时间线页候选应用 `select` 在 `430px` 视口下因固有最小宽度保留而造成的 `14px` 横向溢出。
  • 保留最新评论在应用详情底部的结构不变,但通过真实视口检查确认其位置仍然在页面最下方,没有因为从 `detail-layout` 中抽离而回到中段。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py && ./.venv/bin/pytest -q`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py && ./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `9 passed in 0.89s`;全量 `75 passed in 0.97s`
  • `./.venv/bin/python - <<'PY' ... Playwright 430px viewport probe for /timeline and /apps/app_store/6498883328 ... PY`
  • Result: PASS
  • Detail: 时间线页 `scrollWidth=430`、`innerWidth=430`、`bodyOverflow=false`;应用详情页 `scrollWidth=430`、`bodyOverflow=false`,最新评论模块仍位于页面底部(`commentsTop=3173.75`)。
发现与结论
  • 这轮真正的移动端问题不是大布局错了,而是新加的交互控件沿用了桌面端的固有宽度。尤其是时间线页候选应用 `select`,即使外层已经改单列,在手机宽度下仍会保留自身的最小内容宽度,导致页面出现轻微横向溢出。
  • 用真实视口测 `scrollWidth > innerWidth` 比肉眼看截图更可靠,这次能很快把问题收敛到单个 `select` 节点,而不是继续在整页容器上盲目调 CSS。
下一步
  • 如果继续推进,下一步建议把 `320px / 375px / 430px` 三档移动端 Playwright 布局检查固化成脚本,覆盖时间线页、应用列表和应用详情页,防止后续再被局部桌面样式带回横向溢出。
v0.75.0 2026-04-04 00:31 CST
简化应用详情页结构:移除“变更记录”模块,并把“最新评论”移动到页面最下方。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,从详情页主内容区移除整个 `app-changes` 模块及其表格、媒体对比、原始记录折叠区。
  • 同时调整 `detail_nav_items` 顺序,改为 `应用截图 -> 当前应用信息 -> 应用描述 -> 最新评论`,让“最新评论”在导航和实际页面流里都位于最后。
  • 将 `detail_comments_panel(history, lang)` 从 `detail-main` 内部移出,改为放在整个 `detail-layout` 之后,作为页面底部的独立模块渲染。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,从详情页主内容区移除整个 `app-changes` 模块及其表格、媒体对比、原始记录折叠区。
  • 同时调整 `detail_nav_items` 顺序,改为 `应用截图 -> 当前应用信息 -> 应用描述 -> 最新评论`,让“最新评论”在导航和实际页面流里都位于最后。
  • 将 `detail_comments_panel(history, lang)` 从 `detail-main` 内部移出,改为放在整个 `detail-layout` 之后,作为页面底部的独立模块渲染。
  • 修改 `orchestrator/tests/test_admin_service.py`,移除对详情页旧变更记录模块、旧媒体对比属性和旧表格类名的断言,并更新导航顺序断言。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.85s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `75 passed in 1.06s`
发现与结论
  • 这次不需要动数据层或查询层,问题纯粹是页面结构。把“变更记录”保留在时间线页后,应用详情继续保留一份类似信息只会造成重复和视觉干扰。
  • “最新评论”放在 `detail-layout` 内部时,它的优先级会和信息侧栏并列,阅读路径不清晰。移到详情页最下方之后,信息流更符合“先看应用本身,再看评论反馈”的顺序。
下一步
  • 如果继续推进,下一步建议把应用详情页的 `detail-main` 和 `detail-side` 在平板断点下再做一次间距收紧,避免“截图 + 信息侧栏”在中等宽度下显得过松。
v0.74.0 2026-04-04 00:18 CST
优化独立时间线页的查询逻辑与首屏排版,改成“关键字筛候选 + 唯一 app_key 选应用”的清晰交互,并补上快照分页与应用列表/详情页的时间线快捷入口。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_app_snapshots_page()`,按 `platform + app_id` 对应用快照做分页查询,返回 `total/page/page_size/has_prev/has_next` 元数据。
  • 修改 `orchestrator/admin_service/app.py`,为 `/timeline` 增加 `page/page_size` 参数,并把“真正加载时间线”的条件收紧为仅接受 `app_key=platform:app_id`。`q` 现在只用于过滤候选应用,不再触发模糊命中后直接展示结果。
  • 修改 `orchestrator/admin_service/presentation.py`,将时间线页文案改为更明确的两步式说明:`筛选关键字` 仅用于缩小候选范围,`目标应用(唯一标识)` 才是实际查询条件;同时补充 `每页快照数` 和 `查看时间线` 文案。
展开完整改动
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_app_snapshots_page()`,按 `platform + app_id` 对应用快照做分页查询,返回 `total/page/page_size/has_prev/has_next` 元数据。
  • 修改 `orchestrator/admin_service/app.py`,为 `/timeline` 增加 `page/page_size` 参数,并把“真正加载时间线”的条件收紧为仅接受 `app_key=platform:app_id`。`q` 现在只用于过滤候选应用,不再触发模糊命中后直接展示结果。
  • 修改 `orchestrator/admin_service/presentation.py`,将时间线页文案改为更明确的两步式说明:`筛选关键字` 仅用于缩小候选范围,`目标应用(唯一标识)` 才是实际查询条件;同时补充 `每页快照数` 和 `查看时间线` 文案。
  • 修改 `orchestrator/admin_service/templates/timeline.html`,去掉容易混淆的 `datalist`,保留“关键字 + 唯一候选应用 + 每页快照数”三段式筛选;顶部统计改成 `平台 / 快照总数 / 每页快照数`,不再把长应用名塞进 banner 指标区。并接入现有 `pagination()` 组件展示快照分页。
  • 修改 `orchestrator/admin_service/templates/apps.html` 与 `templates/app_detail.html`,分别在应用列表跳转列和应用详情 hero 操作区增加“查看时间线”快捷入口,统一跳到 `/timeline?app_key=platform:app_id`。
  • 修改 `orchestrator/admin_service/static/admin.css`,重做时间线筛选区栅格与按钮布局,避免按钮占满整行导致的视觉混乱;同时新增 `app-jump-actions`,让应用列表里的时间线入口与商店入口保持一致的按钮组节奏。
验证结果
验证预览 2/9
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 时间线路由、查询层与展示层都能正常编译。
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.07s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `75 passed in 1.03s`
发现与结论
  • 用户反馈的核心问题不是“没有分页”,而是时间线页在交互语义上不够严谨:`q` 既像筛选条件又像真实查询条件,导致“模糊输入直接出结果”的感觉很强。这类场景下必须把“候选过滤”和“唯一标识查询”明确拆开,否则即使功能能跑,运营也会认为数据口径不可靠。
  • 另一个真实问题是 `with_lang()` 拼接 query string 时会对 `app_key` 里的冒号做 URL 编码,所以测试里不能把链接写死成原始 `platform:app_id` 文本,应按最终渲染的 `%3A` 结果校验。
下一步
  • 如果继续推进,下一步建议给时间线页补一个轻量的前端联想选择器,把“关键字过滤候选”的反馈从整页刷新改成更即时的下拉筛选,但实际结果页仍然只由 `app_key` 决定。
v0.73.0 2026-04-03 14:12 CST
统一后台顶部按钮风格,并将应用详情中的快照时间线抽离为独立的“时间线 / Timeline”模块,同时补齐后台专项测试与全量回归。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/templates/base.html` 与 `orchestrator/admin_service/static/admin.css`,将顶部菜单、返回、刷新、主题切换、语言切换统一成同一套圆角矩形图标按钮;主题切换改为图标态并按 `light -> dark -> system` 顺序循环,语言切换改为单个图标按钮在中英文之间切换,侧边栏菜单图标也统一改为 SVG 图标。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `时间线 / Timeline` 导航、页面文案、搜索提示以及主题切换与语言切换相关文案。
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_timeline_apps()`,支持通过 `app_name / app_id / studio_name` 查询候选应用,为独立时间线页面提供应用联想和下拉候选。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/base.html` 与 `orchestrator/admin_service/static/admin.css`,将顶部菜单、返回、刷新、主题切换、语言切换统一成同一套圆角矩形图标按钮;主题切换改为图标态并按 `light -> dark -> system` 顺序循环,语言切换改为单个图标按钮在中英文之间切换,侧边栏菜单图标也统一改为 SVG 图标。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `时间线 / Timeline` 导航、页面文案、搜索提示以及主题切换与语言切换相关文案。
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_timeline_apps()`,支持通过 `app_name / app_id / studio_name` 查询候选应用,为独立时间线页面提供应用联想和下拉候选。
  • 修改 `orchestrator/admin_service/app.py`,新增 `GET /timeline` 页面路由和 `GET /api/timeline/apps` 接口;时间线页支持手动输入应用关键字、通过候选应用下拉定位应用,并渲染独立快照时间线。
  • 新增 `orchestrator/admin_service/templates/timeline.html` 与 `templates/partials/timeline_panel.html`,把原应用详情页里的快照时间线抽成复用模块;同时从 `templates/app_detail.html` 中移除嵌入式快照时间线,只保留应用截图、变更记录、最新评论、当前应用信息和应用描述。
  • 修改 `orchestrator/tests/test_admin_service.py`,将时间线相关断言从应用详情页迁移到独立 `/timeline` 页面,并修正测试种子实际只产生 `1` 个变更字段的断言;同时修复 `timeline_panel.html` 中 `tojson(indent=2, ensure_ascii=False)` 与当前 Jinja 版本不兼容的问题,改为 `tojson(indent=2)`。
验证结果
验证预览 2/9
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 独立时间线路由、查询层和展示层模块都能正常编译加载。
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.07s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `75 passed in 1.23s`
发现与结论
  • 这轮真正暴露出的 bug 不是功能实现本身,而是测试和模板兼容性。时间线从详情页抽离后,原来依附在详情页上的媒体预览断言和旧图标 URL 断言都变成了假失败,需要迁移到新页面;同时 `tojson(..., ensure_ascii=False)` 这个参数在当前 Jinja 版本下不可用,独立时间线页首次真正走到原始快照折叠区时才暴露出来。
  • 把时间线抽成独立模块后,应用详情页的信息层次更干净,后续要做应用级“跨版本快照比对”或时间线筛选也更容易扩展,不需要再继续把详情页堆成一个过长的大模板。
下一步
  • 如果继续推进,下一步建议把独立时间线页补上分页或快照数量上限控制,并在应用列表页增加“直接查看时间线”的入口,减少运营侧在应用详情和时间线页之间来回跳转的成本。
v0.72.0 2026-04-03 13:36 CST
按严格验收标准复核移动端后台首屏,并继续收掉首屏层级与信息重复问题。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/dashboard.html`、`apps.html`、`comments.html`、`jobs.html`,移除与顶部 `surface banner` 重复的模块副标题,避免移动端首屏在“功能说明”和列表区再次重复同一段文案。
  • 修改 `orchestrator/admin_service/static/admin.css`,在 `<=720px` 下进一步压缩移动端首屏:减小标题字号、收紧 `surface banner` 的摘要和统计卡片、隐藏移动端重复的 `metric-grid`,并收掉面板副标题,让页面更像移动端信息流而不是桌面后台缩小版。
  • 同步缩小 `surface-banner-label` 与统计卡片密度,减少首屏死白和重复信息。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.04s`
  • `./.venv/bin/python - <<'PY' ... Playwright 375px screenshots for dashboard/apps/comments/detail ... PY`
  • Result: PASS
  • Detail: 复核 `dashboard/apps/comments/detail` 首屏,确认移动端已去掉重复副标题;`dashboard` 的重复指标卡在首屏已隐藏,页面信息密度明显改善。
发现与结论
  • 严格验收下,上一版移动端虽然“能用”,但还不够合格,主要问题不是单个控件错位,而是系统性地重复解释同一件事:顶部 `surface banner` 讲一遍,下面列表模块再讲一遍,`dashboard` 甚至连指标也重复两次。这会直接削弱移动端首屏的层级和节奏。
  • 收掉这些重复信息后,页面仍然保留了必要的筛选、统计和导航,但首屏阅读路径更清楚:先看标题和核心概览,再进入操作或数据列表。
下一步
  • 如果继续推进,下一步建议把 `jobs/comments` 的移动端数据行也进一步做成更紧凑的“键值对卡片”,而不是沿用桌面表格语义的堆叠版本。
v0.71.0 2026-04-03 13:24 CST
收口移动端页头排版,统一所有页面顶部标题、返回/菜单按钮、刷新/主题按钮的视觉层级,并修复侧边栏展开后出现两个返回入口的问题。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/base.html`,将移动端侧边栏展开态的浮动按钮图标从“返回箭头”改为关闭图标 `✕`,避免与页面返回按钮形成两个语义相近的入口。
  • 修改 `orchestrator/admin_service/static/admin.css`,在 `<=960px` 下将 `.content` 顶部留白从 `88px` 收回到 `16px`,让标题和菜单按钮回到同一顶线;同步把 `page-header` 改成顶部对齐并为语言切换预留下方空间。
  • 调整移动端 `.header-actions`,固定为两颗等尺寸图标按钮的第一行,主题切换只保留图标,不再显示文字;语言切换从按钮栅格中剥离,避免继续撑高页头并干扰首屏对齐。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/base.html`,将移动端侧边栏展开态的浮动按钮图标从“返回箭头”改为关闭图标 `✕`,避免与页面返回按钮形成两个语义相近的入口。
  • 修改 `orchestrator/admin_service/static/admin.css`,在 `<=960px` 下将 `.content` 顶部留白从 `88px` 收回到 `16px`,让标题和菜单按钮回到同一顶线;同步把 `page-header` 改成顶部对齐并为语言切换预留下方空间。
  • 调整移动端 `.header-actions`,固定为两颗等尺寸图标按钮的第一行,主题切换只保留图标,不再显示文字;语言切换从按钮栅格中剥离,避免继续撑高页头并干扰首屏对齐。
  • 修改 `<=720px` 下的按钮宽度规则,移除对 `.header-control/.theme-toggle/.lang-switch` 的全宽覆盖,避免页头控件在手机宽度下再次被拉成长条。
验证结果
验证预览 2/6
  • `./.venv/bin/python - <<'PY' ... Playwright 430px header/top screenshots for /apps and /apps/google_play/com.joyfulark.coloring.housecolor ... PY`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python - <<'PY' ... Playwright 430px header/top screenshots for /apps and /apps/google_play/com.joyfulark.coloring.housecolor ... PY`
  • Result: PASS
  • Detail: `apps` 与 `app detail` 页面 `page-header.top=14`、`title.top=14`,标题与菜单按钮齐平;应用详情页展开侧边栏后只剩一个可见关闭按钮。
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.94s`
发现与结论
  • 这次顶部错位的主因不是单个按钮样式,而是移动端 `.content` 还继承了桌面抽屉的顶部预留,直接把整个 `page-header` 压到了第二行;同时 `lang-switch` 仍在页头栅格流内,导致标题即使对齐了也会被整行高度拖低。
  • 侧边栏展开后出现“两个返回按钮”的视觉问题,本质上是浮动侧边栏按钮沿用了返回型箭头图标。移动端抽屉的主操作应当是“关闭”,不是“返回”,改成关闭图标后语义就清楚了。
下一步
  • 如果继续推进,下一步建议把 `320px / 375px / 430px` 三档移动端页头截图固化成前端回归检查,避免后续 CSS 改动再次把页头挤回两行。
v0.70.0 2026-04-03 13:02 CST
修复应用详情页在移动端下的三类排版问题:内容导航布局错误、导航下方模块继续横向挤压、最新评论 metadata 换行混乱。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/static/admin.css`,为 `.detail-rail/.detail-main/.detail-side`、`.detail-nav-panel`、`.detail-anchor-nav`、`.detail-anchor-link` 增加显式 `min-width: 0 / width: 100%` 约束,避免内容导航根据子项最大内容宽度把整列撑爆。
  • 修改 `@media (max-width: 1180px)` 下的详情页布局,将 `.detail-layout` 从单列表面上的 CSS Grid 改成真正的纵向 Flex 容器,并重置 `.detail-side` 之前遗留的 `grid-column: 2` 行为。修复后,内容导航、主内容和信息区都会按单列顺序串联,而不会再产生隐式第二列。
  • 调整 `@media (max-width: 560px)` 下的内容导航样式,将横向滚动锚点改为两列胶囊网格,避免导航项被截断或需要横向拖动才能看全。
展开完整改动
  • 修改 `orchestrator/admin_service/static/admin.css`,为 `.detail-rail/.detail-main/.detail-side`、`.detail-nav-panel`、`.detail-anchor-nav`、`.detail-anchor-link` 增加显式 `min-width: 0 / width: 100%` 约束,避免内容导航根据子项最大内容宽度把整列撑爆。
  • 修改 `@media (max-width: 1180px)` 下的详情页布局,将 `.detail-layout` 从单列表面上的 CSS Grid 改成真正的纵向 Flex 容器,并重置 `.detail-side` 之前遗留的 `grid-column: 2` 行为。修复后,内容导航、主内容和信息区都会按单列顺序串联,而不会再产生隐式第二列。
  • 调整 `@media (max-width: 560px)` 下的内容导航样式,将横向滚动锚点改为两列胶囊网格,避免导航项被截断或需要横向拖动才能看全。
  • 调整 `@media (max-width: 560px)` 下的最新评论样式,将前四个字段改成 `minmax(0, 1fr) + 3 个 max-content` 的 metadata 网格,正文固定占满下一行,避免“用户 / 评分 / 版本 / 时间”被拆成多行标签块。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.20s`
  • `./.venv/bin/python - <<'PY' ... Playwright 430px app detail layout probe ... PY`
  • Result: PASS
  • Detail: `detailMainWidth=400`,`detailSideWidth=400`,`detailAnchorNavWidth=370`,`detail-comments` 首行 metadata 为单行 grid,正文独立占 `1 / -1`
  • `./.venv/bin/python - <<'PY' ... Playwright element screenshot for .detail-nav-panel / #app-comments ... PY`
  • Result: PASS
  • Detail: 移动端局部截图确认内容导航已变为两列胶囊布局,最新评论 metadata 为单行展示,正文单独成段
发现与结论
  • 这次的关键问题不是单个模块样式不够细,而是详情页之前残留了桌面三栏布局的隐式约束。尤其是 `detail-side` 在较宽断点里设置过 `grid-column: 2`,但在更窄断点没有完全回收,导致即使表面上写了 `grid-template-columns: 1fr`,真实渲染仍会创建隐式第二列。
  • 内容导航如果在移动端继续用横向滚动,不一定是“错”,但在这个后台场景下不适合:导航项数量固定且不多,两列胶囊布局比横向滚动更稳定,也更利于用户快速扫读。
下一步
  • 如果继续推进,下一步建议把这次 Playwright 430px 布局检查整理成脚本,纳入后台前端回归集,避免详情页响应式被后续 CSS 覆盖再次打回桌面三栏逻辑。
v0.69.0 2026-04-03 12:44 CST
排查浏览器设备模拟下所有页面出现“一字一行”和主内容被压成窄列的问题,并快速修复移动端主布局冲突。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/static/admin.css`,在移动端断点下显式覆盖 `html[data-sidebar="collapsed"] .layout` 和 `html[data-sidebar="expanded"] .layout`,确保窄屏始终使用单列主布局,而不会继续被桌面态的 `0 minmax(0, 1fr)` 网格规则压缩。
  • 同步为 `.content` 增加显式 `width: 100%`,并在移动端断点下补充 `grid-column: 1` 与 `min-width: 0`;同时为 `page-header h1` 增加 `flex: 1 1 auto`、`min-width: 0` 和 `overflow-wrap: anywhere`,避免标题在小屏下退化成逐字换行。
  • 使用 Playwright 在 `430x932` 视口下对 `/apps?lang=zh-CN` 做了 headless 布局测量,确认修复前 `.content` 实际宽度只有 `30px`,修复后恢复为 `430px`,`surface-banner-summary` 宽度恢复到 `370px`。
验证结果
验证预览 2/6
  • `./.venv/bin/python - <<'PY' ... Playwright 430px viewport layout probe ... PY`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python - <<'PY' ... Playwright 430px viewport layout probe ... PY`
  • Result: PASS
  • Detail: 修复前 `.content.width=30`;修复后 `.content.width=430`,`.page-header.width=400`,`.surface-banner.width=400`
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.06s`
发现与结论
  • 根因不是 `viewport` 缺失,而是我们前面新增的移动端 CSS 没有完全压过更早定义的 `html[data-sidebar="collapsed"] .layout { grid-template-columns: 0 minmax(0, 1fr); }`。桌面收起侧栏的规则在小屏下仍然生效,主内容列被挤成接近 0 宽度,进而导致标题、说明文案和筛选区都退化成最小内容宽度布局。
  • 这类问题单靠肉眼看截图不容易快速定位,用 headless 浏览器直接抓 `bounding box` 很有效,后面排查移动端错位可以继续沿用这套方法。
下一步
  • 如果继续推进,下一步建议补一条前端回归脚本,至少对 `apps/comments/app detail` 三个页面在 `430px` 视口下做宽度断言,避免后续再被响应式覆盖顺序问题回归。
v0.68.0 2026-04-03 12:31 CST
对应用详情页做第二轮移动端收口,重点处理 `390px` 左右手机宽度下的首屏密度、内容导航、截图/媒体预览和评论展示。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,将“最新评论”表格接入 `table-stack-mobile`,并补齐 `data-label` 字段,移动端可自动降级为纵向字段块。
  • 修改 `orchestrator/admin_service/static/admin.css`,新增 `560px` 和 `420px` 两级断点:压缩页头与首屏 Hero、缩小图标与胶囊标签、将内容导航改为横向滚动锚点、收紧时间线卡片和媒体缩略图密度,并让截图区/媒体区在极窄宽度下退化为单列展示。
  • 调整灯箱在手机端的留白和关闭按钮尺寸,避免全屏查看图片时顶部控件挤占过多空间。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.00s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台入口、展示层和查询层模块编译通过
发现与结论
  • 第一轮移动端适配主要解决了“能否使用”,但应用详情页在真实手机宽度下仍然偏向桌面思路,最明显的是内容导航仍像侧栏目录、评论表仍偏表格思维、时间线媒体和截图在极窄宽度下没有进一步降密度。
  • 这次收口后,详情页在手机端更接近“分段阅读”的信息流,而不是把桌面三栏页面硬压成一列。
下一步
  • 如果继续推进,下一步建议用真实设备或浏览器设备模式把 `320px / 375px / 430px` 三个宽度走一遍,重点检查语言切换、主题切换和灯箱交互是否还有局部溢出。
v0.67.0 2026-04-03 12:18 CST
为后台页面补齐移动端适配,解决侧边栏、详情页和多张列表页在窄屏上的可用性问题。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/base.html`,新增移动端侧边栏遮罩层,并将侧边栏状态初始化逻辑改成在窄屏默认折叠;补充遮罩点击关闭与窗口尺寸变化时的侧边栏状态同步。
  • 修改 `orchestrator/admin_service/templates/apps.html`、`comments.html`、`jobs.html`、`studios.html`、`dashboard.html`,为主要表格补充 `table-stack-mobile` 类和 `data-label` 字段标签,移动端可自动降级为卡片式纵向信息块。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充移动端抽屉侧栏、遮罩层、详情页单列降级、筛选表单双列/单列切换、按钮与操作区自适应,以及 `table-stack-mobile` 在小屏上的堆叠展示规则。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.78s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台入口、展示层和查询层模块编译通过
发现与结论
  • 现有后台之前只做了部分断点压缩,没有真正进入移动端布局模式,所以看起来“页面变小了”,但交互结构并没有随之变化。最明显的问题是侧边栏仍按桌面常驻结构工作,列表页仍假定表格有充足横向空间。
  • 这次先保证“可用性”和“信息不丢失”:窄屏时用抽屉侧栏和堆叠表格兜底。更高阶的移动端细节,比如某些详情模块进一步组件化、不同页面的首屏密度二次收敛,还可以继续做。
下一步
  • 如果继续推进,下一步建议针对真实手机视口再做一轮 UI 走查,重点看应用详情页的时间线、图片灯箱和头部控件在 `390px` 左右宽度下的密度是否还需要继续压缩。
v0.66.0 2026-03-26 10:40 CST
排查管理后台启动后出现的 `Failed to get app detail for ...` 日志,确认来源并降低瞬时网络失败带来的噪音。
改动内容
改动预览 2/2
  • 修改 `app_store_scrap/adapters/itunes_search_api.py`,确认这些日志来自 Apple `lookup` 详情请求;将原先的裸 `print` 改为标准日志输出,并为 `httpx.TimeoutException` / `httpx.TransportError` 增加有限重试和退避。
  • 修改 `app_store_scrap/tests/test_itunes_search_api.py`,新增 Apple 详情请求在瞬时连接失败后自动重试成功的回归测试。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_itunes_search_api.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_itunes_search_api.py`
  • Result: PASS
  • Detail: `3 passed in 0.04s`
  • `./.venv/bin/python -m py_compile app_store_scrap/adapters/itunes_search_api.py`
  • Result: PASS
  • Detail: App Store iTunes API 适配器编译通过
发现与结论
  • 这些日志不是页面请求报错,而是管理后台启动后调度器会立即触发 `app_store_sync` / `app_store_comments` 等后台任务;日志里的失败来自 Apple API 的瞬时 TLS 握手超时或连接被对端重置。
  • 当前工程默认启动调度器;如果只是本地调试页面、不希望后台任务同时跑,可以使用 `python -m orchestrator.admin_service.run --host 127.0.0.1 --port 8010 --no-scheduler`。
下一步
  • 继续观察启动后的 App Store 抓取日志;如果仍有持续性失败,再补更细的错误分类和失败计数展示。
v0.65.0 2026-03-25 18:56 CST
修复 App Store 应用错误归属到工作室的问题,并为 App Store / Google Play 主抓取链路补上抓取后的二次归属校验。
改动内容
改动预览 3/8
  • 修改 `app_store_scrap/adapters/itunes_search_api.py`,将开发者搜索从“直接取搜索结果第一条 artistId”改成“只接受与 studio 预期开发者名精确匹配的 artistId”;如果没有精确匹配,直接返回空结果,避免将其他开发者整批误归属到目标 studio。
  • 修改 `app_store_scrap/collector/studio_collector.py`,统一生成 studio 的开发者候选名列表(`source_value / studio_name / aliases`),并把这组候选名同时用于 iTunes API 精确匹配和结果过滤。
  • 修改 `app_store_scrap/workflow.py`,新增 App Store 侧 `_matches_studio_identity()` 二次校验,并将校验放在“详情抓取 + 稀疏数据保值合并”之后、`parse/upsert` 之前;开发者名不匹配的 app 现在会直接进入 `filtered_apps`,不再落库。
展开完整改动
  • 修改 `app_store_scrap/adapters/itunes_search_api.py`,将开发者搜索从“直接取搜索结果第一条 artistId”改成“只接受与 studio 预期开发者名精确匹配的 artistId”;如果没有精确匹配,直接返回空结果,避免将其他开发者整批误归属到目标 studio。
  • 修改 `app_store_scrap/collector/studio_collector.py`,统一生成 studio 的开发者候选名列表(`source_value / studio_name / aliases`),并把这组候选名同时用于 iTunes API 精确匹配和结果过滤。
  • 修改 `app_store_scrap/workflow.py`,新增 App Store 侧 `_matches_studio_identity()` 二次校验,并将校验放在“详情抓取 + 稀疏数据保值合并”之后、`parse/upsert` 之前;开发者名不匹配的 app 现在会直接进入 `filtered_apps`,不再落库。
  • 修改 `google_play_scrap/workflow.py`,补强 Google Play 侧的归属校验:`developer_name` 源继续按开发者名精确过滤;`developer_id` 源增加对采集来源开发者 URL 的校验,并保留开发者名回退校验。
  • 新增 `app_store_scrap/tests/test_itunes_search_api.py`,覆盖 App Store 开发者精确匹配与“不匹配则返回 None”两种场景。
  • 修改 `app_store_scrap/tests/test_app_store_studio_collector.py` 和 `app_store_scrap/tests/test_workflow.py`,补充 App Store 发现过滤与 workflow 错归属过滤测试。
  • 修改 `orchestrator/tests/test_google_play_workflow.py`,补充 Google Play 错归属过滤测试。
  • 清理 `data/scrap.db` 中 `play-fusion-games` 下面的错误 App Store 应用污染数据,并使用新逻辑对该 studio 重新跑了一次同步。修复后只保留 2 个真实应用:`Color My Bible (6670268327)`、`Grace Color (6751024070)`;错误应用 `Dino Cataclysm: Survival (6757999357)` 已从后台移除。
验证结果
验证预览 2/12
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_itunes_search_api.py app_store_scrap/tests/test_app_store_studio_collector.py app_store_scrap/tests/test_workflow.py orchestrator/tests/test_google_play_workflow.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_itunes_search_api.py app_store_scrap/tests/test_app_store_studio_collector.py app_store_scrap/tests/test_workflow.py orchestrator/tests/test_google_play_workflow.py`
  • Result: PASS
  • Detail: `15 passed in 0.49s`
  • `./.venv/bin/python -m py_compile app_store_scrap/adapters/itunes_search_api.py app_store_scrap/collector/studio_collector.py app_store_scrap/workflow.py google_play_scrap/workflow.py`
  • Result: PASS
  • Detail: App Store / Google Play 抓取主链路模块编译通过
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `74 passed in 1.41s`
  • 后台验证:
  • Result: PASS
  • Detail: `http://127.0.0.1:8010/apps/app_store/6757999357?lang=zh-CN` 返回 `404`;`Play Fusion Games` 在后台应用列表中只剩 2 条 App Store 应用
发现与结论
  • 这次污染的根因不是详情解析,而是 App Store 开发者搜索阶段过于宽松:`Play Fusion Games` 这个关键词搜索会先返回 `Phantix Games`、`Abstract Software Inc` 等其他开发者的 app,旧逻辑直接拿第一条结果的 `artistId`,导致整批误归属。
  • 存量修复时需要先停掉后台服务。否则旧进程的启动任务会继续按旧逻辑回写脏数据,表现为“刚删除又回来了”。
  • 手动重跑 `play-fusion-games` 这次同步时,当前 workflow 触发了 1 张飞书新增卡片,内容是 2 个真实 App Store 应用的创建提醒。
下一步
  • 如果继续推进,下一步建议补一个“工作室归属异常巡检”任务,定期扫描 `apps.developer_name` 与 `studios.source_value/studio_name` 不一致的存量记录,避免旧数据或外部导入再次污染后台。
v0.64.0 2026-03-24 18:38 CST
为应用详情页补充下载量展示,避免首屏摘要和当前应用信息缺少这一核心指标。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,在应用详情首屏指标卡中新增 `下载量 / Installs` 卡片,位置放在版本号与评论数之间。
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,在“当前应用信息”模块中新增下载量字段,和价格、评分等基础信息一起展示。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `installs` 的中英文文案,供详情页直接复用。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,在应用详情首屏指标卡中新增 `下载量 / Installs` 卡片,位置放在版本号与评论数之间。
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,在“当前应用信息”模块中新增下载量字段,和价格、评分等基础信息一起展示。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `installs` 的中英文文案,供详情页直接复用。
  • 修改 `orchestrator/tests/test_admin_service.py`,为测试夹具补充 `installs="50,000+"`,并增加详情页对下载量标题和值的回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.85s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.14s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台入口、展示层和查询层模块编译通过
发现与结论
  • 数据层本身已经有 `installs` 字段,缺口只在详情页展示层,因此这次不需要动 repository 或采集链路。
  • 对 App Store 应用,这个字段如果源站没有提供仍会显示 `-`;Google Play 应用会直接显示已入库的安装量字符串。
下一步
  • 如果继续推进,下一步建议把下载量也接入快照时间线的初始快照摘要,让首次快照状态和当前详情信息保持一致。
v0.63.0 2026-03-23 18:06 CST
统一后台页头控件与首屏说明块样式,并完成最近变更记录中媒体预览的折叠显示能力。
改动内容
改动预览 3/7
  • 修改 `orchestrator/admin_service/templates/partials/ui.html` 与 `orchestrator/admin_service/templates/dashboard.html`、`apps.html`、`comments.html`、`studios.html`、`jobs.html`、`iterations.html`、`milestones.html`,将 `surface_banner` 宏改为显式接收 `lang`,修复英文页面首屏标签一直回落为中文的问题;同时在首屏说明块中加入独立的 `功能说明 / Page Brief` 标签位。
  • 修改 `orchestrator/admin_service/presentation.py`,新增 `surface_banner_label` 中英文文案,供所有导航入口页面首屏统一使用。
  • 修改 `orchestrator/admin_service/static/admin.css`,将页头改成 `主标题区 + 控件条` 的稳定双区布局,`header-actions` 改为始终单行、不换行、可横向滚动的控制条;同时重绘返回按钮箭头为更柔和的几何箭头,并统一首屏说明块的视觉层级。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/partials/ui.html` 与 `orchestrator/admin_service/templates/dashboard.html`、`apps.html`、`comments.html`、`studios.html`、`jobs.html`、`iterations.html`、`milestones.html`,将 `surface_banner` 宏改为显式接收 `lang`,修复英文页面首屏标签一直回落为中文的问题;同时在首屏说明块中加入独立的 `功能说明 / Page Brief` 标签位。
  • 修改 `orchestrator/admin_service/presentation.py`,新增 `surface_banner_label` 中英文文案,供所有导航入口页面首屏统一使用。
  • 修改 `orchestrator/admin_service/static/admin.css`,将页头改成 `主标题区 + 控件条` 的稳定双区布局,`header-actions` 改为始终单行、不换行、可横向滚动的控制条;同时重绘返回按钮箭头为更柔和的几何箭头,并统一首屏说明块的视觉层级。
  • 同一样式文件中补充 `change-media-stack / change-media-actions / change-media-button.is-hidden` 等规则,让最近变更记录里的媒体预览支持折叠而不破坏当前表格密度。
  • 修改 `orchestrator/admin_service/templates/base.html`,为通用 gallery 控制器增加 `data-preview-count` 支持,使不同页面可以显式指定预览阈值,而不是依赖网格列数推断。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将最近变更记录中的媒体预览阈值固定为 `4`,超过部分默认折叠,仅在需要时展示“展开剩余 N 张”按钮。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充对 `surface-banner-label`、英文 `Page Brief`、最近变更记录媒体折叠阈值标记以及新返回按钮结构的回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.70s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 0.94s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台入口、展示层和查询层模块编译通过
发现与结论
  • `surface_banner` 之前虽然已经加了新标签,但作为独立宏并没有自动拿到页面上下文里的 `lang`,因此英文页会继续显示中文标签。把 `lang` 改成显式参数后,这类多语言宏不会再依赖隐式上下文。
  • 最近变更记录里的媒体折叠不能直接复用截图区“按网格列数预览”的逻辑,因为这里是 `flex` 布局而不是 `grid` 布局。通过给 gallery 控制器增加显式 `preview_count`,不需要为不同媒体区写第二套脚本。
下一步
  • 如果继续推进,下一步可以把最近变更记录中的媒体预览再做成“超过 N 张后首屏显示摘要计数 + 展开区按组展示”,进一步降低大批量截图更新时的表格高度。
v0.62.0 2026-03-23 17:07 CST
修正应用详情页两个可见问题:最近变更记录中的媒体字段没有显示预览,以及内容导航顺序与当前页面实际阅读顺序不一致。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/presentation.py`,新增 `is_media_change_field()` 和 `event_change_media_urls()`,把 `change_events` 中 `icon_url / header_image_url / screenshot_urls / video_urls` 这类字段统一按媒体资源解析,而不是继续当长字符串输出。
  • 修改 `orchestrator/admin_service/app.py`,将上述媒体辅助函数注入到 Jinja 模板环境中,供应用详情页直接判断变更字段类型并渲染预览。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将内容导航顺序调整为 `应用截图 -> 快照时间线 -> 变更记录 -> 最新评论 -> 当前应用信息 -> 应用描述`,与当前响应式布局下页面的实际阅读顺序保持一致。
展开完整改动
  • 修改 `orchestrator/admin_service/presentation.py`,新增 `is_media_change_field()` 和 `event_change_media_urls()`,把 `change_events` 中 `icon_url / header_image_url / screenshot_urls / video_urls` 这类字段统一按媒体资源解析,而不是继续当长字符串输出。
  • 修改 `orchestrator/admin_service/app.py`,将上述媒体辅助函数注入到 Jinja 模板环境中,供应用详情页直接判断变更字段类型并渲染预览。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将内容导航顺序调整为 `应用截图 -> 快照时间线 -> 变更记录 -> 最新评论 -> 当前应用信息 -> 应用描述`,与当前响应式布局下页面的实际阅读顺序保持一致。
  • 同一模板中,将 `最近变更记录` 的媒体字段从纯文本输出改成缩略图预览;当字段为截图、头图、图标或视频时,单元格里直接展示媒体缩略图,并继续支持灯箱放大查看。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充 `change-media-grid / change-media-button / change-media-thumb` 样式,让变更记录中的媒体预览密度和快照时间线保持一致。
  • 修改 `orchestrator/tests/test_admin_service.py`,为测试夹具补充一条 `screenshot_urls` 变更事件,并增加对 `change-media-grid / change-media-thumb` 以及导航顺序的回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.87s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.09s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 最近变更记录之前没有媒体预览,不是数据层缺失,而是这一块一直沿用了通用表格的 `format_value()` 文本渲染路径。快照时间线已经能显示图,但变更记录没有共享这套能力。
  • 内容导航顺序问题,本质上来自前面把右侧信息栏在较窄视口下下放到主内容区之后,页面真实阅读顺序已经变了,但导航配置没有同步调整。
下一步
  • 如果继续推进,下一步建议给最近变更记录里的媒体字段也补一层“超过 N 张时折叠显示”,否则某些截图变更量很大的应用会把整张变更表拉得过长。
v0.61.0 2026-03-23 16:37 CST
继续修正应用详情页在当前窗口宽度下的三栏挤压问题。
改动内容
改动预览 1/1
  • 修改 `orchestrator/admin_service/static/admin.css`,将应用详情页从“三栏同时常驻”改成“更高断点下提前切换为两栏”:当宽度小于 `1680px` 时,保留左侧内容导航,右侧信息栏自动下放到主内容列下方,并改成双列信息区,避免继续与中间主内容争抢横向空间。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.90s`
发现与结论
  • 之前的修正只是在三栏内部缩小左右栏宽度,但这类窗口宽度下真正的问题不是“某一栏太宽”,而是“三栏同时存在”本身就不成立。把右侧信息栏提前下放,主内容列才能恢复正常阅读宽度。
下一步
  • 如果继续推进,下一步建议再给右侧信息区做一版更紧凑的双列信息卡样式,避免它在下放后显得像简单堆叠的长列表。
v0.60.0 2026-03-23 16:18 CST
增加 `1.0.0` 正式发布里程碑模块,并优先修正应用详情页当前宽度下的三栏排版挤压问题。
改动内容
改动预览 3/7
  • 新增 `docs/milestones.json`,创建 `1.0.0` 正式发布里程碑记录,内容覆盖发布功能简介、开发时长、调试次数、Bug 数量、测试状态和后续迭代计划。
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_milestones()`,将里程碑读取逻辑收进后台查询层,保持与 `worklog` 时间线相同的数据读取模式。
  • 修改 `orchestrator/admin_service/app.py`,为后台服务增加 `milestones_path` 配置注入,并新增 `/milestones` 页面路由与 `/api/milestones` 接口。
展开完整改动
  • 新增 `docs/milestones.json`,创建 `1.0.0` 正式发布里程碑记录,内容覆盖发布功能简介、开发时长、调试次数、Bug 数量、测试状态和后续迭代计划。
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_milestones()`,将里程碑读取逻辑收进后台查询层,保持与 `worklog` 时间线相同的数据读取模式。
  • 修改 `orchestrator/admin_service/app.py`,为后台服务增加 `milestones_path` 配置注入,并新增 `/milestones` 页面路由与 `/api/milestones` 接口。
  • 修改 `orchestrator/admin_service/templates/base.html` 与 `orchestrator/admin_service/presentation.py`,在左侧导航中新增 `版本里程碑` 入口,并补齐中英文页面标题、摘要与字段文案。
  • 新增 `orchestrator/admin_service/templates/milestones.html`,复用现有时间线与 panel 组件展示版本级里程碑内容。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充里程碑指标区样式;同时收紧应用详情页 `detail-layout` 的左右侧栏宽度,将主内容列恢复到更合理的可视宽度,避免截图区和时间线在当前窗口宽度下被过度挤压。
  • 修改 `orchestrator/tests/test_admin_service.py`,增加里程碑临时夹具、页面/API 断言,并让所有后台页面测试显式使用临时 `milestones.json`,避免测试隐式依赖真实工作区文件。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.99s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.04s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 这次截图里看到的 `nav_milestones` 字面量问题,本质是新导航入口已经挂到模板上,但运行中的页面还没切到补齐翻译后的服务实例。现在路由、模板和翻译都已经接通,运行态检查也确认不再回退成 key。
  • 应用详情页的“排版错乱”主要不是单个模块错位,而是三栏布局在当前宽度下左右两栏占比过大,主内容列被持续挤压。先把两侧宽度收回,再继续做局部样式优化,收益更直接。
下一步
  • 如果继续推进,下一步建议把里程碑页和功能迭代页打通,让 `1.0.0` 里程碑能直接跳到对应的发布前后关键迭代记录。
v0.59.0 2026-03-23 15:00 CST
继续统一应用详情页中段模块,收紧 `变更记录 / 当前应用信息 / 最新评论` 的标题层级、表格密度和卡片边距。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,为 `当前应用信息 / 应用描述 / 最新评论` 统一接入 `section-head detail-section-head` 头部结构,并给信息网格、评论表格、评论内容单元格补充专用类,避免继续沿用列表页的通用表格密度。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,为 `最近变更记录` 模块补充与其它详情模块一致的 section head,同时给变更表格增加 `table-scroll` 包裹和 `detail-change-table` 类,保持中段模块的边距、标题和内容层次一致。
  • 修改 `orchestrator/admin_service/static/admin.css`,统一中段模块的节奏:收紧 `detail-info-grid` 项的内边距,降低描述 lead 和正文的字号与行距,压缩 `change-card` 内边距,并单独为 `detail-change-table / detail-comments-table / comment-body-cell` 定义更适合详情页的表格密度和列宽。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,为 `当前应用信息 / 应用描述 / 最新评论` 统一接入 `section-head detail-section-head` 头部结构,并给信息网格、评论表格、评论内容单元格补充专用类,避免继续沿用列表页的通用表格密度。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,为 `最近变更记录` 模块补充与其它详情模块一致的 section head,同时给变更表格增加 `table-scroll` 包裹和 `detail-change-table` 类,保持中段模块的边距、标题和内容层次一致。
  • 修改 `orchestrator/admin_service/static/admin.css`,统一中段模块的节奏:收紧 `detail-info-grid` 项的内边距,降低描述 lead 和正文的字号与行距,压缩 `change-card` 内边距,并单独为 `detail-change-table / detail-comments-table / comment-body-cell` 定义更适合详情页的表格密度和列宽。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充对 `detail-section-head / detail-info-grid / detail-comments-table / detail-change-table / comment-body-cell` 的结构回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.85s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.13s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 详情页中段之前的问题不是单个模块难看,而是它们分别继承了不同来源的样式密度:信息区偏卡片、变更区偏列表、评论区偏通用表格,放在一起就很容易显得像三套系统。把 header、表格和边距统一后,详情页从首屏到中段的节奏终于连起来了。
下一步
  • 如果继续推进,下一步建议把应用详情页右侧信息栏做成“跟随阅读位置改变强调状态”的侧栏,让用户在长时间线滚动中始终知道当前应用的核心上下文。
v0.58.0 2026-03-23 14:26 CST
继续用 `polished-frontend` 收紧应用详情页首屏,把 `hero + 指标卡` 从偏展示页的体量收成更适合监控后台的密度。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,给应用详情首屏补充 `app-hero-copy / hero-action-button / app-summary-grid / app-stat-card` 这组结构类,把标题区、操作区和指标卡带拆成更稳定的两层结构。
  • 修改 `orchestrator/admin_service/static/admin.css`,重做 `.app-hero` 和 `.app-hero-main` 的布局关系:首屏从松散的横向大块改成更紧凑的双列 hero,图标缩小、标题字级收紧、`app_id` 和标签间距减小,操作按钮同步改成较轻的后台按钮规格。
  • 同一文件中,继续收紧指标卡带:为 `.app-summary-grid` 调整更小的卡片宽度和间距,为 `.app-stat-card` 降低内边距、标题高度和数值字号,避免 hero 和指标卡都使用过大的视觉体量。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,给应用详情首屏补充 `app-hero-copy / hero-action-button / app-summary-grid / app-stat-card` 这组结构类,把标题区、操作区和指标卡带拆成更稳定的两层结构。
  • 修改 `orchestrator/admin_service/static/admin.css`,重做 `.app-hero` 和 `.app-hero-main` 的布局关系:首屏从松散的横向大块改成更紧凑的双列 hero,图标缩小、标题字级收紧、`app_id` 和标签间距减小,操作按钮同步改成较轻的后台按钮规格。
  • 同一文件中,继续收紧指标卡带:为 `.app-summary-grid` 调整更小的卡片宽度和间距,为 `.app-stat-card` 降低内边距、标题高度和数值字号,避免 hero 和指标卡都使用过大的视觉体量。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充应用详情页对 `app-hero-copy / hero-action-button / app-summary-grid / app-stat-card` 的结构回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 1.67s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.63s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 之前应用详情首屏的主要问题,不只是个别字号偏大,而是 hero、标签、操作按钮、指标卡都试图成为第一视觉层,导致首屏信息密度低但体量很重。把它拆成“紧凑 hero + 轻量指标带”后,监控信息的阅读顺序会更清晰。
下一步
  • 如果继续推进,下一步建议统一 `变更记录 / 当前应用信息 / 最新评论` 三个模块的标题层级和表格密度,让详情页中段也和现在的首屏、时间线保持同一节奏。
v0.57.0 2026-03-23 12:47 CST
修正应用详情页快照时间线里短数值字段仍然显示成大空框的问题。
改动内容
改动预览 2/2
  • 修改 `orchestrator/admin_service/static/admin.css`,将 `.snapshot-value-compare.compact-compare` 从“两列等宽栅格”改成“按内容宽度收缩的弹性布局”,避免短值虽然命中了紧凑分支,却仍被等宽列撑成两个大容器。
  • 同一文件中,继续收紧 `.snapshot-value-box.compact`:移除无意义的最小宽度,改成 `max-content` 宽度、较小内边距和 `white-space: nowrap`,让 `评分 / 评分数` 这类短值真正以指标胶囊的形式展示,而不是填满整列。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.73s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 这次问题不是模板判定没命中。实际 HTML 已经带上了 `compact-compare` 和 `snapshot-value-box compact`,但 CSS 仍把它们放在两列等宽轨道里,所以视觉上看起来还是两个大空框。根因在布局层,不在模板层。
下一步
  • 如果继续推进,下一步建议把应用详情页 hero 区和首屏指标卡继续收紧,避免当前首屏仍然存在的“标题大、卡片大、留白也大”叠加感。
v0.56.0 2026-03-23 12:31 CST
继续用 `polished-frontend` 收口应用详情页首屏和快照时间线的视觉一致性,重点统一后台壳层控件样式,并把媒体类快照差异切换为真正的缩略图对比模式。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/static/admin.css`,将后台主布局列宽从 `240px` 调整为 `272px`,并重做左侧品牌区:品牌标题、说明文案和背景面板现在使用统一的壳层样式,避免中文标题在窄侧栏中出现尴尬换行。
  • 同一文件中,为折叠菜单按钮、浮动菜单按钮、刷新按钮和返回按钮增加统一的 `icon-control` 视觉规格,统一它们的尺寸、圆角、阴影和玻璃感背景;同时拉齐页面标题字号、字距和首屏间距,让左侧导航壳层与右侧详情首屏在几何关系上更一致。
  • 继续修改 `orchestrator/admin_service/static/admin.css`,将快照时间线中的媒体类对比从“填满列宽的大网格”改成两种密度:多图场景使用固定缩略图网格并支持收起/展开,单图场景使用更紧凑的 `compact-media-grid`,缩略图按原始比例展示,不再被大面积空白包围。
展开完整改动
  • 修改 `orchestrator/admin_service/static/admin.css`,将后台主布局列宽从 `240px` 调整为 `272px`,并重做左侧品牌区:品牌标题、说明文案和背景面板现在使用统一的壳层样式,避免中文标题在窄侧栏中出现尴尬换行。
  • 同一文件中,为折叠菜单按钮、浮动菜单按钮、刷新按钮和返回按钮增加统一的 `icon-control` 视觉规格,统一它们的尺寸、圆角、阴影和玻璃感背景;同时拉齐页面标题字号、字距和首屏间距,让左侧导航壳层与右侧详情首屏在几何关系上更一致。
  • 继续修改 `orchestrator/admin_service/static/admin.css`,将快照时间线中的媒体类对比从“填满列宽的大网格”改成两种密度:多图场景使用固定缩略图网格并支持收起/展开,单图场景使用更紧凑的 `compact-media-grid`,缩略图按原始比例展示,不再被大面积空白包围。
  • 修改 `orchestrator/admin_service/templates/base.html`,将截图画廊脚本抽象成通用 gallery controller,同时接管 `data-media-gallery / data-media-toggle` 这组媒体对比标记,让快照时间线里的长媒体列表也支持“预览 + 点击展开全部”。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将快照时间线媒体对比里的 `data-media-item` 标记移动到按钮层,确保收起/展开时整张缩略图卡片一起控制,而不是只隐藏内部图片元素。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充应用详情页对 `data-media-gallery / data-media-toggle / compact-media-grid / icon-control` 的回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.94s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.08s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 之前这块“不统一”的核心问题不是单个按钮难看,而是壳层没有稳定的几何规则:品牌区、折叠按钮、返回按钮分别用各自的尺寸和节奏,导致首屏一眼看上去像拼接界面。把它们收进同一组 icon control 规格后,页面的起始视觉层级明显更稳定。
  • 媒体类快照差异和数值型差异一样,不能共用同一套展示密度。单图和多图如果都走“大对比容器”,就会出现缩略图过小、空白过多、列表拉得很长的问题。把媒体 diff 切成 compact / grid 两种模式后,时间线信息密度才真正接近监控后台应有的状态。
下一步
  • 如果继续推进,下一步建议把应用详情页 hero 区和首屏指标卡也做一次同样的密度收口,减少“大标题 + 大卡片 + 大留白”同时出现时的压迫感。
v0.55.0 2026-03-23 12:07 CST
使用 `polished-frontend` 收紧应用详情页快照时间线的视觉质量,重点修正数值型变更在“变更前 / 变更后”区域里出现大面积空框的问题。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,为快照时间线中的文本型差异增加 `compact_compare` 判定:当 `before / after` 都是短值且非长文本字段时,自动给对比布局和数值盒子加上紧凑样式类,而不是继续沿用长内容容器。
  • 修改 `orchestrator/admin_service/static/admin.css`,为 `.snapshot-value-compare.compact-compare` 和 `.snapshot-value-box.compact` 增加更紧凑的展示样式:数值居中、字号上提、内边距收紧、盒子按内容大小自然收缩,不再出现内容很少但边框区域很大的情况。
  • 同一文件中继续补强详情页导航体验:为 `.detail-rail` 增加 `height:max-content`,并让 active 导航项在文字和指示点上都有更明确的高亮反馈。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,为快照时间线中的文本型差异增加 `compact_compare` 判定:当 `before / after` 都是短值且非长文本字段时,自动给对比布局和数值盒子加上紧凑样式类,而不是继续沿用长内容容器。
  • 修改 `orchestrator/admin_service/static/admin.css`,为 `.snapshot-value-compare.compact-compare` 和 `.snapshot-value-box.compact` 增加更紧凑的展示样式:数值居中、字号上提、内边距收紧、盒子按内容大小自然收缩,不再出现内容很少但边框区域很大的情况。
  • 同一文件中继续补强详情页导航体验:为 `.detail-rail` 增加 `height:max-content`,并让 active 导航项在文字和指示点上都有更明确的高亮反馈。
  • 修改 `orchestrator/admin_service/templates/base.html`,给内容导航补上点击平滑滚动和顶部偏移修正,同时保留当前阅读位置高亮逻辑。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.73s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 0.98s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 这类“数值型 diff 看起来像大表单空框”的问题,本质不是功能错,而是展示组件没有区分内容密度。对 polished 界面来说,短值和长文本必须分开处理,否则时间线的下半部分会显得松散且廉价。
  • 这轮处理后,数值型快照差异会更像监控指标卡,长文本差异仍保留展开阅读能力,两类信息的视觉语义终于分开了。
下一步
  • 如果继续推进,下一步建议把媒体型差异也做同样的密度分层:单张图用紧凑对比,长截图列表再切到网格视图,这样时间线整体会更稳。
v0.54.0 2026-03-23 11:58 CST
继续修正应用详情页的阅读体验,收口内容导航 sticky 问题,并让截图按钮文案和快照时间线前后值样式更贴合内容本身。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/static/admin.css`,将 `.content` 的溢出策略从 `overflow-x:auto` 收回到 `overflow:visible`,并为 `detail-layout` 明确增加 `align-items:start`;这是内容导航 sticky 继续生效的关键前提。与此同时,为 `.detail-anchor-link` 增加 `is-active / aria-current=true` 的高亮态,为快照时间线里的 `.snapshot-value-compare > div` 和 `.snapshot-value-box` 调整成“按内容自然高度和宽度收缩”的样式,去掉内容很少时仍然撑出过大框体的问题。
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,为内容导航的 `nav` 和链接增加 `data-detail-nav / data-detail-anchor` 标记,供前端进行当前阅读位置跟踪。
  • 修改 `orchestrator/admin_service/templates/base.html`,新增应用详情页锚点高亮逻辑:在滚动过程中根据各 `detail-module` 的视口位置,实时为当前模块对应的导航项加上 `is-active` 和 `aria-current`。同时调整截图画廊脚本,展开按钮收起态不再显示固定文案,而是优先使用 `data-expand-template` 动态渲染“剩余 X 张截图 / Show X More Screenshots”。
展开完整改动
  • 修改 `orchestrator/admin_service/static/admin.css`,将 `.content` 的溢出策略从 `overflow-x:auto` 收回到 `overflow:visible`,并为 `detail-layout` 明确增加 `align-items:start`;这是内容导航 sticky 继续生效的关键前提。与此同时,为 `.detail-anchor-link` 增加 `is-active / aria-current=true` 的高亮态,为快照时间线里的 `.snapshot-value-compare > div` 和 `.snapshot-value-box` 调整成“按内容自然高度和宽度收缩”的样式,去掉内容很少时仍然撑出过大框体的问题。
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,为内容导航的 `nav` 和链接增加 `data-detail-nav / data-detail-anchor` 标记,供前端进行当前阅读位置跟踪。
  • 修改 `orchestrator/admin_service/templates/base.html`,新增应用详情页锚点高亮逻辑:在滚动过程中根据各 `detail-module` 的视口位置,实时为当前模块对应的导航项加上 `is-active` 和 `aria-current`。同时调整截图画廊脚本,展开按钮收起态不再显示固定文案,而是优先使用 `data-expand-template` 动态渲染“剩余 X 张截图 / Show X More Screenshots”。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,为截图画廊补充 `data-expand-template`,让按钮文案可按剩余张数实时生成。
  • 修改 `orchestrator/admin_service/presentation.py`,新增 `screenshots_expand_remaining` 文案,作为截图画廊的动态按钮模板。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充对 `data-detail-nav / data-detail-anchor / data-expand-template / data-collapse-label` 的回归断言。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.94s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 这次 sticky 问题的剩余根因,不只是评论区位置,还包括 `.content` 这一层曾经保留了横向滚动上下文。对于 sticky 来说,这一层额外 overflow 是不必要的,也容易让行为和预期不一致。
  • 快照时间线里“变更前 / 变更后”如果统一用固定最小高度盒子,在短文本场景下会显得非常空。把它改成基于内容大小自然收缩后,信息密度和视觉节奏都会更合理。
下一步
  • 如果继续推进,下一步建议给内容导航加平滑滚动和点击偏移修正,并给截图画廊补“展开后显示总张数 / 剩余张数”的更细分提示。
v0.53.0 2026-03-23 11:30 CST
优化应用详情页的长页面可用性,让内容导航真正常驻,并把应用截图预览改成按页面宽度自适应。
改动内容
改动预览 3/7
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将最新评论模块并入 `detail-main` 主内容列,确保左侧内容导航所在的 `detail-layout` 覆盖整张应用详情的主要阅读区域,而不是在进入评论区后提前失效。
  • 同一模板中,将截图区从“服务端固定前 4 张预览 + details 展开剩余”改成“服务端直接输出全部截图 + 前端按当前栅格列数动态计算预览数量”,并增加 `data-screenshot-gallery / data-screenshot-item / data-screenshot-toggle` 这组前端控制标记。
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,给 `当前应用信息 / 应用描述 / 最新评论` 补齐 `detail-module` 类,使它们与截图、时间线、变更记录共享统一锚点滚动偏移。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将最新评论模块并入 `detail-main` 主内容列,确保左侧内容导航所在的 `detail-layout` 覆盖整张应用详情的主要阅读区域,而不是在进入评论区后提前失效。
  • 同一模板中,将截图区从“服务端固定前 4 张预览 + details 展开剩余”改成“服务端直接输出全部截图 + 前端按当前栅格列数动态计算预览数量”,并增加 `data-screenshot-gallery / data-screenshot-item / data-screenshot-toggle` 这组前端控制标记。
  • 修改 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,给 `当前应用信息 / 应用描述 / 最新评论` 补齐 `detail-module` 类,使它们与截图、时间线、变更记录共享统一锚点滚动偏移。
  • 修改 `orchestrator/admin_service/templates/base.html`,新增截图画廊脚本:根据 `screenshot-grid` 的实际列数计算预览数量,在收起状态下只保留一行截图;点击按钮后展示全部截图,再次点击可收起。
  • 修改 `orchestrator/admin_service/static/admin.css`,为 `detail-rail` 增加 `max-height + overflow-y:auto`,使内容导航在长页面中保持 sticky 且可独立滚动;新增 `screenshot-actions` 和 `screenshot-card.is-hidden` 样式以支持自适应截图预览。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `screenshots_collapse` 文案,供截图展开/收起交互使用。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充应用详情页对 `app-comments` 锚点、截图画廊数据标记的回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.94s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.65s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 之前“内容导航不够常驻”的根因不是单纯 sticky 失效,而是评论区在 `detail-layout` 之外,导致左侧导航所在容器的高度早于整页内容结束。把评论区并回主布局后,sticky 行为才能覆盖完整阅读路径。
  • 截图预览数量如果继续放在服务端固定切片,会天然与响应式栅格冲突。把预览数量下放到前端按当前列数计算,是这一类“同一模板适配不同宽度”问题里更稳妥的方案。
下一步
  • 如果继续推进,下一步建议给左侧导航增加“当前阅读位置高亮”以及点击平滑定位,并给截图画廊补“展开后显示剩余数量 / 收起时恢复当前位置”的细节优化。
v0.52.0 2026-03-21 22:52 CST
继续按 frontend-expert 的架构方向重构应用详情页,把长页面拆成更明确的内容模块,并增加 sticky 锚点导航。
改动内容
改动预览 3/5
  • 新增 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,将应用详情中的“内容导航 / 当前应用信息 / 应用描述 / 最新评论”抽成可复用宏,减少 `app_detail.html` 继续膨胀。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,把详情页改成三栏组合:左侧 `detail-rail` 负责锚点导航,中间主列负责截图、快照时间线和变更记录,右侧 `detail-side` 负责当前信息和描述;同时为截图、时间线、变更、信息、描述、评论区增加稳定的锚点 `id`。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `detail_navigation / detail_navigation_hint / section_*` 等导航文案,保证中英文都能正确表达长页面导航语义。
展开完整改动
  • 新增 `orchestrator/admin_service/templates/partials/app_detail_sections.html`,将应用详情中的“内容导航 / 当前应用信息 / 应用描述 / 最新评论”抽成可复用宏,减少 `app_detail.html` 继续膨胀。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,把详情页改成三栏组合:左侧 `detail-rail` 负责锚点导航,中间主列负责截图、快照时间线和变更记录,右侧 `detail-side` 负责当前信息和描述;同时为截图、时间线、变更、信息、描述、评论区增加稳定的锚点 `id`。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `detail_navigation / detail_navigation_hint / section_*` 等导航文案,保证中英文都能正确表达长页面导航语义。
  • 修改 `orchestrator/admin_service/static/admin.css`,新增 `detail-rail / detail-nav-panel / detail-anchor-nav / detail-anchor-link / detail-module` 等样式,让左侧导航和右侧信息侧栏都具备 sticky 行为,并在窄屏下自动降级回单列布局。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充应用详情页对锚点导航、导航容器和各模块 `id` 的回归断言。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.96s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台路由、展示层和查询层模块编译通过
发现与结论
  • 应用详情页之前虽然功能完整,但结构本质上还是“一个很长的大模板 + 一个信息侧栏”,页面一旦继续加模块,维护成本会明显上升。把可复用的次级模块先抽成 partial,是当前 Jinja 架构下最稳妥的重构切口。
  • 左侧锚点导航可以明显降低长时间线页面的滚动成本,尤其在截图、快照和评论都较长时,定位速度会比单纯依赖浏览器滚动好很多。
下一步
  • 如果继续推进,下一步建议给锚点导航增加“当前阅读位置高亮”,并把快照时间线和最新评论列表再加一层折叠粒度控制,避免超长应用详情页继续线性变高。
v0.51.0 2026-03-21 20:47 CST
继续收敛后台前端架构,清理已经失效的 `feature_iterations` 数据库链路,并把核心列表页的空状态统一成共享模式。
改动内容
改动预览 3/6
  • 修改 `common/storage/schema.sql`,移除 `feature_iterations` 表及其索引定义;新初始化数据库不再创建这条已经失效的迭代存储链路。
  • 修改 `common/storage/init_db.py`,删除针对 `feature_iterations.details / released_at` 的历史轻量迁移逻辑,避免继续把这条废弃链路保留在初始化路径里。
  • 修改 `orchestrator/admin_service/queries.py`,删除 `get_feature_iteration / save_feature_iteration / delete_feature_iteration` 三个已经无调用方的数据库读写方法,保留唯一有效的 `list_feature_iterations()`,继续只从 `docs/worklog.md` 生成迭代时间线。
展开完整改动
  • 修改 `common/storage/schema.sql`,移除 `feature_iterations` 表及其索引定义;新初始化数据库不再创建这条已经失效的迭代存储链路。
  • 修改 `common/storage/init_db.py`,删除针对 `feature_iterations.details / released_at` 的历史轻量迁移逻辑,避免继续把这条废弃链路保留在初始化路径里。
  • 修改 `orchestrator/admin_service/queries.py`,删除 `get_feature_iteration / save_feature_iteration / delete_feature_iteration` 三个已经无调用方的数据库读写方法,保留唯一有效的 `list_feature_iterations()`,继续只从 `docs/worklog.md` 生成迭代时间线。
  • 修改 `orchestrator/admin_service/presentation.py`,补充 `no_apps / no_studios / no_jobs / no_job_runs / no_recent_events` 文案,为共享空状态提供稳定文案源。
  • 修改 `dashboard.html`、`apps.html`、`comments.html`、`jobs.html`、`studios.html`,统一接入 `table_empty` 宏;当筛选结果为空或当前没有执行记录时,不再直接渲染空表格,而是展示明确的空状态提示。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充“新库不再创建 `feature_iterations` 表”和“空数据页面展示空状态”的回归测试。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.93s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `69 passed in 1.02s`
  • `./.venv/bin/python -m py_compile common/storage/init_db.py orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 初始化、路由、查询和展示层模块编译通过
发现与结论
  • 现在“功能迭代”这条能力链已经彻底收口成 `worklog -> queries.list_feature_iterations() -> iterations page/api`,数据库 schema、初始化迁移、查询写接口三层都不再保留失效入口,后续不会再出现“页面读 worklog,但库里还藏着另一套迭代数据结构”的双事实来源问题。
  • 列表页空状态统一之后,后台在新库、空筛选结果、无执行记录这些低数据密度场景下更稳定,页面结构也更容易继续抽成共享模式。
下一步
  • 如果继续推进,下一步建议把应用详情页也按同样思路继续模块化,抽出“媒体区 / 时间线区 / 信息侧栏”三块组合模式,并给时间线和评论区增加更细的折叠与锚点导航。
v0.50.0 2026-03-21 20:31 CST
按 frontend-expert 的方式优化管理后台的页面壳层与前端结构,在不推翻现有 FastAPI + Jinja 架构的前提下,提高复用度和首屏信息层级。
改动内容
改动预览 3/7
  • 修改 `orchestrator/admin_service/app.py`,新增统一的 `render_page()` 页面渲染 helper,把 `lang / page_title / back_url` 这类跨页面上下文从各路由里抽出来,减少模板渲染时的重复拼装逻辑。
  • 新增 `orchestrator/admin_service/templates/partials/ui.html`,抽出共享的 `surface_banner / panel_header / pagination / table_empty` 页面原语,建立“页面摘要层 + 分区头部 + 分页控制”这套统一模板结构。
  • 修改 `orchestrator/admin_service/templates/base.html`,为后台补上 `favicon` 引用,并在侧栏品牌区增加一句说明性 caption,让后台壳层具备更清晰的品牌和导航语义。
展开完整改动
  • 修改 `orchestrator/admin_service/app.py`,新增统一的 `render_page()` 页面渲染 helper,把 `lang / page_title / back_url` 这类跨页面上下文从各路由里抽出来,减少模板渲染时的重复拼装逻辑。
  • 新增 `orchestrator/admin_service/templates/partials/ui.html`,抽出共享的 `surface_banner / panel_header / pagination / table_empty` 页面原语,建立“页面摘要层 + 分区头部 + 分页控制”这套统一模板结构。
  • 修改 `orchestrator/admin_service/templates/base.html`,为后台补上 `favicon` 引用,并在侧栏品牌区增加一句说明性 caption,让后台壳层具备更清晰的品牌和导航语义。
  • 新增 `orchestrator/admin_service/static/favicon.svg`,用当前后台蓝绿监控色系补了站点图标。
  • 修改 `dashboard.html`、`apps.html`、`comments.html`、`jobs.html`、`studios.html`、`iterations.html`,统一接入新的 `surface_banner` 和 `panel_header`,同时把大表格统一收进 `table-scroll` 容器;首页改成“主监控区 + 侧监控区”双栏结构,列表页与任务页改成“首屏摘要 + 筛选/结果区”结构。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充 `brand-block / brand-caption / surface-banner / surface-stat / panel-header / dashboard-grid` 等设计系统层样式,开始把后台从“页面各写各的”收敛到共享视觉原语。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充对 `favicon`、页面摘要文案和共享壳层输出的回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.78s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `68 passed in 0.97s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py orchestrator/admin_service/run.py`
  • Result: PASS
  • Detail: 后台入口、展示层和查询层模块编译通过
发现与结论
  • 当前技术栈对这个后台依然是可行的,主要问题并不在 FastAPI/Jinja 本身,而是在“页面壳层、共享 UI 原语、路由上下文组装”之前没有形成明确分层,导致每加一个页面都容易重复造一套工具栏、分页和标题区。
  • 这轮先建立了一个足够小但稳定的前端架构切口:`app.py` 负责页面上下文拼装,`presentation.py` 负责展示规则与文案,`templates/partials/ui.html` 负责共享页面原语,具体页面模板只保留页面组合逻辑。这个分层比之前更容易继续扩。
下一步
  • 如果继续推进,下一步建议把“长表格的列配置”和“列表页/详情页的空状态与加载态”继续抽成共享模式,并给应用详情页补一层更明确的 sticky 侧边导航,让后台在长内容场景下更易用。
v0.49.0 2026-03-21 18:55 CST
统一后台图片展示方式,确保图片缩略图保持原比例,并支持站内大图预览而不是新标签页跳转。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/base.html`,新增全局 lightbox 预览层和通用前端交互,点击带 `data-lightbox-src` 的图片后在站内弹层查看大图,按 `Esc` 或点击关闭按钮/遮罩可关闭。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`、`apps.html`、`comments.html`,将应用图标、应用截图和快照时间线里的媒体差异图统一改成缩略图按钮,点击后走站内大图预览,不再打开新标签页。
  • 修改 `orchestrator/admin_service/static/admin.css`,把 `app-icon / screenshot / snapshot-media-thumb` 的 `object-fit` 改成 `contain`,并改为 `max-width/max-height` 控制缩略图尺寸,避免裁切和拉伸原图比例;同时补充 lightbox 样式。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/base.html`,新增全局 lightbox 预览层和通用前端交互,点击带 `data-lightbox-src` 的图片后在站内弹层查看大图,按 `Esc` 或点击关闭按钮/遮罩可关闭。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`、`apps.html`、`comments.html`,将应用图标、应用截图和快照时间线里的媒体差异图统一改成缩略图按钮,点击后走站内大图预览,不再打开新标签页。
  • 修改 `orchestrator/admin_service/static/admin.css`,把 `app-icon / screenshot / snapshot-media-thumb` 的 `object-fit` 改成 `contain`,并改为 `max-width/max-height` 控制缩略图尺寸,避免裁切和拉伸原图比例;同时补充 lightbox 样式。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充应用详情与评论查询页面对 `data-lightbox-src / data-lightbox-image` 的回归断言。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.68s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口与展示层模块编译通过
发现与结论
  • 原先截图和媒体差异图使用固定宽高 + `object-fit: cover`,会裁切画面;现在统一改成等比例缩略图,视觉上更符合“后台巡检”而不是“营销裁图”。
  • 图片预览改成站内 lightbox 后,查看大图不再打断当前浏览上下文,回看时间线和截图列表的成本明显更低。
下一步
  • 如果继续推进,下一步可以给 lightbox 增加左右切换上一张/下一张,进一步提升多截图浏览效率。
v0.48.0 2026-03-21 18:46 CST
修复应用截图“展开全部”后重复显示前 4 张预览图的问题。
改动内容
改动预览 2/2
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将截图区拆成 `screenshot_preview = screenshots[:4]` 和 `screenshot_remaining = screenshots[4:]` 两段;展开区只渲染剩余截图,并且编号从第 5 张开始继续递增,不再重复显示 1 到 4。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充应用详情页对 `Screenshot 5` 的回归断言,确认展开区包含剩余截图。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.92s`
发现与结论
  • 之前的逻辑是“首屏展示前 4 张 + 展开区再把全量列表渲染一遍”,所以视觉上会把截图 1 到 4 再显示一次。现在展开区只承担“补齐剩余截图”的职责。
下一步
  • 如果继续推进,下一步可以把截图展开按钮文案改成“查看剩余 X 张”,让用户更直观地知道展开后会看到多少新增内容。
v0.47.0 2026-03-21 18:06 CST
修复 App Store 稀疏详情覆盖好数据的问题,并完善应用详情页快照时间线中的长描述展示。
改动内容
改动预览 3/6
  • 修改 `app_store_scrap/workflow.py`,为 App Store 主 workflow 增加“保留上一版有效字段”的合并逻辑:当最新详情返回稀疏数据时,不再用 `app_id`/空值/占位媒体覆盖已有的 `app_name / version / category / icon / developer_name` 等有效字段;同时对开发者名称做 `Developer` 前缀清洗。
  • 修改 `app_store_scrap/parser/app_detail_parser.py`,对 `app_name` 增加解析兜底,避免把纯数字 `app_id` 当成应用名写入。
  • 修改 `orchestrator/admin_service/presentation.py` 与 `templates/app_detail.html`,将 `raw_html_hash / raw_json_hash` 从快照对比主视图中排除;对快照时间线中的 `description / short_description` 长文本差异增加“预览 + 点击展开完整内容”交互。
展开完整改动
  • 修改 `app_store_scrap/workflow.py`,为 App Store 主 workflow 增加“保留上一版有效字段”的合并逻辑:当最新详情返回稀疏数据时,不再用 `app_id`/空值/占位媒体覆盖已有的 `app_name / version / category / icon / developer_name` 等有效字段;同时对开发者名称做 `Developer` 前缀清洗。
  • 修改 `app_store_scrap/parser/app_detail_parser.py`,对 `app_name` 增加解析兜底,避免把纯数字 `app_id` 当成应用名写入。
  • 修改 `orchestrator/admin_service/presentation.py` 与 `templates/app_detail.html`,将 `raw_html_hash / raw_json_hash` 从快照对比主视图中排除;对快照时间线中的 `description / short_description` 长文本差异增加“预览 + 点击展开完整内容”交互。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充时间线长文本展开样式。
  • 修改 `app_store_scrap/tests/test_workflow.py`、`orchestrator/tests/test_admin_service.py` 与 `common/tests/test_notifiers.py`,补充稀疏详情保值、时间线长文本展开、以及通知展示层回退的回归测试。
  • 使用新逻辑实际修复了库里 2 条异常 App Store 记录:`1603801118` 与 `1437615191`;修复后当前库内 `app_name=app_id` 或 `version 为空` 的 App Store 记录数已从 `2` 归零到 `0`。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_workflow.py orchestrator/tests/test_admin_service.py common/tests/test_notifiers.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_workflow.py orchestrator/tests/test_admin_service.py common/tests/test_notifiers.py`
  • Result: PASS
  • Detail: `20 passed in 0.75s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `68 passed in 0.84s`
  • `./.venv/bin/python -m py_compile app_store_scrap/workflow.py app_store_scrap/parser/app_detail_parser.py common/notifier/feishu_notifier.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 相关 Python 模块编译通过
发现与结论
  • 实时抓取 `1603801118` 时,Apple API 与 HTML 详情都能正常返回正确的 `app_name/version`;所以“版本号回退 / app 名称变成数字 ID”并不是稳定的上游接口问题,而是偶发稀疏详情被直接落库导致的覆盖问题。
  • 这轮修复后,即使再次遇到稀疏详情,workflow 也会优先保留上一版有效字段,因此后续 `apps / snapshots / change_events / 飞书通知` 的一致性会明显好很多。
下一步
  • 如果继续推进,下一步建议把“稀疏详情命中次数”做成结构化日志字段,并在后台任务页上展示出来,方便你直接看到上游采集质量是否在波动。
v0.46.0 2026-03-21 17:53 CST
根据飞书通知截图排查通知内容异常,并修复平台文案、应用名回退、空字段展示和字段明细可读性问题。
改动内容
改动预览 3/5
  • 修改 `common/notifier/feishu_notifier.py`,将通知卡片中的平台从原始值 `app_store / google_play` 改成用户可读文案 `App Store / Google Play`。
  • 同一文件里,将通知详情取值逻辑从“仅用 `snapshot_after` 或 `snapshot_before` 单边快照”改成“前后快照按非空值合并”,避免更新通知里因为 `after` 快照字段缺失而出现版本号、更新时间、分类/年龄全部显示为 `-`。
  • 为应用名增加展示层回退策略:当 `event.app_name` 或 `snapshot_after.app_name` 退化成 `app_id`/纯数字时,优先回退到 `snapshot_before.app_name`,避免通知卡片里把真实应用名显示成数字 ID。
展开完整改动
  • 修改 `common/notifier/feishu_notifier.py`,将通知卡片中的平台从原始值 `app_store / google_play` 改成用户可读文案 `App Store / Google Play`。
  • 同一文件里,将通知详情取值逻辑从“仅用 `snapshot_after` 或 `snapshot_before` 单边快照”改成“前后快照按非空值合并”,避免更新通知里因为 `after` 快照字段缺失而出现版本号、更新时间、分类/年龄全部显示为 `-`。
  • 为应用名增加展示层回退策略:当 `event.app_name` 或 `snapshot_after.app_name` 退化成 `app_id`/纯数字时,优先回退到 `snapshot_before.app_name`,避免通知卡片里把真实应用名显示成数字 ID。
  • 为字段差异明细增加展示层清洗:`app_name` 字段补中文标签;对于 `version / updated_at_store / category / sub_category / age_rating / developer_name` 这类字段,如果新值为空且上一版快照有值,则不再把空值写进通知;如果清洗后前后值相同,则直接从明细里跳过,减少噪音。
  • 修改 `common/tests/test_notifiers.py`,补充“平台文案用户化”和“after 快照稀疏时回退前一版有效值”的回归测试。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q common/tests/test_notifiers.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q common/tests/test_notifiers.py`
  • Result: PASS
  • Detail: `6 passed in 0.14s`
  • `./.venv/bin/python -m py_compile common/notifier/feishu_notifier.py`
  • Result: PASS
  • Detail: 飞书通知模块编译通过
发现与结论
  • 从截图反推,通知里至少有 4 个真实问题:平台直接显示内部枚举值、应用名退化成纯数字 ID、after 快照字段缺失导致版本/时间等摘要区大片显示 `-`、以及 `app_name` 这种面向存储层的字段名直接暴露给非开发用户。
  • 这些问题并不一定意味着底层 `change_event` 全部错误,更多是通知层没有做展示侧的数据兜底和可读性清洗。
下一步
  • 如果继续推进,下一步建议把飞书通知的日期展示也统一成后台相同的可读时间格式,并对“异常回退后的字段”加一层告警日志,方便后续反查上游采集质量。
v0.45.0 2026-03-21 17:39 CST
为应用截图增加“预览 + 展开全部”交互,并收口快照时间线中 `raw_html_hash / raw_json_hash` 的展示长度。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将应用截图区改成默认预览前 4 张,超过 4 张时通过“展开全部截图”查看完整列表。
  • 同一文件里,对快照时间线中 `raw_html_hash` 和 `raw_json_hash` 的前后值展示改成前 8 位加 `...`,只影响可视展示,不修改原始快照折叠区。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充截图展开区样式。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将应用截图区改成默认预览前 4 张,超过 4 张时通过“展开全部截图”查看完整列表。
  • 同一文件里,对快照时间线中 `raw_html_hash` 和 `raw_json_hash` 的前后值展示改成前 8 位加 `...`,只影响可视展示,不修改原始快照折叠区。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充截图展开区样式。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充“展开全部截图”和 hash 截断展示的回归断言,并在种子数据里加入多截图与原始 hash 变化样本。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.66s`
发现与结论
  • `raw_html_hash / raw_json_hash` 的完整值仍会保留在“查看原始快照”折叠区,这样既保证主视图简洁,也不会影响排障时查看原始数据。
下一步
  • 如果继续推进,下一步可以把截图预览的数量做成可配置项,或者在移动端再单独调一版预览密度。
v0.44.0 2026-03-21 17:27 CST
调整应用详情页主内容顺序,将“应用截图”区块移动到“快照时间线”之前。
改动内容
改动预览 1/1
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将详情页主栏中的“应用截图”面板上移到“快照时间线”前面,其他时间线内容与交互保持不变。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.67s`
发现与结论
  • 当前详情页主栏顺序已经变成“应用截图 -> 快照时间线 -> 最近变更记录”,更符合先看媒体、再看变动轨迹的浏览习惯。
下一步
  • 如果继续推进,下一步可以考虑把“最近变更记录”进一步弱化成摘要或折叠区,避免和时间线重复竞争主视觉。
v0.43.0 2026-03-21 17:25 CST
收口应用详情页快照时间线中的 hash 展示长度,降低视觉噪音。
改动内容
改动预览 1/1
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将快照时间线里的 `snapshot_hash` 改成只展示前 8 位并追加 `...`,空值仍显示 `-`。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.74s`
发现与结论
  • 全量展示 64 位 hash 在详情页里信息密度过高,而且对人工巡检几乎没有额外价值;截断后仍保留了区分能力。
下一步
  • 如果继续推进,下一步可以把原始完整 hash 收到 hover title 或折叠详情里,兼顾简洁和排障需求。
v0.42.0 2026-03-21 17:18 CST
收口应用详情页的主展示结构、公共头部控件样式与飞书媒体变更通知,统一后台视觉并提升可读性。
改动内容
改动预览 3/5
  • 修改 `common/notifier/feishu_notifier.py`,将 `screenshot_urls / icon_url / header_image_url / video_urls` 这类媒体字段在飞书卡片中改成“素材已更新,请点击详情查看”的摘要展示,不再直接展开旧 URL 和新 URL。
  • 修改 `common/tests/test_notifiers.py`,补充飞书通知对截图变更的回归校验,确保媒体字段不会把具体 URL 写进卡片正文。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,把“快照时间线”提到应用详情主内容首屏,保留图片差异展示;同时把描述区改成“预览 + 点击展开完整描述”,去掉应用截图卡片下方的 URL 文本,并将包名字段改成可断行样式。
展开完整改动
  • 修改 `common/notifier/feishu_notifier.py`,将 `screenshot_urls / icon_url / header_image_url / video_urls` 这类媒体字段在飞书卡片中改成“素材已更新,请点击详情查看”的摘要展示,不再直接展开旧 URL 和新 URL。
  • 修改 `common/tests/test_notifiers.py`,补充飞书通知对截图变更的回归校验,确保媒体字段不会把具体 URL 写进卡片正文。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,把“快照时间线”提到应用详情主内容首屏,保留图片差异展示;同时把描述区改成“预览 + 点击展开完整描述”,去掉应用截图卡片下方的 URL 文本,并将包名字段改成可断行样式。
  • 修改 `orchestrator/admin_service/templates/base.html` 与 `orchestrator/admin_service/static/admin.css`,统一刷新按钮、主题切换按钮、多语言切换控件的视觉风格,并同步收口返回按钮、截图卡片、详情页主副栏比例、时间线主卡片和整体按钮阴影/圆角,减少页面间风格漂移。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充应用详情页对“时间线优先展示 / 描述展开入口 / 包名断行类名 / 去掉截图 URL 辅助文本 / 统一头部控件类名”的回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q common/tests/test_notifiers.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q common/tests/test_notifiers.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `13 passed in 0.90s`
  • `./.venv/bin/python -m py_compile common/notifier/feishu_notifier.py orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 通知模块与后台入口/展示层模块编译通过
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `66 passed in 1.17s`
发现与结论
  • 应用详情页原先同时保留“字段级变更表”和“快照时间线”,但主视觉先展示描述,导致真正最有价值的变动轨迹被压到了侧栏。把时间线提到主区首屏后,媒体差异和关键字段变更能直接成为默认阅读路径。
  • 飞书卡片里媒体字段直接展示 URL 会迅速拉长消息体,也会把图片变更的可读性拖垮;摘要化后,消息更短,且和后台详情页的图片化展示形成了明确分工。
下一步
  • 如果继续推进,下一步建议把“最近变更记录”也逐步向时间线式卡片收口,最终减少应用详情页里“表格 diff”和“时间线 diff”两套并行展示带来的重复感。
v0.41.0 2026-03-21 16:08 CST
修复侧栏折叠态下,浮动菜单按钮与页面返回按钮在左上角重叠的样式问题。
改动内容
改动预览 1/1
  • 修改 `orchestrator/admin_service/static/admin.css`,为 `html[data-sidebar=\"collapsed\"] .content` 增加额外左侧内边距,在桌面和移动端分别预留浮动菜单按钮占位,避免内容标题区继续压到按钮下方。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.90s`
发现与结论
  • 这次冲突不是按钮本身样式错误,而是折叠态下右侧内容区没有给固定浮动按钮留出安全区,导致返回键进入了同一块视觉区域。
下一步
  • 如果继续推进,下一步建议把侧栏折叠态的浮动按钮从“全局固定”进一步收成“页头对齐”模式,视觉会更统一。
v0.40.0 2026-03-21 15:27 CST
修复评论查询页的表格渲染问题,并确保左侧菜单在展开状态下不再随着右侧内容横向滚动一起移动。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/comments.html`,为评论查询表格增加独立的 `table-scroll` 容器,并为平台、应用、工作室、包名、用户、评分、版本、时间、内容列补齐明确的列类名。
  • 修改 `orchestrator/admin_service/static/admin.css`,为评论表增加固定列宽、`nowrap` 时间/评分/版本列、内容三行预览、以及右侧主内容区独立 `overflow-x: auto`;同时将页面级 `body` 改为 `overflow-x: hidden`,避免页面整体横向滚动把左侧侧栏一起带走。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充评论查询页对 `table-scroll` 和 `comment-content-preview` 渲染存在性的回归断言。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.98s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口与展示层模块编译通过
发现与结论
  • 原先评论页是整张表直接铺在 `panel` 里,列宽完全由内容挤压,长应用名、长用户名、长评论和时间列会彼此抢宽,导致换行混乱。
  • 左侧菜单“看起来在跟着内容左右动”,本质上是页面整体发生了横向滚动;把横向滚动收回到右侧内容区后,侧栏就能保持常驻不动。
下一步
  • 如果继续推进,下一步建议给评论内容加“点击展开全文”交互,并给评论查询表增加可选的紧凑/标准两种密度模式。
v0.39.0 2026-03-21 15:15 CST
为后台壳层补侧栏隐藏/展示能力,并给所有内容页面统一增加刷新按钮。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/base.html`,在公共页头新增刷新按钮,并为侧栏增加“内嵌收起按钮 + 折叠态固定浮动展开按钮”两套入口;同时将侧栏状态持久化到浏览器本地存储。
  • 修改 `orchestrator/admin_service/static/admin.css`,将布局网格改成 `240px + minmax(0, 1fr)`,补充侧栏独立固定区域、折叠态过渡、固定浮动按钮、以及内容区 `min-width: 0` 收口,避免右侧内容影响左侧菜单位置。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“刷新页面 / 展开菜单 / 收起菜单”中英文文案。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/base.html`,在公共页头新增刷新按钮,并为侧栏增加“内嵌收起按钮 + 折叠态固定浮动展开按钮”两套入口;同时将侧栏状态持久化到浏览器本地存储。
  • 修改 `orchestrator/admin_service/static/admin.css`,将布局网格改成 `240px + minmax(0, 1fr)`,补充侧栏独立固定区域、折叠态过渡、固定浮动按钮、以及内容区 `min-width: 0` 收口,避免右侧内容影响左侧菜单位置。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“刷新页面 / 展开菜单 / 收起菜单”中英文文案。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充后台详情页对 `data-refresh-button` 和 `data-sidebar-toggle` 的回归断言。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.87s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口与展示层模块编译通过
发现与结论
  • 当前刷新按钮和侧栏开关都放在 `base.html` 公共壳层,所以所有页面会自动继承,不需要逐页重复实现。
  • 侧栏展开态现在是独立固定区域,折叠态则切成 0 宽并保留左上角浮动入口,能同时满足“常驻显示”和“可完全隐藏”两种使用方式。
下一步
  • 如果继续推进,下一步建议给侧栏当前激活页增加更明显的图标或标识,并在移动端把折叠按钮位置再单独微调一轮。
v0.38.0 2026-03-21 14:09 CST
排查“App Store 评论抓取任务结果总是 0”的原因,并修正后台监控口径,让“抓取到评论”和“新增入库评论”分开展示。
改动内容
改动预览 3/5
  • 实查 `job_runs`、数据库评论表和 App Store 评论采集器,确认任务本身没有失效;根因是当前评论任务采用增量去重,旧口径只展示 `comment_collected_count`,因此在“本轮没有新评论”时会显示 `0`,容易被误解成“没有抓到评论”。
  • 修改 `app_store_scrap/comment_collector/comment_collector.py` 与 `google_play_scrap/comment_collector/comment_collector.py`,为评论 collector 增加最近一次采集统计,包括源评论数、已存在过滤数和批内重复过滤数。
  • 修改 `app_store_scrap/workflow.py` 与 `google_play_scrap/comment_workflow.py`,将评论任务结果扩展为 `comment_source_count / comment_new_count / comment_existing_filtered_count / comment_duplicate_filtered_count`,同时保留原有 `comment_collected_count` 兼容旧口径。
展开完整改动
  • 实查 `job_runs`、数据库评论表和 App Store 评论采集器,确认任务本身没有失效;根因是当前评论任务采用增量去重,旧口径只展示 `comment_collected_count`,因此在“本轮没有新评论”时会显示 `0`,容易被误解成“没有抓到评论”。
  • 修改 `app_store_scrap/comment_collector/comment_collector.py` 与 `google_play_scrap/comment_collector/comment_collector.py`,为评论 collector 增加最近一次采集统计,包括源评论数、已存在过滤数和批内重复过滤数。
  • 修改 `app_store_scrap/workflow.py` 与 `google_play_scrap/comment_workflow.py`,将评论任务结果扩展为 `comment_source_count / comment_new_count / comment_existing_filtered_count / comment_duplicate_filtered_count`,同时保留原有 `comment_collected_count` 兼容旧口径。
  • 修改后台 `orchestrator/admin_service/queries.py`、`app.py`、`templates/jobs.html`、`templates/dashboard.html` 与 `presentation.py`,把评论任务监控改成展示“最近抓取数 / 最近新增数 / 最近失败数”,避免再把“无新增”误判成“未抓到”。
  • 修改评论 workflow 与后台页面回归测试,覆盖新统计字段和新页面文案。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q google_play_scrap/tests/test_comment_workflow.py app_store_scrap/tests/test_workflow.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q google_play_scrap/tests/test_comment_workflow.py app_store_scrap/tests/test_workflow.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `15 passed in 1.02s`
  • `./.venv/bin/python -m py_compile google_play_scrap/comment_workflow.py app_store_scrap/workflow.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py orchestrator/admin_service/app.py`
  • Result: PASS
  • Detail: 评论 workflow、后台查询层与展示层模块编译通过
发现与结论
  • 当前 App Store 评论任务最近一次启动执行的真实结果是:`comment_source_count=4081`、`comment_new_count=0`、`comment_existing_filtered_count=4081`、`comment_failed_count=0`。这说明 Apple 源站评论是抓到了,但这些评论都已经在库里,所以本轮没有新增入库。
  • 之前你看到“结果都是 0”,本质上是监控口径问题,不是任务失效。
下一步
  • 如果继续推进,下一步建议把任务执行记录里的结果摘要也切到“抓取数 / 新增数 / 过滤数”新口径,顺手把历史旧记录和新记录做兼容展示。
v0.37.0 2026-03-21 03:27 CST
清理工程内明确无用的生成物与噪音文件,并基于当前代码结构评估项目架构、可扩展性与技术选型边界。
改动内容
改动预览 3/3
  • 将工程根目录 `.DS_Store`、未被引用的 `data/real_test.log`、项目级 `__pycache__` 与 `.pytest_cache` 送入回收站,清理掉本轮无运行价值的缓存和测试噪音。
  • 修改 `.gitignore`,新增 `*.egg-info/`,并将 `scrap.egg-info/` 送入回收站,避免构建生成物继续滞留在工程目录中。
  • 复核 `scripts/`、`orchestrator/jobs/`、后台服务和 workflow 入口,确认目前保留的双入口脚本大多仍有实际用途,不属于可直接删除的死文件。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `65 passed in 1.20s`
发现与结论
  • 当前项目适合作为“单机部署、单 SQLite、单后台进程”的监控服务继续推进;`FastAPI + APScheduler + SQLite + Python workflows` 在当前规模下可行,开发与排障成本也低。
  • 真正限制后续扩展的不是采集能力,而是架构耦合:`google_play_scrap/workflow.py` 与 `app_store_scrap/workflow.py` 的主流程高度重复,评论 workflow 也基本对称,如果后面继续扩平台或加更多任务类型,重复修改成本会快速上升。
  • 后台查询层 `orchestrator/admin_service/queries.py` 一半走 `sqlite3` 原生 SQL,一半业务写入又走 repository/SQLAlchemy;数据访问方式分裂,会让后续权限、分页、缓存和迁移越来越难统一。
  • `feature_iterations` 相关 schema 与查询写接口还在,但当前“功能迭代”页面实际上已经完全切换为读取 `docs/worklog.md`;这部分属于历史遗留,建议下一轮从 schema、迁移和查询层一起收掉,避免误导后续开发。
  • 如果后面要做多实例部署或增加 job 数量,内嵌式 `APScheduler + SQLite` 会成为瓶颈:缺少分布式锁、任务隔离和更强的并发写能力,届时应优先迁移到 `PostgreSQL + 外部任务队列/调度器`。
下一步
  • 如果继续推进,下一步建议按优先级做三件事:先抽象跨平台 workflow 基类/公共 pipeline;再统一后台查询与 repository 的数据访问层;最后评估数据库从 SQLite 迁移到 PostgreSQL 的成本和时机。
v0.36.0 2026-03-21 03:20 CST
继续收口“功能迭代”时间线布局,将版本号与时间统一放回左侧时间轨道,避免版本信息仍停留在卡片内容区。
改动内容
改动预览 2/2
  • 修改 `orchestrator/admin_service/templates/iterations.html`,把版本号标签从卡片头部移到左侧时间线轨道,与蓝色时间点和发布时间组成同一组时间标识。
  • 修改 `orchestrator/admin_service/static/admin.css`,将迭代时间轨道重新居中对齐,统一版本号、圆点、时间和竖线的视觉中心,并放宽时间文本宽度,避免版本号移位后时间换行过碎。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.78s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py orchestrator/admin_service/queries.py`
  • Result: PASS
  • Detail: 后台入口、展示层和查询层模块编译通过
发现与结论
  • 仅仅把版本号从模板里挪到左侧还不够,如果轨道列继续使用左对齐,蓝点和竖线会和版本号、时间形成错位;这次把轨道列整体重新居中后,时间线结构才真正闭合。
下一步
  • 如果继续推进,下一步可以把左侧轨道的时间格式再压成“两行日期 + 时间”,进一步降低长时间戳对纵向空间的占用。
v0.35.0 2026-03-21 02:52 CST
修复“job 看起来没有在跑”的调度感知问题,补齐启动即执行、状态回填,以及后台页面中的调度时间信息展示。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/scheduler.py`,为调度器增加 `service_started_at` 与自动执行状态字段;在服务启动时先从 `job_runs` 回填最近运行状态,再为启用任务注册 interval job,并额外触发一次 `startup` 自动执行。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“服务启动时间 / 上次调度时间”中英文文案,以及 `startup` 触发来源标签。
  • 修改 `orchestrator/admin_service/templates/jobs.html` 与 `orchestrator/admin_service/templates/dashboard.html`,在任务表中明确展示 `服务启动时间 / 上次调度时间 / 下次执行时间`,减少“页面像没跑过”的误判。
展开完整改动
  • 修改 `orchestrator/admin_service/scheduler.py`,为调度器增加 `service_started_at` 与自动执行状态字段;在服务启动时先从 `job_runs` 回填最近运行状态,再为启用任务注册 interval job,并额外触发一次 `startup` 自动执行。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“服务启动时间 / 上次调度时间”中英文文案,以及 `startup` 触发来源标签。
  • 修改 `orchestrator/admin_service/templates/jobs.html` 与 `orchestrator/admin_service/templates/dashboard.html`,在任务表中明确展示 `服务启动时间 / 上次调度时间 / 下次执行时间`,减少“页面像没跑过”的误判。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充调度器从 `job_runs` 回填状态、服务启动后自动触发启用任务,以及后台页面展示新时间字段的回归测试。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `8 passed in 0.80s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/scheduler.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、调度器和展示层模块编译通过
发现与结论
  • 原来的 `last_run_*` 是纯内存态,服务一重启就会丢,所以页面会像“从未运行过”;现在通过 `job_runs` 回填后,这个错觉已经消掉。
  • interval job 默认是“从服务启动时刻开始计时”,不会一启动就执行;这也是你之前感觉“没在跑”的直接原因。现在已经改成“启动即跑一次,再进入定时节奏”。
下一步
  • 如果继续推进,下一步建议把 `job_runs` 里 `triggered_by=startup` 的首轮启动执行,在页面上单独加个小标识,和普通 `scheduler` 调度区分开。
v0.34.0 2026-03-21 02:38 CST
统一重做后台页面左上角的“返回上一层”控件,只保留返回箭头,不再显示文字。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/base.html`,移除返回键可见文字,只保留箭头图标,同时保留 `aria-label` 和 `title` 作为可访问性说明。
  • 修改 `orchestrator/admin_service/static/admin.css`,将返回键重做为更明确的单图标返回控件,补充发光底、悬浮位移和更强的箭头视觉权重。
  • 修改 `orchestrator/tests/test_admin_service.py`,将英文页面断言从可见文本 `Back` 调整为 `aria-label=\"Back\"`,避免测试继续依赖已移除的可见文字。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `7 passed`
发现与结论
  • 返回控件只保留箭头后,标题区会更干净;但如果完全移除语义说明,键盘和读屏体验会下降,所以这次保留了 `aria-label/title`。
下一步
  • 如果继续推进,下一步建议把返回键在浅色/深色两种主题下的边框和阴影再做轻微区分,进一步提高手感一致性。
v0.33.0 2026-03-21 02:35 CST
按页面视觉反馈继续优化“功能迭代”时间线,强化标题与内容的层级差异,并将版本时间移动到时间线轨道位置。
改动内容
改动预览 3/5
  • 修改 `orchestrator/admin_service/templates/iterations.html`,将每个版本的时间从卡片头部挪到时间线轨道列,放到蓝色时间点所在区域;卡片头部只保留版本标签和版本总结。
  • 修改 `orchestrator/admin_service/static/admin.css`,提升区块小标题的字号和颜色对比,降低正文内容的字号和颜色,让标题与内容层级更清晰。
  • 调整版本总结样式为更大、更黑、更醒目的标题呈现。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/iterations.html`,将每个版本的时间从卡片头部挪到时间线轨道列,放到蓝色时间点所在区域;卡片头部只保留版本标签和版本总结。
  • 修改 `orchestrator/admin_service/static/admin.css`,提升区块小标题的字号和颜色对比,降低正文内容的字号和颜色,让标题与内容层级更清晰。
  • 调整版本总结样式为更大、更黑、更醒目的标题呈现。
  • 强化“展开完整改动 / 展开完整验证”按钮样式,改为更突出的强调态,而不是接近普通正文。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充对 `iteration-time-label / iteration-summary-title` 渲染存在性的回归校验。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `7 passed`
发现与结论
  • 原先把时间放在卡片头部,会让时间线左侧轨道只剩一个装饰性圆点,时间轴感较弱;把时间移动到轨道列后,时间线结构更符合直觉。
  • 现在小标题、正文、展开动作已经拆成三层视觉层级,阅读顺序会更稳定。
下一步
  • 如果继续推进,下一步建议把时间轴左侧的时间格式再压缩成“日期 + 时间”两行,减少长时间字符串占用的纵向空间。
v0.32.0 2026-03-21 02:28 CST
继续优化后台导航与“功能迭代”页面交互,包括左侧菜单悬浮效果、长侧栏滚动、时间线内容压缩,以及整体字号和颜色收口。
改动内容
改动预览 3/5
  • 修改 `orchestrator/admin_service/static/admin.css`,将左侧菜单改成 `sticky + max-height: 100vh + overflow-y: auto`,保证功能项很多时侧栏仍可独立滚动;同时为菜单项增加轻量 hover 浮起效果。
  • 修改 `orchestrator/admin_service/templates/iterations.html`,让“改动内容”也采用预览 + 折叠展开形式;默认展示前 3 条,点击后再查看完整改动。
  • 调整“发现与结论 / 下一步”布局,不再让“下一步”独占整行,而是回到双列网格中,默认显示在“发现与结论”右侧。
展开完整改动
  • 修改 `orchestrator/admin_service/static/admin.css`,将左侧菜单改成 `sticky + max-height: 100vh + overflow-y: auto`,保证功能项很多时侧栏仍可独立滚动;同时为菜单项增加轻量 hover 浮起效果。
  • 修改 `orchestrator/admin_service/templates/iterations.html`,让“改动内容”也采用预览 + 折叠展开形式;默认展示前 3 条,点击后再查看完整改动。
  • 调整“发现与结论 / 下一步”布局,不再让“下一步”独占整行,而是回到双列网格中,默认显示在“发现与结论”右侧。
  • 修改 `orchestrator/admin_service/static/admin.css` 与 `orchestrator/admin_service/presentation.py`,将迭代页内容字号整体下调一档,字体颜色改浅,补充“改动预览 / 展开完整改动”中英文文案。
  • 修改 `orchestrator/tests/test_admin_service.py`,扩充 worklog 测试样本,并补充“改动预览 3/4 / 展开完整改动”的回归校验。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `7 passed`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、查询服务和展示层模块编译通过
发现与结论
  • 左侧菜单如果只是做 hover 动效但不处理滚动,高度超过一屏时会出现“底部菜单访问困难”的问题;现在 sticky 和独立滚动已经一起处理掉了。
  • 迭代页当前最长的内容仍然集中在历史条目的“验证结果 / 改动内容”,通过双重预览后首屏密度已经明显下降,但如果后面 worklog 单条变得更长,`发现与结论` 也可能需要同样的预览阈值。
下一步
  • 如果继续推进,下一步建议给“发现与结论”也加可折叠预览,并把时间线卡片的展开状态做成本地记忆,减少重复操作。
v0.31.0 2026-03-21 02:08 CST
优化“功能迭代”页面的时间线布局,并把“验证结果”改成默认只展示预览,点击后再展开完整内容。
改动内容
改动预览 3/5
  • 修改 `orchestrator/admin_service/templates/iterations.html`,将每条迭代改成“版本与时间头部 + 摘要 + 分区卡片”的更紧凑布局;当 `Scope` 与摘要重复时不再单独重复展示。
  • 在“验证结果”区块中新增预览逻辑,默认只展示前两条验证记录,并通过折叠区块展开完整验证内容。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充迭代页专属样式,包括版本标签、摘要行、两列分区布局、验证预览标签、展开按钮和移动端响应式收口。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/iterations.html`,将每条迭代改成“版本与时间头部 + 摘要 + 分区卡片”的更紧凑布局;当 `Scope` 与摘要重复时不再单独重复展示。
  • 在“验证结果”区块中新增预览逻辑,默认只展示前两条验证记录,并通过折叠区块展开完整验证内容。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充迭代页专属样式,包括版本标签、摘要行、两列分区布局、验证预览标签、展开按钮和移动端响应式收口。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“验证预览 / 展开完整验证”中英文文案。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充对新布局与“验证预览 2/3 + 展开完整验证”展示行为的回归校验。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `7 passed in 0.83s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、查询服务和展示层模块编译通过
发现与结论
  • 原来的时间线布局把“摘要”和“范围”几乎按同样内容重复展示,导致页面纵向很长,阅读效率低;现在默认只保留一处核心摘要,重复范围不再单独占用版面。
  • “验证结果”常常是命令、结果、详情的多行组合,默认全展开会把单条迭代拉得过长;改成预览后,首屏更容易扫读,完整细节仍然保留在展开层里。
下一步
  • 如果继续推进,下一步建议给“改动内容 / 发现与结论 / 下一步”也加同样的预览阈值,避免单条迭代在内容很多时继续拉得太长。
v0.30.0 2026-03-21 01:39 CST
调整后台左侧导航结构,让“功能迭代”与“App 监控”同级,并将迭代页内容改为直接根据 `docs/worklog.md` 的时间线展示。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/templates/base.html`,将“功能迭代”从 `App 监控` 分组下挪出,作为独立导航分组展示。
  • 修改 `orchestrator/admin_service/queries.py`,新增 `worklog.md` 解析逻辑,按 `## 时间` 节点拆分迭代,并从 `Scope / Changes / Tests / Findings / Next` 结构中生成页面展示数据;同时为每条迭代生成顺序版本号。
  • 修改 `orchestrator/admin_service/app.py`,为后台服务增加可注入的 `worklog_path`,并让 `/iterations` 页面与 `/api/iterations` 都统一读取 worklog 时间线。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/base.html`,将“功能迭代”从 `App 监控` 分组下挪出,作为独立导航分组展示。
  • 修改 `orchestrator/admin_service/queries.py`,新增 `worklog.md` 解析逻辑,按 `## 时间` 节点拆分迭代,并从 `Scope / Changes / Tests / Findings / Next` 结构中生成页面展示数据;同时为每条迭代生成顺序版本号。
  • 修改 `orchestrator/admin_service/app.py`,为后台服务增加可注入的 `worklog_path`,并让 `/iterations` 页面与 `/api/iterations` 都统一读取 worklog 时间线。
  • 修改 `orchestrator/admin_service/templates/iterations.html`,去掉手工新增/编辑表单,改为只读时间线卡片,按“版本号 / 时间 / 摘要 / 范围 / 改动 / 验证 / 发现 / 下一步”展示。
  • 修改 `orchestrator/admin_service/presentation.py` 与 `orchestrator/admin_service/static/admin.css`,补充新的多语言文案、worklog 来源提示、列表样式,以及 worklog 时间戳格式兼容。
  • 修改 `orchestrator/tests/test_admin_service.py`,将迭代模块测试改为基于临时 worklog 样本校验,确保页面和接口都按 worklog 时间线返回。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `7 passed in 0.71s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、查询服务和展示层模块编译通过
发现与结论
  • 现在“功能迭代”是只读视图,数据源完全来自 `docs/worklog.md`,因此后台展示与实际工作记录天然一致,不会再出现“数据库里手工写了一条版本记录,但 worklog 没同步”的口径偏差。
  • 版本号是根据 worklog 时间线顺序自动生成的顺序号,旧记录序号稳定;如果后面你要改成语义化版本或发布日期版本号,需要再定义一套映射规则。
下一步
  • 如果继续推进,下一步建议在迭代页里增加“跳到对应应用 / 任务 / 变更记录”的快捷链接,把 worklog 时间线和后台业务数据进一步串起来。
v0.29.0 2026-03-21 01:30 CST
排查并修复 Google Play 应用截图列表重复 2 遍的问题,补充去重逻辑,并清理已经入库的重复截图数据。
改动内容
改动预览 3/6
  • 新增 `google_play_scrap/screenshot_utils.py`,实现 Google Play 截图 URL 规范化和去重逻辑:对 `play-lh.googleusercontent.com` 媒体 URL 去掉尺寸后缀做同图识别,并在同图多尺寸时优先保留高分辨率版本。
  • 修改 `google_play_scrap/adapters/google_play_library_adapter.py`,对 `google-play-scraper` 返回的截图列表做去重,解决库接口直接返回完全重复截图的问题。
  • 修改 `google_play_scrap/collector/app_detail_collector.py` 与 `google_play_scrap/collector/app_detail_browser_collector.py`,在 HTML 和浏览器抓取路径中按规范化后的截图 key 去重,并在过滤 icon/header 时同时按规范化 key 比较,避免同一张图不同尺寸漏过滤。
展开完整改动
  • 新增 `google_play_scrap/screenshot_utils.py`,实现 Google Play 截图 URL 规范化和去重逻辑:对 `play-lh.googleusercontent.com` 媒体 URL 去掉尺寸后缀做同图识别,并在同图多尺寸时优先保留高分辨率版本。
  • 修改 `google_play_scrap/adapters/google_play_library_adapter.py`,对 `google-play-scraper` 返回的截图列表做去重,解决库接口直接返回完全重复截图的问题。
  • 修改 `google_play_scrap/collector/app_detail_collector.py` 与 `google_play_scrap/collector/app_detail_browser_collector.py`,在 HTML 和浏览器抓取路径中按规范化后的截图 key 去重,并在过滤 icon/header 时同时按规范化 key 比较,避免同一张图不同尺寸漏过滤。
  • 修改 `google_play_scrap/parser/app_detail_parser.py`,在入库前再次做一层截图去重,防止任何上游路径漏网后把重复截图写入 `apps` 表。
  • 新增 `google_play_scrap/tests/test_screenshot_dedup.py`,覆盖“完全重复截图去重”“同一截图不同尺寸去重且优先保留高分辨率版本”“parser 入库前兜底去重”三类回归场景。
  • 执行一次库内回写,规范化已有 Google Play 应用和历史快照里的截图列表,共更新 `apps` 表 `28` 条、`app_snapshots` 表 `510` 条,清掉后台当前可见的旧重复截图数据。
验证结果
验证预览 2/18
  • `./.venv/bin/pytest -q google_play_scrap/tests/test_screenshot_dedup.py google_play_scrap/tests/test_google_play_comment_collector.py orchestrator/tests/test_google_play_workflow.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q google_play_scrap/tests/test_screenshot_dedup.py google_play_scrap/tests/test_google_play_comment_collector.py orchestrator/tests/test_google_play_workflow.py`
  • Result: PASS
  • Detail: `7 passed in 0.31s`
  • `./.venv/bin/python -m py_compile google_play_scrap/screenshot_utils.py google_play_scrap/adapters/google_play_library_adapter.py google_play_scrap/collector/app_detail_collector.py google_play_scrap/collector/app_detail_browser_collector.py google_play_scrap/parser/app_detail_parser.py`
  • Result: PASS
  • Detail: Google Play 截图相关模块编译通过
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `64 passed in 1.00s`
  • `./.venv/bin/python - <<'PY' ... fetch_app_detail('com.dotdot.abo') / collect(...) ... PY`
  • Result: PASS
  • Detail: `google-play-scraper` 路径截图数已从重复前的 `15` 收口到 `10`;HTML 路径返回的截图列表不再包含“同图双尺寸重复”问题
  • `./.venv/bin/python - <<'PY' ... normalize existing google_play screenshots in apps/app_snapshots ... PY`
  • Result: PASS
  • Detail: `app_updates=28`,`snapshot_updates=510`,`remaining_duplicate_apps=0`
  • `./.venv/bin/python - <<'PY' ... verify com.dotdot.abo / com.christmas... / com.fairy... ... PY`
  • Result: PASS
  • Detail: 抽样校验三条历史重复样本,当前分别为 `10/10`、`14/14`、`19/19`,即“总数 == 去重后唯一数”
发现与结论
  • 这次重复问题有两个来源:一是 `google-play-scraper` 会直接返回完全相同的截图 URL;二是页面抓取会把同一张截图的不同尺寸 URL 当成不同图片。仅做字符串级 `set()` 去重不够,必须做 URL 规范化后再去重。
  • 当前回写已经清理了 `apps` 和 `app_snapshots`,所以后台当前应用详情页和快照时间线都不会再继续展示旧重复截图;但旧 `change_events` 里的历史 JSON 快照没有同步回写,如果后面要完全统一历史事件原文,还需要再单独处理。
下一步
  • 如果继续推进,下一步建议把 Google Play 截图去重逻辑抽到 App Store/Google Play 共用的媒体规范化层,顺便把一些 `=s20-rw` 这类极小图标型资源从候选截图里进一步排除。
v0.28.0 2026-03-21 01:22 CST
在左侧导航增加“功能迭代”模块,支持用版本号记录迭代摘要,并以时间线方式展示历史迭代。
改动内容
改动预览 3/9
  • 修改 `common/storage/schema.sql` 与 `common/storage/init_db.py`,新增 `feature_iterations` 表,并补老库轻量迁移。
  • 修改 `orchestrator/admin_service/queries.py`,新增迭代记录的列表、查询、保存、删除方法。
  • 修改 `orchestrator/admin_service/app.py`,新增 `/iterations` 页面路由、表单保存/删除入口,以及 `/api/iterations` CRUD 接口。
展开完整改动
  • 修改 `common/storage/schema.sql` 与 `common/storage/init_db.py`,新增 `feature_iterations` 表,并补老库轻量迁移。
  • 修改 `orchestrator/admin_service/queries.py`,新增迭代记录的列表、查询、保存、删除方法。
  • 修改 `orchestrator/admin_service/app.py`,新增 `/iterations` 页面路由、表单保存/删除入口,以及 `/api/iterations` CRUD 接口。
  • 修改 `orchestrator/admin_service/templates/base.html`,在左侧导航增加“功能迭代”页签。
  • 新增 `orchestrator/admin_service/templates/iterations.html`,实现“版本号 + 迭代时间 + 功能摘要 + 详情”的录入表单与时间线展示。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充迭代详情录入所需的 `textarea` 样式。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“功能迭代 / 迭代时间线 / 版本号 / 功能摘要 / 功能详情 / 保存迭代”等中英文文案。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充迭代页展示和 API CRUD 回归测试。
  • 在正式库中插入一条初始迭代记录:`v0.1.0 / 信息监控后台初始版本`,用于作为当前后台的基线版本。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `7 passed in 0.66s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `61 passed in 0.96s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、查询服务和展示层模块编译通过
  • `curl -sS 'http://127.0.0.1:8010/iterations?lang=zh-CN'`
  • Result: PASS
  • Detail: 页面已显示 `功能迭代 / 迭代时间线 / 版本号 / 功能摘要 / 功能详情`
  • `curl -sS 'http://127.0.0.1:8010/api/iterations'`
  • Result: PASS
  • Detail: 已返回初始记录 `v0.1.0 / 信息监控后台初始版本`
发现与结论
  • 现在迭代记录既可以直接通过后台页面录入,也可以通过 `/api/iterations` 做外部集成。
  • 这类记录目前按 `released_at DESC, id DESC` 排序;如果后面你想区分“计划中 / 已发布 / 已废弃”,可以再加状态字段。
下一步
  • 如果继续推进,下一步建议把“功能迭代”与 `job_runs / change_events` 做关联,支持从某次版本记录直接跳到对应的任务运行和改动页面。
v0.27.0 2026-03-21 00:36 CST
优化应用详情页的“最近快照”模块,改为时间线方式展示每次快照的修改内容,并在图片字段变更时直接展示图片对比。
改动内容
改动预览 3/5
  • 修改 `orchestrator/admin_service/presentation.py`,新增 `snapshot_timeline()` 快照时间线 helper,对相邻快照做字段级 diff,并将 `icon_url / header_image_url / screenshot_urls / video_urls` 识别为媒体变更。
  • 修改 `orchestrator/admin_service/app.py`,把 `snapshot_timeline` 注入模板全局。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将“最近快照”从原先的折叠原始信息改为时间线卡片;每个时间点展示变更字段数、文本前后对比、图片前后缩略图,并保留原始快照折叠查看入口。
展开完整改动
  • 修改 `orchestrator/admin_service/presentation.py`,新增 `snapshot_timeline()` 快照时间线 helper,对相邻快照做字段级 diff,并将 `icon_url / header_image_url / screenshot_urls / video_urls` 识别为媒体变更。
  • 修改 `orchestrator/admin_service/app.py`,把 `snapshot_timeline` 注入模板全局。
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将“最近快照”从原先的折叠原始信息改为时间线卡片;每个时间点展示变更字段数、文本前后对比、图片前后缩略图,并保留原始快照折叠查看入口。
  • 修改 `orchestrator/admin_service/static/admin.css`,新增时间线轨道、时间线卡片、字段对比、图片对比和缩略图样式。
  • 修改 `orchestrator/tests/test_admin_service.py`,给测试数据补两份带媒体变化的快照,并校验应用详情页能渲染时间线标题和旧图片 URL。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `6 passed in 0.80s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `60 passed in 0.96s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口与展示层模块编译通过
  • `curl -sS 'http://127.0.0.1:8010/apps/app_store/6498883328?lang=zh-CN'`
  • Result: PASS
  • Detail: 页面已显示 `快照时间线 / 变更字段 / 变更前图片 / 变更后图片`,并渲染图片缩略图
  • `curl -sS 'http://127.0.0.1:8010/apps/app_store/6498883328?lang=en-US'`
  • Result: PASS
  • Detail: 英文页面已显示 `Snapshot Timeline / Changed Fields / Before Images / After Images`
发现与结论
  • 这次时间线是基于“相邻快照”的字段差异计算的,不依赖 `change_events`,因此即使历史变更事件不完整,只要有快照就能看到变更内容。
  • 当前媒体字段会完整展示所有图片,如果某次截图变化很多,时间线会比较长;后续如果你觉得信息量过大,可以再加“只展示前 N 张 + 展开更多”。
下一步
  • 如果继续推进,下一步建议把时间线和“最近变更记录”进一步合并,减少应用详情页里两套相似信息的重复展示。
v0.26.0 2026-03-21 00:24 CST
为后台补充评论任务状态监控,在首页、任务页和 `/api/jobs` 里展示最近成功时间、最近采集数、最近失败数等摘要信息。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/queries.py`,新增 `get_job_monitor_map()`,从 `job_runs` 聚合每个任务的最近一次运行、最近一次成功、最近采集数、最近失败数、最近处理 app/studio 数以及运行次数统计。
  • 修改 `orchestrator/admin_service/app.py`,新增 `attach_job_monitors()`,将监控摘要透传到首页、任务页和 `/api/jobs`;同时给无历史任务补默认空 monitor,避免页面因缺字段报错。
  • 修改 `orchestrator/admin_service/templates/dashboard.html`,在“任务状态”表中为评论任务展示简版摘要,并新增单独的“评论任务监控”区块。
展开完整改动
  • 修改 `orchestrator/admin_service/queries.py`,新增 `get_job_monitor_map()`,从 `job_runs` 聚合每个任务的最近一次运行、最近一次成功、最近采集数、最近失败数、最近处理 app/studio 数以及运行次数统计。
  • 修改 `orchestrator/admin_service/app.py`,新增 `attach_job_monitors()`,将监控摘要透传到首页、任务页和 `/api/jobs`;同时给无历史任务补默认空 monitor,避免页面因缺字段报错。
  • 修改 `orchestrator/admin_service/templates/dashboard.html`,在“任务状态”表中为评论任务展示简版摘要,并新增单独的“评论任务监控”区块。
  • 修改 `orchestrator/admin_service/templates/jobs.html`,在任务表和执行记录前增加评论任务监控区块,展示最近成功时间、最近采集数、最近失败数、最近触发方式和最近成功耗时。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“评论任务监控 / 最近成功 / 最近采集数 / 最近失败数 / 最近触发 / 监控摘要 / 无数据”中英文文案。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充评论任务监控在首页、任务页和 `/api/jobs` 的回归测试。
验证结果
验证预览 2/18
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `6 passed in 0.62s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `60 passed in 1.15s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、查询服务和展示层模块编译通过
  • `curl -sS 'http://127.0.0.1:8010/?lang=zh-CN'`
  • Result: PASS
  • Detail: 首页已显示“评论任务监控”“最近采集数”“最近失败数”
  • `curl -sS 'http://127.0.0.1:8010/jobs?lang=zh-CN'`
  • Result: PASS
  • Detail: 任务页已显示评论任务监控区块和任务级摘要
  • `curl -sS 'http://127.0.0.1:8010/api/jobs'`
  • Result: PASS
  • Detail: 返回结果已包含 `monitor.last_success_at / last_collected_count / last_failed_count`
发现与结论
  • 监控摘要当前基于 `job_runs`,因此只统计“通过后台调度器执行”的任务运行结果;像直接在终端里调用 `AppStoreCommentWorkflow().run_once()` 这类运行,不会自动写入 `job_runs`。
  • 当前 `app_store_comments` 的最近一次调度记录显示 `last_collected_count=0`,这代表“最近一次调度增量没有新评论”,不是后台详情页没有评论数据。
下一步
  • 如果继续推进,下一步建议给 CLI/脚本触发的任务也接入 `job_runs` 记录,统一后台监控口径。
v0.25.0 2026-03-20 20:46 CST
排查并修复 App Store 评论采集残留异常,同时确认后台详情页与评论查询页的 App Store 评论展示状态。
改动内容
改动预览 3/5
  • 修改 `app_store_scrap/comment_collector/comment_collector.py`,增强 Apple RSS 评论解析容错:跳过非 dict 的 `link` 项和异常 review entry,兼容标量字段,解析失败时输出更可定位的 warning 日志。
  • 修改 `google_play_scrap/comment_collector/comment_collector.py`,将原先的 `print("Failed to parse review item ...")` 改为结构化 warning 日志,并兼容非 dict 的 `replyContent` 与 `userMetadata`,避免把字符串当作 dict 访问。
  • 修改 `app_store_scrap/tests/test_comment_collector.py`,补充“非 dict link + 标量 review 字段”容错测试。
展开完整改动
  • 修改 `app_store_scrap/comment_collector/comment_collector.py`,增强 Apple RSS 评论解析容错:跳过非 dict 的 `link` 项和异常 review entry,兼容标量字段,解析失败时输出更可定位的 warning 日志。
  • 修改 `google_play_scrap/comment_collector/comment_collector.py`,将原先的 `print("Failed to parse review item ...")` 改为结构化 warning 日志,并兼容非 dict 的 `replyContent` 与 `userMetadata`,避免把字符串当作 dict 访问。
  • 修改 `app_store_scrap/tests/test_comment_collector.py`,补充“非 dict link + 标量 review 字段”容错测试。
  • 新增 `google_play_scrap/tests/test_google_play_comment_collector.py`,补充“非 dict replyContent / 非 dict 输入”容错测试。
  • 执行一轮全量 `AppStoreCommentWorkflow.run_once()`,将 App Store 评论数据补回数据库。
验证结果
验证预览 2/18
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_comment_collector.py google_play_scrap/tests/test_google_play_comment_collector.py app_store_scrap/tests/test_workflow.py google_play_scrap/tests/test_comment_workflow.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_comment_collector.py google_play_scrap/tests/test_google_play_comment_collector.py app_store_scrap/tests/test_workflow.py google_play_scrap/tests/test_comment_workflow.py`
  • Result: PASS
  • Detail: `12 passed in 0.40s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `60 passed in 1.02s`
  • `./.venv/bin/python -m py_compile app_store_scrap/comment_collector/comment_collector.py google_play_scrap/comment_collector/comment_collector.py`
  • Result: PASS
  • Detail: 评论 collector 模块编译通过
  • `./.venv/bin/python - <<'PY' ... AppStoreCommentWorkflow().run_once() ...`
  • Result: PASS
  • Detail: `studio_count=10`、`app_count=112`、`comment_collected_count=5354`、`comment_failed_count=0`
  • `curl -sS 'http://127.0.0.1:8010/apps/app_store/6498883328?lang=zh-CN'`
  • Result: PASS
  • Detail: 详情页已显示 `评论=130`,最新评论区可见 `Great game`
  • `curl -sS 'http://127.0.0.1:8010/comments?lang=zh-CN&platform=app_store&q=Tile%20Explorer'`
  • Result: PASS
  • Detail: 评论查询页已命中 `Tile Explorer: Tiles Clear!`,分页统计 `共 130 条`
发现与结论
  • 之前记录里的 `Failed to parse review item: 'str' object has no attribute 'get'` 实际来源于 Google Play 评论 collector,不是 App Store;这次已经一起修掉,避免后续排查方向被带偏。
  • App Store 详情页“没有评论”并不都代表采集异常。像 `Toycraft! (6714472804)`、`Ducky Match (6756010811)` 这类应用,Apple RSS 在 `us/cn/gb` 抓取结果都为 `0`,当前属于源站无可见评论,不是展示层问题。
  • 本轮全量评论补采后,App Store 评论总量从 `1169` 增长到 `6523`,说明此前主要问题是数据未全量补齐,而不是详情页渲染失败。
下一步
  • 如果继续推进,下一步建议给 App Store 评论任务补“增量运行 + 最近成功时间”监控,避免后续再靠页面现象反推采集状态。
v0.24.0 2026-03-20 20:30 CST
为评论查询模块新增评分筛选,支持“5分 / 非5分 / 全部”。
改动内容
改动预览 3/5
  • 修改 `orchestrator/admin_service/queries.py`,在 `list_comments_page()` 中新增 `rating_filter` 参数,支持 `five` 与 `non_five` 两种评分过滤条件。
  • 修改 `orchestrator/admin_service/app.py`,为 `/comments` 与 `/api/comments` 增加 `rating_filter` 查询参数,并透传到查询层。
  • 修改 `orchestrator/admin_service/templates/comments.html`,新增评分筛选下拉框,支持 `全部 / 5分 / 非5分`,并保留当前选中状态。
展开完整改动
  • 修改 `orchestrator/admin_service/queries.py`,在 `list_comments_page()` 中新增 `rating_filter` 参数,支持 `five` 与 `non_five` 两种评分过滤条件。
  • 修改 `orchestrator/admin_service/app.py`,为 `/comments` 与 `/api/comments` 增加 `rating_filter` 查询参数,并透传到查询层。
  • 修改 `orchestrator/admin_service/templates/comments.html`,新增评分筛选下拉框,支持 `全部 / 5分 / 非5分`,并保留当前选中状态。
  • 修改 `orchestrator/admin_service/presentation.py`,补充评分筛选相关中英文文案。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充评论分页页面的评分筛选文案断言,以及 API 级 5 分 / 非 5 分回归测试。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `6 passed in 0.67s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、查询服务与展示层模块编译通过
  • `curl -sS 'http://127.0.0.1:8010/api/comments?page=1&page_size=5&rating_filter=five'`
  • Result: PASS
  • Detail: 返回结果已全部为 `rating=5`,当前统计 `total=5451`
  • `curl -sS 'http://127.0.0.1:8010/api/comments?page=1&page_size=5&rating_filter=non_five'`
  • Result: PASS
  • Detail: 返回结果已全部为 `rating!=5`,当前统计 `total=1204`
  • `curl -sS 'http://127.0.0.1:8010/comments?lang=zh-CN&page=1&page_size=20&rating_filter=non_five'`
  • Result: PASS
  • Detail: 页面已显示 `5分 / 非5分`,且 `非5分` 选项保持选中;分页统计显示 `共 1204 条 / 第 1 / 61 页`
发现与结论
  • 评分筛选是直接落在数据库查询层的,不是前端二次过滤,因此分页总数和 API 返回值都会同步收敛。
  • 本地服务需要重启后才会加载这轮新增参数;本轮已经完成重启并验证。
下一步
  • 继续修复 App Store 评论采集残留异常,确保应用详情页和评论查询页都能看到完整的 App Store 评论数据。
v0.23.0 2026-03-20 20:22 CST
在“App 监控”下新增评论查询模块,支持分页以及按应用名称、工作室、包名 / Bundle ID 模糊查询。
改动内容
改动预览 3/7
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_comments_page()`,基于 `comments` 与 `apps` 联表分页查询评论,并支持 `app_name / studio_name / package_name / bundle_id / app_id` 模糊搜索。
  • 修改 `orchestrator/admin_service/app.py`,新增页面路由 `/comments` 与 API 路由 `/api/comments`,支持 `platform / q / page / page_size` 查询参数。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“评论查询”“包名 / Bundle ID”等中英文文案。
展开完整改动
  • 修改 `orchestrator/admin_service/queries.py`,新增 `list_comments_page()`,基于 `comments` 与 `apps` 联表分页查询评论,并支持 `app_name / studio_name / package_name / bundle_id / app_id` 模糊搜索。
  • 修改 `orchestrator/admin_service/app.py`,新增页面路由 `/comments` 与 API 路由 `/api/comments`,支持 `platform / q / page / page_size` 查询参数。
  • 修改 `orchestrator/admin_service/presentation.py`,补充“评论查询”“包名 / Bundle ID”等中英文文案。
  • 修改 `orchestrator/admin_service/templates/base.html`,在“App 监控”分组下新增评论查询页签。
  • 新增 `orchestrator/admin_service/templates/comments.html`,实现评论查询页的筛选表单、评论表格、应用图标与跳转、分页控件。
  • 修改 `orchestrator/admin_service/templates/apps.html` 与现有分页能力,确保应用列表与评论列表统一使用分页查询逻辑。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充“应用列表全部平台分页”与“评论查询分页和模糊搜索”的回归测试。
验证结果
验证预览 2/12
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `6 passed in 0.73s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/queries.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 后台入口、查询服务与展示层模块编译通过
  • `curl -sS 'http://127.0.0.1:8010/comments?lang=zh-CN&page=1&page_size=20'`
  • Result: PASS
  • Detail: 页面已显示 `评论查询 / 共 6655 条 / 第 1 / 333 页`,并能展示评论列表与应用跳转入口
  • `curl -sS 'http://127.0.0.1:8010/api/comments?page=1&page_size=5&q=Tile%20Explorer'`
  • Result: PASS
  • Detail: API 返回 `total=122`、`total_pages=25`,并命中 `Tile Explorer - Triple Match / oakever-games / com.oakever.tiletrip`
发现与结论
  • 之前“应用列表平台=全部”看起来像没显示全,根因是列表页固定 `limit=100`;现在已经统一改成真正的分页查询。
  • 当前评论查询页的数据来源是 `comments` 表,因此对于 App Store 某些“没有评论展示”的应用,若底层采集未入库,这个页面也不会凭空展示;这部分仍需继续修 App Store 评论采集链路。
下一步
  • 继续修复 App Store 评论解析残留异常,并补应用详情页与评论查询页之间的联动验证。
v0.22.0 2026-03-20 20:10 CST
修复应用详情页在浅色模式下的指标卡可读性问题。
改动内容
改动预览 1/1
  • 修改 `orchestrator/admin_service/static/admin.css`,将 `.metric` 的颜色从写死的浅白色改为跟随主题变量 `var(--title)`,避免浅色模式下“平台 / 版本 / 评论 / 截图数 / 最近评论”这类卡片数值几乎不可见。
验证结果
验证预览 2/3
  • `curl -sS http://127.0.0.1:8010/static/admin.css`
  • Result: PASS
展开完整验证
  • `curl -sS http://127.0.0.1:8010/static/admin.css`
  • Result: PASS
  • Detail: `.metric` 已使用 `color: var(--title);`,不再使用固定浅白色
发现与结论
  • 这个问题是浅色主题适配遗漏,不是详情数据本身异常。
  • 当前本地服务已重启,刷新页面后即可看到修复结果。
下一步
  • 继续排查 App Store 评论解析残留异常,确认为什么部分应用详情页仍没有评论数据。
v0.21.0 2026-03-20 20:02 CST
优化“返回上一层”交互,并新增手动切换白天/黑夜模式的头部按钮。
改动内容
改动预览 3/4
  • 修改 `orchestrator/admin_service/templates/base.html`,将返回按钮升级为“浏览器历史返回优先、站内 fallback 兜底”的交互,并增加左箭头图标;同时新增头部主题切换按钮和对应前端脚本。
  • 修改 `orchestrator/admin_service/presentation.py`,补充主题按钮中英文文案。
  • 修改 `orchestrator/admin_service/static/admin.css`,新增 `html[data-theme="dark"]` 手动深色覆盖变量、返回按钮箭头样式、主题按钮样式;保留 `prefers-color-scheme: dark` 系统跟随逻辑。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/base.html`,将返回按钮升级为“浏览器历史返回优先、站内 fallback 兜底”的交互,并增加左箭头图标;同时新增头部主题切换按钮和对应前端脚本。
  • 修改 `orchestrator/admin_service/presentation.py`,补充主题按钮中英文文案。
  • 修改 `orchestrator/admin_service/static/admin.css`,新增 `html[data-theme="dark"]` 手动深色覆盖变量、返回按钮箭头样式、主题按钮样式;保留 `prefers-color-scheme: dark` 系统跟随逻辑。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充 `data-back-button` 与 `data-theme-toggle` 的模板回归断言。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `4 passed in 0.54s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 关键后台入口与展示层模块编译通过
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6714472804?lang=en-US`
  • Result: PASS
  • Detail: 页面已包含 `data-back-button`、`data-theme-toggle`、`Back`、`App Detail`、`Screenshots`
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6714472804?lang=zh-CN`
  • Result: PASS
  • Detail: 页面已包含 `data-back-button`、`data-theme-toggle`、`返回上一层`、`应用详情`、`应用截图`
  • `curl -sS http://127.0.0.1:8010/static/admin.css`
  • Result: PASS
  • Detail: 样式表已包含 `html[data-theme="dark"]`、`prefers-color-scheme: dark`、`theme-toggle`、`button-back-icon`
发现与结论
  • 返回按钮现在更接近用户直觉:同标签页内优先回退历史记录,避免丢失筛选条件;如果没有可用站内历史,再回默认上级页面。
  • 主题切换按钮不会破坏“默认跟随系统”逻辑,只有用户点击后才会将亮/暗模式持久化到本地。
下一步
  • 如果继续推进,下一步应单独修复 App Store 评论解析残留异常,并补对应回归测试。
v0.20.0 2026-03-20 19:49 CST
调整后台头部交互与主题适配,补齐页面内部中英文切换,并移除页面中的数据库路径展示。
改动内容
改动预览 3/6
  • 修改 `orchestrator/admin_service/templates/base.html`,移除数据库路径展示;将“返回上一层”按钮移动到页面左上区域,放在标题前方;增加 `color-scheme` 元信息。
  • 修改 `orchestrator/admin_service/presentation.py`,补充页面主体所需的中英文文案,并新增 `job_label`、`trigger_label`、`online_status_label` 等展示 helper。
  • 修改 `orchestrator/admin_service/app.py`,把新增展示 helper 注入到模板环境。
展开完整改动
  • 修改 `orchestrator/admin_service/templates/base.html`,移除数据库路径展示;将“返回上一层”按钮移动到页面左上区域,放在标题前方;增加 `color-scheme` 元信息。
  • 修改 `orchestrator/admin_service/presentation.py`,补充页面主体所需的中英文文案,并新增 `job_label`、`trigger_label`、`online_status_label` 等展示 helper。
  • 修改 `orchestrator/admin_service/app.py`,把新增展示 helper 注入到模板环境。
  • 修改 `orchestrator/admin_service/templates/dashboard.html`、`apps.html`、`jobs.html`、`studios.html`、`app_detail.html`,将主体区块、表头、按钮、状态、详情字段和空状态文案全部改为按 `lang` 渲染。
  • 修改 `orchestrator/admin_service/static/admin.css`,重构主题变量为“默认浅色 + `prefers-color-scheme: dark` 自动深色”,并新增标题区左上返回按钮布局。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充“数据库路径不展示”与英文正文跟随切换的回归断言。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `4 passed in 0.48s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 关键后台入口与展示层模块编译通过
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6714472804?lang=zh-CN`
  • Result: PASS
  • Detail: 页面显示 `返回上一层 / 应用截图 / 截图数 / 工作室 / 最新评论`,且不再显示数据库路径
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6714472804?lang=en-US`
  • Result: PASS
  • Detail: 页面主体已随语言切换为 `Back / Description / Screenshots / Current App Info / Recent Comments`
  • `curl -sS http://127.0.0.1:8010/static/admin.css`
  • Result: PASS
  • Detail: 样式表已包含 `color-scheme: light`、`prefers-color-scheme: dark`、`page-header-main`、`button-back`
发现与结论
  • 中英文切换之前只覆盖了导航和标题,这次已经把页面主体补齐。
  • 样式现在会跟随系统白天/黑夜模式自动切换;当前视觉方向保留了极客风,但不再强制所有用户都看深色。
  • 在重启后台时观察到 `app_store_comments` 任务仍在输出 `Failed to parse review item: 'str' object has no attribute 'get'`,这是评论链路的残留问题,不是本轮 UI 改动引入的。
下一步
  • 如果继续推进,下一步应单独修复 App Store 评论解析残留异常,并补对应回归测试。
v0.19.0 2026-03-20 19:34 CST
修复 App Store 详情页截图采集,并将后台整体色调调整为更偏极客风的深色主题。
改动内容
改动预览 3/4
  • 修改 `app_store_scrap/collector/app_detail_collector.py`,将截图提取逻辑从 `img[src]` 切换为 `picture > source[srcset]`,优先提取新版 App Store 页面中的真实截图 URL,并统一过滤 `/assets/artwork/1x1.gif` 占位图。
  • 修改 `app_store_scrap/tests/test_app_detail_collector.py`,新增 `picture/srcset` 截图解析回归测试。
  • 修改 `orchestrator/admin_service/static/admin.css`,重做后台色调,切换为深色石墨背景、电绿/青蓝点缀、网格化背景和更强的卡片层次感。
展开完整改动
  • 修改 `app_store_scrap/collector/app_detail_collector.py`,将截图提取逻辑从 `img[src]` 切换为 `picture > source[srcset]`,优先提取新版 App Store 页面中的真实截图 URL,并统一过滤 `/assets/artwork/1x1.gif` 占位图。
  • 修改 `app_store_scrap/tests/test_app_detail_collector.py`,新增 `picture/srcset` 截图解析回归测试。
  • 修改 `orchestrator/admin_service/static/admin.css`,重做后台色调,切换为深色石墨背景、电绿/青蓝点缀、网格化背景和更强的卡片层次感。
  • 执行一次目标回填脚本,将 `app_store / 6714472804` 的截图字段从占位值更新为 5 张真实截图,并补写最新 snapshot / change_event。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_app_detail_collector.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_app_detail_collector.py`
  • Result: PASS
  • Detail: `5 passed in 0.08s`
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_app_detail_collector.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `9 passed in 0.38s`
  • `./.venv/bin/python - <<'PY' ... collector.collect('https://apps.apple.com/us/app/toycraft/id6714472804') ... PY`
  • Result: PASS
  • Detail: 实抓返回 `5` 张真实截图 URL
  • `./.venv/bin/python - <<'PY' ... targeted backfill for app_store/6714472804 ... PY`
  • Result: PASS
  • Detail: 成功回填,`screenshots=5`,生成 `updated` 变更事件
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6714472804`
  • Result: PASS
  • Detail: 页面已显示 `截图数=5`、`应用截图` 区块、`截图 1` 缩略图,以及新的深色极客风样式
发现与结论
  • 这次截图问题的根因不是 Apple lookup API,而是新版 App Store Web 页面把真实媒体资源放在了 `source[srcset]`,而 `img[src]` 只是一张占位 `1x1.gif`。
  • `6714472804` 现在已经完成修复;类似历史占位截图数据如果还存在,需要再跑一轮 App Store 历史回填才能整体刷新。
下一步
  • 如果继续推进,下一步建议批量扫描 `app_store` 中仍带占位媒体的应用,并跑一轮全量或增量回填。
v0.18.0 2026-03-20 19:18 CST
优化应用详情页的截图展示,并为后台二级页面统一补上“返回上一层”按钮。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/templates/app_detail.html`,将截图区块前置到详情主区域,展示截图数量、缩略图卡片和原始截图链接;同时增加从最新快照中兜底读取截图列表的逻辑。
  • 修改 `orchestrator/admin_service/static/admin.css`,补充截图卡片、区块标题和空状态样式。
  • 修改 `orchestrator/admin_service/templates/base.html`、`orchestrator/admin_service/app.py`、`orchestrator/admin_service/presentation.py`,统一为二级页面增加“返回上一层”按钮,优先回来源页,没有来源页则回默认上级页面。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `4 passed in 0.60s`
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6498883328`
  • Result: PASS
  • Detail: 页面已显示 `截图数=16`,并渲染 `应用截图` 区块和多张截图缩略图
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6714472804`
  • Result: PASS
  • Detail: 页面已显示 `截图数=0`,并给出“当前应用还没有可展示的截图数据”的空状态提示
  • `curl -sS http://127.0.0.1:8010/apps?platform=app_store`
  • Result: PASS
  • Detail: 页面头部已显示“返回上一层”按钮
  • `curl -sS http://127.0.0.1:8010/jobs`
  • Result: PASS
  • Detail: 页面头部已显示“返回上一层”按钮
发现与结论
  • `6498883328` 这类已完成回填的 App Store 应用,截图展示已经正常。
  • `6714472804` 当前数据库里仍只有占位截图 `/assets/artwork/1x1.gif`,Apple lookup API 也未返回真实截图,因此详情页只能展示空状态;这是数据源问题,不是页面渲染问题。
下一步
  • 如果要让 `6714472804` 这类应用也显示真实截图,需要继续排查 App Store HTML 页面里的截图提取逻辑,并补一轮数据回填。
v0.17.0 2026-03-20 19:05 CST
调整后台信息架构和导航层级,把站点名称切换为“信息监控后台”,并将现有功能重新归类到“任务处理 / App 监控”两组导航下。
改动内容
改动预览 3/5
  • 修改 `orchestrator/admin_service/presentation.py`,更新站点名称、多语言页面标题和导航分组文案。
  • 修改 `orchestrator/admin_service/templates/base.html`,将侧边栏重组为“任务处理”与“App 监控”两段,并补当前页高亮。
  • 修改 `orchestrator/admin_service/static/admin.css`,新增导航分组标题和激活态样式。
展开完整改动
  • 修改 `orchestrator/admin_service/presentation.py`,更新站点名称、多语言页面标题和导航分组文案。
  • 修改 `orchestrator/admin_service/templates/base.html`,将侧边栏重组为“任务处理”与“App 监控”两段,并补当前页高亮。
  • 修改 `orchestrator/admin_service/static/admin.css`,新增导航分组标题和激活态样式。
  • 修改 `orchestrator/admin_service/app.py`,同步 FastAPI 应用标题为 `Info Monitor Console`。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充新站点名和导航分组回归断言。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `4 passed in 0.64s`
  • `./.venv/bin/python -m py_compile orchestrator/admin_service/app.py orchestrator/admin_service/presentation.py`
  • Result: PASS
  • Detail: 关键后台入口和展示层模块编译通过
  • `curl -sS http://127.0.0.1:8010/`
  • Result: PASS
  • Detail: 页面标题和侧边栏已显示 `信息监控后台 / 任务处理 / App 监控 / 监控概览`
发现与结论
  • 这次调整只改了导航与文案层,不影响现有路由和页面访问地址。
  • 侧边栏现在按“任务处理”和“App 监控”分组,后续继续扩页面时信息架构会更稳定。
下一步
  • 如果继续做后台体验,优先把中英文切换补全到所有页面内部文案,避免目前只完成顶层导航和标题的双语切换。
v0.16.0 2026-03-20 18:42 CST
完善后台管理系统的核心可用性,补 studio 管理、job 执行记录、中文默认界面,以及应用详情页的可读性。
改动内容
改动预览 3/8
  • 修改 `common/storage/schema.sql`,新增 `job_runs` 表和索引。
  • 修改 `common/storage/repositories/studio_repository.py`,补充 studio 查询、删除、计数能力。
  • 修改 `google_play_scrap/workflow.py`、`app_store_scrap/workflow.py`、`app_store_scrap/backfill.py`,将 studio 加载策略调整为“数据库优先,空库时从 `config/studios.yaml` 初始化导入”,让后台管理页对后续 job 生效。
展开完整改动
  • 修改 `common/storage/schema.sql`,新增 `job_runs` 表和索引。
  • 修改 `common/storage/repositories/studio_repository.py`,补充 studio 查询、删除、计数能力。
  • 修改 `google_play_scrap/workflow.py`、`app_store_scrap/workflow.py`、`app_store_scrap/backfill.py`,将 studio 加载策略调整为“数据库优先,空库时从 `config/studios.yaml` 初始化导入”,让后台管理页对后续 job 生效。
  • 修改 `orchestrator/admin_service/queries.py`、`orchestrator/admin_service/app.py`、`orchestrator/admin_service/scheduler.py`,新增 studio CRUD API、job 执行记录落库、job 执行记录查询、页面手动触发入口。
  • 新增 `orchestrator/admin_service/presentation.py`,统一中文展示文案、字段名映射、结果摘要格式化。
  • 修改 `orchestrator/admin_service/templates/*.html` 与 `orchestrator/admin_service/static/admin.css`,完成中文默认界面、studio 管理表单、jobs 立即执行按钮、执行记录表格、应用 icon / 商店跳转 / 结构化详情 / 友好变更记录展示。
  • 修改 `orchestrator/tests/test_admin_service.py`,补充 studio CRUD API 与 job_runs 记录回归测试。
  • 修改 `README.md`、`docs/architecture.md`、`docs/doc_test_backlog.md`,同步后台最新能力和服务待办状态。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `4 passed in 0.49s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `53 passed in 0.76s`
  • `curl -sS http://127.0.0.1:8010/studios`
  • Result: PASS
  • Detail: 页面已切换为中文,能看到 studio 新增表单、编辑/删除按钮和列表筛选
  • `curl -sS http://127.0.0.1:8010/jobs`
  • Result: PASS
  • Detail: 页面已展示“立即执行”按钮和执行记录表格
  • `curl -sS http://127.0.0.1:8010/apps/app_store/6498883328`
  • Result: PASS
  • Detail: 应用详情页已展示 icon、应用名、app_id、商店跳转、结构化应用信息和友好变更记录
发现与结论
  • 后台现在已经具备最基本的运营可用性,不再只是只读 JSON 巡检页。
  • Studio 管理现在以数据库为主,`config/studios.yaml` 只在对应平台空库时用于一次性初始化导入,避免后台删改被 job 自动覆盖。
  • `job_runs` 已经打通,后续如果要做执行统计、失败告警或历史检索,不需要再回头补底层表结构。
下一步
  • 如果继续做后台体验,优先补分页、筛选增强和批量操作。
v0.15.0 2026-03-20 18:06 CST
实现可部署的初版后台管理服务,把页面查询、API 查询和定时 job 调度统一进一个进程,并补齐文档与验证记录。
改动内容
改动预览 3/5
  • 修改 `pyproject.toml`,新增 `fastapi`、`uvicorn`、`jinja2` 依赖。
  • 新增 `orchestrator/admin_service/`,实现 SQLite 查询服务、FastAPI 页面/API、Jinja2 模板、静态样式和 APScheduler 调度器。
  • 新增 `config/scheduler.yaml`,集中配置后台服务内嵌调度的时区和 job 间隔。
展开完整改动
  • 修改 `pyproject.toml`,新增 `fastapi`、`uvicorn`、`jinja2` 依赖。
  • 新增 `orchestrator/admin_service/`,实现 SQLite 查询服务、FastAPI 页面/API、Jinja2 模板、静态样式和 APScheduler 调度器。
  • 新增 `config/scheduler.yaml`,集中配置后台服务内嵌调度的时区和 job 间隔。
  • 新增 `orchestrator/tests/test_admin_service.py`,覆盖后台页面、查询 API 和手动触发 job API。
  • 修改 `README.md`、`docs/architecture.md`、`docs/doc_test_backlog.md`,补充后台服务运行方式、页面/API 结构、调度说明和后续服务待办。
验证结果
验证预览 2/15
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `2 passed in 1.08s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `51 passed in 0.85s`
  • `curl -sS http://127.0.0.1:8010/`
  • Result: PASS
  • Detail: 首页可访问,页面显示当前 DB 路径与 `Studios / Apps / Comments / Change Events` 概览卡片
  • `curl -sS http://127.0.0.1:8010/api/jobs`
  • Result: PASS
  • Detail: 成功返回 5 个调度任务状态;验证时 `app_store_sync` 正在执行,其余任务已正确展示 `enabled / interval / next_run_time`
  • `curl -sS http://127.0.0.1:8010/api/overview`
  • Result: PASS
  • Detail: 后台服务存活,返回当前真实库统计;当时 `app_store apps=34`、`google_play apps=96`
发现与结论
  • 初版后台已经能直接映射当前数据模型,足够支撑日常巡检、按 app 查看历史,以及从页面/API 侧观察调度任务状态。
  • 当前服务仍是“单进程 FastAPI + APScheduler + SQLite”的轻量部署形态,适合先跑起来联调;后续若接公网或多人使用,优先补鉴权和任务执行历史。
下一步
  • 如果要进入部署阶段,下一步优先补服务启动方式(systemd / Docker)和访问鉴权。
v0.14.0 2026-03-20 17:38 CST
收口 Google Play 的 `studios.yaml` 配置,并新增按 `app_id` 查看 `apps / snapshots / change_events` 的本地检查工具。
改动内容
改动预览 3/4
  • 修改 `config/studios.yaml`,将 Google Play 的 `developer_id` 场景收口为最小必填字段;仅保留 `developer_name` 场景真正需要的 `source_type` / `source_value`。
  • 新增 `scripts/show_app_history.py`,支持按 `app_id` 和可选 `platform` 输出当前 `apps` 记录、最近 snapshots 和最近 change events,支持 `--json`。
  • 新增 `common/tests/test_show_app_history.py`,覆盖命中 app 和未命中 app 两种脚本输出路径。
展开完整改动
  • 修改 `config/studios.yaml`,将 Google Play 的 `developer_id` 场景收口为最小必填字段;仅保留 `developer_name` 场景真正需要的 `source_type` / `source_value`。
  • 新增 `scripts/show_app_history.py`,支持按 `app_id` 和可选 `platform` 输出当前 `apps` 记录、最近 snapshots 和最近 change events,支持 `--json`。
  • 新增 `common/tests/test_show_app_history.py`,覆盖命中 app 和未命中 app 两种脚本输出路径。
  • 修改 `README.md`,补充 Google Play / App Store 的配置约定和 `show_app_history.py` 用法。
验证结果
验证预览 2/12
  • `./.venv/bin/python scripts/validate_config.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python scripts/validate_config.py`
  • Result: PASS
  • Detail: `配置验证通过,未发现问题`
  • `./.venv/bin/pytest -q common/tests/test_show_app_history.py google_play_scrap/tests/test_studio_collector.py common/tests/test_config_loader.py`
  • Result: PASS
  • Detail: `6 passed in 0.37s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `49 passed in 0.53s`
  • `./.venv/bin/python scripts/show_app_history.py --app-id 6498883328 --platform app_store --json`
  • Result: PASS
  • Detail: 成功输出最新 `apps` 记录、最近 snapshots 和 change events
发现与结论
  • Google Play 的 `developer_id` 场景里,`studio_url`、`aliases`、`country`、`source_value` 都不是当前链路必需字段,已从配置样例中移除。
  • 新增检查工具后,可以直接看到 `6498883328` 当前已回填为真实值,例如 `bundle_id=com.oakever.tiletrip`、`version=1.76.0`、`sub_category=Casual`,并能同时看到最近一次回填生成的 `updated` 事件。
下一步
  • 如果你要继续看具体数据,可以直接用 `scripts/show_app_history.py` 检查任意 `app_id`。
  • 如果后续要清理数据库里的早期噪声 `change_events`,可以单独做一轮历史事件整理脚本。
v0.13.0 2026-03-20 17:28 CST
执行一次真实 App Store 历史数据回填,并清理 `app.yaml` / `studios.yaml` 里与当前 Apple API 方案不一致的冗余配置。
改动内容
改动预览 3/8
  • 执行 `./.venv/bin/python -m orchestrator.jobs.app_store_backfill_job`,对当前库中的唯一 App Store 候选记录完成真实回填。
  • 修改 `common/utils/config_loader.py` 与 `common/utils/__init__.py`,新增 `resolve_collector_config()`,支持 `collector` 的全局默认值与平台级覆盖合并。
  • 修改 `google_play_scrap/workflow.py` 与 `google_play_scrap/comment_workflow.py`,改为读取 `collector.platforms.google_play` 配置。
展开完整改动
  • 执行 `./.venv/bin/python -m orchestrator.jobs.app_store_backfill_job`,对当前库中的唯一 App Store 候选记录完成真实回填。
  • 修改 `common/utils/config_loader.py` 与 `common/utils/__init__.py`,新增 `resolve_collector_config()`,支持 `collector` 的全局默认值与平台级覆盖合并。
  • 修改 `google_play_scrap/workflow.py` 与 `google_play_scrap/comment_workflow.py`,改为读取 `collector.platforms.google_play` 配置。
  • 修改 `app_store_scrap/workflow.py` 与 `app_store_scrap/backfill.py`,改为读取 `collector.platforms.app_store` 配置,并将 App Store 的默认 `source_type` 调整为 `developer_name`。
  • 修改 `config/app.yaml`,把 Google Play 专用采集开关收口到 `collector.platforms.google_play`,App Store 只保留 `country` 这一平台级配置。
  • 修改 `config/studios.yaml`,清理 App Store 段的 `developer_name`、显式 `source_type: developer_name` 和过时的手工 `apps` 注释。
  • 新增 `common/tests/test_config_loader.py`,覆盖平台级 collector 配置合并规则。
  • 修改 `README.md` 与 `docs/doc_test_backlog.md`,补充 App Store 配置约定并勾掉配置样例说明项。
验证结果
验证预览 2/12
  • `./.venv/bin/python -m orchestrator.jobs.app_store_backfill_job`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python -m orchestrator.jobs.app_store_backfill_job`
  • Result: PASS
  • Detail: `candidate_app_count=1, backfilled_app_count=1, snapshot_count=1, change_event_count=1, failed_app_count=0`
  • `./.venv/bin/pytest -q common/tests/test_config_loader.py app_store_scrap/tests/test_backfill.py app_store_scrap/tests/test_workflow.py orchestrator/tests/test_app_store_backfill_job.py orchestrator/tests/test_app_store_smoke_test.py orchestrator/tests/test_google_play_workflow.py google_play_scrap/tests/test_comment_workflow.py`
  • Result: PASS
  • Detail: `15 passed in 0.69s`
  • `./.venv/bin/python scripts/validate_config.py`
  • Result: PASS
  • Detail: `配置验证通过,未发现问题`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `47 passed in 0.46s`
发现与结论
  • 当前本地库里的 App Store 历史脏数据已完成第一次真实回填,`oakever-games / 6498883328` 这条记录已经重新抓取并生成了更新快照与变更事件。
  • `config/app.yaml` 原先把 Google Play 专用采集开关和 App Store 共用配置混在一起,容易误导;现在 App Store 只保留当前 Apple API 方案真实使用的配置。
  • `config/studios.yaml` 的 App Store 段里,`developer_name` 和显式 `source_type: developer_name` 都是冗余项,已经收口成最小必填集合。
下一步
  • 如需确认回填结果,可直接检查 `apps` / `app_snapshots` / `change_events` 里 `6498883328` 的最新记录。
  • 如果后续要扩 App Store 多国家区服采集,再考虑让 `studios.yaml` 的 `country` 真正进入 App Store collector 逻辑。
v0.12.0 2026-03-20 17:21 CST
对当前本地库执行 App Store 历史数据回填 `dry-run`,确认候选记录范围。
改动内容
改动预览 1/1
  • 无代码改动。
验证结果
验证预览 2/3
  • `./.venv/bin/python -m orchestrator.jobs.app_store_backfill_job --dry-run`
  • Result: PASS
展开完整验证
  • `./.venv/bin/python -m orchestrator.jobs.app_store_backfill_job --dry-run`
  • Result: PASS
  • Detail: `studio_count=10, scanned_app_count=1, candidate_app_count=1, failed_app_count=0`
发现与结论
  • 当前本地库里仅存在 1 条已入库 App Store app 记录,且该记录被判定为候选回填项。
  • 候选记录属于 `oakever-games / 6498883328 / Tile Explorer: Tiles Clear!`,与前面用户给出的缺字段样例一致。
下一步
  • 如果你确认,我下一步可以直接执行一次真实回填:`./.venv/bin/python -m orchestrator.jobs.app_store_backfill_job`
v0.11.0 2026-03-20 17:20 CST
实现 App Store 历史数据回填 workflow / job,默认只处理已有 `apps` 记录中的缺字段和占位媒体 URL。
改动内容
改动预览 3/6
  • 新增 `app_store_scrap/backfill.py`,实现 App Store 历史数据回填 workflow:按 studio 遍历已入库 app,识别缺字段/占位媒体候选记录,抓取新详情后做非破坏性合并,并可选写入 `apps`、`app_snapshots`、`change_events`。
  • 新增 `orchestrator/jobs/app_store_backfill_job.py`,提供 `--dry-run`、`--all-apps`、`--skip-snapshots`、`--skip-change-events` 等 CLI 参数。
  • 新增 `app_store_scrap/tests/test_backfill.py`,覆盖“只回填不完整记录”和“详情不完整时保留已有有效字段”的回归场景。
展开完整改动
  • 新增 `app_store_scrap/backfill.py`,实现 App Store 历史数据回填 workflow:按 studio 遍历已入库 app,识别缺字段/占位媒体候选记录,抓取新详情后做非破坏性合并,并可选写入 `apps`、`app_snapshots`、`change_events`。
  • 新增 `orchestrator/jobs/app_store_backfill_job.py`,提供 `--dry-run`、`--all-apps`、`--skip-snapshots`、`--skip-change-events` 等 CLI 参数。
  • 新增 `app_store_scrap/tests/test_backfill.py`,覆盖“只回填不完整记录”和“详情不完整时保留已有有效字段”的回归场景。
  • 新增 `orchestrator/tests/test_app_store_backfill_job.py`,覆盖回填 job 的 CLI 参数透传。
  • 修改 `README.md` 与 `docs/architecture.md`,补充 App Store 历史数据回填的使用方式与架构位置。
  • 修改 `docs/doc_test_backlog.md`,将“App Store 历史数据回填”项标记为已实现。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_backfill.py orchestrator/tests/test_app_store_backfill_job.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_backfill.py orchestrator/tests/test_app_store_backfill_job.py`
  • Result: PASS
  • Detail: `3 passed in 0.24s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `45 passed in 0.40s`
发现与结论
  • 回填阶段如果直接用新采集结果覆盖旧记录,容易把当前源里暂时缺失的字段写成空值;因此已改为“新值覆盖旧值,缺失值保留旧值”,避免二次数据损伤。
  • 占位媒体 URL 的识别需要精确到 `example.com` 主机和 `1x1.gif` 路径,不能用宽泛的子串匹配,否则会误伤正常 CDN 地址。
  • 当前回填 job 默认不会发送通知,且默认只处理可疑记录,适合先 `--dry-run` 评估范围,再决定是否执行实库回填。
下一步
  • 补配置样例说明,明确哪些字段必填、哪些字段当前仅部分消费。
  • 如果你确认,我下一轮就可以先跑一次 `app_store_backfill_job --dry-run`,统计当前库里 App Store 候选回填记录数量。
v0.10.0 2026-03-20 17:06 CST
补 notifier / 结构化日志测试,并统一 report notifier 的返回值结构。
改动内容
改动预览 3/4
  • 修改 `common/notifier/report_notifier.py`,在有事件/无事件两种路径下统一返回 `sent` 字段,便于 workflow 和日志侧稳定消费。
  • 新增 `common/tests/test_notifiers.py`,覆盖 `ReportNotifier` 的无事件返回值、Markdown 报告写入,以及 `FeishuNotifier` 的无事件跳过和按 `change_type` 分组发卡行为。
  • 新增 `common/tests/test_log_infra.py`,覆盖 `structured_log.log_event()` 的 JSON 输出,以及 `scripts/log_stats.py` 对带时间戳前缀日志和混合输入文件的兼容解析。
展开完整改动
  • 修改 `common/notifier/report_notifier.py`,在有事件/无事件两种路径下统一返回 `sent` 字段,便于 workflow 和日志侧稳定消费。
  • 新增 `common/tests/test_notifiers.py`,覆盖 `ReportNotifier` 的无事件返回值、Markdown 报告写入,以及 `FeishuNotifier` 的无事件跳过和按 `change_type` 分组发卡行为。
  • 新增 `common/tests/test_log_infra.py`,覆盖 `structured_log.log_event()` 的 JSON 输出,以及 `scripts/log_stats.py` 对带时间戳前缀日志和混合输入文件的兼容解析。
  • 修改 `docs/doc_test_backlog.md`,勾掉 notifier / 结构化日志测试项。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q common/tests/test_notifiers.py common/tests/test_log_infra.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q common/tests/test_notifiers.py common/tests/test_log_infra.py`
  • Result: PASS
  • Detail: `7 passed in 0.13s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `42 passed in 0.41s`
发现与结论
  • `ReportNotifier.notify()` 原先缺少 `sent` 字段,workflow 侧虽然能容忍,但会让通知状态记录不完整;现在已统一。
  • `scripts/log_stats.py` 已能兼容“纯 JSON 行”和“带 logging 前缀的 JSON 行”两类输入。
  • 当前 `log_stats.py` 的 app 详情抓取统计仍更偏向 Google Play 的 `detail_fetch` 事件模型,后续如果要做 App Store 精确统计,需要单独补口径。
下一步
  • 规划 App Store 历史数据回填,处理已有缺字段和占位媒体 URL 数据。
  • 补配置样例说明,明确哪些字段必填、哪些字段当前仅部分消费。
v0.9.0 2026-03-20 17:03 CST
补 App Store 主 workflow 成功路径测试与 smoke test 回归测试,并修正 smoke test 的变更检测时序。
改动内容
改动预览 3/4
  • 修改 `app_store_scrap/tests/test_workflow.py`,新增 `AppStoreWorkflow.run_once` 成功路径测试,覆盖正常落库、快照写入和 `created` 事件生成。
  • 修改 `orchestrator/jobs/app_store_smoke_test.py`,将 `previous_app` 查询与 `build_change_event()` 提前到 `upsert/save_snapshot` 之前,修复首次采集被误判为“无变化”的问题。
  • 新增 `orchestrator/tests/test_app_store_smoke_test.py`,用有状态假仓库锁定 smoke test 的首次采集 `created` 判定与写库时序。
展开完整改动
  • 修改 `app_store_scrap/tests/test_workflow.py`,新增 `AppStoreWorkflow.run_once` 成功路径测试,覆盖正常落库、快照写入和 `created` 事件生成。
  • 修改 `orchestrator/jobs/app_store_smoke_test.py`,将 `previous_app` 查询与 `build_change_event()` 提前到 `upsert/save_snapshot` 之前,修复首次采集被误判为“无变化”的问题。
  • 新增 `orchestrator/tests/test_app_store_smoke_test.py`,用有状态假仓库锁定 smoke test 的首次采集 `created` 判定与写库时序。
  • 修改 `docs/doc_test_backlog.md`,勾掉 App Store `workflow.run_once` 成功路径和 smoke test 回归测试项。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_workflow.py orchestrator/tests/test_app_store_smoke_test.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_workflow.py orchestrator/tests/test_app_store_smoke_test.py`
  • Result: PASS
  • Detail: `6 passed in 0.35s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `35 passed in 0.51s`
发现与结论
  • App Store smoke test 原先确实存在“先写后比”的时序问题,会吞掉首次采集应有的 `created` 事件。
  • App Store 主 workflow 现在已有成功路径和失败跳过路径双侧测试,主链路的核心行为已被回归保护。
下一步
  • 继续补 notifier / 结构化日志测试。
  • 规划 App Store 历史数据回填,处理已有缺字段和占位媒体 URL 数据。
v0.8.0 2026-03-20 16:57 CST
校对 App Store API 策略文档与实际入口命名,并复跑全量测试确认文档/实现一致。
改动内容
改动预览 1/1
  • 修改 `README.md`,补充 App Store 评论 job 入口、`comment_max_comments` 配置说明与历史数据需重同步的边界说明。
验证结果
验证预览 2/3
  • `./.venv/bin/pytest -q`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `33 passed in 0.58s`
发现与结论
  • `orchestrator.jobs.app_store_comment_job` 入口存在,README 中的评论抓取命令与当前工程实现一致。
  • 文档中关于 App Store 列表发现、详情抓取和评论最大数量配置的描述已与当前代码对齐。
下一步
  • 继续补 App Store `workflow.run_once` 成功路径测试与 smoke test 回归测试。
v0.7.0 2026-03-20 16:50 CST
将 Apple 官方 Search / Lookup API 的开发者搜 app 与单 app 详情策略接入 App Store 链路,并补全文档与字段映射。
改动内容
改动预览 3/7
  • 修改 `app_store_scrap/adapters/itunes_search_api.py`,按“开发者名搜索 `artistId` -> `lookup(entity=software)` 分页拉 app 列表 -> `lookup(id=<app_id>)` 拉单 app 详情”的方式重构 App Store API 适配层,并补充结构化字段映射。
  • 修改 `app_store_scrap/collector/studio_collector.py`,返回标准化后的 App Store app 列表字段,统一字段缺省值。
  • 修改 `app_store_scrap/collector/app_detail_collector.py`,改为优先使用 Lookup API 获取详情,再用 HTML 页面补充截图、头图等媒体字段。
展开完整改动
  • 修改 `app_store_scrap/adapters/itunes_search_api.py`,按“开发者名搜索 `artistId` -> `lookup(entity=software)` 分页拉 app 列表 -> `lookup(id=<app_id>)` 拉单 app 详情”的方式重构 App Store API 适配层,并补充结构化字段映射。
  • 修改 `app_store_scrap/collector/studio_collector.py`,返回标准化后的 App Store app 列表字段,统一字段缺省值。
  • 修改 `app_store_scrap/collector/app_detail_collector.py`,改为优先使用 Lookup API 获取详情,再用 HTML 页面补充截图、头图等媒体字段。
  • 修改 `app_store_scrap/parser/app_detail_parser.py`,补充 `package_name`、`release_date`、`short_description`、`sub_category`、`currency`、`languages`、`tags` 等字段映射。
  • 修改 `app_store_scrap/tests/test_app_store_studio_collector.py`、`app_store_scrap/tests/test_app_detail_collector.py`、`app_store_scrap/tests/test_app_detail_parser.py`,覆盖 API 列表发现、API/HTML 合并详情与新增字段映射。
  • 修改 `README.md` 与 `docs/architecture.md`,补齐 App Store 列表发现、详情抓取和字段补全策略说明。
  • 修改 `docs/doc_test_backlog.md`,勾掉 App Store 发现策略文档项,并新增“历史数据回填”待办。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q app_store_scrap/tests`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests`
  • Result: PASS
  • Detail: `13 passed in 0.31s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `33 passed in 0.39s`
发现与结论
  • App Store 不需要依赖手工维护 app 列表,当前实现已经接入 Apple 官方 Search / Lookup API,可按开发者维度发现 app 并获取结构化详情。
  • `apps` 表里已有的缺字段记录不会自动补齐,必须重新执行 App Store 同步或单独做历史回填。
  • 以 API 结构化结果为主、HTML 页面为补充后,App Store 的 `bundle_id`、`release_date`、`currency`、`languages`、`tags`、媒体字段补全能力明显高于纯页面解析。
下一步
  • 补 App Store `workflow.run_once` 成功路径测试与 smoke test 回归测试。
  • 规划一次 App Store 历史数据回填,重点修复已有 `icon_url/header_image_url/screenshot_urls` 异常占位数据和缺字段记录。
v0.6.0 2026-03-20 16:40 CST
完成评论抓取链路的双层防重,并把单次最大抓取量收口为可配置默认值加命令行覆盖。
改动内容
改动预览 3/6
  • 修改 `config/app.yaml`,新增 `collector.comment_max_comments` 默认配置。
  • 修改 `google_play_scrap/comment_collector/comment_collector.py` 与 `app_store_scrap/comment_collector/comment_collector.py`,在抓取阶段加入最大数量校验、已存在评论去重和单批次重复评论去重。
  • 修改 `google_play_scrap/comment_workflow.py` 与 `app_store_scrap/workflow.py` 中的评论工作流,在入库前再次按 `comment_id` 过滤,防止已有评论和单批重复评论进入 repository。
展开完整改动
  • 修改 `config/app.yaml`,新增 `collector.comment_max_comments` 默认配置。
  • 修改 `google_play_scrap/comment_collector/comment_collector.py` 与 `app_store_scrap/comment_collector/comment_collector.py`,在抓取阶段加入最大数量校验、已存在评论去重和单批次重复评论去重。
  • 修改 `google_play_scrap/comment_workflow.py` 与 `app_store_scrap/workflow.py` 中的评论工作流,在入库前再次按 `comment_id` 过滤,防止已有评论和单批重复评论进入 repository。
  • 新增 `google_play_scrap/tests/test_comment_workflow.py`,覆盖 Google Play 评论默认最大数量和入库去重。
  • 修改 `app_store_scrap/tests/test_workflow.py` 与 `app_store_scrap/tests/test_comment_collector.py`,覆盖 App Store 评论默认最大数量、抓取阶段去重和入库阶段去重。
  • 修改 `docs/doc_test_backlog.md`,同步评论测试项状态。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_comment_collector.py app_store_scrap/tests/test_workflow.py google_play_scrap/tests/test_comment_workflow.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests/test_comment_collector.py app_store_scrap/tests/test_workflow.py google_play_scrap/tests/test_comment_workflow.py`
  • Result: PASS
  • Detail: `8 passed in 0.37s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `32 passed in 0.45s`
发现与结论
  • 仅依赖 repository 的唯一键不够,抓取阶段和入库阶段都需要显式按 `comment_id` 去重,否则会有重复解析与无效 upsert。
  • 评论最大抓取量此前只靠 workflow 默认参数控制,已经收口为配置默认值 `collector.comment_max_comments`,CLI 仍可覆盖。
  • App Store 与 Google Play 评论链路现在在数量控制和防重策略上已对齐。
下一步
  • 补 App Store `workflow.run_once` 成功路径测试与 smoke test 回归测试。
  • 之后再补 notifier / 结构化日志测试,并把 `comment_max_comments` 配置说明写进文档。
v0.5.0 2026-03-20 16:21 CST
继续补 App Store collector/parser 测试与 Google Play 对称回归测试,并顺手提高 App Store studio 搜索可用性。
改动内容
改动预览 3/8
  • 修改 `google_play_scrap/workflow.py`,让 Google Play 主 workflow 在详情抓取失败后跳过落库。
  • 修改 `app_store_scrap/collector/studio_collector.py`,优先使用 `source_value` 解析开发者查询名,提升 App Store 搜索准确率。
  • 新增 `app_store_scrap/tests/test_comment_collector.py`,覆盖评论去重逻辑。
展开完整改动
  • 修改 `google_play_scrap/workflow.py`,让 Google Play 主 workflow 在详情抓取失败后跳过落库。
  • 修改 `app_store_scrap/collector/studio_collector.py`,优先使用 `source_value` 解析开发者查询名,提升 App Store 搜索准确率。
  • 新增 `app_store_scrap/tests/test_comment_collector.py`,覆盖评论去重逻辑。
  • 新增 `app_store_scrap/tests/test_app_detail_parser.py`,覆盖 App Store detail parser 字段映射与哈希生成。
  • 新增 `app_store_scrap/tests/test_app_store_studio_collector.py`,覆盖 App Store studio collector 查询名选择与基础映射。
  • 修改 `app_store_scrap/tests/test_app_detail_collector.py`,补充正常解析与请求失败测试。
  • 修改 `orchestrator/tests/test_google_play_workflow.py`,补充“详情失败不落库”回归测试。
  • 修改 `docs/doc_test_backlog.md`,同步本轮已完成项。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q app_store_scrap/tests orchestrator/tests/test_google_play_workflow.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests orchestrator/tests/test_google_play_workflow.py`
  • Result: PASS
  • Detail: `11 passed in 0.39s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `27 passed in 0.40s`
发现与结论
  • App Store studio 搜索只使用 `studio_name` 会损失配置里 `source_value` 的显式查询意图,已补上优先级逻辑。
  • App Store 详情采集与 detail parser 现在已有正常路径、失败路径和字段映射回归测试。
  • Google Play 与 App Store 两个平台的“详情失败不落库”行为现在已经对齐,并有测试保护。
下一步
  • 继续补 App Store `workflow.run_once` 成功路径测试与 `comment_workflow.run_once` 评论去重路径测试。
  • 之后再考虑 notifier / 结构化日志测试和 App Store smoke test 回归测试。
v0.4.0 2026-03-20 16:13 CST
补 App Store 高风险链路测试,并做最小实现修复以支持回归测试。
改动内容
改动预览 3/6
  • 修改 `app_store_scrap/collector/app_detail_collector.py`,移除详情缺字段时的示例默认值回填。
  • 修改 `app_store_scrap/workflow.py`,让 App Store 主 workflow 在详情抓取失败后跳过落库;修复评论 workflow 的 parser、重复逻辑和调试输出,并清理重复取 studio 的遗留代码。
  • 新增 `app_store_scrap/parser/comment_parser.py`,提供 App Store 评论解析器。
展开完整改动
  • 修改 `app_store_scrap/collector/app_detail_collector.py`,移除详情缺字段时的示例默认值回填。
  • 修改 `app_store_scrap/workflow.py`,让 App Store 主 workflow 在详情抓取失败后跳过落库;修复评论 workflow 的 parser、重复逻辑和调试输出,并清理重复取 studio 的遗留代码。
  • 新增 `app_store_scrap/parser/comment_parser.py`,提供 App Store 评论解析器。
  • 新增 `app_store_scrap/tests/test_app_detail_collector.py`,覆盖“缺字段不灌假值”。
  • 新增 `app_store_scrap/tests/test_workflow.py`,覆盖“详情失败不落库”和“评论 workflow 基本路径”。
  • 修改 `docs/doc_test_backlog.md`,同步本轮已完成项。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q app_store_scrap/tests`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q app_store_scrap/tests`
  • Result: PASS
  • Detail: `3 passed in 0.33s`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `20 passed in 0.40s`
发现与结论
  • App Store 详情采集原先的默认示例值确实会掩盖采集失败并制造脏数据,已移除。
  • App Store 评论 workflow 原先会因 parser 类型错误与 `Comment` 未导入而在运行时失败,已修到可回归测试状态。
  • App Store 主 workflow 现在至少能在详情失败时停止落库,避免继续扩大脏数据范围。
下一步
  • 继续补 App Store 详情采集“正常解析路径”与“请求失败路径”测试。
  • 再补 App Store 评论去重路径测试,以及 Google Play 详情失败不落库测试。
v0.3.0 2026-03-20 16:05 CST
文档扫描、代码扫描、待办梳理;建立文档/测试补齐清单与结构化记录机制。
改动内容
改动预览 3/4
  • 修改 `README.md`,新增文档索引并对齐当前边界说明。
  • 修改 `docs/architecture.md`,更新 App Store 当前成熟度描述。
  • 新增 `docs/doc_test_backlog.md`,整理文档与测试补齐项、优先级和验收口径。
展开完整改动
  • 修改 `README.md`,新增文档索引并对齐当前边界说明。
  • 修改 `docs/architecture.md`,更新 App Store 当前成熟度描述。
  • 新增 `docs/doc_test_backlog.md`,整理文档与测试补齐项、优先级和验收口径。
  • 新增 `docs/worklog.md`,作为后续每轮改动与测试结果的结构化记录入口。
验证结果
验证预览 2/6
  • `./.venv/bin/pytest -q`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `17 passed in 0.30s`
  • `./.venv/bin/python scripts/validate_config.py`
  • Result: PASS
  • Detail: `配置验证通过,未发现问题`
发现与结论
  • App Store 详情采集存在示例默认值回填,当前会制造脏数据与误报风险。
  • App Store 评论 workflow 存在 parser 错用、重复逻辑和调试输出,当前不可作为稳定链路。
  • 双平台 workflow 在详情抓取失败后仍继续落库,缺少失败隔离。
  • 文档与实现存在多处不一致,需要先对齐状态描述,再推进修复。
下一步
  • 先更新 README / 架构文档,使状态、边界与代码现状一致。
  • 再补 App Store 相关测试,优先锁定详情失败和评论 workflow 的回归场景。
v0.2.0 2026-04-04 03:08 CST
排查时间线页“查不到数据”的问题,并修复首屏查询、非法 `app_key` 和本地旧进程版本混淆导致的异常表现。
改动内容
改动预览 3/3
  • 修改 `orchestrator/admin_service/app.py`,对 `app_key=undefined/null/空字符串` 做服务端兜底清洗,避免非法值污染时间线查询。
  • 修改 `orchestrator/admin_service/templates/timeline.html`,将时间线查询改成“服务端先渲染初始候选 + 前端继续联想”的模式;补上提交前 `app_key` 校验、非法选择拦截,以及有关键字但无选中应用时的自动候选加载。
  • 修改 `orchestrator/tests/test_admin_service.py`,新增“仅输入关键字时能直接看到候选应用”和“`app_key=undefined` 不会生成空时间线”的回归测试。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `10 passed in 1.50s`
  • `./.venv/bin/python - <<'PY' ... playwright ... PY`
  • Result: PASS
  • Detail: `在 127.0.0.1:8766 上实测:首屏候选数=2,选择后 hidden app_key=app_store:1557392270,提交后 URL 正常且时间线条目数=10`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `80 passed in 1.23s`
发现与结论
  • 数据接口本身没有问题,`/api/timeline/apps?q=Zen Color - Color By Number` 能正常返回 App Store / Google Play 两条候选。
  • 实际导致“查不到数据”的主因是:页面首屏只带了 `q`,但前端没有主动把服务端候选渲染出来,也没有在加载完成后触发候选查询;因此用户看起来像“输入了关键字但没有任何可选项”。
  • 你截图里出现的 `timeline_clear_target / TIMELINE_CANDIDATE_RESULTS`,说明当时运行在 `localhost:8765` 的本地进程还是旧模板版本;当前代码已验证不再输出这些 raw key,但需要重启本地服务进程才能生效。
下一步
  • 如果继续提升可用性,下一步给时间线候选列表补键盘上下选择和回车确认。
  • 再给时间线页增加“最近查看应用”缓存,减少重复搜索成本。
v0.1.0 2026-04-04 02:46 CST
新增“工作室归属异常巡检”定时任务,定期扫描 `apps` 存量记录里 `developer_name` 与 studio 预期归属不一致的问题,并与现有双平台归属过滤口径对齐。
改动内容
改动预览 3/7
  • 新增 `common/utils/studio_identity.py`,抽出 studio 身份归一化与候选集合构建逻辑,避免 App Store / Google Play workflow 与离线巡检出现口径漂移。
  • 修改 `app_store_scrap/workflow.py` 与 `google_play_scrap/workflow.py`,改为复用共享的 studio 身份 helper。
  • 新增 `common/scheduler/studio_ownership_audit.py`,实现跨平台的工作室归属异常巡检 workflow,输出总计数、平台汇总、缺失 `developer_name` 数与异常样本。
展开完整改动
  • 新增 `common/utils/studio_identity.py`,抽出 studio 身份归一化与候选集合构建逻辑,避免 App Store / Google Play workflow 与离线巡检出现口径漂移。
  • 修改 `app_store_scrap/workflow.py` 与 `google_play_scrap/workflow.py`,改为复用共享的 studio 身份 helper。
  • 新增 `common/scheduler/studio_ownership_audit.py`,实现跨平台的工作室归属异常巡检 workflow,输出总计数、平台汇总、缺失 `developer_name` 数与异常样本。
  • 修改 `common/scheduler/__init__.py`,导出新的巡检 workflow。
  • 修改 `orchestrator/admin_service/scheduler.py` 与 `config/scheduler.yaml`,注册 `studio_ownership_audit` 定时任务,默认启用并每 720 分钟执行一次。
  • 新增 `common/tests/test_studio_ownership_audit.py`,覆盖“正常归属 / 错归属 / 缺失开发者名”三类场景。
  • 修改 `orchestrator/tests/test_admin_service.py`,补 scheduler 中新任务注册的回归测试。
验证结果
验证预览 2/9
  • `./.venv/bin/pytest -q common/tests/test_studio_ownership_audit.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
展开完整验证
  • `./.venv/bin/pytest -q common/tests/test_studio_ownership_audit.py orchestrator/tests/test_admin_service.py`
  • Result: PASS
  • Detail: `11 passed in 1.22s`
  • `./.venv/bin/python -m py_compile common/scheduler/studio_ownership_audit.py orchestrator/admin_service/scheduler.py app_store_scrap/workflow.py google_play_scrap/workflow.py common/utils/studio_identity.py`
  • Result: PASS
  • Detail: `编译通过`
  • `./.venv/bin/pytest -q`
  • Result: PASS
  • Detail: `80 passed in 1.45s`
发现与结论
  • 旧数据或外部导入的脏数据,即使已绕过实时抓取过滤,也会在 `apps` 表中长期残留;仅靠主 workflow 的详情抓取过滤不够,需要单独的存量巡检任务。
  • 归属判断口径如果分散在多个 workflow 和巡检脚本里,很容易再次产生“线上过滤”和“后台巡检”结论不一致;本轮已经统一到共享 helper。
  • 当前巡检默认只扫描 `active` studios 下的存量 apps,适合做后台健康检查;如果后面要清理历史停用 studio,也需要扩到 `list_all_by_platform()`。
下一步
  • 把巡检结果补一个后台只读展示入口,便于直接查看异常 app 列表和归属候选。
  • 如果后面要自动治理,再增加“仅对确认异常记录执行软隔离 / 标记”的修复流程,但不要和当前巡检任务混在一轮里。