D1 schema v7:新表 lexgo_chapter_progress(chapter_id 主键、owner_id、book_id、language、read_sha256、read_at 与时间戳;外键级联章节、书籍、账号)。仍然只用可重放的 CREATE TABLE IF NOT EXISTS,不用 ALTER TABLE ADD COLUMN。
D2 接口:POST /api/v1/chapters/:id/complete 与 GET /api/v1/progress,server/app/lexgo/progress.go。
D3 计数口径:readAtByChapter/LearnerProgressFor 的已读判定都是 read_sha256 = content_sha256 AND status = ready,编辑正文后自动回到未读、只改标题保留标记、重新标记复用同一行(不新增),这四种情形分别有测试断言。已知/学习中/新词/忽略分类计数用一次 GROUP BY status 得出,不会互相合并;dueNow 通过把原来 ReviewQueueFor 内联的查询提取成 dueTermsQuery 共享给复习队列和进度页两处调用,我核对了 review.go 的这处重构,两处用的是同一个函数、同一个 now 参数,不是「分别实现、恰好结果一致」。TestMySQLProgressCountsMatchRecords 更进一步:用原始 SQL 直接对 lexgo_chapters/lexgo_chapter_progress 计数,与接口返回的数字逐项比对,同时验证 progress.DueNow 与 GET /reviews/queue 的 total 相等。达标,且验证方式比一般单测更严格。
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
来源与目标
2026-09-10 用户确认 F01–F12、原型通过,并要求按四阶段建议推进。原型验收:#1 评论 7498。阶段 4;覆盖 F11。
学习者完成章节后查看已读、已知词与待复习等基本数量,重复操作不重复计数。
验收标准
依赖与执行
前置:#10, #8, #11。状态:待验收(2026-09-15,提交 a9952b3,PR #33;契约见评论 8022,实施证据见最新评论)。前置尚未通过时不得标进行中;每单完成停在待验收,由用户验收后关闭。技术验证可与不依赖其结论的工作分工,但不提前冻结未验证契约。
参考模块与设计证据
领域统计查询;go-admin 看板样式仅参考,不引入现有无关管理指标。
沿用已验收章节完成和进度页面。
后端不为建表/API 单独画页面原型;先写数据、接口、状态、隔离与幂等契约。学习端采用 #1 已验收 v1,未做的 LinguaCafe 对照不标完成。
工作量、范围与风险
预计 2~3 人日(8 小时/人日),包含本单设计、前后端实现、相关测试、修正和文档;是规划估算,不是交付日期或 AI 运行时间。仅 F 范围,X 系列不纳入。数据变更先在隔离测试库验证迁移/回退,保留既有数据和原型。
文档与证据
实施时按影响更新 Architecture-and-Code-Map、Business-Rules-and-Glossary、Local-Development-and-Verification;需求变化更新 Product-Requirements-Overview,交付单补实际部署恢复文档。Wiki 先写再回读同步;结束评论记录测试、未验证内容、提交及 revision。工单正文保留基线,重要变化追加评论。
Gitea MCP 指向其他站点,沿用目标 git.ilapage.cn API 回退;凭据仅进入进程。
#13 方案确认与实施启动(2026-09-15)
用户 2026-09-15 指示「做 #13,都按你说的来做」,授权按本契约实施。覆盖 F11(完成阅读与基础进度),前置 #10、#8、#11 均已验收关闭。分支
feat/13-progress从 mainf2de395创建。D1 数据:schema v7 新增 lexgo_chapter_progress
新表而不是给
lexgo_chapters加列。本项目每个版本的 DDL 都是可重放的CREATE TABLE IF NOT EXISTS,ALTER TABLE ADD COLUMN不可重放,会破坏已测属性「回滚标记 → 重新升级仍可用」(#11 D3 的结论)。content_sha256的快照,用于内容版本门控D2 接口
ready章节可标记(否则 409);重复调用返回同一行并带duplicate=true,不重复计数;他人或不存在 404;未登录 401D3 计数口径(明确写进 Wiki)
read_sha256等于该章当前content_sha256,且章节ready、所属书语言=本人空间语言。因此编辑正文后该章自动回到未读(重新标记即更新同一行),只改标题不清除,删除章节随级联消失。ready的章节数;处理中或失败的章节不计入分母,界面写「已读章节 3 / 8 章(可阅读)」把规则显示出来。status ∈ 新词/学习中且due_at ≤ now),避免两处漂移;集成测试会断言dueNow与GET /reviews/queue的total一致。D4 完成入口
阅读页正文末尾一个显式按钮「标记本章已读」,不做滚动到底自动标记;已读时显示已读时间;不做「取消已读」(增强项,正文版本变化会自然回到未读);不批量修改词语等级(X10 不做)。书籍页章节列表对已读章节显示标记。
D5 进度页面
新增
/progress与导航「进度」:已读章节、已知词、待复习、学习中、新词、忽略、已保存词条,加每本书的已读进度与进入书籍的链接;没有书时引导导入。不含每日目标、日历、难度评分(工单明确排除)。D6 前端状态
新增
stores/progress.ts(加载、失败、代次校验、账号切换重置),stores/library.ts增加markChapterRead,并把已读状态合并进阅读与书籍页。D7 范围外
X10 批量标已知、每日目标/日历/难度、复习范围筛选(X11)、撤销已读、统计导出、CSV(X04)。
D8 验证
Go 集成(测试库 lexgo_test_issue13):重复标记幂等、正文新版本回到未读、只改标题保持已读、章节删除后计数、非 ready 409、他人与不存在 404、未登录 401、计数与复习队列一致、按本人与语言隔离、迁移 v6→v7 与「回滚标记后重新升级」。前端单测与 E2E 覆盖标记、列表标记、进度页数字与空状态。真实 API+MySQL 额外用 SQL 直查与接口数字比对,满足「固定数据验证页面数字与实际记录一致」。
D9 文档与迁移
更新 Architecture-and-Code-Map、Business-Rules-and-Glossary、Local-Development-and-Verification、Product-Requirements-Overview 与 Home。开发库需显式
python scripts/server.py migrate(v6→v7),迁移测试只能用 lexgo_test_ 前缀专用库。D10 回退
备份二进制
.local/lexgo-pre-issue13.exe;回退时把lexgo_schema.version写回 6 并丢弃新表即可,业务数据不受影响。Gitea MCP 仍指向其他站点,沿用目标站点 API 回退并在此记录;凭据仅从既有安全配置读入进程。
#13 实施完成,待用户验收(2026-09-15)
用户 2026-09-15 指示「做 #13,都按你说的来做」,按契约评论 8022 实施。分支
feat/13-progress从 mainf2de395创建,功能提交a9952b3已推送;PR #33 未合并,工单不关闭,停在待用户验收。实现与差异
lexgo_chapter_progress(chapter_id主键、owner_id、book_id、language、read_sha256、read_at与时间戳;外键级联章节、书籍、账号)。仍然只用可重放的CREATE TABLE IF NOT EXISTS,不用ALTER TABLE ADD COLUMN。POST /api/v1/chapters/:id/complete与GET /api/v1/progress,server/app/lexgo/progress.go。ready章节;完成记录保存标记时的content_sha256,正文新版本后该章回到未读(记录保留,重读后更新同一行),只改标题不影响;已知/学习中/新词/忽略分别计数;dueNow与复习队列共用新抽出的dueTermsQuery与同一服务端时钟。/progress与导航「进度」,六张统计卡 + 每本书的已读进度 + 已保存词条 + 空态与失败重试;无目标、日历与难度评分。stores/progress.ts(代次校验、账号切换重置、失败保留旧数据)、stores/library.ts的markChapterRead。实际验证
go vet ./...LEXGO_TEST_DB_NAME=lexgo_test_issue13 python scripts/server.py test-integrationnpx vitest --run/vue-tsc --build/pnpm run build/playwright testpnpm test/pnpm lintpython -m unittest discover -s tests/harness.py check --strict/sync --check覆盖:完成幂等(不移动时间、只有一行)、非 ready 409、越权与未登录、阅读器/列表已读状态一致、正文新版本回到未读、只改标题保持已读、章节删除后计数与记录同时消失、四种词条状态分别计数、
dueNow与队列total一致、每本书进度、账号与语言隔离、迁移 v6→v7 与回滚重升级、新表唯一性与级联。页面数字与 SQL 直查逐项比对全部相等(已读/可读章节、待复习、四种状态、已保存总数),满足「固定数据验证页面数字与实际记录一致」。迁移证据:开发库 lexgo_dev 迁移前后逐表计数完全一致,schema 6→7(
.local/issue13-before.json、.local/issue13-after.json);本机 lexgo-api 已用新二进制重启(v7),/healthz200;回退二进制.local/lexgo-pre-issue13.exe。顺带记录的既有缺陷(不在本单范围)
真实验证时发现并已单独建单:#32 编辑章节正文回到曾经用过的版本会返回 500。根因是
edit.go用edit:<章节>:<内容摘要>派生任务键,与lexgo_ingest_jobs唯一键冲突(MySQL 1062 → 通用 500,事务整体回滚、数据不损坏)。已在 #32 给出稳定复现(A→B→A→B)与两个候选修复方向,本单未修改该代码;本单的真实验证脚本因此改为自建 fixture 书籍、不改写既有正文。请用户决定是否排期与选择方案(方案 1 需要 schema 变更)。未验证与边界
文档
9bc2de753de2249c586306cdc27b566052941da267b43213bdbf87c5af03c324d40ccc1b37a11ea68222610b19a406a74771ded2f3edfa94a69f6295abf6629dfe44d838dd235b11543d9fdf944298999ee88d82494e22e689086b053770c4f4620d3657sync --check通过,另对 4 个变更页做逐字节正文比对(全部一致)AGENTS.md与README.md已同步口径与进度回退
把
lexgo_schema.version写回 6、丢弃lexgo_chapter_progress,并恢复.local/lexgo-pre-issue13.exe后重启 lexgo-api;既有书籍、词条、复习记录不受影响。Gitea MCP 仍指向其他站点,沿用目标站点 API 回退;凭据仅从既有安全配置读入进程。
附件:issue13-reader-unread/read、issue13-progress(mock 浏览器流程)与 issue13-real-reader-unread/read、issue13-real-progress(真实 API+MySQL 链路);本会话模型不能读取图片,功能断言来自程序化检查与 SQL 比对。
#13 代码审核:达标(2026-09-15,Claude Code)
审核对象:提交
a9952b3(PR #33)。只读审阅代码与测试差异,没有重跑测试。契约 D1~D10(评论 8022)逐条核对如下。逐条核对
lexgo_chapter_progress用chapter_id作主键(一章一行,天然幂等),read_sha256做内容版本门控,外键级联章节/书籍/账号;仍是CREATE TABLE IF NOT EXISTS。TestMigrationFromV6AddsChapterProgress直接验证了「标记回退到 v6 再重新升级」不丢数据、唯一约束生效、删除章节级联清理——这正是 #11 用来否决ALTER TABLE ADD COLUMN方案的那条测试,这次一次性设计对了。达标。CompleteChapter在同一事务里锁章节、判断ready、锁已有进度行、按ReadSHA256是否等于当前ContentSHA256决定是否已是同一次标记;重复调用返回duplicate=true且不移动时间;非ready409、他人/不存在 404、未登录 401 均由TestMySQLChapterCompletionIsIdempotentAndContentBound逐项覆盖,包括恶意边界(chapterId=0)。达标。readAtByChapter/LearnerProgressFor的已读判定都是read_sha256 = content_sha256 AND status = ready,编辑正文后自动回到未读、只改标题保留标记、重新标记复用同一行(不新增),这四种情形分别有测试断言。已知/学习中/新词/忽略分类计数用一次GROUP BY status得出,不会互相合并;dueNow通过把原来ReviewQueueFor内联的查询提取成dueTermsQuery共享给复习队列和进度页两处调用,我核对了review.go的这处重构,两处用的是同一个函数、同一个now参数,不是「分别实现、恰好结果一致」。TestMySQLProgressCountsMatchRecords更进一步:用原始 SQL 直接对lexgo_chapters/lexgo_chapter_progress计数,与接口返回的数字逐项比对,同时验证progress.DueNow与GET /reviews/queue的total相等。达标,且验证方式比一般单测更严格。CompleteChapter全程只操作ChapterProgress表,没有触碰Term/TermReview。达标。SchemaVersion确认为 7,schemaV7Statements是本次唯一新增的 DDL 语句。达标。顺带发现的既有缺陷(#32)处理方式:符合流程
真实链路验证时发现「正文改回曾用过的版本会 500」,根因在 #10 的
edit.go任务键派生逻辑,与本单无关。核对edit.go的 diff,本单只新增了readAtByChapter调用来解析编辑响应里的readAt字段,没有触碰任何任务键或版本门控逻辑;已确认过的工单 #32 有清晰的根因定位、最小复现步骤和两个候选方案,明确停在「待用户确认修复方案」,没有借这个机会顺手改掉。这是正确的处理方式——发现问题记录、不越界修复、等待确认。结论
达标,可以进入用户验收。 这单在「同一份查询逻辑喂给两处界面」(
dueTermsQuery)和「用原始 SQL 反向核对接口输出」两点上做得比前几单更严格,直接命中了验收标准里「固定数据验证页面数字与实际记录一致」和「到期数量使用同一时钟/规则」这两条不容易验证到位的要求。迁移测试复用了 #11 踩过的坑(重放性),说明确实吸收了之前的审核经验。本次审核只读代码,没有重跑
test-integration(lexgo_test_issue13)与 learner 的 vitest/E2E;pi 报告的 70 项集成用例、130 项前端单测、迁移前后逐表计数比对等结果未被本次复核重复验证。Gitea MCP 仍指向其他站点,本次沿用已记录的目标站点 API 回退;凭据只从
~/.claude/gitea.env安全配置读入进程。#13 验收通过(2026-09-15)
用户于 2026-09-15 明确回复「#13 通过验收」。
a9952b3;验收文档提交5f86f17已推送。d40a8f10717a5d7bb7de58e5ee5fd4f56220cc86、Architecture2cfb6d795ddea33cbb348c743a4928ec62b4678f、Product-Requirementsf1f5f9729f30f6679b7fdbdd5bd6e0568a8688db、Project-Profile0c0354499b49ef5973ffcdc47e9eba40f447536b。harness.py sync --check通过,并对 4 个变更页做逐字节正文比对(全部一致)。站点对整页批量回读仍返回 HTTP 429,沿用「revision 级 check + 变更页正文比对」,回退原因记录于此。lexgo_chapter_progress),开发库迁移前后逐表计数一致;回退时把lexgo_schema.version写回 6、丢弃新表并恢复.local/lexgo-pre-issue13.exe重启 lexgo-api,业务数据不受影响。单元工单关闭;#16 的 #13 复选框同步勾选。