重写
This commit is contained in:
185
docs/technical/ASSET_OBJECT_CONFIRM_FLOW_DESIGN_2026-04-21.md
Normal file
185
docs/technical/ASSET_OBJECT_CONFIRM_FLOW_DESIGN_2026-04-21.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# 资产对象上传完成确认接口设计
|
||||
|
||||
日期:`2026-04-21`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份文档用于把 `M6` 中“上传完成后的对象确认接口”冻结到可直接编码的级别。
|
||||
|
||||
当前要解决的不是完整资产发布链,而是最小闭环:
|
||||
|
||||
1. 浏览器先通过 `PostObject` 把文件上传到私有 OSS
|
||||
2. 服务端确认对象真实存在
|
||||
3. 服务端把对象元数据写入当前阶段的 `asset_object` 真相存储
|
||||
4. 后续业务绑定与 SpacetimeDB reducer 再基于这条已确认对象继续扩展
|
||||
|
||||
## 2. 当前前提
|
||||
|
||||
已落地事实:
|
||||
|
||||
1. `POST /api/assets/direct-upload-tickets` 已能签发浏览器 `PostObject` 直传票据。
|
||||
2. `platform-oss` 已能生成私有读签名 URL。
|
||||
3. `asset_object` 已在 `spacetime-module` 中落下首版表骨架。
|
||||
|
||||
当前仍未落地:
|
||||
|
||||
1. 上传完成确认接口
|
||||
2. 对象 HEAD 校验
|
||||
3. `asset_object` 实际写入路径
|
||||
4. 业务实体绑定
|
||||
|
||||
## 3. 接口职责
|
||||
|
||||
`POST /api/assets/objects/confirm` 当前阶段只负责三件事:
|
||||
|
||||
1. 校验请求给出的 `bucket + object_key` 是否合法
|
||||
2. 调 OSS 做一次私有 `HEAD Object` 校验,确认对象真实存在
|
||||
3. 把对象元数据写入当前阶段的 `asset_object` 进程内存储,并返回确认结果
|
||||
|
||||
当前阶段明确不做:
|
||||
|
||||
1. 不做业务实体绑定
|
||||
2. 不做图片尺寸探测
|
||||
3. 不做 hash 计算
|
||||
4. 不做重复对象合并
|
||||
5. 不直接调用 SpacetimeDB reducer
|
||||
|
||||
## 4. 请求体设计
|
||||
|
||||
请求路径:
|
||||
|
||||
`POST /api/assets/objects/confirm`
|
||||
|
||||
请求体:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `bucket` | `String` | 否 | 当前阶段允许不传;不传时默认回落到服务端 OSS bucket。 |
|
||||
| `objectKey` | `String` | 是 | 正式对象路径真相字段。 |
|
||||
| `contentType` | `String` | 否 | 客户端已知 MIME,可回写到对象元数据。 |
|
||||
| `contentLength` | `u64` | 否 | 客户端可传期望大小;当前仅用于一致性校验,不作为唯一真相来源。 |
|
||||
| `contentHash` | `String` | 否 | 后续内容摘要预留字段。 |
|
||||
| `assetKind` | `String` | 是 | 业务资产类型,例如 `character_visual`。 |
|
||||
| `accessPolicy` | `String` | 否 | 默认 `private`。 |
|
||||
| `sourceJobId` | `String` | 否 | 来源任务 ID。 |
|
||||
| `ownerUserId` | `String` | 否 | 归属用户 ID。 |
|
||||
| `profileId` | `String` | 否 | 归属 profile ID。 |
|
||||
| `entityId` | `String` | 否 | 归属业务实体 ID。 |
|
||||
|
||||
补充约束:
|
||||
|
||||
1. `bucket` 当前若传入,必须与服务端已配置 bucket 一致。
|
||||
2. `objectKey` 必须落在受支持的 `generated-*` 前缀下。
|
||||
3. `assetKind` 当前不能为空。
|
||||
|
||||
## 5. 校验顺序
|
||||
|
||||
接口校验顺序固定如下:
|
||||
|
||||
1. 检查 OSS 配置是否存在
|
||||
2. 校验请求参数基础合法性
|
||||
3. 校验 `bucket` 与服务端配置 bucket 是否一致
|
||||
4. 调用 OSS `HEAD Object`
|
||||
5. 若客户端传了 `contentLength`,则与 OSS 返回的真实 `Content-Length` 做一致性校验
|
||||
6. 通过后写入 `asset_object`
|
||||
|
||||
## 6. OSS 校验结果口径
|
||||
|
||||
OSS `HEAD Object` 当前至少回填:
|
||||
|
||||
1. `content_length`
|
||||
2. `content_type`
|
||||
3. `last_modified_at`
|
||||
4. `etag`
|
||||
|
||||
当前阶段以 OSS 返回值为准:
|
||||
|
||||
1. `content_length` 真相取 OSS
|
||||
2. `content_type` 优先取 OSS,OSS 未返回时再回退请求体
|
||||
3. `content_hash` 暂不强行等于 `etag`
|
||||
|
||||
原因:
|
||||
|
||||
1. `etag` 对 multipart 上传和不同上传模式并不总等价于内容 hash
|
||||
2. 当前阶段先留出字段,不把错误假设固化进 schema
|
||||
|
||||
## 7. 写入规则
|
||||
|
||||
确认成功后写入 `asset_object`:
|
||||
|
||||
1. `asset_object_id` 由服务端生成,固定 `assetobj_` 前缀
|
||||
2. `bucket` 与 `object_key` 按正式真相写入
|
||||
3. `access_policy` 当前默认 `private`
|
||||
4. `content_length` 以 OSS HEAD 为准
|
||||
5. `content_type` 优先 OSS HEAD
|
||||
6. `version` 当前固定为 `1`
|
||||
7. `created_at` / `updated_at` 在确认时写当前 UTC 时间
|
||||
|
||||
当前阶段重复确认同一 `bucket + object_key` 的行为固定为:
|
||||
|
||||
1. 若已存在,则返回已存在记录并更新 `updated_at`
|
||||
2. 不生成第二条重复对象记录
|
||||
|
||||
## 8. 响应体设计
|
||||
|
||||
成功响应核心字段:
|
||||
|
||||
1. `assetObjectId`
|
||||
2. `bucket`
|
||||
3. `objectKey`
|
||||
4. `accessPolicy`
|
||||
5. `contentType`
|
||||
6. `contentLength`
|
||||
7. `contentHash`
|
||||
8. `assetKind`
|
||||
9. `sourceJobId`
|
||||
10. `ownerUserId`
|
||||
11. `profileId`
|
||||
12. `entityId`
|
||||
13. `version`
|
||||
14. `createdAt`
|
||||
15. `updatedAt`
|
||||
|
||||
## 9. 错误口径
|
||||
|
||||
### 9.1 请求参数错误
|
||||
|
||||
返回 `400`:
|
||||
|
||||
1. `objectKey` 为空
|
||||
2. `objectKey` 不在受支持前缀下
|
||||
3. `assetKind` 为空
|
||||
4. `bucket` 与当前配置 bucket 不一致
|
||||
5. 客户端声明的 `contentLength` 与 OSS HEAD 不一致
|
||||
|
||||
### 9.2 OSS 未配置
|
||||
|
||||
返回 `503`
|
||||
|
||||
### 9.3 OSS 对象不存在
|
||||
|
||||
返回 `404`
|
||||
|
||||
### 9.4 OSS 探测失败
|
||||
|
||||
返回 `502`
|
||||
|
||||
## 10. 当前阶段实现边界
|
||||
|
||||
当前阶段实现固定为:
|
||||
|
||||
1. `platform-oss` 增加服务端 `HEAD Object` helper
|
||||
2. `module-assets` 提供进程内 `asset_object` 确认服务
|
||||
3. `api-server` 接入 `POST /api/assets/objects/confirm`
|
||||
|
||||
下一阶段再继续:
|
||||
|
||||
1. 对接真实 SpacetimeDB reducer
|
||||
2. 业务实体绑定 reducer
|
||||
3. 更细的元数据探测
|
||||
|
||||
## 11. 关联文档
|
||||
|
||||
1. [SPACETIMEDB_ASSET_OBJECT_STORAGE_DESIGN_2026-04-21.md](./SPACETIMEDB_ASSET_OBJECT_STORAGE_DESIGN_2026-04-21.md)
|
||||
2. [SPACETIMEDB_ASSET_OBJECT_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_ASSET_OBJECT_TABLE_DESIGN_2026-04-21.md)
|
||||
3. [SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](./SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
|
||||
Reference in New Issue
Block a user