Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.

fix(courses): restore selection data pipeline - #74

Merged
yzxoi merged 2 commits into
mainfrom
codex/fix-courses-data-pipeline
Jul 17, 2026
Merged

yzxoi merged 2 commits into
mainfrom
codex/fix-courses-data-pipeline

Conversation

@yzxoi

@yzxoi yzxoi commented Jul 17, 2026

Copy link
Copy Markdown
Member

需求与摘要

修复本地 D1 选课数据导入后搜索不可用、历史评分缺失、教学班安排/时间/教师/本地化字段物化不完整,以及模拟排课无法消费可信时段的问题。N/A(来自产品排障请求)。

  • 将同一 D1 快照中的 13 张 selection 表与 3 张 legacy course 表纳入一个原子导入与双审计链。
  • 按真实上游格式解析教师身份、星期/节次/周次/地点;无法证明的教师归属保持 unknown。
  • 恢复可证明映射的历史评分 aggregate,并向 Web/Flutter 暴露 count、average、scope。
  • 为 catalogue/selection Meilisearch 增加 PostgreSQL generation/readiness、启动/周期恢复、外部索引丢失与 source row drift 自愈。

产品行为与不变量

  • PostgreSQL 仍是权威源;Meilisearch 空索引或 stale generation 不再伪装成零搜索结果,关键词搜索返回 503,恢复后自动可用。
  • 历史评分优先按当前教师工号/姓名匹配,无法匹配时才使用课程 alias fallback;零样本 average 为 null。
  • 旧评分正文、作者、点赞、举报和身份不迁移;无法唯一映射的 aggregate 留在 raw 层。
  • Catalogue community aggregate 与 legacy aggregate 分开追踪,重复物化只替换 legacy 部分,不重复累加 community 部分。
  • Web/Flutter 按同一 wire invariant 校验评分;Flutter 本地课表 schema 升至 v4,并兼容读取 v3。

影响矩阵

  • 产品行为或 Web UI
  • contract/openapi.yaml 与 Web 生成类型
  • PostgreSQL migration、backfill 或兼容窗口
  • 身份、授权、PII、隐私、保留或审计
  • 积分合规、签名、防重放或 ledger 完整性
  • Media/OSS、search、cache、counter、notification 或 background job
  • 依赖、配置、CI、部署或外部 provider

影响说明:新增 migration 0071、D1 导入/物化流程、selection API 评分字段、Web/Flutter 课表展示和搜索投影恢复。没有新增依赖、PII、身份权限、积分或外部 provider 配置;旧 identity-bearing review 数据明确不进入本流程。

文档影响

已同步更新:

  • docs/product/courses-selection-and-reviews.md
  • docs/product/current-state-and-roadmap.md
  • docs/architecture/contracts-and-data.md
  • docs/operations/data-import.md
  • docs/development/testing.md
  • tools/d1/README.md

文档包含 16-table rollout、双 audit、重复 aggregate 风险、search readiness、真实快照证据和 forward-fix 回退原则。

验证

  • python3 scripts/check_docs.py
  • Backend focused 与 CI-parity checks
  • Web generate/lint/typecheck/build
  • Fresh migration 与数据/并发检查
  • Desktop/mobile/accessibility 人工验收

实际结果:

  • cargo fmt --all --check
  • cargo clippy --all-targets --all-features -- --deny warnings
  • cargo test --all -- --nocapture --test-threads=1:通过
  • migration_0071_upgrade、selection_tests(18)、search_projection_tests(2):在全新 PostgreSQL + Redis + Meilisearch 上通过
  • python3 -m unittest discover -s tools/d1/tests:10/10
  • npm run generate:api、lint、typecheck、test、build:77 files / 302 tests 通过;build 仅保留既有 oss-vendor chunk warning
  • scripts/generate_mobile_api.sh:生成、Dart analyze、23 tests 通过;本机没有 Flutter SDK,完整 Flutter analyze/widget/platform build 留给 CI
  • fresh database 从 migration 0001 升到 0071:通过

仓库外真实 D1 快照隔离复验:16 表 184,351 行;物化得到 4,816 catalogue courses、20,834 offerings、40,530 timeslots。8,206/8,434 个历史评分样本唯一映射到 catalogue;6,743 个 teaching classes 获得评分(teacher 3,772 / course 2,971)。重复物化结果稳定;selection fresh run 约 41.3 秒。

Migration、部署与回滚

  • 0071 新增 courses.pk_legacy_*、courses.legacy_import_runs、评分来源列和 search projection state。
  • 升级后旧 13-key manifest 不兼容;维护窗口必须从同一获批 D1 快照原子重建全部 16 张 raw table,不能手工只补 3 张表或伪造 audit。
  • 首次物化前必须证明现有 courses aggregate 未预先包含同一批 D1 aggregate;如果存在重叠,停止 rollout,先做 source-id 去重/backfill migration,否则会重复计数。
  • Projection clear→add 仍非原子;readiness 在此窗口返回 503。应用回退保留新增 schema/raw/audit,停用新版 sync 并 forward-fix,不 drop 数据列。

Preview 与证据

PR preview 由 CI 创建后验证:课程/选课关键词搜索、calendar/major/time filters、评分 scope 展示和本地课表添加。真实 D1 数据不会提交或复制到 preview。

已知限制与 Review 重点

  • 请重点审查 legacy alias 唯一映射、community/legacy aggregate 拆分和首次 rollout 去重前提。
  • Meilisearch 仍使用 clear→add,不是版本化 alias 原子切换;本 PR 通过 503/readiness fail closed,而不是声称消除了空窗。
  • 上游仍缺可靠停开课状态、容量和变更序列;跨设备课表同步、历史 review 正文/身份迁移不在本 PR。

@yzxoi

yzxoi commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

Preview smoke 已完成:

  • http://20.205.27.178:8080/pr-74/ → 200
  • /pr-74/api/v2/health → 200
  • /pr-74/api/v2/ready → 200
  • /pr-74/api/v2/selection/calendars → 200
  • /pr-74/api/v2/courses?q=数据&limit=5 → 200
  • /pr-74/api/v2/selection/courses/search?q=数据&calendarId=1&limit=5 → 200

PR preview 使用 fresh/empty database,因此课程与教学班结果为空是预期;真实 D1 快照没有复制到 preview,16-table import/materialization 证据来自仓库外隔离数据库。第二轮 CI 的 backend/frontend/deploy、docs/lint/web/integration、Flutter Android 与 iOS 全部通过。

@yzxoi
yzxoi merged commit 613e0ac into main Jul 17, 2026
11 checks passed
@yzxoi
yzxoi deleted the codex/fix-courses-data-pipeline branch July 17, 2026 07:43
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant