主题
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-双端整合实施计划.md与rvh/CHANGELOG.mdv56 段都记着它当天就做完了。 根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 统一改名,不留坑。 已锁定决策:
- i18n
excluded/排除→known/已认识(用户可见文案改写,统一到已有"认识/Known"术语)。 - "生词本/Notebook" 品牌词保留(表/代码改 learning_entries,用户文案仍"生词本/Notebook")。
- 全量标识符跟改(类型/方法/变量/文件/目录/索引)。
- 系统停用词表 =
default_stopwords。 - 停用词改纯本地预装:删掉 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/DisplayDto→LearningEntry*(lib/features/vocabulary_notebook/data/models/notebook_entry_model.dart等)ReadingSourceModel/Entity、ReadingSourceWithWordCount→ReadingPage*(lib/features/reading_tracking/...)WordSourceModel/Entity、WordSourceWithContext、WordSourceInfo(注意文件名word_source_relation_model.dart更旧)→WordPageLink*ExcludedWordModel/Entity→KnownWord*(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_impl、notebook_repository_impl、excluded_words_repository_impl、add_notebook_entry_usecase、create_reading_source_usecase、*_excluded_word_usecase等 ocr_position_datasource.dart(reading_source_id 列)
sync(lib/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 触发。
i18n(lib/l10n/app_en.arb + app_zh.arb;ja 无 exclude 键):
- 翻转 exclude→known:
excluded*前缀键(en ~32)、commonExclude/commonExcluded、sharedExcludedMenuTitle/Desc/StatsDesc、notebookExcludeWord/Desc/Confirm、notebookAddedToExcluded、filterAddToNotebook*等——文案改"已认识/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 红线注释)跟改。
子串碰撞铁律(先长后短、先列后表)
- 先替
recommended_excluded_words→default_stopwords、user_excluded_words→user_known_words、user_reading_sources→user_reading_pages、user_word_sources→user_word_page_links、user_notebook_entries→user_learning_entries。 - 再替列
notebook_entry_id→learning_entry_id、reading_source_id→reading_page_id。 - 最后替裸表名。排除:
recommended_excluded_words(已处理)、reference_words(不改,与 excluded 相邻)、reading_notes(不改)、word_source_id类。RVH 无 sites,不要碰任何 sites/website 串。
完成标准
dart run build_runner build --delete-conflicting-outputs重生成 freezed/g.dart 无错。flutter analyze通过。- 删本地库重建(bump _schemaVersion 后 app 启动自动删旧库)→ 建表成功、
PRAGMA foreign_key_check空。 - grep 审计:lib/ + assets/sql/ 无残留旧表/列名(除 git 历史);
reference_words/ocr_word_positions(表名)/reading_notes未被误伤;ocr_word_positions 的列已改 reading_page_id。 - sync 往返冒烟:与 RB 共享 Supabase(新表名),登录 → 加词/标记已认识 → push → 在另一端/
user_learning_entries等可见 → pull 回来。前提:Supabase DDL 已执行。 - i18n:exclude→known 文案与 RB 统一,"生词本"保留。
完成后
- 记到
~/reading-browser/docs/cross-end/的编号 handoff(what+why),并更新该目录 README 总表。 ⚠️ 旧docs/09-双端整合实施计划.md已于 2026-08-14 归档进docs/archive/,不要再回填它。 - 回 RB 会话知会:RVH 侧已对齐。