Skip to content

RVH 端表/列重命名交接清单(三端统一改名)

状态:✅ 已完成(2026-07-09,RVH schema v56;07-10 三端闭环 verified)。 2026-08-29(rvh-merge-plan T4-7)归档。 ⚠️ 本文件的状态行从 2026-07-09 起错了七周——一直写着「⬜ 阻塞于 RVH 会话 / 镜像未做」, 而 docs/archive/09-双端整合实施计划.mdrvh/CHANGELOG.md v56 段都记着它当天就做完了。 根 CLAUDE.md §9 同期指着这里说「RVH 待镜像」,两份文件互相印证了一个不存在的状态。 教训:交接单的状态行必须由收尾的那一方回填,没人回填就等于留了一张错误地图。

物理仓库:本文件写于双仓时期,存于 RB(~/reading-browser/docs/plans/),执行对象是 RVH(当时的 ~/reading_vocab_helper,现为本仓 rvh/)。 必在 RVH 专门会话执行(CLAUDE.md §9:Flutter/Dart 与 RB 工具链不同,混合上下文易错)。 新会话打开后先 Read ~/reading-browser/docs/plans/table-rename-three-end-plan.md(三端总清单)+ 本文件。 前置:RB 端已改(本清单同步语义),Supabase DDL 已在 Dashboard 执行(表/列已改名 + recommended_excluded_words 已 DROP)。

背景与决策(与 RB/Supabase 一致)

开发阶段、未上线、无兼容负担、可删库重建。三端表/列/代码标识符/注释/i18n 统一改名,不留坑。 已锁定决策:

  1. i18n excluded/排除known/已认识(用户可见文案改写,统一到已有"认识/Known"术语)。
  2. "生词本/Notebook" 品牌词保留(表/代码改 learning_entries,用户文案仍"生词本/Notebook")。
  3. 全量标识符跟改(类型/方法/变量/文件/目录/索引)。
  4. 系统停用词表 = default_stopwords
  5. 停用词改纯本地预装:删掉 RVH 从 Supabase 拉取 recommended_excluded_words 的路径,只留本地 seed(03_init_data.sql)+ merge 进 known_words。

改名映射

(RVH 无 sites/favorite_sites,跳过):

  • notebook_entries → learning_entries
  • reading_sources → reading_pages
  • word_sources → word_page_links
  • excluded_words → known_words
  • recommended_excluded_words → default_stopwords(本地表改名;删远端拉取

  • notebook_entry_id → learning_entry_id
  • reading_source_id → reading_page_id(注意:RVH 特有的 ocr_word_positions 表(表名不改)也有此列,别漏

远端 user_ 表名*(sync 代码字符串,跟 Supabase 改):

  • user_notebook_entries → user_learning_entries
  • user_reading_sources → user_reading_pages
  • user_word_sources → user_word_page_links
  • user_excluded_words → user_known_words
  • (user_reading_notes 不改;user_sites 无)

保持不变:vocabulary, reading_notes, reference_words, ocr_word_positions(表名), 及其它 RVH 表。

迁移机制(最省事)

  • lib/shared/data/database/app_database.dart_schemaVersion 55 → 56(bump 即触发 dev _onUpgrade 抛异常 → 删库重建,无需写 migration)。
  • 改 SQL 资产:
    • assets/sql/01_create_tables.sql:5 张表 CREATE + FK + UNIQUE + 列名(含 ocr_word_positions.reading_source_id)。
    • assets/sql/02_create_indexes.sql:索引名 + 表/列(idx_notebook_→idx_learning_、idx_reading_sources_→idx_reading_pages_、idx_word_sources_→idx_word_page_links_、idx_excluded_words_→idx_known_words_、idx_recommended_excluded_words_→idx_default_stopwords_、idx_ocr_word_positions_* 里的 reading_source_id 列)。
    • assets/sql/03_init_data.sql:停用词 seed 若在此,表名跟改(default_stopwords);确认 seed 仍在(决策 5 依赖本地 seed)。

Dart 代码面

model 类(+ 各自 .freezed.dart/.g.dart 由 build_runner 重生成,勿手改):

  • NotebookEntryModel/Entity/DisplayDtoLearningEntry*lib/features/vocabulary_notebook/data/models/notebook_entry_model.dart 等)
  • ReadingSourceModel/EntityReadingSourceWithWordCountReadingPage*lib/features/reading_tracking/...
  • WordSourceModel/EntityWordSourceWithContextWordSourceInfo(注意文件名 word_source_relation_model.dart 更旧)→ WordPageLink*
  • ExcludedWordModel/EntityKnownWord*lib/features/excluded_words/...
  • OcrWordPositionModel/Entity(表名不改,仅 reading_source_id 列 key 改)
  • fromMap/toMap 的 key 字符串 = DB 列名,改列名时同步(例 word_source_relation_model.dart'notebook_entry_id'/'reading_source_id')。

datasource / repository / usecase(原始 SQL 表名字面量 + 方法名):

  • lib/features/reading_tracking/data/datasources/reading_source_datasource.dart(最重,~99 命中:'reading_sources'/'word_sources' 字面量、JOIN 里 wsr.reading_source_id/wsr.notebook_entry_id、ON CONFLICT 列、方法名 createReadingSource/getAllReadingSources/createWordSourceRelation…→ reading_page/word_page_link)
  • lib/features/vocabulary_notebook/data/datasources/notebook_datasource.dart'notebook_entries'、方法 getNotebookEntries/addNotebookEntry…;方法名可保留"notebook"品牌语义还是改 learning? 建议:DB/表层改 learning_entries,方法名可跟改 getLearningEntries,但面向"生词本"UI 的文案保留"生词本")
  • lib/features/excluded_words/data/datasources/local_excluded_words_datasource.dart'excluded_words'/'recommended_excluded_words'、方法 addOrReviveUserWord/isExcludedForUser/getExcludedWordSetForUser/mergeRecommendedIntoExcluded→ mergeStopwordsIntoKnown)
  • repository_impl + domain 抽象 + usecase:reading_source_repository_implnotebook_repository_implexcluded_words_repository_impladd_notebook_entry_usecasecreate_reading_source_usecase*_excluded_word_usecase
  • ocr_position_datasource.dart(reading_source_id 列)

