Files
Genarrative/docs/technical/ASSET_ENTITY_BINDING_REDUCER_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.0 KiB
Raw Permalink Blame History

资产对象业务实体绑定 reducer 设计

日期:2026-04-21

1. 文档目的

这份文档用于冻结 M6 中“对象绑定业务实体 reducer”的首版落地方案。

当前已经完成:

  1. 浏览器可通过 PostObject 把文件直传到私有 OSS。
  2. POST /api/assets/objects/confirm 已能确认对象存在。
  3. asset_object 已按 bucket + object_key 写入 SpacetimeDB。

下一步要补上的最小闭环是:

  1. 已确认的 asset_object 能绑定到某个业务实体。
  2. 绑定关系由 SpacetimeDB 持久化。
  3. Axum 提供最小 HTTP facade避免前端直接拼 SpacetimeDB reducer 参数。

2. 当前阶段不直接创建强业务表的原因

当前先落通用 asset_entity_binding,不直接创建 character_visual_asset / scene_image_asset / sprite_sheet_asset

原因固定如下:

  1. 角色、场景、精灵等强业务表的完整字段还没有冻结。
  2. 当前最紧急的工程闭环是“确认后的对象能被实体引用”,不是完整发布模型。
  3. 通用绑定表可以先承接旧接口迁移中的 entityId + slot 关系,后续再由强业务表逐步替换或派生。

3. 表设计

首版新增 private table

  1. asset_entity_binding

字段如下:

字段名 类型 必填 说明
binding_id String 主键,固定 assetbind_ 前缀。
asset_object_id String 被绑定的 asset_object.asset_object_id
entity_kind String 业务实体类型,例如 charactersceneprofile
entity_id String 业务实体 ID。
slot String 实体上的资产槽位,例如 primary_visualcoversprite_sheet
asset_kind String 资产类型,例如 character_visual
owner_user_id Option<String> 归属用户,当前仅作为服务端传入的记录字段。
profile_id Option<String> 归属 profile。
created_at Timestamp 首次绑定时间。
updated_at Timestamp 最近绑定更新时间。

索引如下:

  1. entity_kind + entity_id + slot 用于按实体槽位查当前绑定。
  2. asset_object_id 用于按对象反查被哪些业务实体引用。

4. 幂等规则

绑定写入按以下规则执行:

  1. asset_object_id 必须已存在于 asset_object
  2. entity_kind + entity_id + slot 作为首版幂等定位键。
  3. 同一实体槽位重复绑定时,不新增第二行。
  4. 重复绑定会复用原 binding_idcreated_at,更新 asset_object_id / asset_kind / owner_user_id / profile_id / updated_at
  5. 不同槽位可以绑定同一个 asset_object_id

5. reducer / procedure 设计

SpacetimeDB 新增:

  1. bind_asset_object_to_entity reducer只返回 Result<(), String>,供后续模块内部复用。
  2. bind_asset_object_to_entity_and_return procedure面向 Axum 同步接口返回最终绑定快照。

procedure 返回结构采用:

  1. ok
  2. record
  3. error_message

asset_object 确认 procedure 保持一致,便于 spacetime-client 做统一错误映射。

6. Axum HTTP facade

首版新增接口:

POST /api/assets/objects/bind

请求体:

字段名 类型 必填 说明
assetObjectId String 已确认对象 ID。
entityKind String 业务实体类型。
entityId String 业务实体 ID。
slot String 资产槽位。
assetKind String 资产类型。
ownerUserId String 当前阶段由后端调用方显式传入。
profileId String 当前阶段由后端调用方显式传入。

响应体核心字段:

  1. bindingId
  2. assetObjectId
  3. entityKind
  4. entityId
  5. slot
  6. assetKind
  7. ownerUserId
  8. profileId
  9. createdAt
  10. updatedAt

7. 当前阶段安全边界

当前接口是 Axum facade不是前端直接调用 SpacetimeDB reducer 的最终权限模型。

约束如下:

  1. 当前不把长期 OSS AK/SK 下发给客户端。
  2. 当前不让客户端直接写 private table。
  3. owner_user_id 当前只作为记录字段,不作为可信授权依据。
  4. 后续接入 SpacetimeDB 身份透传后,绑定 reducer 的授权必须改为基于可信身份,不信任客户端传入的用户 ID。

8. 完成定义

首版完成条件:

  1. module-assets 提供绑定输入、快照、结果结构与字段校验。
  2. spacetime-module 新增 asset_entity_binding 表与绑定 reducer/procedure。
  3. spacetime-client 生成最新 Rust bindings 并封装绑定 procedure。
  4. api-server 暴露 POST /api/assets/objects/bind
  5. 本地测试覆盖字段错误与 “asset_object 不存在不能绑定”。

9. 一句话结论

当前阶段先用通用 asset_entity_binding 把已确认 OSS 对象绑定到业务实体槽位,强业务资产表等字段稳定后再继续拆分。