MVP 粘贴英语文本,处理后进入本人章节阅读 #5

Closed
opened 2026-09-10 17:09:04 +08:00 by ila · 6 comments
Owner

来源与目标

2026-09-10 用户确认 F01–F12、原型通过,并要求按四阶段建议推进。原型验收:#1 评论 7498。阶段 3;覆盖 F01 基础、F02、F04 基础、F05 原文。

学习者粘贴英语文本,新建书籍或向本人书籍追加章节,查看处理状态并进入可读正文。

验收标准

  • 标题、正文、语言和新建/追加校验明确;固定分章规则及长度上限,原文空白与标点保留。
  • 书籍、章节和处理任务有持久化归属;任务重启可恢复,重试/重复提交不重复生成章节,失败原因可读。
  • 普通用户不能通过书籍 ID、任务 ID、接口或文件地址访问他人内容;后台任务不信任客户端用户 ID。
  • 书库→导入→处理中/失败/重试→就绪→阅读完整路径有集成测试,手机基础布局可用。

依赖与执行

前置:#2, #3。状态:已验收关闭(2026-09-11;未验证项见结单评论)。前置尚未通过时不得标进行中;每单完成停在待验收,由用户验收后关闭。技术验证可与不依赖其结论的工作分工,但不提前冻结未验证契约。

参考模块与设计证据

参考常规 router/apis/service/dto/models 与迁移;SysJob 仅提供状态操作参考,cron 不能替代持久任务的幂等和恢复。

使用已验收导入/状态/阅读原型;后端补任务状态机、幂等与回退设计。

后端不为建表/API 单独画页面原型;先写数据、接口、状态、隔离与幂等契约。学习端采用 #1 已验收 v1,未做的 LinguaCafe 对照不标完成。

工作量、范围与风险

