feat(creation): 新增 patbond-creation 模块骨架——端点、租约队列 Worker、provider 适配层(T4-02)
CI / backend-test (push) Successful in 5m43s
CI / backend-test (push) Successful in 5m43s
M4 新模块,:8085,前缀 /api/v1/creation/**(ADR-023 §2,E3 拍板方案 A)。 它编排生成任务但不调用 AI 服务商——上游调用在独立的 patbond-ai(Python) 仓库,经 REST 到达。 落地内容: - 客户端端点 4 个(创建/查询/列表/取消)+ 目录端点。创建强制 Idempotency-Key (ADR-019:生成在上游要花钱,重试绝不能变成第二个计费任务) - 租约队列仓储:FOR UPDATE SKIP LOCKED 领取 + 租约续期 + 回收器。目标模型 只给了索引没给回收器代码,这里补上 - Worker 三条独立调度通道(提交/轮询/回收),任一卡住不饿死其余两条。 轮询与续租在同一语句内完成——分开做会让长任务在跑时租约过期被抢, 那正是重复扣费的经典路径 - provider 适配层接口按「两阶段 submit/poll」定形 + 两个实现位 - 服务身份 JWT 签发器(独立密钥对) - common 增 3 个错误码(40407/40408/42206);配额与限流的两个 429 码随 配额工单落地,此处不加死枚举 关键实现纪律(注释中已逐条说明原因): - provider_request_id 在上游产生任何计费动作之前落库。顺序反过来,每次 超时都会变成重复扣费 - 租约回收时刻意不清 provider_request_id:保留它,下次尝试才知道「已提交过, 该轮询而非重发」 - provider 按 provider_code_snapshot 而非当前目录解析,故目录改指向后 在途任务仍由原适配器轮询 - Worker 各 pass 捕获异常:Spring 会取消抛异常的 fixedDelay 任务, 不捕获等于一次偶发失败静默停掉整个队列 刻意未实现且会响亮失败(不装作能用):provider 两个实现位(待服务商文档 确认幂等键支持、查询接口与频率限制、单任务最长时长、产物返回形式、 限流与配额响应形态五项)、预签名上传目标获取、产物入 media.assets (后两者取决于挂起中的对象存储决策,ADR-023 §7)。 共享物按 ADR-023 §2 复制 3 件(JWT 校验栈、UuidV7、RequestHashes); **存储适配层不复制**——presignPut 已存在,产物走预签名上传由 patbond-user 签发,故无需第 4 份存储层,也使 patbond-ai 零对象存储凭证。 测试:新增 GenerationJobQueueIntegrationTest 11 项,全部针对「写错了也 能跑、只在生产表现为丢任务或重复扣费」的语义:SKIP LOCKED 不把同一行 交给两个 worker、轮询确实续了它依赖的租约、回收器保留 provider_request_id、 first_attempt_at 跨重排队存活、write-once 保护、取消被状态守卫。 全量 393 → 404,零失败零跳过。 过程中被测试抓出两个真问题:UPDATE...FROM 的 RETURNING 列表未限定别名 导致 id 歧义(列表已改为单一来源派生两种形式,避免漂移);以及夹具伪造 了物理上不可能的历史(租约过期但 started_at 是当下),被 ck_generation_jobs_lease 正确拒绝。 Readme.md 全文改中文并补齐 M2 之后的内容:原文停在 M2(pet 骨架阶段)、 完全没有 patbond-community、配置表缺 M2 之后全部条目。现覆盖六模块、 迁移链归属说明、两对密钥的区别与原因、以及各服务端点。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,11 +28,16 @@ public enum ErrorCode {
|
||||
MEDIA_NOT_FOUND(40405, 404, "媒体资源不存在"),
|
||||
COMMENT_NOT_FOUND(40404, 404, "评论不存在"),
|
||||
TARGET_USER_NOT_FOUND(40406, 404, "用户不存在"),
|
||||
// M4 生成域(ADR-023 §9)。40407/40408 与既有 404 族同列;配额与限流的
|
||||
// 两个 429 码随配额工单落地时补入——此处只加骨架实际使用的码,避免死枚举。
|
||||
GENERATION_JOB_NOT_FOUND(40407, 404, "生成任务不存在"),
|
||||
GENERATION_MODEL_NOT_FOUND(40408, 404, "模型或风格不可用"),
|
||||
VACCINATION_RULE_VIOLATION(42201, 422, "疫苗状态或日期约束不满足"),
|
||||
REMINDER_RULE_VIOLATION(42202, 422, "提醒状态或 completedAt 约束不满足"),
|
||||
MEDIA_NOT_READY(42203, 422, "媒体尚未就绪"),
|
||||
FOLLOW_RULE_VIOLATION(42204, 422, "不能关注自己"),
|
||||
MEDIA_UPLOAD_STATE_INVALID(42205, 422, "上传状态不允许确认"),
|
||||
GENERATION_STATE_INVALID(42206, 422, "生成任务状态不允许该操作"),
|
||||
LOGIN_LOCKED(42300, 423, "登录失败次数过多,账号已临时锁定"),
|
||||
INTERNAL_ERROR(50000, 500, "服务器内部错误"),
|
||||
DOWNSTREAM_UNAVAILABLE(50300, 503, "依赖服务暂不可用");
|
||||
|
||||
Reference in New Issue
Block a user