edit to a new version: 200
edit back to the original version: 200
edit to the first new version again: 500 服务暂不可用,请稍后再试
job 519 sha 6a6d1475 status pending
job 520 sha 724576eb status pending
chapter now 724576eb pending
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.
来源与目标
#13 真实链路验证时发现:把章节正文改回曾经编辑过的那个版本,接口返回 HTTP 500「服务暂不可用,请稍后再试」。这是 #10 编辑章节功能的既有缺陷,不属于 #13 范围,按工作流单独建单记录,未在 #13 中顺手修复。
复现(真实 API+MySQL,虚构账号):
PATCH /api/v1/chapters/:id返回 500。Go 集成级最小复现(三行编辑):
根因(已定位,未修改)
server/app/lexgo/edit.go的UpdateChapter用「章节编号+内容摘要」派生任务键:而
lexgo_ingest_jobs有唯一约束uq_job_request (owner_id, request_key)。同一章回到同一内容版本时,派生出与更早任务完全相同的键,Create触发 MySQL 1062 重复键,整个事务回滚并冒泡为通用 500。事务回滚保证了数据一致(章节仍保持上一次成功的版本),但用户看到的是不可解释的错误。影响
建议方向(待确认后再定)
version列或复用updated_at),保证「同一内容可再次成为新版本」;需要 schema 变更与迁移测试。pending、attempts=0、finished_at=NULL,不新增行;不需要 schema 变更,但要让 #10 的「旧版本任务不得复活新版本章节」门控继续成立。依赖与执行
前置:#10 已完成。状态:待验收(2026-09-16,提交 cbb4840,PR #39;方案见评论 8210,实施与验证见最新评论)。
#32 修复方案确认与实施启动(2026-09-16)
用户 2026-09-16 指示「清掉 #32」,采纳本工单建议中方案 2(复用既有任务行,不改 schema);方案 1(任务键加入单调版本序号)不采用,因此本单没有数据库结构变化。
根因(已在建单时定位)
server/app/lexgo/edit.go用「章节编号+内容摘要」派生编辑任务键:contentSHA("edit:<章节>:<内容摘要>"),而lexgo_ingest_jobs有唯一约束uq_job_request (owner_id, request_key)。同一章回到同一内容版本时派生出与更早任务完全相同的键,Create触发 MySQL 1062,整个事务回滚并冒泡为通用 500。事务回滚保证了数据一致,但用户看到的是不可解释的错误,且任何曾经用过的版本都无法再成为当前版本(改回原文本、撤销误改都会永久 500)。修复方案(方案 2)
新增
stageEditJob:在事务内先按(owner_id, request_key)锁定查询既有任务行——content_sha256与本次版本一致pending、attempts = 0、清空error_reason与finished_at、刷新updated_at;不新增行,因此也不会累积历史版本任务保持不变的门控:
RetryIngestJob仍然拒绝「任务内容摘要 ≠ 章节当前内容」的旧任务(409),所以旧版本任务不会把新版本章节拉回处理;worker 的认领条件(status = pending且章节content_sha256与任务一致)依然成立;同一个内容版本重复处理是幂等的(#6/#9 已确认),因此复用并重置重试计数是安全的。验证计划
Gitea MCP 仍指向其他站点,沿用目标站点 API 回退;凭据仅从既有安全配置读入进程。
#32 修复完成,待用户验收(2026-09-16)
用户 2026-09-16 指示「清掉 #32」,采纳本工单方案 2(复用既有任务行、不改 schema),契约见评论 8210。分支
fix/32-edit-version-reuse从 mainc293a41创建,提交cbb4840已推送;PR #39 未合并,工单停在待用户验收。修复与一处方案细化
server/app/lexgo/edit.go的stageEditJob:编辑产生新版本时,先按(owner_id, chapter_id, content_sha256)锁定查询该内容版本的任务行,找到就复用(重置status=pending、attempts=0、清空error_reason/finished_at),找不到才用派生键创建。RetryIngestJob仍拒绝「任务内容摘要 ≠ 章节当前内容」的旧任务(409),旧版本任务不会把新版本章节拉回处理;正文未改动时不产生新版本也不新增任务;同一版本重复处理幂等。.local/lexgo-pre-issue32.exe。验证
go vet ./.../gofmt -lLEXGO_TEST_DB_NAME=lexgo_test_issue13 python scripts/server.py test-integrationTestMySQLChapterEditBackToAPreviousVersion).local/issue32-api-evidence.json):开发实例上对 fixture 书籍执行 A→B→A→B,三步全部 200 并各自最终 ready;该章只有两条任务行且都以 ready 收尾、没有任务停在 pending;读者返回最后一次编辑的正文;章节内容摘要等于某个任务版本;再切回另一版本仍 200 且仍是两行;删除 fixture 书籍后任务级联清空check --strict通过文档
f3ffc4fb7a75fccb147cb39ad3804f29be76b3e7e248e18282d7d2599e1689007ed36b1f1c076267632b9d8b7c9951d05bf3c0b2f34f08da704e65dcsync --check通过,3 个变更页逐字节正文比对一致未验证
回退
无 schema 变化:停止 lexgo-api、恢复
.local/lexgo-pre-issue32.exe重启即可;既有书籍与学习数据不受影响。#32 验收通过(2026-09-16)
用户 2026-09-16 回复「#32 #40 验收通过、#42 验收通过」,本单验收结论如下。
f565c1c。因该分支携带两个仅改核心镜像的过程提交,rebase 逐提交重放会冲突;按"镜像以线上 Wiki 为准"处理,
最终以 squash 落成单个提交,未改写任何已推送分支的历史。
owner_id + chapter_id + content_sha256匹配,取最早一行,重置为 pending、清零尝试次数并清空失败原因),不再出现 500;正文未变不新建任务,同版本重复提交保持幂等,失败过的旧版本重新发布会被复活。
f3ffc4fb、Business-Rulese248e182、Local-Development632b9d8b已在实施阶段发布;验收记录写入 Home
e6e3b2a9与 Project-Profilea482d5ba,核心镜像随验收提交42945de导出并通过harness.py sync --check。工单关闭。