预计 5~8 人日(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 回退;凭据仅进入进程。

## 来源与目标 2026-09-10 用户确认 F01–F12、原型通过,并要求按四阶段建议推进。原型验收:#1 评论 7498。阶段 3;覆盖 F01 基础、F02、F04 基础、F05 原文。 学习者粘贴英语文本,新建书籍或向本人书籍追加章节,查看处理状态并进入可读正文。 ## 验收标准 - [x] 标题、正文、语言和新建/追加校验明确;固定分章规则及长度上限,原文空白与标点保留。 - [x] 书籍、章节和处理任务有持久化归属;任务重启可恢复,重试/重复提交不重复生成章节,失败原因可读。 - [x] 普通用户不能通过书籍 ID、任务 ID、接口或文件地址访问他人内容;后台任务不信任客户端用户 ID。 - [ ] 书库→导入→处理中/失败/重试→就绪→阅读完整路径有集成测试,手机基础布局可用。 ## 依赖与执行 前置:#2, #3。状态:已验收关闭(2026-09-11;未验证项见结单评论)。前置尚未通过时不得标进行中;每单完成停在待验收,由用户验收后关闭。技术验证可与不依赖其结论的工作分工,但不提前冻结未验证契约。 ## 参考模块与设计证据 参考常规 router/apis/service/dto/models 与迁移;SysJob 仅提供状态操作参考,cron 不能替代持久任务的幂等和恢复。 使用已验收导入/状态/阅读原型;后端补任务状态机、幂等与回退设计。 后端不为建表/API 单独画页面原型;先写数据、接口、状态、隔离与幂等契约。学习端采用 #1 已验收 v1,未做的 LinguaCafe 对照不标完成。 ## 工作量、范围与风险 预计 5~8 人日(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 回退;凭据仅进入进程。
Author
Owner

#5 启动与范围确认(用户确认边界)

2026-09-10 用户在当前会话指示“执行 Gitea 工单 #5”,并明确:使用独立任务分支、保留已有改动、不自行合并已有 PR、停在待验收、不实施 #21;同时指出 #3 的 Python NLP 目前仍只是独立验证,正式接入前必须确认“Go+Python NLP”还是“全 Go”,不能默认该问题已解决;#4 的真机详细测试证据缺口保留。

前置核对:#2、#18、#3、#4 均已通过用户验收并关闭,本单声明的依赖 #2、#3 满足。PR #17/#19/#20/#22 仍未合并;本单分支 feat/5-paste-chapter-reading 基于已验收的 feat/4-reading-selection-spike,不合并任何已有 PR。

本次用户确认的两项边界:

  1. 本单按 Go-only 处理,不接入 Python NLP。 处理=校验+固定分章+原文持久化+任务状态机;token/lemma 与词典查词留给 #6。“Go+Python 还是全 Go”仍未决,本单不视为已解决,进入正式 NLP 接入前必须由用户再次确认。本单不写任何 Python 调用、不部署 NLP 进程。
  2. 固定分章规则:一次粘贴=一个章节,不自动按空行或长度再分章;单次粘贴上限 100000 Unicode code point;原文空白与标点逐字保留,不做 NFC、大小写或换行归一化。语言默认英语。

实现计划:schema v3 新增 books/chapters/ingest_jobs 三张表;粘贴文本用于新建书籍或向本人书籍追加章节;持久任务状态机(pending/processing/ready/failed)含启动恢复与幂等(客户端 requestId,重复提交不重复生成章节,失败任务重试复用同一章节);全部接口按认证身份过滤所有权,他人编号返回 404,后台任务只使用任务行内的 owner,不接受客户端用户 ID;学习端补齐粘贴导入、书库/章节状态、失败重试与原文阅读;MySQL 集成测试覆盖“书库→导入→处理中/失败/重试→就绪→阅读”、Unicode 原文保留与越权拒绝。

风险与回退:仅新增表和接口,不改动既有账号、空间、会话与审计数据;回退=停止 API,将 lexgo_schema 版本从 3 改回 2 并恢复上一二进制,新增表保留不删除。不实施 #21。真机手机证据仍按 #4 记录保持缺失,不用桌面窄屏代替。

Gitea MCP 指向其他站点,本单继续使用目标站点 git.ilapage.cn API 回退;凭据仅进入进程。

## #5 启动与范围确认(用户确认边界) 2026-09-10 用户在当前会话指示“执行 Gitea 工单 #5”,并明确:使用独立任务分支、保留已有改动、不自行合并已有 PR、停在待验收、不实施 #21;同时指出 #3 的 Python NLP 目前仍只是独立验证,正式接入前必须确认“Go+Python NLP”还是“全 Go”,不能默认该问题已解决;#4 的真机详细测试证据缺口保留。 前置核对:#2、#18、#3、#4 均已通过用户验收并关闭,本单声明的依赖 #2、#3 满足。PR #17/#19/#20/#22 仍未合并;本单分支 `feat/5-paste-chapter-reading` 基于已验收的 `feat/4-reading-selection-spike`,不合并任何已有 PR。 本次用户确认的两项边界: 1. **本单按 Go-only 处理,不接入 Python NLP。** 处理=校验+固定分章+原文持久化+任务状态机;token/lemma 与词典查词留给 #6。“Go+Python 还是全 Go”仍未决,本单不视为已解决,进入正式 NLP 接入前必须由用户再次确认。本单不写任何 Python 调用、不部署 NLP 进程。 2. **固定分章规则:一次粘贴=一个章节**,不自动按空行或长度再分章;单次粘贴上限 100000 Unicode code point;原文空白与标点逐字保留,不做 NFC、大小写或换行归一化。语言默认英语。 实现计划:schema v3 新增 books/chapters/ingest_jobs 三张表;粘贴文本用于新建书籍或向本人书籍追加章节;持久任务状态机(pending/processing/ready/failed)含启动恢复与幂等(客户端 requestId,重复提交不重复生成章节,失败任务重试复用同一章节);全部接口按认证身份过滤所有权,他人编号返回 404,后台任务只使用任务行内的 owner,不接受客户端用户 ID;学习端补齐粘贴导入、书库/章节状态、失败重试与原文阅读;MySQL 集成测试覆盖“书库→导入→处理中/失败/重试→就绪→阅读”、Unicode 原文保留与越权拒绝。 风险与回退:仅新增表和接口,不改动既有账号、空间、会话与审计数据;回退=停止 API,将 lexgo_schema 版本从 3 改回 2 并恢复上一二进制,新增表保留不删除。不实施 #21。真机手机证据仍按 #4 记录保持缺失,不用桌面窄屏代替。 Gitea MCP 指向其他站点,本单继续使用目标站点 `git.ilapage.cn` API 回退;凭据仅进入进程。
Author
Owner

#5 实施完成,待用户验收

提交:a55708cd37c7eb4f706d1cf573a54d44692f67ca(分支 feat/5-paste-chapter-reading,已推送)
PR:#23,基准分支 feat/4-reading-selection-spike,与既有 PR 链一致;未合并任何已有 PR。

范围与边界

按用户确认(评论 7644)实现:Go-only 处理,不接入 Python NLP;一次粘贴=一个章节,单次上限 100000 Unicode code point,语言当前只接受 en,原文按收到的字符串逐字保留。“Go+Python NLP 还是全 Go”仍未决,本单不视为已解决,正式接入前须用户再次确认。

实现

  • schema v3:lexgo_books、lexgo_chapters、lexgo_ingest_jobs;显式迁移,服务启动不自动迁移。owner_id 在章节与任务上冗余存放,任何查询都直接按认证身份过滤;UNIQUE(book_id,ordinal) 与 UNIQUE(owner_id,request_key) 分别阻止重复章节与重复提交。
  • 后端:server/app/lexgo/library.go(粘贴校验、固定分章、书籍/章节/任务写入、本人归属查询、幂等、重试)、ingest.go(claim/完成/固定失败原因/启动恢复)、router.go(新路由与粘贴请求 4 MiB 体积上限)、cmd/lexgo/main.go(启动恢复 + 每秒后台处理,单实例)。
  • 接口:POST /books、POST /books/:id/chapters、GET /books、GET /books/:id、GET /chapters/:id、GET /jobs/:id、POST /jobs/:id/retry。状态 pending/processing/ready/failed(章节与任务同词表);仅 ready 返回 originalText;章节列表带 jobId 与可读失败原因,便于重试。
  • 归属:全部接口按认证身份过滤 owner_id,他人书籍/章节/任务编号统一 404;管理员角色不解除本人归属过滤;后台任务只使用任务行内的 owner,请求体 ownerId、查询参数 ownerId 均返回 400。
  • 学习端:ImportView(语言固定英语、标题、正文、新建书籍/追加到已有书籍)、LibraryView(书籍与状态摘要)、BookView(章节列表、状态、失败重试)、ReaderView(原文 pre-wrap 逐字展示、上一章/下一章);stores/library.ts 负责 1500 ms 轮询、代次与归属守卫、离开页面与退出登录后停止轮询。

测试(均为实际执行)

  • python scripts/server.py test-integration(LEXGO_TEST_DB_NAME=lexgo_test_issue5,专用库):18 个顶层用例全部通过,其中 #5 新增 8 个(含 12 个子用例):导入→处理中→就绪→阅读完整路径、四种失败原因与可读提示、失败后重试复用同一章节、同 requestId 重复与并发提交只产生一个章节、跨账号与管理员越权的全部 404、启动恢复重入队、输入上限与边界(100000 通过、100001 拒绝、超大请求体拒绝)、Unicode 原文逐字符相等;另有 v2→v3 迁移保留既有数据的用例。
  • 学习端:test:unit --run 31 项通过(session 9/library 16/reading 6);build 通过(vue-tsc 类型检查 + vite 构建);test:e2e 3 项通过(虚构 API 响应)。治理测试 56 项与 harness.py check --strict 通过。
  • 真实 API + MySQL(lexgo_dev):issue5_a 粘贴 HTTP 201 → 实测状态序列 pending → ready(约 1132 ms)→ 阅读返回原文与提交内容逐字符相等(CRLF、制表符、弯引号、破折号、省略号、é+组合重音、emoji、行尾空格、空行全部保留),sha256 前缀 ce7357ea22a3;同 requestId 重复提交 HTTP 200 duplicate=true 且章节/任务编号不变,换正文 409;issue5_b 读取与越权追加、重试全部 404;追加章节 ordinal=2 且前后章节编号正确;空标题、纯空白正文、非 en、缺 requestId、超限分别 400 并可读。
  • 真实浏览器(真实学习端 5173 + 真实 API + 真实 MySQL):登录 → 书库显示书籍 → 导入页粘贴含空行/制表符/行尾空格的正文 → 书库页由“处理中”变“已就绪” → 阅读页 textContent 与粘贴正文逐字符相等、white-space 为 pre-wrap → “下一章”切换后正文精确相等 → 390×844 视口横向溢出 0 px。

处理中就绪后的书库页与阅读页(工单附件):

书库页
阅读页

其余附件:书库、390×844 阅读页。截图未经 Agent 目视复核(本会话模型不能读取图片),功能断言来自上面的程序化检查。

迁移与回退

lexgo_dev 由 v2 显式迁移到 v3:迁移前后 sys_user 4、lexgo_spaces 4、lexgo_sessions 3、lexgo_login_logs 23、lexgo_operation_logs 1 完全一致,新增三张表。本机按 supervisor 流程操作:停止 lexgo-api → build → migrate → 启动,/healthz 200,lexgo-admin 与 lexgo-learner 的 PID 未变化。

回退:停止 API,把 lexgo_schema 中 id=1 的版本从 3 改回 2,并恢复上一二进制;三张新表保留不删除,旧程序不读写它们。未在开发库演练回退,v2→v3 数据保留与重复迁移仅由隔离测试库覆盖。

本机 fixture 与遗留数据

本单新建虚构账号 issue5_a(id=6)、issue5_b(id=8),口令只在本机忽略的 .local/issue5-accounts.json;lexgo_dev 中 issue5_a 名下保留 1 本虚构书(bookId=1,2 章就绪)供人工验收查看。未改动 admin、dev、learner_a、learner_b。

未验证与限制

  • 处理失败→重试的界面路径只由集成测试覆盖:失败原因是针对其他写入路径的防御性校验,无法通过 API 主动制造,因此没有浏览器端的真实失败截图。
  • #4 真机手机证据缺口保留:本次只用桌面浏览器 390×844 检查(无横向溢出),不能当作真机、触摸、长按手柄或虚拟键盘结果。
  • 学习端页面按已验收 v1 的字段、状态与流程实现,未与 Quant-UX 模型逐像素比对(本会话没有读取原型模型的方式);布局观感请以本次截图与实机为准。
  • Python NLP 未接入,token、lemma 与词典索引仍为 #3 小样范围。
  • 未设置每账号容量配额;删除书籍/章节属 #10;导入失败不自动重试,只在启动时恢复被中断的 processing 任务。
  • 处理为轻量校验,本地通常 1 秒内完成,因此“处理中”状态在界面上一般瞬时可见,其状态机行为由集成测试分步验证。

文档

Wiki 先写后回读,再导出核心镜像(镜像包含于上述提交):

  • Architecture-and-Code-Map:fa7d3d66e6e68164ee97a23dc20a737fcaba7a11
  • Business-Rules-and-Glossary:ff2abb673fe97e7eaa6934f76180ffdde701da8d
  • Local-Development-and-Verification:b8741ac70a524bb79741183f592f19c1206cd71b
  • Product-Requirements-Overview:125df697d3d45dee098616d25970bb65a96727cd
  • Home:5b936b75caeec89cd54f7147c842f0b66f901307

需要说明的一次失误与修复:首次写入 Wiki 时误用了 content 字段(Gitea 该接口要求 content_base64),架构页正文被写入空内容。同会话立即用本机保存的编辑前正文恢复并回读确认,#2/#4 小节与新 #5 小节均完整;Wiki 历史保留两次错误提交,上述 revision 已是修复后的版本。

Gitea 交互

MCP 仍指向其他站点,本单继续使用目标站点 git.ilapage.cn 的 API。本机 git credential fill 因凭据助手要求交互而不可用,改用本机忽略的本地 Gitea 环境配置中的令牌,只读入进程,未输出、未写入仓库或工单。

状态保持待验收,未关闭工单;#21 未实施;未合并任何已有 PR。

## #5 实施完成,待用户验收 **提交**:`a55708cd37c7eb4f706d1cf573a54d44692f67ca`(分支 `feat/5-paste-chapter-reading`,已推送) **PR**:[#23](https://git.ilapage.cn/OPC/lexgo/pulls/23),基准分支 `feat/4-reading-selection-spike`,与既有 PR 链一致;未合并任何已有 PR。 ### 范围与边界 按用户确认(评论 7644)实现:**Go-only 处理,不接入 Python NLP**;**一次粘贴=一个章节**,单次上限 100000 Unicode code point,语言当前只接受 en,原文按收到的字符串逐字保留。“Go+Python NLP 还是全 Go”仍未决,本单不视为已解决,正式接入前须用户再次确认。 ### 实现 - **schema v3**:`lexgo_books`、`lexgo_chapters`、`lexgo_ingest_jobs`;显式迁移,服务启动不自动迁移。`owner_id` 在章节与任务上冗余存放,任何查询都直接按认证身份过滤;`UNIQUE(book_id,ordinal)` 与 `UNIQUE(owner_id,request_key)` 分别阻止重复章节与重复提交。 - **后端**:`server/app/lexgo/library.go`(粘贴校验、固定分章、书籍/章节/任务写入、本人归属查询、幂等、重试)、`ingest.go`(claim/完成/固定失败原因/启动恢复)、`router.go`(新路由与粘贴请求 4 MiB 体积上限)、`cmd/lexgo/main.go`(启动恢复 + 每秒后台处理,单实例)。 - **接口**:`POST /books`、`POST /books/:id/chapters`、`GET /books`、`GET /books/:id`、`GET /chapters/:id`、`GET /jobs/:id`、`POST /jobs/:id/retry`。状态 pending/processing/ready/failed(章节与任务同词表);仅 ready 返回 `originalText`;章节列表带 `jobId` 与可读失败原因,便于重试。 - **归属**:全部接口按认证身份过滤 `owner_id`,他人书籍/章节/任务编号统一 404;管理员角色不解除本人归属过滤;后台任务只使用任务行内的 owner,请求体 `ownerId`、查询参数 `ownerId` 均返回 400。 - **学习端**:`ImportView`(语言固定英语、标题、正文、新建书籍/追加到已有书籍)、`LibraryView`(书籍与状态摘要)、`BookView`(章节列表、状态、失败重试)、`ReaderView`(原文 `pre-wrap` 逐字展示、上一章/下一章);`stores/library.ts` 负责 1500 ms 轮询、代次与归属守卫、离开页面与退出登录后停止轮询。 ### 测试(均为实际执行) - `python scripts/server.py test-integration`(`LEXGO_TEST_DB_NAME=lexgo_test_issue5`,专用库):**18 个顶层用例全部通过**,其中 #5 新增 8 个(含 12 个子用例):导入→处理中→就绪→阅读完整路径、四种失败原因与可读提示、失败后重试复用同一章节、同 requestId 重复与并发提交只产生一个章节、跨账号与管理员越权的全部 404、启动恢复重入队、输入上限与边界(100000 通过、100001 拒绝、超大请求体拒绝)、Unicode 原文逐字符相等;另有 v2→v3 迁移保留既有数据的用例。 - 学习端:`test:unit --run` **31 项通过**(session 9/library 16/reading 6);`build` 通过(vue-tsc 类型检查 + vite 构建);`test:e2e` **3 项通过**(虚构 API 响应)。治理测试 56 项与 `harness.py check --strict` 通过。 - **真实 API + MySQL(lexgo_dev)**:issue5_a 粘贴 HTTP 201 → 实测状态序列 `pending → ready`(约 1132 ms)→ 阅读返回原文与提交内容逐字符相等(CRLF、制表符、弯引号、破折号、省略号、é+组合重音、emoji、行尾空格、空行全部保留),`sha256` 前缀 `ce7357ea22a3`;同 requestId 重复提交 HTTP 200 `duplicate=true` 且章节/任务编号不变,换正文 409;issue5_b 读取与越权追加、重试全部 404;追加章节 ordinal=2 且前后章节编号正确;空标题、纯空白正文、非 en、缺 requestId、超限分别 400 并可读。 - **真实浏览器(真实学习端 5173 + 真实 API + 真实 MySQL)**:登录 → 书库显示书籍 → 导入页粘贴含空行/制表符/行尾空格的正文 → 书库页由“处理中”变“已就绪” → 阅读页 `textContent` 与粘贴正文逐字符相等、`white-space` 为 `pre-wrap` → “下一章”切换后正文精确相等 → 390×844 视口横向溢出 0 px。 处理中就绪后的书库页与阅读页(工单附件): ![书库页](https://git.ilapage.cn/attachments/3b150a1e-4c10-4e05-ba38-83833f48e037) ![阅读页](https://git.ilapage.cn/attachments/5ea3bd4d-8a8f-4908-b2ea-2cdccce82fdf) 其余附件:[书库](https://git.ilapage.cn/attachments/d492f929-5ed8-4dac-811a-87ee4c1cc749)、[390×844 阅读页](https://git.ilapage.cn/attachments/7764bde8-bacc-42b5-9032-ce330a2e14ac)。截图未经 Agent 目视复核(本会话模型不能读取图片),功能断言来自上面的程序化检查。 ### 迁移与回退 lexgo_dev 由 v2 显式迁移到 v3:迁移前后 `sys_user 4`、`lexgo_spaces 4`、`lexgo_sessions 3`、`lexgo_login_logs 23`、`lexgo_operation_logs 1` 完全一致,新增三张表。本机按 supervisor 流程操作:停止 lexgo-api → build → migrate → 启动,`/healthz` 200,lexgo-admin 与 lexgo-learner 的 PID 未变化。 回退:停止 API,把 `lexgo_schema` 中 id=1 的版本从 3 改回 2,并恢复上一二进制;三张新表保留不删除,旧程序不读写它们。未在开发库演练回退,v2→v3 数据保留与重复迁移仅由隔离测试库覆盖。 ### 本机 fixture 与遗留数据 本单新建虚构账号 `issue5_a`(id=6)、`issue5_b`(id=8),口令只在本机忽略的 `.local/issue5-accounts.json`;lexgo_dev 中 issue5_a 名下保留 1 本虚构书(bookId=1,2 章就绪)供人工验收查看。未改动 admin、dev、learner_a、learner_b。 ### 未验证与限制 - **处理失败→重试的界面路径只由集成测试覆盖**:失败原因是针对其他写入路径的防御性校验,无法通过 API 主动制造,因此没有浏览器端的真实失败截图。 - **#4 真机手机证据缺口保留**:本次只用桌面浏览器 390×844 检查(无横向溢出),不能当作真机、触摸、长按手柄或虚拟键盘结果。 - 学习端页面按已验收 v1 的字段、状态与流程实现,未与 Quant-UX 模型逐像素比对(本会话没有读取原型模型的方式);布局观感请以本次截图与实机为准。 - Python NLP 未接入,token、lemma 与词典索引仍为 #3 小样范围。 - 未设置每账号容量配额;删除书籍/章节属 #10;导入失败不自动重试,只在启动时恢复被中断的 processing 任务。 - 处理为轻量校验,本地通常 1 秒内完成,因此“处理中”状态在界面上一般瞬时可见,其状态机行为由集成测试分步验证。 ### 文档 Wiki 先写后回读,再导出核心镜像(镜像包含于上述提交): - Architecture-and-Code-Map:`fa7d3d66e6e68164ee97a23dc20a737fcaba7a11` - Business-Rules-and-Glossary:`ff2abb673fe97e7eaa6934f76180ffdde701da8d` - Local-Development-and-Verification:`b8741ac70a524bb79741183f592f19c1206cd71b` - Product-Requirements-Overview:`125df697d3d45dee098616d25970bb65a96727cd` - Home:`5b936b75caeec89cd54f7147c842f0b66f901307` 需要说明的一次失误与修复:首次写入 Wiki 时误用了 `content` 字段(Gitea 该接口要求 `content_base64`),架构页正文被写入空内容。同会话立即用本机保存的编辑前正文恢复并回读确认,#2/#4 小节与新 #5 小节均完整;Wiki 历史保留两次错误提交,上述 revision 已是修复后的版本。 ### Gitea 交互 MCP 仍指向其他站点,本单继续使用目标站点 `git.ilapage.cn` 的 API。本机 `git credential fill` 因凭据助手要求交互而不可用,改用本机忽略的本地 Gitea 环境配置中的令牌,只读入进程,未输出、未写入仓库或工单。 状态保持**待验收**,未关闭工单;#21 未实施;未合并任何已有 PR。
Author
Owner

#5 审核整改与实施交接(2026-09-11)

用户已授权:把审核结果更新到本工单,由 DeepSeek agent 实施整改,完成后由当前主审 agent 复审。当前结论:主体已实现,但尚不满足验收要求。工单保持 open,PR #23 不合并;本评论作为最新进度,原需求基线不改写。

审核基线:分支 feat/5-paste-chapter-reading,提交 a55708cd37c7eb4f706d1cf573a54d44692f67ca;原实现基线为已验收 #4 的 ed5da31888329873cd87c50237ea111388fe35af。

必须修复

  • R1 / P1:学习端追加章节返回 HTTP 400。前端 learner/src/stores/library.ts:341 对新建和追加共用包含 language 的请求;后端 server/app/lexgo/library.go:208 的 PasteChapterInput 不含 language,严格 JSON 解码拒绝未知字段。已用实际 Router + MySQL 测试复现前端格式被拒绝。统一请求契约,追加沿用所属书籍语言,保留未知字段/owner 注入防护。验收:从学习端向已有书籍追加成功,进入第二章,序号与前后章导航正确;补覆盖真实请求格式的回归测试,不仅 mock 成功响应。
  • R2 / P2:后台任务领取后,完成事务超时/取消/数据库异常会遗留 processing;下一批只领取 pending,手动重试又仅接受 failed,因此服务持续运行时无法恢复。位置 server/app/lexgo/ingest.go:145、server/cmd/lexgo/main.go:117-126。已通过领取后取消上下文复现:下一批处理后仍 processing,人工 retry 被拒绝。补单实例运行期间的异常恢复策略,不能只依赖启动恢复;防止无限快速重试和重复章节,日志不得声称已重试实际却跳过。验收:故障解除后无需重启即可完成或进入有明确原因且允许重试的 failed;原章节/任务 ID 不变;启动恢复仍有效。
  • R3 / P2:离页时没有作废在途加载请求。learner/src/stores/library.ts:392-405 的 closeBook/closeChapter 只清状态;晚到响应重新填充状态并启动轮询。已内存执行实际 store 复现 closeBook 后 pending 响应令 timerCount=1。作废对应请求序号/生命周期并合理清理 loading。连同检查 ImportView.vue:58-68:提交过程中离开页面后,不应被晚成功响应强制导航回来。验收:目录页、阅读页在请求完成前离开,旧响应不写回、不重启轮询、不触发意外跳转;重新进入和退出登录行为正常。
  • R4 / P2:重试已被后端接受,随后静默刷新网络失败时,页面永久停留 failed。learner/src/stores/library.ts:371-382 没有先应用 retry 返回状态,silent GET 吞掉异常且未建立轮询。已内存执行实际 store 复现:retry resolve,但 status=failed、error为空、timerCount=0。正确应用 pending 状态并保证后续状态追踪/错误反馈。验收:书籍页与阅读页均覆盖 POST 成功 + 首次 GET 失败 + 网络恢复,自动显示最终结果,无需手工刷新。

后续优化建议(记录,不作为本轮必须实施范围)

  • server/app/lexgo/library.go:472 只返回最新 200 本,无分页/总数/截断提示;超过上限后旧书无法从书库列表访问。后续设计分页或明确限制。
  • server/app/lexgo/library.go:527 目录查询载入每章 original_text;目录应只读取所需列,降低长书开销。

审核证据

  • 本轮独立重跑 Go 全包测试:18 项顶层测试通过,使用专用 lexgo_test_issue5,未操作开发库业务数据。
  • 独立前端审查:31 项单测通过,类型检查/Vite 构建通过;补充模拟网络时序执行实际 store,确认 R3/R4。
  • 两项额外 Go 复现以临时 overlay 执行(非产品源文件改动),确认 R1/R2;这些测试“通过”表示成功证明缺陷存在,并非整改完成。
  • 未据既有 mock E2E 或响应式 CSS 声称真实接口联调/真机验证通过。产品工作区仍干净。

DeepSeek 实施要求与交付

  1. 先读 AGENTS.md、本工单需求及本评论、PR #23;确认实际分支/HEAD 和他人改动。沿 #5 分支提交修复,避免改写历史;不合并 PR,不关闭工单,不推进 #6/#21。
  2. 保持 #5 已确认 Go-only 导入范围:英语、一次粘贴一章、100000 Unicode 码点、原文精确保留、多账号隔离、同请求幂等;不引入 Python NLP,不扩展数据库/权限范围。确需改变长期契约时先在工单说明。
  3. 针对 R1-R4 补有意义的回归测试,再修复;运行受影响 Go/MySQL、前端测试及构建,并补真实 API 的新建/追加/阅读验证。秘密仅从本地安全配置读取,不写入报告。不要重置真实用户或数据库,不重启无关服务。
  4. 在本工单集中回报:每个 R 编号的修复方式、提交哈希、测试命令和结果、真实联调证据、未验证项、文档影响;需要长期文档更新时按 Wiki 为事实来源的规则执行,否则说明无长期文档影响。
  5. 完成后交回主审 agent;主审独立检查差异并复测 R1-R4,给出是否可提交用户验收的结论。最终用户验收前不标记通过。

执行安排:整改说明已准备;当前会话可调用模型不含 DeepSeek,尚未启动或指派替代模型,正在确认用户已有 DeepSeek agent 的接入位置。

工具回退:当前 Gitea MCP 指向 http://ilaer.eicp.net:8418,与项目 https://git.ilapage.cn 不一致,因此本次使用项目既有安全配置,通过目标仓库 Gitea API 追加并回读评论。

## #5 审核整改与实施交接(2026-09-11) 用户已授权:把审核结果更新到本工单,由 DeepSeek agent 实施整改,完成后由当前主审 agent 复审。当前结论:主体已实现,但尚不满足验收要求。工单保持 open,PR #23 不合并;本评论作为最新进度,原需求基线不改写。 审核基线:分支 `feat/5-paste-chapter-reading`,提交 `a55708cd37c7eb4f706d1cf573a54d44692f67ca`;原实现基线为已验收 #4 的 `ed5da31888329873cd87c50237ea111388fe35af`。 ### 必须修复 - [ ] R1 / P1:学习端追加章节返回 HTTP 400。前端 `learner/src/stores/library.ts:341` 对新建和追加共用包含 language 的请求;后端 `server/app/lexgo/library.go:208` 的 PasteChapterInput 不含 language,严格 JSON 解码拒绝未知字段。已用实际 Router + MySQL 测试复现前端格式被拒绝。统一请求契约,追加沿用所属书籍语言,保留未知字段/owner 注入防护。验收:从学习端向已有书籍追加成功,进入第二章,序号与前后章导航正确;补覆盖真实请求格式的回归测试,不仅 mock 成功响应。 - [ ] R2 / P2:后台任务领取后,完成事务超时/取消/数据库异常会遗留 processing;下一批只领取 pending,手动重试又仅接受 failed,因此服务持续运行时无法恢复。位置 `server/app/lexgo/ingest.go:145`、`server/cmd/lexgo/main.go:117-126`。已通过领取后取消上下文复现:下一批处理后仍 processing,人工 retry 被拒绝。补单实例运行期间的异常恢复策略,不能只依赖启动恢复;防止无限快速重试和重复章节,日志不得声称已重试实际却跳过。验收:故障解除后无需重启即可完成或进入有明确原因且允许重试的 failed;原章节/任务 ID 不变;启动恢复仍有效。 - [ ] R3 / P2:离页时没有作废在途加载请求。`learner/src/stores/library.ts:392-405` 的 closeBook/closeChapter 只清状态;晚到响应重新填充状态并启动轮询。已内存执行实际 store 复现 closeBook 后 pending 响应令 timerCount=1。作废对应请求序号/生命周期并合理清理 loading。连同检查 `ImportView.vue:58-68`:提交过程中离开页面后,不应被晚成功响应强制导航回来。验收:目录页、阅读页在请求完成前离开,旧响应不写回、不重启轮询、不触发意外跳转;重新进入和退出登录行为正常。 - [ ] R4 / P2:重试已被后端接受,随后静默刷新网络失败时,页面永久停留 failed。`learner/src/stores/library.ts:371-382` 没有先应用 retry 返回状态,silent GET 吞掉异常且未建立轮询。已内存执行实际 store 复现:retry resolve,但 status=failed、error为空、timerCount=0。正确应用 pending 状态并保证后续状态追踪/错误反馈。验收:书籍页与阅读页均覆盖 POST 成功 + 首次 GET 失败 + 网络恢复,自动显示最终结果,无需手工刷新。 ### 后续优化建议(记录,不作为本轮必须实施范围) - `server/app/lexgo/library.go:472` 只返回最新 200 本,无分页/总数/截断提示;超过上限后旧书无法从书库列表访问。后续设计分页或明确限制。 - `server/app/lexgo/library.go:527` 目录查询载入每章 original_text;目录应只读取所需列,降低长书开销。 ### 审核证据 - 本轮独立重跑 Go 全包测试:18 项顶层测试通过,使用专用 `lexgo_test_issue5`,未操作开发库业务数据。 - 独立前端审查:31 项单测通过,类型检查/Vite 构建通过;补充模拟网络时序执行实际 store,确认 R3/R4。 - 两项额外 Go 复现以临时 overlay 执行(非产品源文件改动),确认 R1/R2;这些测试“通过”表示成功证明缺陷存在,并非整改完成。 - 未据既有 mock E2E 或响应式 CSS 声称真实接口联调/真机验证通过。产品工作区仍干净。 ### DeepSeek 实施要求与交付 1. 先读 AGENTS.md、本工单需求及本评论、PR #23;确认实际分支/HEAD 和他人改动。沿 #5 分支提交修复,避免改写历史;不合并 PR,不关闭工单,不推进 #6/#21。 2. 保持 #5 已确认 Go-only 导入范围:英语、一次粘贴一章、100000 Unicode 码点、原文精确保留、多账号隔离、同请求幂等;不引入 Python NLP,不扩展数据库/权限范围。确需改变长期契约时先在工单说明。 3. 针对 R1-R4 补有意义的回归测试,再修复;运行受影响 Go/MySQL、前端测试及构建,并补真实 API 的新建/追加/阅读验证。秘密仅从本地安全配置读取,不写入报告。不要重置真实用户或数据库,不重启无关服务。 4. 在本工单集中回报:每个 R 编号的修复方式、提交哈希、测试命令和结果、真实联调证据、未验证项、文档影响;需要长期文档更新时按 Wiki 为事实来源的规则执行,否则说明无长期文档影响。 5. 完成后交回主审 agent;主审独立检查差异并复测 R1-R4,给出是否可提交用户验收的结论。最终用户验收前不标记通过。 执行安排:整改说明已准备;当前会话可调用模型不含 DeepSeek,尚未启动或指派替代模型,正在确认用户已有 DeepSeek agent 的接入位置。 工具回退:当前 Gitea MCP 指向 `http://ilaer.eicp.net:8418`,与项目 `https://git.ilapage.cn` 不一致,因此本次使用项目既有安全配置,通过目标仓库 Gitea API 追加并回读评论。
Author
Owner

#5 审核整改完成(R1~R4),待复审

提交:b18f9cc(分支 feat/5-paste-chapter-reading,已推送;未改写历史,未合并任何已有 PR)
PR:#23 仍指向 feat/4-reading-selection-spike,本次只追加提交。
范围:只做 R1~R4;两条“后续优化建议”已转为独立工单 #24,未混入本次提交。未接入 Python NLP,未扩展数据或权限范围。

R1 / P1 学习端追加章节返回 HTTP 400

结论:确认成立。 我先用真实 API 复现:带 language 的追加请求(学习端当时的实际请求体)返回 400 请求内容无效,不带则 201。上一轮的真实联调只覆盖了 API 契约本身,没有覆盖学习端追加路径,这一点由上轮 mock 的成功响应掩盖了,属于我的验证缺口。

修复:学习端把新建与追加拆成两个请求类型(SubmitBookBody / SubmitChapterBody),追加只发送 requestId、title、text;后端保持严格解码与未知字段/owner 注入防护,追加沿用所属书籍的语言。

测试:后端新增断言“追加带 language → 400、不带 → 201”;学习端单测断言新建带 language、追加请求体不含 language(store 与组件各一项,均先在整改前代码上失败)。真实浏览器:从书籍页进入“追加章节”→提交→回到书籍页→新章节就绪→阅读正文逐字符相等,抓取到的请求体无 language。

追加后阅读

R2 / P2 领取后完成事务失败会遗留 processing

结论:确认成立。 claim 先独立提交,FinishIngestJob 失败后任务永久停在 processing,而 claim 只取 pending、人工重试只接受 failed,只有重启能恢复;我每批 30 秒的超时确实能触发该路径。

修复:把启动恢复与运行期恢复合并为同一处清扫——启动阈值 0,运行期每轮清扫使用 15 秒阈值;任务领取次数达到 5 次时置为 failed(原因码 attempts_exhausted,提示“处理多次失败,请重试或重新提交”);人工重试重置尝试次数(人工操作不被自动上限阻塞);worker 每秒先清扫再处理,日志分别说明“已重新入队”与“本批未完成、等待下一次清扫”,不再出现声称已重试实际跳过的表述。任务从不创建章节,因此重复处理不会产生重复章节。

测试:新增 TestMySQLIngestRecoveryWithoutRestart(取消上下文制造完成事务失败 → 运行期清扫恢复 → 同一章节/任务编号完成)与 TestMySQLIngestAttemptsAreBoundedAndManualRetryRestarts(预算用尽 → 停止自动领取 → failed 原因可读 → 人工重试重置后成功)。

真实联调:新建章节后用 SQL 把任务与章节置为 processing 且 updated_at 早于阈值(UTC),不重启服务,运行期清扫在 544 ms 内重新入队并发布为就绪;章节与任务编号不变,正文逐字符相等,全书仍为同一批章节。

R3 / P2 离页未作废在途加载请求

结论:确认成立。 closeBook/closeChapter 只清状态、不推进请求序号,晚到响应会写回状态并重启轮询;ImportView 提交后无条件 router.replace。

修复:两个 close 函数推进各自的请求序号并清理 loading;ImportView 记录是否已卸载,卸载后的成功响应不再触发跳转。

测试:store 两项(目录页、阅读页在响应到达前关闭 → 不写回、不重启轮询)与组件一项(提交中离开后不跳转),均在整改前代码上失败。

R4 / P2 重试后静默刷新失败会永久停在 failed

结论:确认成立。 retryChapter 没有应用重试返回的状态,静默刷新吞掉异常且不安排轮询,一次网络抖动就会让页面停在处理失败。

修复:重试被接受后先把返回的 pending 状态应用到章节列表与阅读器并重新安排轮询,再做静默刷新。

测试:store 两项(阅读页、书籍页:POST 成功 + 首次 GET 失败 + 网络恢复后自动显示就绪),整改前失败。

真实联调:用 SQL 制造真实失败任务(content_changed)后,在浏览器点击“重试”并中断紧随其后的第一次刷新,页面立即由“处理失败”变为“处理中”,随后自行变为“已就绪”,无需手工刷新。

重试自愈

回归测试的有效性(整改前实际失败)

7 项新增前端用例在整改前的 library.ts/ImportView.vue 上运行并实际失败:追加发送 language、目录页/阅读页晚到响应写回、阅读页/书籍页重试后停在失败、组件级追加请求体、提交中离页后跳转;改回修复版本后 38 项全部通过。后端两项新用例覆盖运行期恢复与尝试上限。

测试命令与结果

命令 结果
python scripts/server.py test-integration(LEXGO_TEST_DB_NAME=lexgo_test_issue5) 20 个顶层用例通过(#5 现为 10 项 + 12 子用例)
npx --yes pnpm@9.15.1 --dir learner test:unit --run 38 项通过(library 21、reading 8、session 9)
npx --yes pnpm@9.15.1 --dir learner build 通过(vue-tsc + vite)
npx --yes pnpm@9.15.1 --dir learner test:e2e 3 项通过(虚构 API 响应)
python -m unittest discover -s tests / harness.py check --strict 56 项通过 / 严格检查通过

需要主审与用户知晓的契约变化

  1. POST /api/v1/books/:id/chapters 不接受 language(发送即 400);追加沿用书籍语言。
  2. 同一导入任务的自动领取上限为 5 次,超过后任务与章节为 failed,原因码 attempts_exhausted。
  3. 人工重试(POST /jobs/:id/retry)会把尝试次数重置为 0,并保留原章节与任务编号。
  4. 运行期清扫阈值为 15 秒;正常领取到完成的窗口远小于该值,重复处理不会产生重复章节。

未验证与限制

  • R3 的真实浏览器时序(离页与响应同时发生)仍只由单测覆盖;真实联调未构造该竞态。
  • #4 真机手机证据缺口不变,本轮未做真机测试。
  • #24(书库分页/截断提示、目录列选择)未实施。
  • 本机 fixture:lexgo_dev 的 issue5_a 名下新增了几条用于 R1/R2/R4 验证的虚构章节(含一条已就绪的追加章节与一条失败后重试成功的章节),未改动其他账号;测试库仍为专用 lexgo_test_issue5。
  • 推送时本机凭据助手出现一次交互提示导致首次 push 失败,重试即成功;未改变提交内容。

文档

Wiki 先写后回读,再导出核心镜像(镜像含于提交 b18f9cc):

  • Architecture-and-Code-Map:f981b86a7c6e5d2e353142d3006dcd6e0636c3ee
  • Business-Rules-and-Glossary:4bd843d1f0134f5688407b7072d98333b40b6da1
  • Local-Development-and-Verification:3bce5fae964b1050513c37319262679814d68f14

本地验证页同时记录了 MySQL 会话时区为 SYSTEM(本机 UTC+8)而服务按 UTC 存储 DATETIME 的差异:核对任务时间需用 UTC_TIMESTAMP,用 NOW() 会产生 8 小时假偏差(我自己第一次构造 R2 场景时就踩到,已更正后复测)。

状态保持待验收,未关闭工单,交回主审独立检查与复测。

## #5 审核整改完成(R1~R4),待复审 **提交**:`b18f9cc`(分支 `feat/5-paste-chapter-reading`,已推送;未改写历史,未合并任何已有 PR) **PR**:[#23](https://git.ilapage.cn/OPC/lexgo/pulls/23) 仍指向 `feat/4-reading-selection-spike`,本次只追加提交。 **范围**:只做 R1~R4;两条“后续优化建议”已转为独立工单 [#24](https://git.ilapage.cn/OPC/lexgo/issues/24),未混入本次提交。未接入 Python NLP,未扩展数据或权限范围。 ### R1 / P1 学习端追加章节返回 HTTP 400 **结论:确认成立。** 我先用真实 API 复现:带 `language` 的追加请求(学习端当时的实际请求体)返回 **400 请求内容无效**,不带则 201。上一轮的真实联调只覆盖了 API 契约本身,没有覆盖学习端追加路径,这一点由上轮 mock 的成功响应掩盖了,属于我的验证缺口。 **修复**:学习端把新建与追加拆成两个请求类型(`SubmitBookBody` / `SubmitChapterBody`),追加只发送 requestId、title、text;后端保持严格解码与未知字段/owner 注入防护,追加沿用所属书籍的语言。 **测试**:后端新增断言“追加带 language → 400、不带 → 201”;学习端单测断言新建带 language、追加请求体不含 language(store 与组件各一项,均先在整改前代码上失败)。**真实浏览器**:从书籍页进入“追加章节”→提交→回到书籍页→新章节就绪→阅读正文逐字符相等,抓取到的请求体无 language。 ![追加后阅读](https://git.ilapage.cn/attachments/2a57ddc4-91be-44d1-8cb9-6c1c43b9c623) ### R2 / P2 领取后完成事务失败会遗留 processing **结论:确认成立。** claim 先独立提交,`FinishIngestJob` 失败后任务永久停在 processing,而 claim 只取 pending、人工重试只接受 failed,只有重启能恢复;我每批 30 秒的超时确实能触发该路径。 **修复**:把启动恢复与运行期恢复合并为同一处清扫——启动阈值 0,运行期每轮清扫使用 **15 秒**阈值;任务领取次数达到 **5 次**时置为 failed(原因码 `attempts_exhausted`,提示“处理多次失败,请重试或重新提交”);**人工重试重置尝试次数**(人工操作不被自动上限阻塞);worker 每秒先清扫再处理,日志分别说明“已重新入队”与“本批未完成、等待下一次清扫”,不再出现声称已重试实际跳过的表述。任务从不创建章节,因此重复处理不会产生重复章节。 **测试**:新增 `TestMySQLIngestRecoveryWithoutRestart`(取消上下文制造完成事务失败 → 运行期清扫恢复 → 同一章节/任务编号完成)与 `TestMySQLIngestAttemptsAreBoundedAndManualRetryRestarts`(预算用尽 → 停止自动领取 → failed 原因可读 → 人工重试重置后成功)。 **真实联调**:新建章节后用 SQL 把任务与章节置为 processing 且 `updated_at` 早于阈值(UTC),**不重启服务**,运行期清扫在 **544 ms** 内重新入队并发布为就绪;章节与任务编号不变,正文逐字符相等,全书仍为同一批章节。 ### R3 / P2 离页未作废在途加载请求 **结论:确认成立。** `closeBook`/`closeChapter` 只清状态、不推进请求序号,晚到响应会写回状态并重启轮询;`ImportView` 提交后无条件 `router.replace`。 **修复**:两个 close 函数推进各自的请求序号并清理 loading;`ImportView` 记录是否已卸载,卸载后的成功响应不再触发跳转。 **测试**:store 两项(目录页、阅读页在响应到达前关闭 → 不写回、不重启轮询)与组件一项(提交中离开后不跳转),均在整改前代码上失败。 ### R4 / P2 重试后静默刷新失败会永久停在 failed **结论:确认成立。** `retryChapter` 没有应用重试返回的状态,静默刷新吞掉异常且不安排轮询,一次网络抖动就会让页面停在处理失败。 **修复**:重试被接受后先把返回的 pending 状态应用到章节列表与阅读器并重新安排轮询,再做静默刷新。 **测试**:store 两项(阅读页、书籍页:POST 成功 + 首次 GET 失败 + 网络恢复后自动显示就绪),整改前失败。 **真实联调**:用 SQL 制造真实失败任务(`content_changed`)后,在浏览器点击“重试”并中断紧随其后的第一次刷新,页面立即由“处理失败”变为“处理中”,随后自行变为“已就绪”,无需手工刷新。 ![重试自愈](https://git.ilapage.cn/attachments/561d1caa-1e47-4a57-b346-13bc4753a789) ### 回归测试的有效性(整改前实际失败) 7 项新增前端用例在整改前的 `library.ts`/`ImportView.vue` 上运行并实际失败:追加发送 language、目录页/阅读页晚到响应写回、阅读页/书籍页重试后停在失败、组件级追加请求体、提交中离页后跳转;改回修复版本后 38 项全部通过。后端两项新用例覆盖运行期恢复与尝试上限。 ### 测试命令与结果 | 命令 | 结果 | |---|---| | `python scripts/server.py test-integration`(LEXGO_TEST_DB_NAME=lexgo_test_issue5) | **20 个顶层用例通过**(#5 现为 10 项 + 12 子用例) | | `npx --yes pnpm@9.15.1 --dir learner test:unit --run` | **38 项通过**(library 21、reading 8、session 9) | | `npx --yes pnpm@9.15.1 --dir learner build` | 通过(vue-tsc + vite) | | `npx --yes pnpm@9.15.1 --dir learner test:e2e` | 3 项通过(虚构 API 响应) | | `python -m unittest discover -s tests` / `harness.py check --strict` | 56 项通过 / 严格检查通过 | ### 需要主审与用户知晓的契约变化 1. `POST /api/v1/books/:id/chapters` 不接受 `language`(发送即 400);追加沿用书籍语言。 2. 同一导入任务的自动领取上限为 5 次,超过后任务与章节为 failed,原因码 `attempts_exhausted`。 3. 人工重试(`POST /jobs/:id/retry`)会把尝试次数重置为 0,并保留原章节与任务编号。 4. 运行期清扫阈值为 15 秒;正常领取到完成的窗口远小于该值,重复处理不会产生重复章节。 ### 未验证与限制 - R3 的真实浏览器时序(离页与响应同时发生)仍只由单测覆盖;真实联调未构造该竞态。 - #4 真机手机证据缺口不变,本轮未做真机测试。 - #24(书库分页/截断提示、目录列选择)未实施。 - 本机 fixture:lexgo_dev 的 issue5_a 名下新增了几条用于 R1/R2/R4 验证的虚构章节(含一条已就绪的追加章节与一条失败后重试成功的章节),未改动其他账号;测试库仍为专用 lexgo_test_issue5。 - 推送时本机凭据助手出现一次交互提示导致首次 push 失败,重试即成功;未改变提交内容。 ### 文档 Wiki 先写后回读,再导出核心镜像(镜像含于提交 `b18f9cc`): - Architecture-and-Code-Map:`f981b86a7c6e5d2e353142d3006dcd6e0636c3ee` - Business-Rules-and-Glossary:`4bd843d1f0134f5688407b7072d98333b40b6da1` - Local-Development-and-Verification:`3bce5fae964b1050513c37319262679814d68f14` 本地验证页同时记录了 MySQL 会话时区为 SYSTEM(本机 UTC+8)而服务按 UTC 存储 DATETIME 的差异:核对任务时间需用 `UTC_TIMESTAMP`,用 `NOW()` 会产生 8 小时假偏差(我自己第一次构造 R2 场景时就踩到,已更正后复测)。 状态保持**待验收**,未关闭工单,交回主审独立检查与复测。
Author
Owner

#5 整改独立复审(2026-09-11)

审核提交:b18f9cc,相对 a55708c 的 11 个文件变更;已核对 PR #23 的当前提交与本地一致,工作区干净。

结论:R1~R4 的修复合理,本轮未发现阻止提交用户验收的新问题。在当前单实例、同一 worker 串行清扫和处理的前提下,可以进入用户验收;这不代替用户验收,工单保持 open,PR 不合并。

  • R1:新建与追加请求类型分开,追加不带 language;后端继续严格解码。前后端新增用例覆盖此契约。
  • R2:运行期清扫恢复未完成任务,5 次尝试上限防止持续自动重试,人工重试重置预算并复用任务/章节,方案符合当前 Go-only 单实例范围。
  • R3:closeBook/closeChapter 作废在途请求并清 loading;ImportView 卸载后不再跳转。
  • R4:先应用 retry 响应中的 pending 状态并启动轮询,再静默刷新;首个刷新失败后仍会继续追踪。

本轮独立验证:专用 lexgo_test_issue5 上 Go 全包 20 项顶层用例通过;独立前端审查执行 38 项单测和 vue-tsc/Vite 构建,均通过。已核查新增回归用例,不只依据上一条交付声明。

说明与非阻塞建议:

  1. 15 秒是清扫资格阈值,不是完成恢复的时限;每轮还受处理耗时影响。544 ms 是人为设置已超时任务后的观测值,不能解释为从故障发生起的完整恢复时间。
  2. ingest.go 注释中“15 秒大于最长合法处理窗口”的表述不严谨:批处理上下文允许 30 秒。当前安全前提是清扫与处理在同一 worker 串行执行;以后增加并行 worker 或多实例时必须重新设计领取/租约保护,不能照搬这套清扫逻辑。
  3. 本轮未重新执行真实浏览器联调或真机测试。R3 浏览器竞态及 #4 真机证据缺口仍如交付评论所述,不标记已验证。R1/R4 的真实浏览器操作记录来自实施者,独立复审范围为代码和上述测试。
  4. #24 承接列表分页/截断提示与目录列选择是合理的,未混入本次整改。

未修改产品代码、未合并 PR、未关闭工单。长期事实无新增变化,本次只追加复审证据,不更新 Wiki。

工具回退沿用本单记录:Gitea MCP 指向不同站点,使用目标 git.ilapage.cn 的既有安全配置调用 API。

## #5 整改独立复审(2026-09-11) 审核提交:b18f9cc,相对 a55708c 的 11 个文件变更;已核对 PR #23 的当前提交与本地一致,工作区干净。 结论:R1~R4 的修复合理,本轮未发现阻止提交用户验收的新问题。在当前单实例、同一 worker 串行清扫和处理的前提下,可以进入用户验收;这不代替用户验收,工单保持 open,PR 不合并。 - R1:新建与追加请求类型分开,追加不带 language;后端继续严格解码。前后端新增用例覆盖此契约。 - R2:运行期清扫恢复未完成任务,5 次尝试上限防止持续自动重试,人工重试重置预算并复用任务/章节,方案符合当前 Go-only 单实例范围。 - R3:closeBook/closeChapter 作废在途请求并清 loading;ImportView 卸载后不再跳转。 - R4:先应用 retry 响应中的 pending 状态并启动轮询,再静默刷新;首个刷新失败后仍会继续追踪。 本轮独立验证:专用 lexgo_test_issue5 上 Go 全包 20 项顶层用例通过;独立前端审查执行 38 项单测和 vue-tsc/Vite 构建,均通过。已核查新增回归用例,不只依据上一条交付声明。 说明与非阻塞建议: 1. 15 秒是清扫资格阈值,不是完成恢复的时限;每轮还受处理耗时影响。544 ms 是人为设置已超时任务后的观测值,不能解释为从故障发生起的完整恢复时间。 2. ingest.go 注释中“15 秒大于最长合法处理窗口”的表述不严谨:批处理上下文允许 30 秒。当前安全前提是清扫与处理在同一 worker 串行执行;以后增加并行 worker 或多实例时必须重新设计领取/租约保护,不能照搬这套清扫逻辑。 3. 本轮未重新执行真实浏览器联调或真机测试。R3 浏览器竞态及 #4 真机证据缺口仍如交付评论所述,不标记已验证。R1/R4 的真实浏览器操作记录来自实施者,独立复审范围为代码和上述测试。 4. #24 承接列表分页/截断提示与目录列选择是合理的,未混入本次整改。 未修改产品代码、未合并 PR、未关闭工单。长期事实无新增变化,本次只追加复审证据,不更新 Wiki。 工具回退沿用本单记录:Gitea MCP 指向不同站点,使用目标 git.ilapage.cn 的既有安全配置调用 API。
Author
Owner

#5 用户确认结单(2026-09-11)

在主审报告 R1~R4 技术复审通过、可进入用户验收后,用户回复“可以结单吗”,本次按该确认结单。

验收版本:b18f9cc4a54dfb7ef4fe9f882a1b4d6a10c27997。实施与整改证据见评论 7651、7677,独立复审见评论 7686:Go 20 项顶层测试、前端 38 项单测及构建通过。

确认关闭本单;不宣称用户已另行执行完整手工测试。R3 真实浏览器竞态和 #4 真机详细证据仍未补齐,保留未验证说明;手机相关完整证据在后续体验/交付工作继续验证。正文最后一项包含手机验收,保持未勾选,避免把未验证内容标为通过。后续书库分页/截断提示与目录查询优化由 #24 跟踪。

本次仅更新工单验收状态与父单 #16 进度;不合并 PR #23 或其他 PR,不自动实施 #6/#21/#24,不改动产品代码和数据库。无新增长期技术契约,不修改 Wiki。Gitea API 回退原因沿用本单记录:MCP 指向其他站点。

## #5 用户确认结单(2026-09-11) 在主审报告 R1~R4 技术复审通过、可进入用户验收后,用户回复“可以结单吗”,本次按该确认结单。 验收版本:b18f9cc4a54dfb7ef4fe9f882a1b4d6a10c27997。实施与整改证据见评论 7651、7677,独立复审见评论 7686:Go 20 项顶层测试、前端 38 项单测及构建通过。 确认关闭本单;不宣称用户已另行执行完整手工测试。R3 真实浏览器竞态和 #4 真机详细证据仍未补齐,保留未验证说明;手机相关完整证据在后续体验/交付工作继续验证。正文最后一项包含手机验收,保持未勾选,避免把未验证内容标为通过。后续书库分页/截断提示与目录查询优化由 #24 跟踪。 本次仅更新工单验收状态与父单 #16 进度;不合并 PR #23 或其他 PR,不自动实施 #6/#21/#24,不改动产品代码和数据库。无新增长期技术契约,不修改 Wiki。Gitea API 回退原因沿用本单记录:MCP 指向其他站点。
ila closed this issue 2026-09-11 11:27:25 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/lexgo#5