66 lines
7.6 KiB
Markdown
66 lines
7.6 KiB
Markdown
# 风格化“详情替换”长时间不出结果(2026-09-26)
|
||
|
||
## 现场证据
|
||
|
||
用户截图中的风格化项目已保存 33 张目标详情参考图,但输入图区为 0 张、结果图为 0 张。卡片上的 96% 不是服务商进度:`DetailResultProcessingScanOverlay` 按 240 秒把时间线性映射成百分比,并用 `Math.min(96, ...)` 封顶,所以只要没收到结果,它就会一直停在 96%。
|
||
|
||
生成请求走的是浏览器保持的对话流(`/api/chat` 的 SSE)。切换标签页、断网或连接中断后服务端结果无法回传,前端那条“处理中”的状态又存在浏览器里,重新打开页面会继续播放假进度,因此表现为“挂几个小时”。
|
||
|
||
另外确认了一个独立故障:详情替换专用模型绑定为 `gpt-image-2.5-flare`,该型号当时持续返回 HTTP 404;普通 `gpt-image-2.5` 有成功出图记录。2026-09-24 17:45 已把线上 `SystemSettings.detail_replacement_image_model_config.modelId` 热更新为 `gpt-image-2.5`。
|
||
|
||
上线前线上 `CanvasImageTask` 中 `taskKind = 'agent-detail-replacement-batch'` 的行数为 0,即任务从未进过服务端队列,符合“没有可恢复任务、只能挂假进度”的判断。
|
||
|
||
## 修复
|
||
|
||
- 详情替换改为服务器端后台批次:`POST /api/agents/detail-replacement/batches` 先落 `CanvasImageTask`,由 `lib/agents/detail-replacement-batch-runner.ts` 按参考图逐张生成,每张结果即时写回项目(`persistAgentDetailReplacementCard`)并更新任务进度。
|
||
- 页面不再依赖长连接:卡片状态来自任务查询(3 秒轮询 + 回到前台重新校验),刷新、切换标签页、换浏览器都能按任务状态恢复;没有后端批次时不再显示“处理中”。
|
||
- 删除按时间爬到 96% 的假进度动画,进度改为真实的排队 / 生成中 / 已完成 / 失败。
|
||
- 提交前校验:没有产品输入图直接拦截提示补图;参考图必须与项目已保存的一致;配额与积分不足在提交阶段返回明确错误。
|
||
- 服务重启恢复:`instrumentation.ts` 启动 5 秒后以及之后每 60 秒调用 `resumeDetailReplacementBatchTasks()`,把 `pending` 或心跳停滞(>90 秒无 `updatedAt`)的批次重新入队;运行期间每 30 秒写心跳。
|
||
- 长批次不再被清理规则误判:详情替换的运行超时单独放宽到 3 小时,`markStaleDatabaseRunningTasksFailed` 与 `getCanvasImageTaskForUser` 都排除该任务类型。
|
||
- 单张失败只落到该卡片并继续后续图片;已写入项目的卡片在恢复时按批次号 + 卡片号认领,不重复调用模型、不重复记账。
|
||
- 去重按“同一份提交内容”而不是“同一项目”:`submissionKey` 覆盖参考图集合、提示词、模型、尺寸等,配合局部唯一索引 `CanvasImageTask_detail_batch_submission_key` 拦住重复点击;单张重新生图因为卡片集合不同,不会被吞掉。
|
||
- 详情替换默认模型由 `gpt-image-2.5-flare` 改回 `gpt-image-2.5`(`lib/ai/detail-replacement-image-model-config.ts` + 迁移 `0101`),并保留前端传参路径不变。
|
||
|
||
## 验证
|
||
|
||
- 服务端链路端到端验证(生产库,纯沿用卡片,不调用模型、不消耗积分):直接插入 `pending` 批次后,恢复循环在 60 秒内接手,任务转为 `succeeded`,两张卡片写入项目 `replacementResultState.detail_replacement`,聊天记录写入 2 条对应结果消息;验证数据随后全部删除,线上无残留。
|
||
- 重启幂等验证:把同一任务改回 `pending`,恢复后 `attempts` 1 → 2,已完成卡片被跳过,结果卡片与消息条数不变,确认不会重复生成、重复记账。
|
||
- 本地 `tsc --noEmit`、Next.js 生产构建、`git diff --check` 通过;`tests/routes/detail-replacement-batch.test.ts` 与 `tests/routes/replacement-product-identity.test.ts` 共 12 项通过,覆盖批次识别、卡片消息 ID 稳定性、重启跳过已完成卡片、提交键去重与单张重跑区分。
|
||
|
||
## 发布
|
||
|
||
2026-09-26 02:45 发布至 45.192.99.48 / ai.yydd.ai。生产代码提交 `0907aaff`,构建目录 `.next-detail-batch-0907aaff-20260926T023939`(BUILD_ID `0wcSfql6ZB2g8EzNEP6Gy`)。切换 `.next` 符号链接后 `pm2 restart ai-chatbot`。
|
||
|
||
回滚点记录在服务器 `/root/detail-batch-rollback.txt`:旧提交 `1c2acee5`、旧构建 `.next-video-home-439af60c-20260924`。回滚只需把 `.next` 指回旧目录并重启。
|
||
|
||
服务器构建使用 `NEXT_IGNORE_BUILD_TYPECHECK=1`:该机器上遗留的 `backups/next-before-vip98k-0911/types/validator.ts` 会被 `tsconfig` 的 `**/*.ts` 扫到,它的相对导入在该目录下必然失效;同一提交已在本地跑通完整类型检查与构建。
|
||
|
||
线上检查:登录页 200、首页 200、未登录访问 `/api/agents/detail-replacement/batches` 返回 401;数据库去重索引已建,`CanvasImageTask` 无遗留批次。分支 `codex/detail-recovery-0924` 已推送 Git。
|
||
|
||
发布时经确认近 3 小时无任何生成任务、近 1 小时无新消息,重启未打断在途生成。
|
||
|
||
## 追加:上线后详情替换完全不可用(2026-09-27)
|
||
|
||
现场:用户点“参考图替换”弹出「任务编号或项目编号无效」,结果区一直是 0/33。nginx 日志显示同一个用户对 `/api/agents/detail-replacement/batches` 的请求全部 400——GET 返回 30 字节(`{"error":"项目编号无效"}`),POST 返回 45 字节(`{"error":"任务编号或项目编号无效"}`),且 GET 请求里的 projectId 是合法 UUID。
|
||
|
||
根因:新增批次路由自带的 UUID 正则漏写一组 `-{4}`,写成了 8-4-4-12,而标准 UUID 是 8-4-4-4-12。结果是**任何**真实编号都过不了校验,包括浏览器 `generateUUID()` 生成的 requestId,所以查询和提交在参数校验阶段就全部返回 400,页面自然拿不到任务状态。仓库里其他位置用的都是正确的 8-4-4-4-12,只有这个新文件写错。
|
||
|
||
为什么上一轮没发现:上一轮的端到端验证是直接往 `CanvasImageTask` 插入任务再观察恢复循环,绕过了 HTTP 层,而编号校验只存在于 HTTP 层。这是一次验证覆盖不足,不是环境问题。
|
||
|
||
修复:
|
||
|
||
- 校验抽成 `isDetailReplacementUuid` 并修正为规范的 8-4-4-4-12,路由不再自带正则。
|
||
- 回归测试覆盖真实线上项目编号、浏览器生成的请求编号,以及导致故障的 8-4-4-12 形状;另加源码守卫,禁止该路由再次出现自带的错误 UUID 正则。
|
||
- 前端在项目尚未保存时直接提示“请先保存项目后再执行详情替换”,不再把空编号丢给接口。
|
||
|
||
验证(这次走真实 HTTP,不再是绕过接口的数据库插桩):用一次性 QA 账号(`activeSessionKey` 与 JWT 会话)和一次性 agent 项目请求线上接口,验证后立即删除,不触碰任何真实账号数据。
|
||
|
||
- `GET ?projectId=<真实项目 UUID>` → 200 `{"task":null}`(修复前恒为 400,即用户遇到的现场)。
|
||
- `GET ?projectId=not-a-uuid` → 400「项目编号无效」,编号校验仍然生效。
|
||
- `POST` 合法 projectId/requestId → 通过编号校验并进入后续业务校验(返回“详情参考图尚未保存或已发生变化”),说明提交链路已恢复。
|
||
- `POST` 非法 requestId → 400「任务编号或项目编号无效」,非法输入仍被拒绝。
|
||
- QA 账号、项目、临时脚本全部删除,线上无残留。
|
||
|
||
发布:2026-09-27 19:16 发布至 45.192.99.48 / ai.yydd.ai,提交 `f75d99ba`,构建目录 `.next-detail-batch-f75d99ba-20260927T191533`(BUILD_ID `f1ghXv5zLVb8BQv8a-ifE`)。回滚点为上一构建 `.next-detail-batch-0907aaff-20260926T023939`。
|