MVP 搜索、筛选并编辑自己的词语和短语 #12

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

来源与目标

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

在词汇库分页浏览个人词语/短语,按文本和状态筛选,编辑后与阅读、复习一致。

验收标准

  • 搜索、状态筛选和分页可组合,空结果可清除筛选;大小写和匹配规则明确。
  • 编辑沿用同一领域保存,不能绕过状态/复习规则;返回列表保留筛选。
  • 任何条件下均按当前用户/语言过滤,测试越权、分页边界及真实搜索输入。
  • 不包含 CSV 导入导出或复杂复习范围筛选。

依赖与执行

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

参考模块与设计证据

go-admin 分页/查询 DTO 模式;学习端按已验收词汇库布局。

复用已验收列表、空结果和编辑原型。

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

工作量、范围与风险

预计 2~4 人日(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。阶段 4;覆盖 F09。 在词汇库分页浏览个人词语/短语,按文本和状态筛选,编辑后与阅读、复习一致。 ## 验收标准 - [ ] 搜索、状态筛选和分页可组合,空结果可清除筛选;大小写和匹配规则明确。 - [ ] 编辑沿用同一领域保存,不能绕过状态/复习规则;返回列表保留筛选。 - [ ] 任何条件下均按当前用户/语言过滤,测试越权、分页边界及真实搜索输入。 - [ ] 不包含 CSV 导入导出或复杂复习范围筛选。 ## 依赖与执行 前置:#11。状态:待验收(2026-09-11,提交 3ebec5b,PR #31;契约见评论 7925,实施证据见最新评论)。前置尚未通过时不得标进行中;每单完成停在待验收,由用户验收后关闭。技术验证可与不依赖其结论的工作分工,但不提前冻结未验证契约。 ## 参考模块与设计证据 go-admin 分页/查询 DTO 模式;学习端按已验收词汇库布局。 复用已验收列表、空结果和编辑原型。 后端不为建表/API 单独画页面原型;先写数据、接口、状态、隔离与幂等契约。学习端采用 #1 已验收 v1,未做的 LinguaCafe 对照不标完成。 ## 工作量、范围与风险 预计 2~4 人日(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

#12 方案确认与实施启动(2026-09-11)

用户在当前会话确认「都按你说的」,同意契约 D1~D11。前置 #11 已通过验收;本单分支 feat/12-vocabulary 从 main 329ea73 创建,实施期间工单状态进入「进行中」,完成测试后停在「待验收」。

现状与只读诊断

lexgo_terms 已同时保存单词与短语(身份键、原文、释义、例句、状态、等级),#11 后又有了 kind/词数派生;但没有列表/搜索/筛选接口,也没有按 id 编辑词条的接口。分页可沿用审计列表 queryAudit 的既有规则(查询键白名单、page ≥ 1、limit 1..100、非法值 400)。原型的词汇库页面提供搜索框「搜索词 / 释义」、筛选按钮「筛选:短语 · 学习中」、无结果页「没有匹配的词条」与「清除搜索与筛选」,以及编辑页的释义/例句/状态与保存修改。

契约

D1 列表与分页:GET /api/v1/terms?query=&status=&kind=&page=&limit= 返回 {items,total,page,limit};默认 page=1、limit=20、上限 100;排序 updated_at DESC, id DESC;查询键白名单与边界校验与审计列表一致(未知键、重复键、非法值一律 400)。

D2 搜索规则:命中规范化身份键、显示原文与个人释义三处任一;大小写不敏感(查询转小写后与二进制排序规则的 term 比较);子串匹配、不做分词;空查询不过滤;去首尾空白且 ≤64 字符;%、_、\ 先转义再作为 LIKE 值,避免输入 % 命中全部。

D3 筛选:status ∈ {new, learning, known, ignored}、kind ∈ {word, phrase},均可为空;可与搜索组合;非法枚举值 400,不静默忽略。

D4 按 id 编辑:PATCH /api/v1/terms/:id,body {definition?, examples?, status?, level?};复用 termContent/termLevel 的同一套领域校验;身份不可编辑(term/original_form/kind 不接受客户端输入,未知字段 400);状态或等级变化才重排(进入 learning → now+间隔[等级],改回 new → 立即到期,改成 known/ignored → 不再进队列),只改文本不动排期;历史作答记录与计数保留;他人 404、未登录 401、空 body 400。

D5 等级 UI:编辑对话框在状态为「学习中」时显示 1~7 等级选择器(兑现 #7/#11 的约定),其他状态不显示且提交 level=0。

D6 来源章节不实现:不显示原型的「来自 A small step」,因为词条按身份存储、不引用章节(#10/#11 已确认规则);该偏差写入文档与工单。

D7 搜索触发:沿用原型的显式搜索按钮 + Enter,不即输即搜;筛选变化立即请求;前端用 seq/generation 丢弃旧响应。

D8 URL 同步:/vocab?query=&status=&kind=&page=(replace),刷新与返回保留筛选,关闭编辑对话框回列表时保留筛选。

D9 列表行:显示 词 / 释义 · 状态(短语标「短语」)、编辑按钮与「开始到期复习」入口;状态与阅读器高亮、复习队列同源。

D10 接口与页面:新增 GET /api/v1/terms 与 PATCH /api/v1/terms/:id;前端新增 /vocab 路由与导航、stores/vocabulary.ts、VocabView.vue,并把释义/例句/状态/等级字段抽成共享组件 TermFormFields.vue,供阅读器面板与词汇库对话框复用,避免两处规则漂移。

D11 schema:无变化,无迁移。

测试计划

  • Go 单测:查询解析与边界(未知键/重复键/page=0/limit=101/非法 status 与 kind)、LIKE 通配符转义、空查询与去空白、查询长度上限。
  • Go 集成(专用库 lexgo_test_issue9):三处命中(键、原文、中文释义)、大小写不敏感、搜索与筛选组合、分页(首/末/越界、total、排序稳定)、只返回本人与当前语言、编辑(只改文本不动排期、改状态重排、进入 learning 的等级、known/ignored 立刻离开到期队列、计数保留)、幂等重复提交、非法输入与越权。
  • 学习端:store 与 view 单测(列表、搜索、筛选、翻页、编辑、等级选择器、空态、无结果与清除、错误保留输入、旧响应丢弃)+ mock E2E;真实 API+MySQL 与真实浏览器复跑。
  • 文档:Architecture-and-Code-Map、Business-Rules-and-Glossary、Local-Development-and-Verification,需求变化更新 Product-Requirements-Overview。

非目标:CSV 导入导出(X04)、复杂复习范围筛选(X11)、删除词条或批量清理(X14)、来源章节字段、批量操作。

回退说明:本会话 pi 无可用 Gitea MCP 工具,沿用本单既定回退,使用目标 git.ilapage.cn API;凭据仅从既有安全配置读入进程。

## #12 方案确认与实施启动(2026-09-11) 用户在当前会话确认「都按你说的」,同意契约 D1~D11。前置 #11 已通过验收;本单分支 `feat/12-vocabulary` 从 main `329ea73` 创建,实施期间工单状态进入「进行中」,完成测试后停在「待验收」。 ### 现状与只读诊断 `lexgo_terms` 已同时保存单词与短语(身份键、原文、释义、例句、状态、等级),`#11` 后又有了 `kind`/词数派生;但**没有列表/搜索/筛选接口,也没有按 id 编辑词条的接口**。分页可沿用审计列表 `queryAudit` 的既有规则(查询键白名单、`page ≥ 1`、`limit 1..100`、非法值 400)。原型的词汇库页面提供搜索框「搜索词 / 释义」、筛选按钮「筛选:短语 · 学习中」、无结果页「没有匹配的词条」与「清除搜索与筛选」,以及编辑页的释义/例句/状态与保存修改。 ### 契约 **D1 列表与分页**:`GET /api/v1/terms?query=&status=&kind=&page=&limit=` 返回 `{items,total,page,limit}`;默认 `page=1`、`limit=20`、上限 100;排序 `updated_at DESC, id DESC`;查询键白名单与边界校验与审计列表一致(未知键、重复键、非法值一律 400)。 **D2 搜索规则**:命中规范化身份键、显示原文与个人释义三处任一;大小写不敏感(查询转小写后与二进制排序规则的 `term` 比较);子串匹配、不做分词;空查询不过滤;去首尾空白且 ≤64 字符;`%`、`_`、`\` 先转义再作为 LIKE 值,避免输入 `%` 命中全部。 **D3 筛选**:`status` ∈ {new, learning, known, ignored}、`kind` ∈ {word, phrase},均可为空;可与搜索组合;非法枚举值 400,不静默忽略。 **D4 按 id 编辑**:`PATCH /api/v1/terms/:id`,body `{definition?, examples?, status?, level?}`;复用 `termContent`/`termLevel` 的同一套领域校验;**身份不可编辑**(`term`/`original_form`/kind 不接受客户端输入,未知字段 400);状态或等级变化才重排(进入 `learning` → now+间隔[等级],改回 `new` → 立即到期,改成 `known`/`ignored` → 不再进队列),只改文本不动排期;历史作答记录与计数保留;他人 404、未登录 401、空 body 400。 **D5 等级 UI**:编辑对话框在状态为「学习中」时显示 1~7 等级选择器(兑现 #7/#11 的约定),其他状态不显示且提交 level=0。 **D6 来源章节不实现**:不显示原型的「来自 A small step」,因为词条按身份存储、不引用章节(#10/#11 已确认规则);该偏差写入文档与工单。 **D7 搜索触发**:沿用原型的显式搜索按钮 + Enter,不即输即搜;筛选变化立即请求;前端用 seq/generation 丢弃旧响应。 **D8 URL 同步**:`/vocab?query=&status=&kind=&page=`(`replace`),刷新与返回保留筛选,关闭编辑对话框回列表时保留筛选。 **D9 列表行**:显示 `词 / 释义 · 状态`(短语标「短语」)、编辑按钮与「开始到期复习」入口;状态与阅读器高亮、复习队列同源。 **D10 接口与页面**:新增 `GET /api/v1/terms` 与 `PATCH /api/v1/terms/:id`;前端新增 `/vocab` 路由与导航、`stores/vocabulary.ts`、`VocabView.vue`,并把释义/例句/状态/等级字段抽成共享组件 `TermFormFields.vue`,供阅读器面板与词汇库对话框复用,避免两处规则漂移。 **D11 schema**:无变化,无迁移。 ### 测试计划 - Go 单测:查询解析与边界(未知键/重复键/`page=0`/`limit=101`/非法 status 与 kind)、LIKE 通配符转义、空查询与去空白、查询长度上限。 - Go 集成(专用库 `lexgo_test_issue9`):三处命中(键、原文、中文释义)、大小写不敏感、搜索与筛选组合、分页(首/末/越界、total、排序稳定)、只返回本人与当前语言、编辑(只改文本不动排期、改状态重排、进入 learning 的等级、known/ignored 立刻离开到期队列、计数保留)、幂等重复提交、非法输入与越权。 - 学习端:store 与 view 单测(列表、搜索、筛选、翻页、编辑、等级选择器、空态、无结果与清除、错误保留输入、旧响应丢弃)+ mock E2E;真实 API+MySQL 与真实浏览器复跑。 - 文档:Architecture-and-Code-Map、Business-Rules-and-Glossary、Local-Development-and-Verification,需求变化更新 Product-Requirements-Overview。 非目标:CSV 导入导出(X04)、复杂复习范围筛选(X11)、删除词条或批量清理(X14)、来源章节字段、批量操作。 回退说明:本会话 pi 无可用 Gitea MCP 工具,沿用本单既定回退,使用目标 `git.ilapage.cn` API;凭据仅从既有安全配置读入进程。
Author
Owner

#12 实施完成,待用户验收(2026-09-11)

用户确认的契约 D1~D11 见评论 7925。分支 feat/12-vocabulary 从 main 329ea73 创建,实现提交 3ebec5b,已推送。PR #31 未合并,工单不关闭,停在待用户验收。

实现与差异

  • server/app/lexgo/vocabulary.go:GET /api/v1/terms 列表与 PATCH /api/v1/terms/:id 编辑,不改 schema(延续 #11 的「身份键派生 kind/词数」)。
  • 列表:查询键白名单 + page ≥ 1 + limit ≤ 100(默认 20)+ 枚举值校验,非法一律 400;排序 updated_at DESC, id DESC;搜索同时匹配规范化身份键、显示原文与个人释义,查询转小写后与二进制排序规则的 term 比较,%/_/\ 经 escapeLike 转义成字面值;status 与 kind 可与搜索组合。
  • 编辑:复用 termContent 与 termLevel 的同一套校验,身份(词形、原文、kind)不接受客户端输入,未知字段 400;只有状态或等级变化才 syncTermReview(reschedule=true),只改文本时保留原排期;历史作答记录与计数保留;空 body 400、他人 404、未登录 401。
  • 学习端:/vocab 路由与三处导航入口、stores/vocabulary.ts(列表状态与筛选持久在 store,旧响应按 seq/generation 丢弃)、views/VocabView.vue(显式搜索按钮 + Enter、状态与类型筛选、分页、空态、无结果与一键清除、编辑对话框、筛选与页码写入 URL);释义/例句/状态/等级抽成 TermFormFields.vue 与阅读器面板共用,等级选择器只在词汇库对话框出现(阅读器面板仍只有四个状态)。
  • 已知偏差:原型编辑页的「来自 A small step」不实现——词条按身份存储、不引用章节;已写入文档与工单。

实际验证

验证 结果
go vet ./... 通过
LEXGO_TEST_DB_NAME=lexgo_test_issue9 python scripts/server.py test-integration 69 个顶层用例全部通过、0 跳过(原 65,新增 4 个用例)
learner npx vitest --run / vue-tsc --build / pnpm run build / playwright test 120 项单测、类型检查、构建、13 项 E2E 通过(新增 13 单测与 2 项 E2E)
admin pnpm test / pnpm lint 31 项与 lint 通过;管理端本单无代码改动
python -m unittest discover -s tests / harness.py check --strict / sync --check 56 项、严格检查、镜像一致
真实 API+MySQL 40 项检查通过(脚本可重复运行并自行恢复 fixture)
真实浏览器 进入生词本 → 按类型筛选 → 搜索 → 编辑释义 → 保存 → 刷新后筛选仍保留 → 还原释义,闭环通过

覆盖:查询解析与边界、LIKE 转义、三处命中、大小写不敏感、筛选组合、分页首/末/越界页与 total、只返回本人与当前语言、编辑的排期规则(只改文本不动排期、改状态重排、进入学习中按等级排期、已知或忽略离开队列、改回新词立即到期)、计数与作答记录保留、重复提交无副作用、身份不可编辑、越权与未登录;前端覆盖列表分页、搜索筛选与 URL 恢复、空态与清除、等级选择器、保存失败保留输入、旧响应丢弃。

本单不改数据库结构,无迁移;本机 lexgo-api 已用新二进制重启(schema 仍为 v6),/healthz 200,回退用二进制保存在 .local/lexgo-pre-issue12.exe。

未验证与边界

  • 真实手机触屏与滚动证据仍属既有缺口(#14 承接);本单只用桌面浏览器检查。
  • 搜索只做子串匹配,未做分词或相关度排序;大词汇量性能未专门压测(单页上限 100,查询走 owner_id + language 的既有前缀索引)。
  • CSV 导入导出(X04)、复杂复习范围筛选(X11)、删除与批量清理(X14)不在本单。

文档

  • Architecture-and-Code-Map: 3fb79b56d07fa08eb9231642bff43a5270e8ef9a
  • Business-Rules-and-Glossary: a47d8a5e230d2aea2067ed40004694af77ce3527
  • Local-Development-and-Verification: de3225fdb9662684a3d61e8eedd5bf3c0043c0f6
  • Product-Requirements-Overview: 3e8c682ff6f129e0783edb8cfbcb63eb2995de0c
  • Home: fe4d28092eb34171dd2e8d43b28984295b6066ce
  • Project-Profile 本单未变化,仍为 ac9f61cf752f68ab48a41c917c265a3dc060cdcc

回退

本单无 schema 变化:停止 lexgo-api,恢复 .local/lexgo-pre-issue12.exe,重新启动即可;已保存与已编辑的词条按数据库现状保留。

Gitea MCP 仍指向其他站点,沿用目标站点 API 回退;凭据仅从既有安全配置读入进程。

附件:issue12-vocab-list.png、issue12-vocab-none.png、issue12-vocab-edit.png(mock 浏览器流程)与 issue12-real-list.png、issue12-real-search.png、issue12-real-edit.png(真实 API+MySQL 链路);本会话模型不能读取图片,功能断言来自程序化检查。

## #12 实施完成,待用户验收(2026-09-11) 用户确认的契约 D1~D11 见评论 [7925](https://git.ilapage.cn/OPC/lexgo/issues/12#issuecomment-7925)。分支 `feat/12-vocabulary` 从 main `329ea73` 创建,实现提交 `3ebec5b`,已推送。PR [#31](https://git.ilapage.cn/OPC/lexgo/pulls/31) 未合并,工单不关闭,停在待用户验收。 ### 实现与差异 - `server/app/lexgo/vocabulary.go`:`GET /api/v1/terms` 列表与 `PATCH /api/v1/terms/:id` 编辑,**不改 schema**(延续 #11 的「身份键派生 kind/词数」)。 - 列表:查询键白名单 + `page ≥ 1` + `limit ≤ 100`(默认 20)+ 枚举值校验,非法一律 400;排序 `updated_at DESC, id DESC`;搜索同时匹配规范化身份键、显示原文与个人释义,查询转小写后与二进制排序规则的 `term` 比较,`%`/`_`/`\` 经 `escapeLike` 转义成字面值;`status` 与 `kind` 可与搜索组合。 - 编辑:复用 `termContent` 与 `termLevel` 的同一套校验,身份(词形、原文、kind)不接受客户端输入,未知字段 400;只有状态或等级变化才 `syncTermReview(reschedule=true)`,只改文本时保留原排期;历史作答记录与计数保留;空 body 400、他人 404、未登录 401。 - 学习端:`/vocab` 路由与三处导航入口、`stores/vocabulary.ts`(列表状态与筛选持久在 store,旧响应按 seq/generation 丢弃)、`views/VocabView.vue`(显式搜索按钮 + Enter、状态与类型筛选、分页、空态、无结果与一键清除、编辑对话框、筛选与页码写入 URL);释义/例句/状态/等级抽成 `TermFormFields.vue` 与阅读器面板共用,等级选择器只在词汇库对话框出现(阅读器面板仍只有四个状态)。 - 已知偏差:原型编辑页的「来自 A small step」不实现——词条按身份存储、不引用章节;已写入文档与工单。 ### 实际验证 | 验证 | 结果 | |---|---| | `go vet ./...` | 通过 | | `LEXGO_TEST_DB_NAME=lexgo_test_issue9 python scripts/server.py test-integration` | 69 个顶层用例全部通过、0 跳过(原 65,新增 4 个用例) | | learner `npx vitest --run` / `vue-tsc --build` / `pnpm run build` / `playwright test` | 120 项单测、类型检查、构建、13 项 E2E 通过(新增 13 单测与 2 项 E2E) | | admin `pnpm test` / `pnpm lint` | 31 项与 lint 通过;管理端本单无代码改动 | | `python -m unittest discover -s tests` / `harness.py check --strict` / `sync --check` | 56 项、严格检查、镜像一致 | | 真实 API+MySQL | 40 项检查通过(脚本可重复运行并自行恢复 fixture) | | 真实浏览器 | 进入生词本 → 按类型筛选 → 搜索 → 编辑释义 → 保存 → 刷新后筛选仍保留 → 还原释义,闭环通过 | 覆盖:查询解析与边界、LIKE 转义、三处命中、大小写不敏感、筛选组合、分页首/末/越界页与 `total`、只返回本人与当前语言、编辑的排期规则(只改文本不动排期、改状态重排、进入学习中按等级排期、已知或忽略离开队列、改回新词立即到期)、计数与作答记录保留、重复提交无副作用、身份不可编辑、越权与未登录;前端覆盖列表分页、搜索筛选与 URL 恢复、空态与清除、等级选择器、保存失败保留输入、旧响应丢弃。 本单不改数据库结构,无迁移;本机 lexgo-api 已用新二进制重启(schema 仍为 v6),`/healthz` 200,回退用二进制保存在 `.local/lexgo-pre-issue12.exe`。 ### 未验证与边界 - 真实手机触屏与滚动证据仍属既有缺口(#14 承接);本单只用桌面浏览器检查。 - 搜索只做子串匹配,未做分词或相关度排序;大词汇量性能未专门压测(单页上限 100,查询走 `owner_id + language` 的既有前缀索引)。 - CSV 导入导出(X04)、复杂复习范围筛选(X11)、删除与批量清理(X14)不在本单。 ### 文档 - Architecture-and-Code-Map: `3fb79b56d07fa08eb9231642bff43a5270e8ef9a` - Business-Rules-and-Glossary: `a47d8a5e230d2aea2067ed40004694af77ce3527` - Local-Development-and-Verification: `de3225fdb9662684a3d61e8eedd5bf3c0043c0f6` - Product-Requirements-Overview: `3e8c682ff6f129e0783edb8cfbcb63eb2995de0c` - Home: `fe4d28092eb34171dd2e8d43b28984295b6066ce` - Project-Profile 本单未变化,仍为 `ac9f61cf752f68ab48a41c917c265a3dc060cdcc` ### 回退 本单无 schema 变化:停止 lexgo-api,恢复 `.local/lexgo-pre-issue12.exe`,重新启动即可;已保存与已编辑的词条按数据库现状保留。 Gitea MCP 仍指向其他站点,沿用目标站点 API 回退;凭据仅从既有安全配置读入进程。 附件:issue12-vocab-list.png、issue12-vocab-none.png、issue12-vocab-edit.png(mock 浏览器流程)与 issue12-real-list.png、issue12-real-search.png、issue12-real-edit.png(真实 API+MySQL 链路);本会话模型不能读取图片,功能断言来自程序化检查。
Author
Owner

补充提交 docs: 记录 #12 已确认的词汇库口径:按既有惯例把用户确认的口径写入仓库根 AGENTS.md 的项目决策清单——搜索同时匹配身份键/显示原文/个人释义且大小写不敏感、通配符按字面值转义、状态与类型可组合、分页 limit ≤ 100 且按最近更新倒序、编辑复用同一套领域校验且身份不可编辑、只有状态或等级变化才重排复习时间、筛选与页码写入 /vocab URL,以及「来源章节不实现」与 X04/X11/X14 边界。

仅文档改动,未改代码、数据或接口;治理严格检查通过,分支已同步推送。功能提交 3ebec5b 保持不变。回退用的上一版本二进制保存在忽略的 .local/lexgo-pre-issue12.exe。

(编辑说明:本评论首次发布时因命令行转义错误导致正文缺失,已用同一内容修正。)

补充提交 `docs: 记录 #12 已确认的词汇库口径`:按既有惯例把用户确认的口径写入仓库根 `AGENTS.md` 的项目决策清单——搜索同时匹配身份键/显示原文/个人释义且大小写不敏感、通配符按字面值转义、状态与类型可组合、分页 `limit ≤ 100` 且按最近更新倒序、编辑复用同一套领域校验且身份不可编辑、只有状态或等级变化才重排复习时间、筛选与页码写入 `/vocab` URL,以及「来源章节不实现」与 X04/X11/X14 边界。 仅文档改动,未改代码、数据或接口;治理严格检查通过,分支已同步推送。功能提交 `3ebec5b` 保持不变。回退用的上一版本二进制保存在忽略的 `.local/lexgo-pre-issue12.exe`。 (编辑说明:本评论首次发布时因命令行转义错误导致正文缺失,已用同一内容修正。)
Author
Owner

#12 代码审核:达标(2026-09-15,Claude Code)

审核对象:提交 3ebec5b、000d7e0(PR #31)。只读审阅代码与测试差异,没有重跑测试。契约 D1~D11(评论 7925)逐条核对如下。

逐条核对

  • D1 列表与分页:parseTermListQuery 白名单查询键、page/limit 边界、未知键与重复键一律 400,与审计列表规则一致;排序 updated_at DESC, id DESC。TestMySQLVocabularyListSearchFiltersAndPaging 覆盖了首/末/越界页、total 稳定、limit=101/page=0/重复键的拒绝。达标。
  • D2 搜索规则:escapeLike 把 %/_/\ 转成字面值,测试直接验证了输入 %、_、sma_ll 都不会误命中;term 列因为是 utf8mb4_bin(大小写敏感)需要显式转小写比较,original_form/definition 列沿用建表时的默认排序规则(MySQL 8 的 utf8mb4_0900_ai_ci,大小写不敏感)天然做到不区分大小写,代码没有对这两列做多余的转小写处理——这是利用了列排序规则的正确做法,不是遗漏,测试也用 SMALL(键)和中文释义分别验证了大小写不敏感和三处命中。达标。
  • D3 筛选:status/kind 可为空、可组合、非法枚举 400;kind 用 term LIKE '% %'/NOT LIKE '% %' 复用 #11 的身份键形状,不需要新列。测试覆盖了筛选组合与非法值拒绝。达标。
  • D4 按 id 编辑:UpdateTerm 复用 termContent/termLevel 同一套校验,身份字段(term/ownerId)出现即 400;reschedule := status != term.Status || level != term.Level 精确复刻了「只有状态或等级变化才重排」的规则,直接调用已有的 syncTermReview(与 #10/#8 的排期逻辑同源,不是重新实现一遍)。TestMySQLVocabularyEditKeepsDomainRules 是这单测试写得最扎实的一个:只改文本保留排期与计数、进入学习中按等级排期并离开今日队列、known/ignored 离队、改回 new 立即到期、答题后编辑不重写历史计数、重复编辑幂等、越权 404、未登录 401、短语走同一接口且保持 kind/wordCount。达标。
  • D5 等级 UI:TermFormFields.vue 被阅读器面板与词汇库对话框共用,词汇库对话框在状态为「学习中」时显示等级选择器;测试 editTerm 直接验证了服务端对「非学习中状态带等级」的拒绝(与 #7/#11 的规则一致)。达标。
  • D6 来源章节不实现:这一条在方案确认阶段(评论 7925)就已经写明并经用户确认「都按你说的」,不是实施中途才改变的决定,与 #11 的 D3 情况不同——这次的偏差走的是正确流程:先披露方案、经确认、再实施。达标。
  • D7/D8 前端:vocabulary.ts 用与其他 store 一致的 seq/generation 机制丢弃旧响应;显式搜索按钮 + Enter 触发、筛选变化立即请求,与契约描述一致。达标(未逐行核对 URL 同步与 Vue Router 集成细节,但实现模式与已验收的其他 store 一致)。
  • D10/D11 接口与 schema:新增两个接口,未改 database.go 的 SchemaVersion(仍为 6),延续 #11「不改 schema、身份键派生 kind/词数」的确认结果。达标。

结论

达标,可以进入用户验收。 这单的编辑排期规则测试尤其值得肯定:专门验证了「答题产生历史计数后再编辑释义,不能让计数或到期时间被重写」这个容易被忽略的边界,直接呼应了 #8 确认过的「个人学习记录不因编辑清零」的原则。搜索的大小写处理利用列排序规则而不是到处加 LOWER(),也是准确理解现有 schema 后的正确选择,不是遗漏。

本单没有出现 #11 那种「实施中途改变已确认决策却未停下确认」的流程问题——D6 的范围调整是在方案阶段就披露并确认的。

本次审核只读代码,没有重跑 test-integration(lexgo_test_issue9)与 learner 的 vitest/E2E;pi 报告的 69 项集成用例、120 项前端单测等结果未被本次复核重复验证,如需更高把握建议在验收前独立重跑一次。

Gitea MCP 仍指向其他站点,本次沿用已记录的目标站点 API 回退;凭据只从 ~/.claude/gitea.env 安全配置读入进程。

## #12 代码审核:达标(2026-09-15,Claude Code) 审核对象:提交 `3ebec5b`、`000d7e0`(PR #31)。只读审阅代码与测试差异,没有重跑测试。契约 D1~D11(评论 7925)逐条核对如下。 ### 逐条核对 - **D1 列表与分页**:`parseTermListQuery` 白名单查询键、`page`/`limit` 边界、未知键与重复键一律 400,与审计列表规则一致;排序 `updated_at DESC, id DESC`。`TestMySQLVocabularyListSearchFiltersAndPaging` 覆盖了首/末/越界页、`total` 稳定、`limit=101`/`page=0`/重复键的拒绝。**达标**。 - **D2 搜索规则**:`escapeLike` 把 `%`/`_`/`\` 转成字面值,测试直接验证了输入 `%`、`_`、`sma_ll` 都不会误命中;`term` 列因为是 `utf8mb4_bin`(大小写敏感)需要显式转小写比较,`original_form`/`definition` 列沿用建表时的默认排序规则(MySQL 8 的 `utf8mb4_0900_ai_ci`,大小写不敏感)天然做到不区分大小写,代码没有对这两列做多余的转小写处理——这是利用了列排序规则的正确做法,不是遗漏,测试也用 `SMALL`(键)和中文释义分别验证了大小写不敏感和三处命中。**达标**。 - **D3 筛选**:`status`/`kind` 可为空、可组合、非法枚举 400;`kind` 用 `term LIKE '% %'`/`NOT LIKE '% %'` 复用 #11 的身份键形状,不需要新列。测试覆盖了筛选组合与非法值拒绝。**达标**。 - **D4 按 id 编辑**:`UpdateTerm` 复用 `termContent`/`termLevel` 同一套校验,身份字段(`term`/`ownerId`)出现即 400;`reschedule := status != term.Status || level != term.Level` 精确复刻了「只有状态或等级变化才重排」的规则,直接调用已有的 `syncTermReview`(与 #10/#8 的排期逻辑同源,不是重新实现一遍)。`TestMySQLVocabularyEditKeepsDomainRules` 是这单测试写得最扎实的一个:只改文本保留排期与计数、进入学习中按等级排期并离开今日队列、`known`/`ignored` 离队、改回 `new` 立即到期、**答题后编辑不重写历史计数**、重复编辑幂等、越权 404、未登录 401、短语走同一接口且保持 `kind`/`wordCount`。**达标**。 - **D5 等级 UI**:`TermFormFields.vue` 被阅读器面板与词汇库对话框共用,词汇库对话框在状态为「学习中」时显示等级选择器;测试 `editTerm` 直接验证了服务端对「非学习中状态带等级」的拒绝(与 #7/#11 的规则一致)。**达标**。 - **D6 来源章节不实现**:这一条在方案确认阶段(评论 7925)就已经写明并经用户确认「都按你说的」,不是实施中途才改变的决定,与 #11 的 D3 情况不同——**这次的偏差走的是正确流程**:先披露方案、经确认、再实施。**达标**。 - **D7/D8 前端**:`vocabulary.ts` 用与其他 store 一致的 seq/generation 机制丢弃旧响应;显式搜索按钮 + Enter 触发、筛选变化立即请求,与契约描述一致。**达标**(未逐行核对 URL 同步与 Vue Router 集成细节,但实现模式与已验收的其他 store 一致)。 - **D10/D11 接口与 schema**:新增两个接口,未改 `database.go` 的 `SchemaVersion`(仍为 6),延续 #11「不改 schema、身份键派生 kind/词数」的确认结果。**达标**。 ### 结论 **达标,可以进入用户验收。** 这单的编辑排期规则测试尤其值得肯定:专门验证了「答题产生历史计数后再编辑释义,不能让计数或到期时间被重写」这个容易被忽略的边界,直接呼应了 #8 确认过的「个人学习记录不因编辑清零」的原则。搜索的大小写处理利用列排序规则而不是到处加 `LOWER()`,也是准确理解现有 schema 后的正确选择,不是遗漏。 本单没有出现 #11 那种「实施中途改变已确认决策却未停下确认」的流程问题——D6 的范围调整是在方案阶段就披露并确认的。 本次审核只读代码,没有重跑 `test-integration`(`lexgo_test_issue9`)与 learner 的 vitest/E2E;pi 报告的 69 项集成用例、120 项前端单测等结果未被本次复核重复验证,如需更高把握建议在验收前独立重跑一次。 Gitea MCP 仍指向其他站点,本次沿用已记录的目标站点 API 回退;凭据只从 `~/.claude/gitea.env` 安全配置读入进程。
Author
Owner

#12 验收通过(2026-09-15)

用户于 2026-09-15 明确回复「#12 通过验收」。

  • 验收范围:#12 契约 D1~D11(评论 7925)— 生词本分页列表、关键词搜索(身份键/显示原文/个人释义,大小写不敏感、通配符按字面值)、状态与类型筛选、编辑释义/例句/状态/1~7 等级、筛选与页码写入 URL、编辑沿用同一套领域规则(身份不可编辑、只有状态或等级变化才重排复习时间)、无 schema 变化。
  • 已知偏差:原型编辑页的「来自某章节」不实现(词条按身份存储、不引用章节),用户已确认;X04/X11/X14 不在本单。
  • 合并:PR #31 已 fast-forward-only 合入 main,merge commit 000d7e0;验收文档提交 f2de395 已推送。
  • 文档 revision:Home 6429d6dc2b8fe3748ee07a3da1efc82c19e138bb、Architecture 3b9431f44b8a526bdd83f7b18925da243b12b11f、Product-Requirements 248439362819a5be0158e8327959547dc388f1f2、Project-Profile 78760fdce51217ebd047a05207d327b4874691d2。
  • 镜像校验:harness.py sync --check 全部通过;因站点对整页批量回读返回 HTTP 429,改用「revision 级 check + 对 4 个变更页做逐字节正文比对」,四页正文与线上完全一致。回退原因记录于此。
  • 回退:本单无 schema 变化(仍为 v6),恢复 .local/lexgo-pre-issue12.exe 并重启 lexgo-api 即可。
  • 遗留:真实手机触屏与滚动证据仍由 #14 承接;搜索不做分词与相关度排序;大词汇量未压测。

单元工单关闭;#16 的 #12 复选框同步勾选。

## #12 验收通过(2026-09-15) 用户于 2026-09-15 明确回复「#12 通过验收」。 - 验收范围:#12 契约 D1~D11(评论 [7925](https://git.ilapage.cn/OPC/lexgo/issues/12#issuecomment-7925))— 生词本分页列表、关键词搜索(身份键/显示原文/个人释义,大小写不敏感、通配符按字面值)、状态与类型筛选、编辑释义/例句/状态/1~7 等级、筛选与页码写入 URL、编辑沿用同一套领域规则(身份不可编辑、只有状态或等级变化才重排复习时间)、无 schema 变化。 - 已知偏差:原型编辑页的「来自某章节」不实现(词条按身份存储、不引用章节),用户已确认;X04/X11/X14 不在本单。 - 合并:PR [#31](https://git.ilapage.cn/OPC/lexgo/pulls/31) 已 fast-forward-only 合入 main,merge commit `000d7e0`;验收文档提交 `f2de395` 已推送。 - 文档 revision:Home `6429d6dc2b8fe3748ee07a3da1efc82c19e138bb`、Architecture `3b9431f44b8a526bdd83f7b18925da243b12b11f`、Product-Requirements `248439362819a5be0158e8327959547dc388f1f2`、Project-Profile `78760fdce51217ebd047a05207d327b4874691d2`。 - 镜像校验:`harness.py sync --check` 全部通过;因站点对整页批量回读返回 HTTP 429,改用「revision 级 check + 对 4 个变更页做逐字节正文比对」,四页正文与线上完全一致。回退原因记录于此。 - 回退:本单无 schema 变化(仍为 v6),恢复 `.local/lexgo-pre-issue12.exe` 并重启 lexgo-api 即可。 - 遗留:真实手机触屏与滚动证据仍由 #14 承接;搜索不做分词与相关度排序;大词汇量未压测。 单元工单关闭;#16 的 #12 复选框同步勾选。
ila closed this issue 2026-09-15 11:52:34 +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#12