This commit is contained in:
2026-04-21 19:17:31 +08:00
parent d234d27cc0
commit 89129ef1f4
83 changed files with 13329 additions and 176 deletions

View File

@@ -0,0 +1,175 @@
# `asset_object` 表设计
日期:`2026-04-21`
## 1. 文档目的
这份文档用于把 `asset_object` 从“概念字段列表”推进到**可以直接编码**的级别。
当前要冻结的不只是字段名,还包括:
1. 表职责边界
2. `bucket + object_key` 的正式主存储口径
3. 首版字段类型
4. 当前阶段必须先落下的索引
5.`api-server / platform-oss / 后续业务绑定表` 的边界
## 2. 当前已确认前提
目前与 `asset_object` 直接相关的事实已经确认如下:
1. 阿里云 OSS 当前按**私有 bucket** 模式接入。
2. 浏览器上传通过 `POST /api/assets/direct-upload-tickets` 下发的 `PostObject` 票据完成。
3. `platform-oss` 已输出:
- `bucket`
- `objectKey`
- `legacyPublicPath`
- `access`
4. 匿名公开 URL 当前返回 `403`,不能作为正式读取真相。
因此 `asset_object` 必须承担的真实职责是:
1. 记录对象在 OSS 中的唯一定位
2. 记录对象元数据
3. 为后续业务绑定表提供稳定引用
4. 不把 URL 当成正式主键
## 3. 表职责边界
### 3.1 `asset_object` 负责的内容
1. `asset_object_id`:对象主键
2. `bucket`:对象所属 bucket
3. `object_key`bucket 内对象路径
4. `access_policy`:对象访问策略
5. `content_type`
6. `content_length`
7. `content_hash`
8. `version`
9. 来源任务、归属用户、profile、业务实体等轻量关联键
10. `asset_kind`
11. `created_at / updated_at`
### 3.2 `asset_object` 不负责的内容
1. 不负责大对象二进制本体
2. 不负责生成任务完整编排状态
3. 不负责直接暴露公共 URL
4. 不负责角色、动作、场景、精灵表等强业务关系本身
### 3.3 与其他表的边界
1. `asset_job` 负责任务态,不替代对象真相。
2. `asset_manifest` 负责对象集合或发布清单,不替代单对象行。
3. `character_visual_asset / scene_image_asset / sprite_sheet_asset` 这类强业务关系表优先引用 `asset_object_id`
## 4. 表访问级别
`asset_object` 当前固定为 **private table**
原因:
1. bucket 当前是私有读写。
2. 对象读取需要服务端签名 URL 或下载代理。
3. 前端不应直接把 `asset_object` 当公开订阅表使用。
补充口径:
1. 当前阶段先不把 `asset_object` 做成 public。
2. 未来若要给客户端读对象列表,优先通过 view 或 Axum facade 输出脱敏 DTO。
## 5. 首版字段设计
| 字段名 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `asset_object_id` | `String` | 是 | 主键,固定使用 `assetobj_` 前缀的稳定字符串 ID。 |
| `bucket` | `String` | 是 | 正式对象仓名称。 |
| `object_key` | `String` | 是 | bucket 内对象路径,不带前导 `/`。 |
| `access_policy` | `AssetObjectAccessPolicy` | 是 | 当前先冻结为 `private` / `public_read`。 |
| `content_type` | `Option<String>` | 否 | 真实对象 MIME。 |
| `content_length` | `u64` | 是 | 已确认对象大小,单位字节。 |
| `content_hash` | `Option<String>` | 否 | 内容摘要,当前先保留为空间,不预设算法强制值。 |
| `version` | `u32` | 是 | 对象版本号,首版默认 `1`。 |
| `source_job_id` | `Option<String>` | 否 | 来源任务 ID。 |
| `owner_user_id` | `Option<String>` | 否 | 归属用户 ID。 |
| `profile_id` | `Option<String>` | 否 | 归属 profile ID。 |
| `entity_id` | `Option<String>` | 否 | 归属业务实体 ID。 |
| `asset_kind` | `String` | 是 | 资产业务类型,例如 `character_visual``scene_image`。 |
| `created_at` | `Timestamp` | 是 | 创建时间。 |
| `updated_at` | `Timestamp` | 是 | 最近更新时间。 |
补充约束:
1. `bucket``object_key` 是正式对象定位真相。
2. 允许存在 `asset_object_id`,但不允许回退成单列 `storage_path` 作为真相字段。
3. `content_hash` 先允许为空,避免在上传确认链路未落地前把 schema 卡死。
## 6. 索引与查询约束
### 6.1 当前阶段必须先落的索引
1. `bucket + object_key` 组合 B-Tree 索引
作用:按真实对象定位回查对象元数据
2. `asset_kind` 单列索引
作用:后续按业务类型聚合或清理对象
### 6.2 为什么先不加更多索引
当前阶段只先解决:
1. 正式对象定位
2. 业务类型过滤
而以下能力尚未落地:
1. `asset_job`
2. 上传完成确认 reducer
3. 业务绑定 reducer
因此暂不提前为每个可空关联键堆索引,避免在真实访问模式还没固定前把 schema 复杂度拉高。
## 7. 写入约束
### 7.1 当前阶段允许的对象写入时机
后续真正写 `asset_object` 时,必须满足以下前提之一:
1. 浏览器直传成功,服务端确认对象存在
2. 后台 worker 成功上传对象到 OSS
3. 旧对象迁移脚本确认对象存在并完成元数据回填
### 7.2 当前阶段不允许的漂移
1. 只根据 `legacyPublicPath` 就写入对象真相
2. 只存完整 URL不拆 `bucket/object_key`
3.`asset_object` 里重复塞角色、场景、动作等业务专属冗余字段
4. 先落 `bucket/object_key` 之外的单列字符串路径,再计划后续拆列
## 8. 与当前代码的对接关系
当前工程中的直接对接关系固定如下:
1. `platform-oss`
- 负责生成 `bucket + object_key` 对象定位
2. `api-server`
- 负责输出直传票据与私有读签名 URL
3. `module-assets`
- 负责沉淀 `AssetObjectAccessPolicy` 与字段校验 helper
4. `spacetime-module`
- 负责聚合 `asset_object` 首版表骨架
## 9. 当前阶段完成定义
当以下条件满足时,`asset_object` 的首版设计与骨架视为完成:
1. `bucket + object_key` 已在表结构中固定为两列
2. 表访问级别已固定为 private
3. 字段和索引已具体到可直接编码
4. `module-assets``spacetime-module` 已落真实 crate scaffold
## 10. 相关文档
1. [SPACETIMEDB_ASSET_OBJECT_STORAGE_DESIGN_2026-04-21.md](./SPACETIMEDB_ASSET_OBJECT_STORAGE_DESIGN_2026-04-21.md)
2. [SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](./SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
3. [../../../server-rs/crates/module-assets/README.md](../../../server-rs/crates/module-assets/README.md)
4. [../../../server-rs/crates/spacetime-module/README.md](../../../server-rs/crates/spacetime-module/README.md)