Files
Genarrative/docs/technical/ASSET_OBJECT_CONFIRM_FLOW_DESIGN_2026-04-21.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

5.3 KiB
Raw Permalink Blame History

资产对象上传完成确认接口设计

日期: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 优先取 OSSOSS 未返回时再回退请求体
  3. content_hash 暂不强行等于 etag

原因:

  1. etag 对 multipart 上传和不同上传模式并不总等价于内容 hash
  2. 当前阶段先留出字段,不把错误假设固化进 schema

7. 写入规则

确认成功后写入 asset_object

  1. asset_object_id 由服务端生成,固定 assetobj_ 前缀
  2. bucketobject_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
  2. SPACETIMEDB_ASSET_OBJECT_TABLE_DESIGN_2026-04-21.md
  3. SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md