synclib/features/sync/data/repositories/sync_repository_impl.dart,~93 命中,最关键):

  • 顶部配对 doc(行 14-18)文字更新。
  • 本地表名(裸 SQL / _markSynced 第二参)+ 远端 user_* 字面量(pushRows/pullRows 第一参)都改
  • INSERT 列列表里 notebook_entry_id/reading_source_id 跟改;Pull FK 顺序注释(行 28)同步。
  • supabase_sync_datasource.dart 泛型 pushRows(table,…),表名由上层传入,一般不用改。

停用词删拉取(决策 5)

  • 删除 lib/features/excluded_words/data/datasources/supabase_recommended_excluded_words_datasource.dart.from('recommended_excluded_words'))+ 其 replaceRecommendedCache 拉取调用链 + 触发点。
  • 保留 本地 default_stopwords(03_init_data.sql seed)+ mergeRecommendedIntoExcluded(→改名 mergeStopwordsIntoKnown,源=本地 default_stopwords)在 init 触发。

i18nlib/l10n/app_en.arb + app_zh.arbja 无 exclude 键):

  • 翻转 exclude→known:excluded* 前缀键(en ~32)、commonExclude/commonExcludedsharedExcludedMenuTitle/Desc/StatsDescnotebookExcludeWord/Desc/ConfirmnotebookAddedToExcludedfilterAddToNotebook* 等——文案改"已认识/Known/标记已认识",与 RB 统一。动作方向重写("添加到排除词"→"标记为已认识";OCR 相关描述同步调整)。
  • commonNotebook("Notebook"/"生词本") 保留(决策 2)。
  • 改 arb key 名会牵动所有 AppLocalizations.of(context).excludedXxx 调用点(presentation 层),一并改。

变量/注释/目录/文件名(决策 3):

  • feature 目录 lib/features/excluded_words/known_words/(动 import 路径,量大,用 IDE 重构)。可选:vocabulary_notebook/reading_tracking/ 是否改属范围决策(建议保留,非表名 1:1)。
  • 文件名 notebook_*/reading_source_*/word_source_relation_*/excluded_* 跟改。
  • camelCase 变量(notebookEntry/readingSource/wordSource/excludedWord…)+ 注释(app_database.dart 头部 35 处、01_create_tables.sql 版本注释、sync_repository_impl 红线注释)跟改。

子串碰撞铁律(先长后短、先列后表)

  1. 先替 recommended_excluded_wordsdefault_stopwordsuser_excluded_wordsuser_known_wordsuser_reading_sourcesuser_reading_pagesuser_word_sourcesuser_word_page_linksuser_notebook_entriesuser_learning_entries
  2. 再替列 notebook_entry_idlearning_entry_idreading_source_idreading_page_id
  3. 最后替裸表名。排除recommended_excluded_words(已处理)、reference_words(不改,与 excluded 相邻)、reading_notes(不改)、word_source_id 类。RVH 无 sites,不要碰任何 sites/website 串

完成标准

  1. dart run build_runner build --delete-conflicting-outputs 重生成 freezed/g.dart 无错。
  2. flutter analyze 通过。
  3. 删本地库重建(bump _schemaVersion 后 app 启动自动删旧库)→ 建表成功、PRAGMA foreign_key_check 空。
  4. grep 审计:lib/ + assets/sql/ 无残留旧表/列名(除 git 历史);reference_words/ocr_word_positions(表名)/reading_notes 未被误伤;ocr_word_positions 的列已改 reading_page_id。
  5. sync 往返冒烟:与 RB 共享 Supabase(新表名),登录 → 加词/标记已认识 → push → 在另一端/user_learning_entries 等可见 → pull 回来。前提:Supabase DDL 已执行。
  6. i18n:exclude→known 文案与 RB 统一,"生词本"保留。

完成后

  • 记到 ~/reading-browser/docs/cross-end/ 的编号 handoff(what+why),并更新该目录 README 总表。 ⚠️ 旧 docs/09-双端整合实施计划.md 已于 2026-08-14 归档进 docs/archive/不要再回填它
  • 回 RB 会话知会:RVH 侧已对齐。