同步Tripo 3D生成的图片输入与完成结果口径

- 主规范明确图片输入先读字节再上传换 file_token,不把带签名的临时地址交给第三方
- 主规范补完成结果读取口径:由既有任务查询的 result 字段返回,标准消费者仅透传调用方显式给出的 result
- 实施计划把图片输入步骤改写为 file_token 口径,文件表补 storage.rs
- 实施计划新增进展一节,写清已落地步骤、未确认的定价数值与尚未跑通的真实端到端
- 风险一节补 worker 并发位与长任务超时的运维前提
This commit is contained in:
2026-09-21 14:38:20 +08:00
parent 29e554a30c
commit 8a828a43ba
2 changed files with 15 additions and 1 deletions
@@ -21,6 +21,7 @@ Milestone Spec: `docs/project-memory/plans/【里程碑】Tripo生成Worker执
| `tripo3d/image_source.rs` | `source` 分支归属校验与 provider 图片输入解析 |
| `tripo3d/provider.rs` | 从 `AppConfig` 构造 `TripoSettings` 与 provider client,缺配置即拒绝 |
| `tripo3d/artifacts.rs` | provider 产物下载、OSS 写入与产物元数据 |
| `tripo3d/storage.rs` | 产物 PUT、HEAD 复核与 `asset_object` 登记 |
| `tripo3d/result.rs` | 按端点构造严格结果与落点引用,写 `result_payload_json` |
| `tripo3d/worker.rs` | checkpoint 判定、submit、轮询、下载落库与 job 收口 |
@@ -43,7 +44,7 @@ Milestone Spec: `docs/project-memory/plans/【里程碑】Tripo生成Worker执
- 验收:缺头 400、缺价格段 fail closed、同键重放不新增扣费。
4. **图片输入解析**
- `source` 只接受站内资源或素材 ID;按分支校验归属并解析 OSS 对象,再生成供 provider 读取的地址,不接受客户端直给 URL
- `source` 只接受站内资源或素材 ID;按分支校验归属并解析对象键,读出字节后上传 provider 换 `file_token` 再提交,不接受客户端直给 URL,也不把带签名的临时地址交给第三方
- 解析失败按 400 返回,不扣费、不入队。
- 验收:跨 owner 的资源 / 素材被拒绝。
@@ -65,6 +66,14 @@ Milestone Spec: `docs/project-memory/plans/【里程碑】Tripo生成Worker执
- 主规范把 `max_attempts = 1` 与 checkpoint 的实际语义写实,里程碑状态同步。
- 验收:`node scripts/check-doc-index.mjs``npm run check:encoding``git diff --check` 通过。
## 进展
步骤 1 至 6 已落地:配置与路由、组合校验、定价、入队、图片输入解析、产物 OSS 写入与 worker 执行链路均已提交;步骤 7 的接线(`app.rs` merge、worker 分支、长任务超时)与文档同步也已完成。尚未完成的是需要真实环境的项:
- 默认配置里的 `model3d` 泥点定价数值仍未确认,缺段时提交按 fail closed 拒绝。
- 没有跑过端到端真实调用(提交 → worker → OSS → 资源登记),现有证据只到 `cargo test -p api-server` 全绿(1085 通过)。
- 新增的 `tripo3d/` 文件里,`storage.rs` 的完整字节写入、`artifacts.rs` 的完整字节读取都留着流式 TODO。
## 验证命令
```bash
@@ -80,4 +89,5 @@ git diff --check
- **真实扣费**:定价数值未写入前 `model3d` 段缺失,提交 fail closed;接口开放前必须先完成定价配置。
- **provider 长任务**:单次 attempt 内轮询依赖 lease 续租与长任务超时;超时按现有语义收口为失败并退款。
- **共用 worker 分支**:新分支只消费自己的 job kind,不改既有分支的入队与写回路径。
- **worker 占用**3D 任务在单次 attempt 内阻塞轮询数分钟,会占住一个 worker 并发位;默认并发为 2,接入真实流量前需要确认并发与 `external_generation_worker_long_job_timeout` 是否够用。
- **回退点**:删除路由 merge、worker 分支与 `tripo3d/` 新文件即可回退;schema 上的 checkpoint 字段保持向后兼容,不需要回滚。
@@ -47,6 +47,8 @@ source = { kind: "resource", resourceId } | { kind: "asset", assetId }
不接受裸 `input` 字符串、任意远程 URL、data URL,也不接受同时给两个 ID。
服务端按 ID 定点确认归属、解析出对象键后,从私有 OSS 读出字节并**上传 provider 换 `file_token`**,再以显式 `file_token` 提交生成任务:不把带签名的临时地址交给第三方,也不依赖 bucket 对公网可达。签名地址会在异步排队期间过期,且等于把私有桶的临时读取权外泄,因此不作为输入通道。
**结果落点**(两个端点都必填)使用 tagged enum
```text
@@ -84,6 +86,8 @@ ImageToModelResult = { modelArtifact, renderedPreview }
每个 artifact 只携带正式资源引用与对象元数据,不携带 provider 临时 URL。
**完成结果的读取**:3D 生成没有画布读回路径,完成结果由既有任务查询 `/api/runtime/external-generation/jobs/{jobId}``result` 字段返回,形状就是上面的严格 tagged enum。标准消费者此前只回来源身份,现在仅在调用方显式给出 `result` 时透传,其它标准任务行为不变。
## 定价与扣费
定价是**真实扣费**,不是计量占位。Tripo 的计费形态是“底价 + 可叠加 add-on”,现有图片/视频那套“模型 → 档位 → 单价”查表表达不了,因此在同一份定价配置中新增 `model3d` 段,全部以泥点计价: