From 42945dec0ad06c452a2135428a5afb96716dea09 Mon Sep 17 00:00:00 2001 From: QiuSW Date: Wed, 16 Sep 2026 21:51:53 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=AE=B0=E5=BD=95=20#32=E3=80=81#40?= =?UTF-8?q?=E3=80=81#42=20=E7=9A=84=E9=AA=8C=E6=94=B6=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E4=B8=8E=E9=95=9C=E5=83=8F=20(#32=20#40=20#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #40 与 #42 已由用户验收;PR #41 与 #43 以 rebase-ff 合入 main,#32 因分支携带仅改镜像的 过程提交改以 squash 合入(f565c1c),三者均未改写分支历史。 - Wiki:Home 的两处"尚未验收"改为已验收文案,Project-Profile 追加三条已确认决策; 会话规则数字、英汉词典与音标规则此前已发布。 - README 与 AGENTS 记录最终 schema v12、音标来源分层、30 天会话代价与 #32 的合并方式。 - docs/ 由 harness.py sync 导出,已通过 sync --check 与 harness check --strict。 --- AGENTS.md | 3 +++ README.md | 2 +- docs/00-project-profile.md | 9 +++++++-- docs/02-architecture-and-code-map.md | 13 ++++++++++-- docs/03-business-rules-and-glossary.md | 6 +++--- docs/04-local-development-and-verification.md | 20 +++++++++++++++++-- docs/README.md | 8 +++++--- 7 files changed, 48 insertions(+), 13 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1200f65..6a93c42 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -300,3 +300,6 @@ MVP 内所有单元任务通过后才能做 MVP 集成验收;MVP 通过后才 - 2026-09-11 用户确认 #10 编辑与删除口径:可改名、改章节标题、编辑章节正文;**只有正文变化才重新处理**,重复保存或改回原内容不新建任务,只改标题不改状态。版本键是 `content_sha256`:任务只在与章节版本一致时才能影响章节,过期版本任务标为 `error_reason=superseded` 且**完全不触碰章节**(认领、恢复扫描、重试都按版本裁决);存储文本重算 SHA 与存储 SHA 不一致时按 `content_changed` 失败。删除为事务内硬删除 + 外键级联,删章后重排序号;个人词条、复习排期与作答记录**不随删除清理**。回收站/撤销、批量操作、章节跨书移动与语言变更不在范围内。编辑器行尾归一为 LF 是已知边界。 - 2026-09-11 用户确认 #9 TXT 导入口径:只接受 UTF-8(允许可选 BOM,解码时剥离且不进入原文),非法字节整体拒绝、不使用替换字符;UTF-16 按 BOM 识别后明确拒绝,GB18030 等按非法 UTF-8 拒绝。文件字节上限 2 MiB,之后仍套用单章 100000 码点上限;换行与空白不归一化。文件只在内存中解码、不创建临时文件,客户端文件名不参与任何路径也不入库。解码后交给既有 `PasteBook`/`PasteChapter`,分章(一次提交一章)、`requestId` 幂等与任务恢复与粘贴一致;不改 schema。EPUB/PDF/字幕、UTF-16 转码、按空行自动分章与断点续传不在范围内。 - 2026-09-11 用户确认 #8 到期单词复习决策表:固定间隔表 1/2/4/7/15/30/60 天,答对升级封顶 7、答错降级最低 1、再学一次不改等级,答错与再学立即回队;已知/忽略不入队,新保存的词立即到期,显式「学习中 level N」排 now+间隔[N];只有新建或状态/等级实际变化才移动复习时间,编辑释义或例句保留原排期,保存未提及等级时保留已获得等级。到期判定用 UTC 绝对时刻(`due_at ≤ now`),不引入本地日边界。作答按 `answerId` 去重并以 `expectedDueAt` 判定过期标签页,重复提交、网络重发与双标签页都不得重复更新次数与间隔(作答响应 `result` 只取 applied/stale,重放另用 `duplicate` 标记并返回首次结果);`correct_count` 只计答对,`wrong_count` 计答错与再学。短语复习归 #11,进度统计归 #13,不做策略配置 UI(X11)、练习模式(X08)与 FSRS。 +- 2026-09-16 用户验收 #40 英汉词典与规范音标:ECDICT 常用子集(约 8.2 万条,MIT)提供中文释义、CMUdict(BSD-2)音标按固定 ARPAbet→IPA 映射转写,WordNet 词表用于裁剪子集;三者 sha256 pin 在 `server/zh-dictionary-resource.json`,`scripts/dict_prepare.py` 显式准备(产物不入库、运行时不联网,整包存 MySQL 因而包含在 #15 备份内)。**schema 最终为 v12**(放开 `lexgo_dictionaries` 单行 CHECK、增加 `provider` 列,两步带条件守卫、可重放;旧二进制见 v12 拒绝启动)。音标来源必须可追溯(`cmudict` 为 IPA 转写、`ecdict` 仅字符级规范化),不可靠就不显示(`grok` 属此类,`^` 丢失首音整条丢弃)。查词中文释义在前、英英释义随后,屈折形经 WordNet lemma 回查中文但不复制词目音标;两本词典可分别启停。许可文件随仓库保存,词典归档不提交。 +- 2026-09-16 用户验收 #42 登录有效期:学习端与管理端共用同一登录接口,有效期由 8 小时改为 **30 天**(具名常量 `SessionLifetime`,绝对有效期、不滑动续期)。已知代价:令牌泄露窗口放大约 90 倍;设备/会话列表与「退出其他设备」未包含,如需另开工单。撤销规则不变(退出撤销当前会话,改密码/停用/重置撤销该账号全部会话,服务端只存 SHA-256 摘要)。 +- 2026-09-16 用户验收 #32 缺陷修复:编辑正文回到曾用版本时复用该内容版本已有的处理任务行(按 `owner_id + chapter_id + content_sha256` 匹配),避免唯一键冲突;因分支带有仅改镜像的过程提交,PR #39 以 squash 方式合入 main(`f565c1c`),未改写分支历史。 diff --git a/README.md b/README.md index 24ec8b6..b518fa1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ - [英语分词与离线词典验证小样](spikes/english/README.md)(#3 已验收,独立本机入口) - [阅读选择验证小样](spikes/selection/README.md)(#4 已验收,真机详细测试证据缺口保留) - [项目档案](docs/00-project-profile.md) · [需求总览](docs/09-product-requirements-overview.md) -- [工作量估算](docs/10-workload-estimate.md):#2、#3、#4、#5、#6、#7、#8、#9、#10、#11、#12、#13、#14、#18 已验收,原规划中的 #5 已完成;#15、#21 与 #37(章级音频、章节插图、章节/书籍作者)已验收;#24 待排期;缺陷 #32 待确认修复方案;后续结合集成结果重估,旧全量研究仅供参考。 +- [工作量估算](docs/10-workload-estimate.md):#2、#3、#4、#5、#6、#7、#8、#9、#10、#11、#12、#13、#14、#18 已验收,原规划中的 #5 已完成;#15、#21 与 #37(章级音频、章节插图、章节/书籍作者)已验收;#32(编辑复用版本缺陷)、#40(英汉词典与规范音标)与 #42(登录有效期 30 天)已验收;#24 待排期;缺陷 #32 待确认修复方案;后续结合集成结果重估,旧全量研究仅供参考。 - [四阶段实施总览 #16](https://git.ilapage.cn/OPC/lexgo/issues/16):14 张单元工单,工程基础 → 技术验证 → 首条学习闭环 → 补齐 MVP;原型 v1 已获用户验收。两端使用账号(用户名)+密码登录,不要求邮箱。 - [原型工单 #1](https://git.ilapage.cn/OPC/lexgo/issues/1):Quant-UX 桌面/手机原型 v1,预览入口与审核记录见工单及需求总览。 - 运维命令:`./lexgo migrate|bootstrap|serve|backup|restore|verify`(纯二进制),开发便利与交叉验证走 `python scripts/server.py …` / `python scripts/ops.py …` diff --git a/docs/00-project-profile.md b/docs/00-project-profile.md index 90ea64c..a7b6ec8 100644 --- a/docs/00-project-profile.md +++ b/docs/00-project-profile.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Project-Profile wiki_url: https://git.ilapage.cn/OPC/lexgo/wiki/Project-Profile.- -wiki_revision: e61817b949693c82e74ad3d1a8adb39983a90fab -synchronized_at: 2026-09-16T05:27:04Z +wiki_revision: a482d5ba1fa50d2b570042376134cc0d7a67ef36 +synchronized_at: 2026-09-16T13:51:35Z # LexGo 项目档案 @@ -196,3 +196,8 @@ server 是 go-admin 的选用模块接入:原样保留 SysUser、SysDept、必 至此 MVP 原范围 #2~#15 与增补 #21、#37 全部实施并验收;仍待处理 #24 书库列表优化与缺陷 #32。#16 的 #37 复选框已勾选。 - 2026-09-16 新增第二本英语词典(#40):ECDICT 常用子集提供中文释义、CMUdict 提供 IPA 转写音标,二者由 sha256 pin 的源显式准备;schema v12 放开 `lexgo_dictionaries` 单行约束并增加 `provider` 列,英英与英汉词典可分别启停。音标来源必须可追溯(IPA 转写 / ECDICT 记法),不确定时不显示。许可:ECDICT 仓库 MIT(数据由公开资料整理,再分发前需自行评估)、CMUdict BSD-2,许可证随仓库保存,词典归档不提交进 Git。 + +- 2026-09-16 登录有效期由 8 小时改为 **30 天**(#42):两端共用同一登录接口,有效期是具名常量 `SessionLifetime`,绝对有效期、不滑动续期;服务端只存令牌 SHA-256 摘要,改密码/停用/重置仍撤销该账号全部会话。设备与会话管理界面未包含,如需另建工单。 +- 2026-09-16 用户验收 #40(英汉词典与规范音标):ECDICT 常用子集提供中文释义、CMUdict 提供 IPA 转写音标,两本英语词典可分别启停。**schema 最终为 v12**(放开 `lexgo_dictionaries` 单行 CHECK、增加 `provider` 列,两步带条件守卫、可重放)。音标来源必须可追溯(`cmudict` IPA / `ecdict` 记法),不可靠就不显示。资源由 `scripts/dict_prepare.py` 从 sha256 pin 的源显式准备,产物不入库;许可为 ECDICT MIT 与 CMUdict BSD-2。 +- 2026-09-16 用户验收 #42(登录有效期):学习端与管理端共用同一登录接口,有效期由 8 小时改为 **30 天**(具名常量 `SessionLifetime`,绝对有效期、不滑动续期)。已知代价:令牌泄露窗口放大约 90 倍;设备/会话列表与"退出其他设备"未包含。 +- 2026-09-16 用户验收 #32(缺陷修复):编辑正文回到曾经用过的版本时复用该内容版本已有的处理任务行;因分支携带仅改镜像的过程提交,PR #39 以 squash 方式合入 main(`f565c1c`),未改写分支历史。 diff --git a/docs/02-architecture-and-code-map.md b/docs/02-architecture-and-code-map.md index bb62618..e82c346 100644 --- a/docs/02-architecture-and-code-map.md +++ b/docs/02-architecture-and-code-map.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Architecture-and-Code-Map wiki_url: https://git.ilapage.cn/OPC/lexgo/wiki/Architecture-and-Code-Map.- -wiki_revision: 8cc11b2fa972babeb4756b68c0b071282cdb1960 -synchronized_at: 2026-09-16T05:27:05Z +wiki_revision: 64a83458a283469672e111f423d37d9478a3d0e3 +synchronized_at: 2026-09-16T13:51:36Z # 架构与代码地图 @@ -573,3 +573,12 @@ schema v8 新增两张表,都只用可重放的 `CREATE TABLE IF NOT EXISTS` 仍是 `resource_missing`。 - 屈折形:WordNet 的规则解析给出词目后,再用该词目回查中文词典,因此 `dogs` 也能看到"狗"; 但音标属于词目,**不复制**到屈折形上(`dogs` 不显示 `/dɔɡ/`)。 + +## 登录会话有效期(#42) + +- 会话有效期是单一具名常量 `SessionLifetime`(`server/app/lexgo/service.go`),当前为 **30 天**; + 登录时写入 `lexgo_sessions.expires_at`,每次请求按 `expires_at > now` 校验。学习端与管理端共用 + 同一登录接口,因此这一个值同时决定两端的有效期。 +- 有效期是**绝对**的,不随请求顺延;没有刷新令牌或轮换机制。撤销路径与有效期无关,保持独立: + 退出删除当前会话行,改密码、停用、重置删除该账号全部会话行,登录时顺手清理该账号已过期的行。 +- 服务端只保存令牌的 SHA-256 摘要(`token_hash` 主键),原始令牌只在客户端 sessionStorage。 diff --git a/docs/03-business-rules-and-glossary.md b/docs/03-business-rules-and-glossary.md index e5ca80b..ba1ed99 100644 --- a/docs/03-business-rules-and-glossary.md +++ b/docs/03-business-rules-and-glossary.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Business-Rules-and-Glossary wiki_url: https://git.ilapage.cn/OPC/lexgo/wiki/Business-Rules-and-Glossary.- -wiki_revision: ba03226b60eee1c44062f4206b970a2047b8ddbc -synchronized_at: 2026-09-16T05:27:06Z +wiki_revision: 1f89692c0e24c3d2235a4cd7ad78b90512852f65 +synchronized_at: 2026-09-16T13:51:36Z # 业务规则与术语 @@ -54,7 +54,7 @@ M0 固定首发语言语料、词条身份规则、短语选择与重叠规则 - 账号去首尾空白并转小写,3~32 位、ASCII 字母开头,可含数字、点、下划线和连字符;唯一约束阻止大小写和并发重复。密码 10~72 字节,使用 bcrypt,不填写必需邮箱。 - 初始管理员只通过显式 bootstrap 建立;库中已有账号时拒绝再次 bootstrap,不覆盖密码。管理页面只创建学习者,不能将输入角色升级为管理员;本阶段不提供账号删除。 -- 同一账号可以登录多个会话。随机令牌仅在客户端保存,服务端存 SHA-256 摘要,8 小时过期。退出仅撤销当前会话,启停/重置会撤销目标账号全部会话;重新启用不能恢复旧会话。 +- 同一账号可以登录多个会话。随机令牌仅在客户端保存,服务端存 SHA-256 摘要,**30 天**过期(2026-09-16 由 8 小时改为 30 天,见 #42;单一常量 `SessionLifetime`,绝对有效期、不滑动续期)。退出仅撤销当前会话,启停/重置会撤销目标账号全部会话;重新启用不能恢复旧会话。 - 每次请求读取账号当前状态与会话有效期;学习数据由认证身份确定,管理员权限不能解除学习接口的本人过滤。 - 两端分别使用 lexgo-admin-token、lexgo-learner-token 的 sessionStorage;401、退出和账号切换清除私人状态,以代次检查阻止迟到响应恢复旧数据。管理端拒绝学习者并撤销其新会话;撤销服务异常会提示未确认,不冒充服务端退出成功。 - 新建账号拥有英语空空间。此处没有书籍、阅读、词典或复习功能,不把空页面视为学习业务已实现。 diff --git a/docs/04-local-development-and-verification.md b/docs/04-local-development-and-verification.md index 2c1c4be..de32f81 100644 --- a/docs/04-local-development-and-verification.md +++ b/docs/04-local-development-and-verification.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Local-Development-and-Verification wiki_url: https://git.ilapage.cn/OPC/lexgo/wiki/Local-Development-and-Verification.- -wiki_revision: afcd40ee530a24f924a39209c23a9dfd090114a2 -synchronized_at: 2026-09-16T05:27:07Z +wiki_revision: 45306a5d50c5674f512ea206739c6b2d89e88a85 +synchronized_at: 2026-09-16T13:51:36Z # 本地开发与验证 @@ -1146,3 +1146,19 @@ python .local/verify-issue40-api.py # 28 项真实 API 读语句不受影响(新列有默认值),但旧二进制见到 v12 会拒绝启动并要求显式迁移,这是有意的保护。 - 排错:`provider` 不存在 → 未执行 migrate;导入 400 且提示版本不符 → 归档不是当前 pin 生成的 (重新运行 `dict_prepare.py`);查词只有英英释义 → 英汉词典未导入或被停用。 + +## 验证登录有效期(#42) + +```powershell +# 固定值本身在测试里断言,改动会让测试立刻失败 +cd server; go test ./app/lexgo/ -run TestMySQLSessionLifetimeIsThirtyDays -v +# 集成测试:到期前仍可用、到期后被拒绝(按 SessionLifetime 计算,不写死小时数) +LEXGO_TEST_DB_NAME=lexgo_test_issue42 python scripts/server.py test-integration +``` + +- `TestMySQLSessionLifetimeIsThirtyDays` 校验三件事:常量等于 30 天、登录响应 `expiresAt` 与库内 + `expires_at` 一致、库内只存在摘要(原始令牌查不到);`TestMySQLAccountIsolationAndRevocation` + 在 `SessionLifetime - 1h` 仍成功、再前进 2 小时被拒。 +- 手工排查:`SELECT owner_id, expires_at FROM lexgo_sessions ORDER BY expires_at DESC;`。 + 若登录后立刻 401,先确认服务器时间与 `expires_at` 都以 UTC 存储(`DATETIME` 不存时区)。 +- 旧的 8 小时会话不受影响:各自按自己的 `expires_at` 自然到期,不需要数据迁移。 diff --git a/docs/README.md b/docs/README.md index c7d725e..2b97be7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Home wiki_url: https://git.ilapage.cn/OPC/lexgo/wiki/Home -wiki_revision: bbea927c73e51b6b5490de3cb52d42a88dfbb31d -synchronized_at: 2026-09-16T05:27:04Z +wiki_revision: e6e3b2a93c4d4f3a809872e8260c3776923c7623 +synchronized_at: 2026-09-16T13:51:35Z # LexGo 文档入口 @@ -101,4 +101,6 @@ Quant-UX 原型 v1 已通过用户验收。[桌面预览](https://qux.ilapage.cn #37 章节级音频与章节插图已于 2026-09-15 实施完成,待用户验收:每章可以上传一段 MP3(≤20 MiB)与一张插图(JPG/PNG/WebP,≤2 MiB、≤4096×4096),封面仍按书;阅读页在正文上方显示本章插图的缩略图、点击后在弹窗里查看原图(书籍页章节列表不显示章节图片),播放器只在本章有音频时出现且不与文本同步,播放位置按「账号+章节」保存并在切换章节时上报;书籍页的封面压成一行紧凑控件(预览+上传/替换/移除),书名右侧显示书级作者,「编辑书籍」可改书名与作者,章节列表每行只有一个「编辑」,点开后在同一对话框里编辑标题、作者、正文与本章插图、音频(标题/作者/正文点保存后生效,文件选中后立即上传;阅读页在标题下显示作者)。类型按文件内容判定,读取需会话并支持 HTTP Range 与 ETag;附件存于数据库,包含在 #15 的备份恢复范围内(第三次恢复演练已验证按字节恢复)。schema v9 需显式迁移,书级音频接口已下线,其实测试期数据在迁移中清空。真机听感与浏览器真实解码播放仍需人工确认。 -#40 英汉词典与规范音标已于 2026-09-16 实现并进入待验收:新增第二本英语词典——ECDICT 常用子集(约 8.2 万条中文释义,3.0 MB)与 CMUdict 转写的 IPA 音标。音标分三层来源:能由 CMUdict 音素按固定映射转写的用真 IPA(35,799 条),否则保留 ECDICT 记法仅做字符级规范化并标注来源(30,580 条),两者都不可靠就不显示(16,342 条,例如 `grok`);含 `^` 等丢失首音的记法整条丢弃,不做猜测。schema 升到 v12(放开 `lexgo_dictionaries` 单行 CHECK、增加 `provider` 列,两步都带条件守卫、可重放),英英与英汉词典可分别启停,查词时中文释义在前、英英释义随后,屈折形经 WordNet lemma 解析后也能看到中文释义但不会复制词目音标。归档由 `scripts/dict_prepare.py` 从 sha256 pin 的三个源显式准备(运行时不联网,产物不入库,仓库只保存 pin 与两份许可证),整包存进 MySQL 因而包含在 #15 的备份范围内。测试:Go 96 项、学习端 159 单测与 26 项 e2e(含 390×844 触摸视口)、管理端 32 项、真实 API 28 项检查;PR #41 待合入,尚未验收。发音音频、整句翻译与汉英方向不在本单范围。 +#40 英汉词典与规范音标已于 2026-09-16 实现并进入待验收:新增第二本英语词典——ECDICT 常用子集(约 8.2 万条中文释义,3.0 MB)与 CMUdict 转写的 IPA 音标。音标分三层来源:能由 CMUdict 音素按固定映射转写的用真 IPA(35,799 条),否则保留 ECDICT 记法仅做字符级规范化并标注来源(30,580 条),两者都不可靠就不显示(16,342 条,例如 `grok`);含 `^` 等丢失首音的记法整条丢弃,不做猜测。schema 升到 v12(放开 `lexgo_dictionaries` 单行 CHECK、增加 `provider` 列,两步都带条件守卫、可重放),英英与英汉词典可分别启停,查词时中文释义在前、英英释义随后,屈折形经 WordNet lemma 解析后也能看到中文释义但不会复制词目音标。归档由 `scripts/dict_prepare.py` 从 sha256 pin 的三个源显式准备(运行时不联网,产物不入库,仓库只保存 pin 与两份许可证),整包存进 MySQL 因而包含在 #15 的备份范围内。测试:Go 96 项、学习端 159 单测与 26 项 e2e(含 390×844 触摸视口)、管理端 32 项、真实 API 28 项检查;PR #41 已合入 main,并由用户于 2026-09-16 验收通过。发音音频、整句翻译与汉英方向不在本单范围。 + +#42 登录有效期已于 2026-09-16 由 8 小时改为 **30 天**(学习端与管理端共用同一登录接口,一处生效):有效期提取为具名常量 `SessionLifetime`,仍然是**绝对**有效期、不做滑动续期,撤销规则不变(退出只撤销当前会话,改密码/停用/重置撤销该账号全部会话,服务端只存 SHA-256 摘要)。代价是令牌泄露后的可用窗口放大约 90 倍;设备/会话列表与"退出其他设备"未包含在本单,如需另开工单。无数据库结构变化,旧会话按各自到期时间自然过渡;测试:Go 92 项(新增 1 项锁定 30 天与摘要存储,并把过期断言改为按常量计算);PR #43 已合入 main,并由用户于 2026-09-16 验收通过。