- 注册表:docs/architecture/README.md §2,37 份 canonical(frontmatter topic+canonical:true)与表双向机器对账
- 门:.agents/tools/docs-gate.py 六检(品牌根/canonical唯一/死链/入口卫生/留痕隔离/设计档申报),挂 .githooks/pre-commit(本仓已激活)+ .gitea/workflows/docs-gate.yml(待runner)+ wave-close 第8步;旧 check-deadlinks.sh 退役并入 G3
- 清缩:删 54 项历史档(plans 16/agent-specs 设计与spike 20/brainstorms 5/memorys 3/goals+作战清单完成史归档/王蓝莓design/SAA现状html/add-game-template);channel-spike 564K 代码资产迁仓级 spikes/;六件删前蒸馏已迁(代码评审16条open项→进度总账§5、prefix-cache字段表→cheap-model skill、意图基线29条→需求清单附录、九门降级rationale→验收门、A11 TODO→tech-decisions、3layer边界→littlejs-game-dev 指针)
- 修口径约90处:六处SAA『现行主线』旧标、Nacos/RocketMQ『未部署』旧述(07-01反转)、gameDefinition残留、全部死链改 git show 定位;AGENTS.md 249→136行(决策史归tech-decisions);_index 改纯在飞板;对外演示版 md→html
- 依据:docs/agent-specs/2026-07-02-文档治理-{全量普查与裁决-report,SoT注册表与治理门-设计}.md(四路普查191份md+Codex/Opus双评审必修项已折入);恢复基线 8ea97234(单档 git checkout 8ea97234 -- <路径>)
361 lines
36 KiB
Markdown
361 lines
36 KiB
Markdown
---
|
||
topic: 数据模型
|
||
canonical: true
|
||
date: 2026-06-22
|
||
---
|
||
|
||
# 后端域 · 全平台数据模型总图
|
||
|
||
> **这是什么**:数据视角的结构总图 —— 全平台落库了哪些表、表与表之间怎么连、有哪些必须先知道才不会踩坑的数据语义。它与 [13模块.md](../架构/13模块.md)(模块边界)、[后端 README](README.md)(工程落地)、[MVP进度总账](../../mvp/MVP进度总账.md)(完成度)正交:那三份分别回答"模块各管什么 / 代码怎么摆 / 建到几成",本页只回答"数据长什么样、怎么连"。
|
||
> **给谁看**:要动表结构或写跨模块查询的后端工程师、做数据侧 code review 的人、想搞清"一款游戏从一句话到能赚钱,数据在库里怎么流转"的人。
|
||
> **怎么读**:第 1 章一张跨域主干图建立全局心智;第 2 章按业务链路下钻看每条链的实体关系;第 3 章是按表速查的索引,从这里跳进具体迁移;第 4、5 章是两处最容易误解的真相 —— 软关联和数据语义。
|
||
|
||
地面实情以 Flyway 迁移为准。执行权威目录是 `game-cloud/huijing-server/src/main/resources/db/migration/`,共 25 个 `V1` 到 `V25` 的 `.sql`。`contracts/db-schemas/` 是跨模块迁移的授权源副本,但只含 18 个(`V1`-`V13` + `V21`-`V25`):单模块的 additive `ALTER`(`V14`-`V20`)不进 contracts,只在执行目录。只读 contracts 会漏掉 `game_aigc_task` 的 `level/trace/source/modify` 系列列、`game_source_project` 的并发幂等唯一键、以及 `game_feed_rank` 的 `exposure_limit`。每个 `V*.sql` 的头注写满了权属决策、幂等语义、seam 边界和状态机流转,是表级细节的权威源;本页只锚点引用,不抄全文。
|
||
|
||
迁移覆盖的是 11 个 `game-module-*` 业务模块加 passport 身份层,约 40 张表。蓝图里另列的 `pay`(支付)和 `ip`(素材授权)两个模块当前没有独立的 `game_pay_*` / `game_ip_*` 迁移落地:赋余额走 trade 的 `game_trade_grant`,素材登记走 studio 的 `game_material`,真实支付与版权授权是 M4 之后的事。
|
||
|
||
---
|
||
|
||
## 1. 一张图看清全平台数据骨架
|
||
|
||
把每个模块的主表和它们的跨模块连线画到一张图上,就是平台数据的主干。明细表(报价、进度、弹幕这类)刻意省掉,留给第 2 章下钻。
|
||
|
||
```mermaid
|
||
erDiagram
|
||
game_player ||--o{ game_project : "creator_user_id 创作"
|
||
game_project ||--o{ game_version : "1:N 版本"
|
||
game_project ||--|| game_version : "current_version_id 指针"
|
||
game_version ||--|| game_runtime_package : "一版本一包"
|
||
game_aigc_task ||--o| game_version : "生成产物回填"
|
||
game_source_project }o--|| game_version : "源↔产物解耦"
|
||
game_project ||--o{ game_feed_rank : "status=4 入流"
|
||
game_runtime_session }o--|| game_telemetry_event : "试玩回灌"
|
||
game_telemetry_event }o--|| game_telemetry_game_stat : "聚合"
|
||
game_telemetry_game_stat ..> game_project : "回灌 play/like"
|
||
game_telemetry_game_stat ..> game_feed_rank : "回灌 quality"
|
||
game_ad_revenue }o--|| game_trade_income : "source_ref 分账"
|
||
game_trade_income }o--|| game_trade_account : "入账"
|
||
game_trade_account ||--o{ game_trade_withdraw : "提现"
|
||
game_compliance_gate_result }o--|| game_project : "publish 落门→回 status"
|
||
```
|
||
|
||
主干上能读出五条链:身份层 `game_player` 是所有创作的归属起点;一款游戏 `game_project` 带多个版本 `game_version`,版本编译出一个对外包 `game_runtime_package`;过了合规门、状态变成已发布的游戏进 feed 排序表 `game_feed_rank`;玩家试玩产生的会话被遥测聚合,聚合结果反向回灌游戏热度和排序;广告收入经分账流水入创作者账户,再走提现。下面五张子图把每条链的明细补全。
|
||
|
||
---
|
||
|
||
## 2. 按业务链路下钻的实体关系
|
||
|
||
约 40 张表全画在一起会糊成一团,所以按业务链路拆成五张子图,每张配一段说明。
|
||
|
||
### 2.1 创作链路(project + aigc + studio + source)
|
||
|
||
创作链从创作者打开工作台开始。`game_studio_session` 是有状态的创作会话,`createDraft` 时调 project 侧 `createProject` 回写 `game_id`;会话里的每一步生成挂在 `game_studio_taskchain` 上,任务链只持引用、不重建生成状态机 —— 它引一个 `game_aigc_task`,引一个 `game_version`。
|
||
|
||
`game_aigc_task` 是无状态的生成原子,一行就是一次异步生成任务。它的 `prompt_hash` 是产物缓存键,命中就跳过模型重算;`status` 走 0 排队 / 1 运行 / 2 成功 / 3 失败 / 4 超时 / 5 取消,成功时把 `version_id` 回填。生命周期相关的几列是后续 `ALTER` 加的:`V15` 的 `level` 是会员档配额维度;`V16` 的 `trace_json` 是九门轨迹账本、`readiness_score` 是就绪分;`V17` 的 `source` 区分 online / orchestrator / worker 三种任务来源;`V19` 的 `modify_mode` / `base_version_id` / `modify_patch` 是"改源不改包"生命周期里 modify、extend 两种操作的载荷,`modify_mode` 为空就是首次 create。
|
||
|
||
一款游戏 `game_project` 和它的版本 `game_version` 是 1:N,外加一根 `current_version_id` 指针指向当前生效版本。版本表的 `package_url` / `checksum` / `bundle_size` 不在生成时写,而是 runtime 编译成功后回写(权威写者见第 5 章)。
|
||
|
||
`game_source_project` 是"改源不改包"范式的源侧存储面,和产物面 `game_version` 解耦:它存 `source_json` 源工件,`source_hash` 是可寻址的幂等键,`version_id` 构建成功才回填、失败留 NULL 成为孤儿(`status` 里 2 就是孤儿态)。`base_version_id` 记 modify 的血缘。`V20` 给它加了 `uk_game_source_hash(game_id, source_hash, deleted, tenant_id)` 做并发幂等。
|
||
|
||
`game_studio_asset` 是会话内的资产槽位 seam,MVP 不做六路并发;它和创作者私有素材库 `game_material` 是两个概念 —— 后者按 `creator_user_id` 归属隔离,`category` 冻结成 sprite / character / effect / scene / ui / music 六类,字节存储归 infra FileApi,表里只登记引用。
|
||
|
||
游戏的归属专区由 `game_zone` 和关系表 `game_project_zone` 承载,双轨设计:`game_zone.type` 区分 1 官方精选 / 2 UGC 普通,迁移末尾种了两条种子(官方精选、创作广场);`game_project_zone` 用 `uk_game_zone(game_id, zone_id)` 表达游戏对专区的多对多归属。
|
||
|
||
```mermaid
|
||
erDiagram
|
||
game_studio_session ||--o{ game_studio_taskchain : "session_id"
|
||
game_studio_session ||--o{ game_studio_asset : "会话内资产槽"
|
||
game_studio_session }o--|| game_project : "createDraft 回写 game_id"
|
||
game_studio_taskchain }o--|| game_aigc_task : "aigc_task_id 引用"
|
||
game_studio_taskchain }o--|| game_version : "version_id 引用"
|
||
game_aigc_task ||--o| game_version : "成功回填 version_id"
|
||
game_aigc_task ||--o{ game_aigc_task : "retry_of 重生成血缘"
|
||
game_project ||--o{ game_version : "1:N"
|
||
game_project ||--|| game_version : "current_version_id 指针"
|
||
game_source_project }o--|| game_project : "game_id 源侧"
|
||
game_source_project ||--o| game_version : "构建成功回填 version_id"
|
||
game_project }o--o{ game_zone : "game_project_zone M:N"
|
||
game_material }o--|| game_player : "creator_user_id 私有库"
|
||
```
|
||
|
||
`game_project` 的 `play_count` / `like_count` 是 telemetry 回灌的物化字段(口径见第 5 章);`status` 状态机走 0 草稿 / 1 审核中 / 2 通过 / 3 拒绝 / 4 已发布 / 5 下架 / 6 封禁,是创作链的总闸,后面编译、合规、分发都围着它转。
|
||
|
||
### 2.2 编译发布与合规闸门(runtime + compliance)
|
||
|
||
aigc 产物交给 runtime 编译。`game_runtime_build` 是编译任务,`status` 走 0 排队 / 1 编译中 / 2 成功 / 3 失败,成功时把 `package_url` / `bundle_size` / `checksum` 回写。编译产出一份对外清单 `game_runtime_package`,一个版本一行(`uk_version`),试玩宿主据它加载游戏:`entry` / `runtime_version` / `preload_policy` / `sandbox_attr` / `allow_origins` 描述 iframe 沙箱;`status` 走 0 预览就绪 / 1 已发布 / 2 已失效,是取包门禁的权威字段,和 `game_version.status` 不是一回事。MVP 没有 OSS,`V10` 给它加的 `package_json LONGTEXT` 让整包内嵌进 DB。
|
||
|
||
试玩会话 `game_runtime_session` 既是试玩入口,也是遥测回灌的源头。`client_play_token` 做幂等(`uk_play_token`),`scene` 区分 preview / play;它的 `sessionId` 透传进遥测做 `session_id` 对账键,但试玩本身不计 `play_count`。`V11` 把 `player_user_id` 放宽成可空、加了 `anon_id`,匿名玩家也能试玩。
|
||
|
||
游戏要发布,先过锁风门。`game_compliance_gate_result` 每次 publish 落一条(非幂等),`verdict` 走 pass / review / block,取各原子检查的 max,`details JSON` 存每个原子的明细。内容分级 `game_content_rating` 给出 all / 8+ / 12+ / 16+,`rated_by` 区分门自动评级还是 admin 人工。门的结论回写 `game_project.status`。封禁台账 `game_user_ban` 用 `target_type` 区分 1 游戏 / 2 用户,`ban_type` 区分 1 封禁 / 2 降权,`uk_target_active` 保证同一目标至多一条生效封禁。政策正文 `game_policy_document` 存富文本加版本,app 取最新已发布版;申诉 `game_compliance_appeal` 软关联 `game_user_ban`(不建外键),它的 `pending_flag` 是 STORED 生成列,借 NULL 互不冲突的特性实现"每个 (user, ban) 至多一条待处理"的原子去重。
|
||
|
||
```mermaid
|
||
erDiagram
|
||
game_aigc_task ||--o| game_runtime_build : "产物触发编译"
|
||
game_runtime_build }o--|| game_version : "version_id"
|
||
game_runtime_build ||--|| game_runtime_package : "成功产清单"
|
||
game_runtime_package ||--|| game_version : "uk_version 一版一行"
|
||
game_runtime_session }o--|| game_version : "试玩 version_id"
|
||
game_runtime_package ..> game_version : "回写 package_url/checksum"
|
||
game_compliance_gate_result }o--|| game_project : "publish 落门"
|
||
game_compliance_gate_result ..> game_project : "verdict 回 status"
|
||
game_content_rating }o--|| game_version : "uk_game_version 分级"
|
||
game_user_ban ..> game_project : "target_id 封游戏"
|
||
game_user_ban ..> game_player : "target_id 封用户"
|
||
game_compliance_appeal }o--o| game_user_ban : "ban_id 软关联"
|
||
```
|
||
|
||
### 2.3 分发与数据回路(feed + telemetry)
|
||
|
||
游戏 `status=4` 后进 feed。`game_feed_rank` 是排序缓存兼精选覆盖层:`zone_id` 为 0 是默认混合流、大于 0 是专区;`quality_score` 由 telemetry 回灌、只读不参与计算;`boost` 是运营加权;`sort_score` 降序出流。`V25` 加的 `exposure_limit` 是限曝光降权项,排序公式变成 `sort_score = quality + boost - exposure_limit`,大于等于 9999 就是硬剔除。互动信号走 `game_feed_interact_log`,`action` 区分 1 点赞 / 2 收藏 / 3 分享 / 4 举报,`uk_user_game_action(user_id, anon_id, game_id, action)` 做幂等、`active` 软开关让取消不删行;举报(`action=4`)转交 compliance。
|
||
|
||
遥测这一侧,`game_telemetry_event` 是原始事件落库,MQ 消费时靠唯一键防重。原来的 `uk_dedup(trace_id, event, ts)` 在同毫秒同名事件上会误吞、重放会重计数,`V10` 把它换成了 `uk_event_id(event_id)` —— `event_id` 是客户端生成的 UUID,逐事件实例全局唯一,才是真正的幂等真身。`process_status` 走 0 待聚合 / 1 已聚合 / 2 失败待重试。聚合结果落 `game_telemetry_game_stat`,按游戏乘日期(`uk_game_date`),它的 `play_count` / `like_count` 回灌 project、`quality_score` 回灌 feed。
|
||
|
||
回灌是双向闭环:游戏进 feed → 玩家试玩产生会话 → 会话事件进遥测 → 聚合 → 热度和质量分回灌 project 和 feed,排序随之变化、把好游戏推得更靠前。
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
P["game_project<br/>status=4"] --> R["game_feed_rank<br/>sort_score 出流"]
|
||
R --> S["game_runtime_session<br/>试玩会话"]
|
||
S --> E["game_telemetry_event<br/>原始事件"]
|
||
E --> G["game_telemetry_game_stat<br/>日聚合"]
|
||
G -. "回灌 play/like" .-> P
|
||
G -. "回灌 quality" .-> R
|
||
IL["game_feed_interact_log<br/>点赞/收藏/分享/举报"] -. "举报转 compliance" .-> R
|
||
```
|
||
|
||
### 2.4 钱财闭环(ad + trade)
|
||
|
||
广告位 `game_ad_slot` 是配置(对齐第 7 类契约):`slot_id` 业务唯一,`type` 区分 rewarded / interstitial / banner,`provider` 区分 csj / gdt / mock,`ecpm_floor` 是底价(分),`block_minor` / `max_per_session` 是合规约束。每次曝光或激励落一条 `game_ad_revenue`:`creator_user_id` 由 `game_id` 反查 project 拿到,`event_type` 区分 1 曝光 / 2 激励,`revenue_amount` 以分计,`settle_status` 走 0 未结算 / 1 已结算,`uk_trace(trace_id, event_type, tenant_id)` 防重。这张表的 `id` 是 ad 到 trade seam 的对账锚点 —— 它就是 `game_trade_income.source_ref`。
|
||
|
||
trade 侧 `game_trade_account` 是创作者收益账户,物化汇总流水,守一条不变式 `balance + frozen + total_withdraw = total_income`,余额行级原子增减。分账后入账走 `game_trade_income`:`source` 区分 1 广告 / 2 打赏,广告的 `source_ref` 就是 `game_ad_revenue.id`,`uk_source(source, source_ref, tenant_id)` 防重复分账,`net_amount = gross × share_rate`,`trace_id` 从 ad 透传过来串起对账链。提现 `game_trade_withdraw` 的 `status` 走 0 待审核 / 1 打款中 / 2 已打款 / 3 驳回 / 4 打款失败,`uk_biz_no` 幂等,`V14` 加的 `transfer_ref` 是打款转账单号的对账锚点。
|
||
|
||
旁支两条都是赋值类:`game_trade_grant` 是 admin 赋余额流水,`source` 固定 `admin_grant`,独立于 income 防止污染营收报表;会员订阅 `game_trade_subscription` 一用户一行(`uk_user`),`plan` 区分月 / 季 / 年,`expire_time` 是判定有效的权威字段、续期叠加,配套的 `game_trade_subscription_grant` 是订阅赋值的幂等账本,`uk_biz_no` 挡任意历史 bizNo 重放(这是 P0 修复:行内 `last_grant_biz_no` 不作幂等判据)。
|
||
|
||
```mermaid
|
||
erDiagram
|
||
game_ad_slot ||--o{ game_ad_revenue : "slot_id 计费"
|
||
game_ad_revenue ..> game_project : "game_id 反查 creator"
|
||
game_ad_revenue }o--|| game_trade_income : "id = source_ref 对账"
|
||
game_trade_income }o--|| game_trade_account : "净额入账"
|
||
game_trade_account ||--o{ game_trade_withdraw : "提现 uk_biz_no"
|
||
game_trade_grant ..> game_trade_account : "admin 赋余额"
|
||
game_player ||--|| game_trade_subscription : "uk_user 一用户一行"
|
||
game_trade_subscription ||--o{ game_trade_subscription_grant : "uk_biz_no 幂等账本"
|
||
```
|
||
|
||
### 2.5 身份、社区与 B 端(passport + community + biz)
|
||
|
||
身份层是整张图的归属起点。`game_player` 是玩家兼创作者的最小身份,`id` 就是 OAuth2 token 里的 `userId`(`userType=MEMBER`),`mobile` 是登录主键(`uk_mobile`),`creator_flag` 是创作者白名单位(0 玩家 / 1 创作者,A2 种子名单),`real_name` / `id_card_no` 是 AES 密文预留、提现前才收集,`first_anon_id` 做匿名到登录的归因。邀请码 `game_invite_code` 是报备前的受限激活旁路,`code` 唯一,条件更新原子核销防超发。这两张表物理上不在 `game_*` 迁移里(详见第 4 章)。
|
||
|
||
社区围着用户和游戏转。`game_community_message` 是站内消息中心,按 `user_id` 归属隔离,`type` 区分系统 / 公告 / 审核 / 收益。创作者等级 `game_community_level`(`uk_creator`)的 `published_count` 在游戏发布通知时原子累加,`last_milestone` 幂等;新人奖励 `game_community_reward`(`uk_creator_milestone`)记触发、发放留 M4。评论 `game_community_comment` 的 `target_id` 指 `game_project.id`,`parent_id` 预留楼中楼,`report_status` 转 compliance。关注 `game_community_follow`(`uk_user_target`)的 `active` 是软开关。排行 `game_community_rank` 是排行的权威源(DB 是真相、Redis ZSET 只是缓存),`dimension` 区分 game_hot / creator_hot。弹幕 `game_community_danmaku` 以 `game_id` 为房间,WS 广播加落库回放。
|
||
|
||
B 端定制走一条单据链。`game_biz_lead` 是询单主单,`biz_type` 区分品牌 / 文旅 / 通用,`status` 走 0 待跟进 / 1 已报价 / 2 制作中 / 3 待验收 / 4 已交付,`demo_version_id` 指向一个 `game_version`。报价 `game_biz_quote` 挂在 lead 上(报价不等于可支付订单,`quote_no` 由 service 串行递增、DB 不强约束)。进度 `game_biz_progress` 每推进一步落一条,组成看板时间线。验收 `game_biz_acceptance` 是三态交付确认,`signed_offline` 是 M4 前的线下兜底,`uk_lead_version` 防重。
|
||
|
||
```mermaid
|
||
erDiagram
|
||
game_player ||--o| game_invite_code : "invite_code_id 核销"
|
||
game_player ||--o{ game_community_message : "user_id 消息"
|
||
game_player ||--|| game_community_level : "uk_creator 等级"
|
||
game_community_level ||--o{ game_community_reward : "uk_creator_milestone"
|
||
game_player ||--o{ game_community_comment : "user_id 评论"
|
||
game_community_comment ..> game_project : "target_id 指游戏"
|
||
game_player ||--o{ game_community_follow : "uk_user_target 关注"
|
||
game_player ||--o{ game_community_danmaku : "弹幕"
|
||
game_player ||--o{ game_biz_lead : "customer/owner 询单"
|
||
game_biz_lead ||--o{ game_biz_quote : "lead_id 报价"
|
||
game_biz_lead ||--o{ game_biz_progress : "lead_id 进度"
|
||
game_biz_lead ||--o{ game_biz_acceptance : "lead_id 验收"
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 全平台主表总清单
|
||
|
||
按表索引,从这里跳进具体迁移看逐列细节。
|
||
|
||
| 表名 | 模块 | 核心职责 | 主键 / 关键唯一键 | 迁移锚点 |
|
||
|---|---|---|---|---|
|
||
| game_project | project | 游戏主表 / Game 实体,创作链总闸 | id(=GamePackage.gameId) | V1 L14 |
|
||
| game_version | project | 版本表,编译产物回写落点 | id(=GamePackage.versionId) | V1 L44 |
|
||
| game_review_record | project | 审核记录,锁风门二态 | game_id / version_id | V1 L66 |
|
||
| game_zone | project | 双轨专区(官方精选 / UGC) | type;末尾种 2 条 | V1 L87 |
|
||
| game_project_zone | project | 游戏↔专区 M:N | uk_game_zone(game_id,zone_id) | V1 L106 |
|
||
| game_aigc_task | aigc | 无状态生成原子 / 异步队列 | id(=taskId);prompt_hash 缓存键 | V2 L23;V15/16/17/19 ALTER |
|
||
| game_runtime_build | runtime | 编译任务 | game_id / version_id | V3 L23 |
|
||
| game_runtime_package | runtime | 对外清单,试玩宿主据此加载 | uk_version(一版一行) | V3 L56;V10 ALTER |
|
||
| game_runtime_session | runtime | 试玩会话,遥测回灌源 | uk_play_token | V3 L90;V11 ALTER |
|
||
| game_feed_rank | feed | 排序缓存 / 精选覆盖层 | uk_game_zone(game_id,zone_id) | V4 L19;V25 ALTER |
|
||
| game_feed_interact_log | feed | 互动信号幂等流水 | uk_user_game_action | V4 L47 |
|
||
| game_telemetry_event | telemetry | 原始事件,MQ 消费幂等落库 | uk_event_id(event_id) | V5 L16;V10 替换 uk |
|
||
| game_telemetry_game_stat | telemetry | 游戏×日聚合 | uk_game_date(game_id,stat_date) | V5 L51 |
|
||
| game_ad_slot | ad | 广告位配置(契约#7) | uk_slot(slot_id) | V6 L21 |
|
||
| game_ad_revenue | ad | 广告收入台账,ad↔trade seam | uk_trace(trace_id,event_type,tenant_id) | V6 L54 |
|
||
| game_trade_account | trade | 创作者收益账户,流水物化汇总 | uk_user(user_id,tenant_id) | V7 L32 |
|
||
| game_trade_income | trade | 分账后入账流水 | uk_source(source,source_ref,tenant_id) | V7 L59 |
|
||
| game_trade_withdraw | trade | 提现申请 | uk_biz_no | V7 L93;V14 ALTER |
|
||
| game_trade_grant | trade | admin 赋余额流水 | uk_biz_no | V21 L27 |
|
||
| game_trade_subscription | trade | 会员订阅,一用户一行 | uk_user(user_id,tenant_id) | V21 L60 |
|
||
| game_trade_subscription_grant | trade | 订阅赋值幂等账本 | uk_biz_no | V21 L95 |
|
||
| game_studio_session | studio | 有状态创作工作台 | creator_user_id / game_id | V8 L24 |
|
||
| game_studio_taskchain | studio | 创作任务链(只持引用) | session_id → session | V8 L52 |
|
||
| game_studio_asset | studio | 会话内资产槽位 seam | session_id | V8 L78 |
|
||
| game_source_project | studio | 源项目工件,源侧存储面 | uk_game_source_hash | V18 L28;V20 ALTER |
|
||
| game_material | studio | 创作者私有素材库(六类) | creator_user_id 隔离 | V24 L22 |
|
||
| game_compliance_gate_result | compliance | 锁风门评估台账(非幂等) | game_id / version_id | V9 L22 |
|
||
| game_content_rating | compliance | 内容分级 | uk_game_version | V9 L45 |
|
||
| game_user_ban | compliance | 封禁台账 | uk_target_active | V9 L68 |
|
||
| game_policy_document | compliance | 政策正文(富文本+版本) | uk_type_version | V22 L23 |
|
||
| game_compliance_appeal | compliance | 申诉台账,生成列原子去重 | uk_user_ban_pending | V22 L56 |
|
||
| game_community_message | community | 站内消息中心 | user_id 隔离 | V12 L23 |
|
||
| game_community_level | community | 创作者等级 | uk_creator | V12 L47 |
|
||
| game_community_reward | community | 新人奖励触发记录 | uk_creator_milestone | V12 L68 |
|
||
| game_community_comment | community | 评论(预留楼中楼) | target_id → project | V23 L25 |
|
||
| game_community_follow | community | 关注(active 软开关) | uk_user_target | V23 L49 |
|
||
| game_community_rank | community | 排行权威源(DB=真相) | uk_dimension_target | V23 L72 |
|
||
| game_community_danmaku | community | 弹幕(WS 广播+落库回放) | game_id 房间 | V23 L93 |
|
||
| game_biz_lead | biz | B/G 端定制询单主单 | customer/owner_user_id | V13 L26 |
|
||
| game_biz_quote | biz | 报价(非可支付订单) | lead_id → lead | V13 L55 |
|
||
| game_biz_progress | biz | 进度工单(看板时间线) | lead_id | V13 L77 |
|
||
| game_biz_acceptance | biz | 交付验收(三态) | uk_lead_version | V13 L98 |
|
||
| game_player | passport | 玩家 / 创作者最小身份 | id(=OAuth2 userId);uk_mobile | V11 L25(寄宿 system) |
|
||
| game_invite_code | passport | 邀请码(报备前激活旁路) | uk_code | V11 L58 |
|
||
|
||
### 模块归属一图
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph P[project · 5 表]
|
||
p1[game_project] --- p2[game_version] --- p3[game_review_record]
|
||
p4[game_zone] --- p5[game_project_zone]
|
||
end
|
||
subgraph G[aigc · 1 表]
|
||
g1[game_aigc_task]
|
||
end
|
||
subgraph RT[runtime · 3 表]
|
||
r1[game_runtime_build] --- r2[game_runtime_package] --- r3[game_runtime_session]
|
||
end
|
||
subgraph FD[feed · 2 表]
|
||
f1[game_feed_rank] --- f2[game_feed_interact_log]
|
||
end
|
||
subgraph TM[telemetry · 2 表]
|
||
t1[game_telemetry_event] --- t2[game_telemetry_game_stat]
|
||
end
|
||
subgraph AD[ad · 2 表]
|
||
a1[game_ad_slot] --- a2[game_ad_revenue]
|
||
end
|
||
subgraph TR[trade · 6 表]
|
||
tr1[account] --- tr2[income] --- tr3[withdraw]
|
||
tr4[grant] --- tr5[subscription] --- tr6[sub_grant]
|
||
end
|
||
subgraph ST[studio · 5 表]
|
||
s1[session] --- s2[taskchain] --- s3[asset]
|
||
s4[source_project] --- s5[material]
|
||
end
|
||
subgraph CP[compliance · 5 表]
|
||
c1[gate_result] --- c2[content_rating] --- c3[user_ban]
|
||
c4[policy_document] --- c5[appeal]
|
||
end
|
||
subgraph CM[community · 7 表]
|
||
m1[message] --- m2[level] --- m3[reward] --- m4[comment]
|
||
m5[follow] --- m6[rank] --- m7[danmaku]
|
||
end
|
||
subgraph BZ[biz · 4 表]
|
||
b1[lead] --- b2[quote] --- b3[progress] --- b4[acceptance]
|
||
end
|
||
subgraph PP[passport · 2 表 · 寄宿 system]
|
||
pl1[game_player] --- pl2[game_invite_code]
|
||
end
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 跨表关系为什么没有外键
|
||
|
||
上面所有的连线在代码里都没有物理外键,全是逻辑软关联。这是单体内有意的解耦:模块之间只经各自的 `-api` 包用 DTO、Feign 软关联,归属隔离靠 Service 的可信边界 —— Mapper 强制带 `getLoginUserId()` 谓词,或走 DataPermission —— 而不是靠 DB 约束。依赖方向在 [13模块.md](../架构/13模块.md) 有权威图。这么做的代价是数据一致性要 Service 自己保,收益是模块能各自演进、后续拆独立仓时不被外键绊住。
|
||
|
||
所有 `game_*` 表里的 `creator_user_id` / `user_id` / `player_user_id`,数值上都等于 `game_player.id`,也就是 OAuth2 的 `userId`,但代码层一根外键都没有。
|
||
|
||
`game_player` 和 `game_invite_code` 这两张身份表有个容易误解的地方:它们由 `V11.0.0__create_passport_player_invite.sql` 建,但物理归属不在任何 `game_*` 业务模块,而在 `huijing-module-system`。`PlayerDO` 用 `@TableName("game_player")` 映射,锚点在 `game-cloud/huijing-module-system/huijing-module-system-server/src/main/java/com/wanxiang/huijing/module/system/dal/dataobject/passport/PlayerDO.java`(`id` 即 `userId`、`userType=MEMBER`)。13 模块都引这张表、但它不在 13 模块清单里。
|
||
|
||
admin 和运营侧的用户是另一套人,走 Huijing 原生的 `system_users`,不走 `game_player`。`ProjectServiceImpl` 引 `AdminUserApi` 就是这条线(锚点 `game-module-project-server/.../ProjectServiceImpl.java`)。一句话:C 端玩家创作者用 `game_player`,B 端管理员用 `system_users`,两套身份在数值空间和代码路径上都分开。
|
||
|
||
---
|
||
|
||
## 5. 必须知道的数据语义
|
||
|
||
几处分散在迁移头注里的语义,踩错会出真 bug,集中讲清。
|
||
|
||
两个 `quality_score` 口径不能互灌。aigc 的 `game_aigc_task.quality_score` 是 `DECIMAL(4,3)`、取值 0 到 1,衡量生成质量(模型把游戏做得好不好);telemetry 和 feed 的 `quality_score` 是 0 到 100,衡量运营质量(玩家玩得好不好、留不留存)。多个迁移头注都显式警告过这一点。把生成质量分灌进运营排序、或反过来,都是错的。
|
||
|
||
`play_count` 有三处口径,只有一处是权威。`game_runtime_session` 的试玩不计 play_count;`game_telemetry_game_stat` 的聚合才是权威源;`game_project.play_count` 是从聚合回灌过来的物化字段,读它没问题,但它的真相在 telemetry 聚合那里。
|
||
|
||
源和产物是两个存储面。`game_source_project` 存源(`source_json`),`game_version` 存产物(`package_url` 指向的包)。"改源不改包"指改了源要重新构建才生成新产物,不是直接动包。两个面解耦,源构建失败时会留下孤儿源(`game_source_project.status=2`)而不污染产物面。
|
||
|
||
`package_url` / `checksum` / `bundle_size` 的权威写者是 runtime(决策5)。生成时这几列是空的,runtime 编译成功后才回写 `game_version`。别在 aigc 或 studio 侧抢着写这几列。
|
||
|
||
全平台的共性范式统一交代一次,后面读任何一张表都按这套理解。每张表都有 Yudao/Huijing 6 审计列:`creator` / `create_time` / `updater` / `update_time` / `deleted BIT(1)` / `tenant_id BIGINT`(MVP 单租户恒为 0)—— 注意业务归属列 `creator_user_id` / `user_id` 和审计列 `creator` 是两回事,前者是业务上谁拥有这条数据,后者是技术上谁创建了这行。金额一律用 `BIGINT` 以"分"计、禁浮点(ad 和 trade 全线),比例才用 `DECIMAL`。状态机一律 `TINYINT`,非法流转由 Service 校验、DO 层不承载。幂等键有四种范式:资金、计费、邀请用 `uk_biz_no` 或 `uk(trace_id + type)` 或 `uk_source`;互动、关注用唯一键加 `active` 软开关,取消时翻 `active` 不删行。逻辑删除维度纳入唯一键(`…, deleted, tenant_id`)是 Huijing 惯例,这样软删后同业务键可以重新插入。
|
||
|
||
---
|
||
|
||
## 6. 维护与边界声明
|
||
|
||
本页是数据结构 SoT 的"总图"层,只画表和表怎么连。表的逐列细节以迁移头注为准,完成度以 [MVP进度总账](../../mvp/MVP进度总账.md) §2 为准,模块边界以 [13模块.md](../架构/13模块.md) 为准,工程落地以 [后端 README](README.md) 为准。
|
||
|
||
迁移只增不改。新增表或列时,回这里补对应的节点和说明 —— 主表进第 1 章主干图、明细进第 2 章对应链路子图、所有表进第 3 章清单,并锚点到新迁移。
|
||
|
||
---
|
||
|
||
## 7. 待落地的数据契约缺口(contract-first 清单)
|
||
|
||
下面这几条都是设计已经讲清、但库里还没改的硬缺口或待建表。它们的共同点是改动横跨契约文件、Flyway 迁移和 `-api` 镜像,属于 contract-first 动作而非顺手加一列,所以集中登记成待落地清单,别因为这里写了就当已建。本节内容均为**创始人 2026-06-22 历史回收判定捡回**。
|
||
|
||
### 7.1 `game_trade_income` 缺 `game_id`:按游戏算钱的连接键断在这里
|
||
|
||
入账流水现在只有 `source` / `source_ref` / `gross_amount` / `share_rate` / `net_amount`,能顺着 `source_ref` 串到广告单或打赏单,却串不到具体哪款游戏。结果是两件事同时被卡:一是按游戏粒度算单位经济(哪款游戏赚了多少、成本回不回得来),二是收益回流语料需要的 join 键(把"线上赚了多少钱"这个结果标签接到某款游戏的源工件上,见 [生成引擎/数据飞轮.md](../架构/生成引擎/数据飞轮.md) §3.3)。`game_ad_revenue` 这一侧本来就带 `game_id`(它靠 game_id 反查创作者),缺口只在 trade 这一段没把它透传下来、也没落列。
|
||
|
||
补法是一条四步链,缺一即断:广告台账查询接口 `AdRevenueApi.getUnsettledRevenue` 返回的 `AdRevenueRespDTO` 先加上 `gameId` 字段;trade 的 `SettlementJob` 拿到后透传进 `IncomeService.recordIncome`,写入新增的 `game_id` 列;打赏线的 `recordIncome` 同样把打赏单所属游戏的 gameId 带进来。这条链在变现侧的工程口径(W4 ③④、防重计数硬约束、为什么否决"往 telemetry 加列")已写在 [运营/变现与单位经济.md](../运营/变现与单位经济.md) §6 待办首条,本页只补它在数据模型侧的列与镜像清单,两处一并维护、别让缺口口径漂移。
|
||
|
||
contract-first 待落地:
|
||
- 契约 `contracts/api-schemas/ad.yaml` —— `AdRevenueRespDTO`(`getUnsettledRevenue` 的 itemType)加 `gameId: Long`,语义"产生这笔收入的游戏 ID,供 trade 落 game_trade_income.game_id 做按游戏归因"。
|
||
- 契约 `contracts/api-schemas/trade.yaml` —— `IncomeRespVO` 加 `gameId`;`recordIncome` 的入参说明补 gameId 透传。
|
||
- Flyway 新迁移(版本号按落地时仓内最新顺延,现仓已到 V25)—— `game_trade_income` `ADD COLUMN game_id BIGINT NULL`(nullable,对存量行无影响、向后兼容),加普通索引 `idx_game_id` 供按游戏聚合。
|
||
- Java 镜像 —— `IncomeDO` 加 `gameId` 字段、`IncomeRespVO` 加 `gameId`、`recordIncome` 签名带 gameId 透传;`AdRevenueRespDTO` 同步加。
|
||
- 注意分账幂等键 `uk_source(source, source_ref, tenant_id)` 不动:game_id 是归因维度、不进幂等键。
|
||
|
||
### 7.2 `game_telemetry_game_stat` 缺留存字段:先定口径再加聚合列
|
||
|
||
聚合表现在只有 play / end / complete / duration / like / favorite 这几个量,拿不出"次留 / N 日留存"。这同样卡着收益回流语料(没有留存标签就训不出"什么样的游戏留得住人")和未来推荐排序里的 freshness / 留存信号。它和 §7.1 同源但落点不同:§7.1 缺的是 trade 侧的连接键,这条缺的是 telemetry 侧的留存口径。
|
||
|
||
这条不是单纯加列,得**先定义留存口径再加聚合字段**——留存按设备(anonId)还是按登录用户算、次留的窗口怎么切(自然日还是 24 小时滚动)、回访事件以哪个埋点为准,这些定下来之前不要急着 ALTER。口径定了之后,聚合字段才好确定是落"次留人数 / 分母人数"两列由读侧算率,还是直接物化一个留存率。
|
||
|
||
contract-first 待落地:
|
||
- 先产出留存口径定义(归到遥测域,可在本页或观测体系档补一段口径说明),作为加列的前置。
|
||
- 契约 `contracts/events.schema.json` —— 若现有埋点不足以判定回访,可能要新增或明确一个回访事件;先核对 `game_play_start` 能否承载,能则不新增事件。
|
||
- Flyway 新迁移 —— `game_telemetry_game_stat` 按定好的口径 `ADD COLUMN`(如 `retained_uv` / 留存分母,均 nullable)。
|
||
- Java 镜像 —— 聚合 Service 补留存计算、`game_telemetry_game_stat` 对应 DO 加字段。
|
||
|
||
### 7.3 锁风三档裁决落点:MVP 写 `details` JSON,不改表
|
||
|
||
锁风三档(标准 / 严格 / 人工复核)的裁决要能回答"这条内容为什么走了严格档",落点是现有的 `game_compliance_gate_result` 台账。但 `GateResultDO` 现在只有 `id` / `game_id` / `version_id` / `verdict` / `rating` / `details` / `trace_id`,没有 `lock_strength` 或 `ip_id` 列。MVP 阶段的取舍是**写进已有的 `details` JSON 列**——`details` 本来就存各原子明细数组 `[{atom,verdict,reason}]`,给它再加 `lockStrength` 和 `ipId` 两个键即可,不改表、不加迁移,溯源时从 JSON 里读。这条选 JSON 而非升列的理由是:MVP 还没有"按档位聚合审核量做报表"的需求,软扩字段够用且零迁移成本。完整设计在 [运营/审核台运营.md](../运营/审核台运营.md) §3.3,本页只登记它在数据模型侧的落点结论。
|
||
|
||
只有在确有按档位做报表(需要 SQL `WHERE lock_strength=…` / `GROUP BY`)的需求时,才 contract-first 升列:`game_compliance_gate_result` `ADD COLUMN lock_strength VARCHAR / ip_id BIGINT`(均 nullable、向后兼容),`GateResultDO` 加两字段,Mapper 不动(MyBatis-Plus 自动映射)。升列迁移的版本号要和 §7.4 的信用台账迁移错开,别撞同一个版本号。
|
||
|
||
### 7.4 创作者信用:独立扣分台账,不在等级表加列
|
||
|
||
创作者信用要把单次违规处置转成对人的长期约束(信用低 → 审核自动拉严档 + feed 基线曝光降低)。它的宿主候选是 community 的等级引擎 `game_community_level`,但那张表只有 `level` / `published_count` / `last_milestone`,没有 `credit_score`,`CommunityNotifyApi` 也没有扣分端点。创始人 2026-06-22 定的落点是**独立扣分台账,而不是在等级表上加一个 `credit_score` 列**——每次扣分记一条流水(谁、因为哪次处置、扣多少、什么时候),信用分由台账聚合得出。选台账而非加列,是为了让信用变化可追溯、可审计、可对账,也便于将来做误伤申诉时逐条回放;一个汇总列做不到这些。
|
||
|
||
contract-first 待落地:
|
||
- 新建 Flyway 迁移建表 `game_community_credit_ledger`(继承 TenantBaseDO 拿 6 审计列),关键字段:`creator_user_id`(归属)、`delta`(本次扣分,负值)、`reason_type`(对应哪类处置:降权 / 下架 / 封禁)、`source_ref`(关联的处置单 / ban id)、`biz_no`(幂等键)、`trace_id`(对账)。当前信用分 = 该创作者全部 delta 之和(或物化一个汇总,但权威源是台账)。
|
||
- 契约 `contracts/api-schemas/community.yaml` —— `CommunityNotifyApi` 现有 4 方法(generate-done / review-result / income-changed / project-published),为它加第 5 个扣分端点 `deduct-credit`,**带幂等键 `bizNo`**(同 community 既有 `(userId,type,bizRef)` 去重范式),供 compliance 在处置时同进程 @Primary 调用。
|
||
- 跨模块依赖 —— compliance-server 调 community 的 `-api` 扣分,扣分失败不回滚处置主操作(扣分是处置的附带副作用),走幂等补偿。
|
||
- 注意迁移版本号与 §7.3 锁风升列(若升)错开。
|
||
- 完整设计与误伤治理在 [运营/审核台运营.md](../运营/审核台运营.md) §3.5,本页只登记数据落点。
|
||
|
||
### 7.5 封号级联:数据语义上的两条断链
|
||
|
||
封一个创作者账号现在只写 `game_user_ban` 台账,既不置 `game_player.status=DISABLE`,也不遍历下架其全部已发布游戏,而创作准入的 `validateCreator` 又不查 ban 表。结果是封了号拦不住本人继续创作、其旧内容仍在 feed 里跑——一个真实的越权 / 绕管缺陷。这条横跨鉴权域(准入)和合规域(处置),两边都容易以为对方管而都漏。数据侧要补两条断链:封号时同步把 `game_player.status` 置 `DISABLE`(走 passport `-api`,复用既有禁用态判定)、按 `creator_id` 查其全部已发布游戏批量走 `offlineRank` 下架。准入侧的 `validateCreator` 改法详见 [鉴权与权限.md](鉴权与权限.md) §5,本页只标这条级联在数据上动了 `game_player.status` 与 feed 出流两处状态,不新建表。
|
||
|
||
### 7.6 绘豆:平台类积分体系的立位(待单独设计)
|
||
|
||
历史上 D3 业务决策里有一套积分体系 `point_rate=10`(1 元 = 10 积分,打赏走积分计入创作者收益)。这套旧口径**不照搬**——创始人 2026-06-22 提出新设计「绘豆」,是平台侧的类积分体系,取代旧 `point_rate=10` 的定价口径。绘豆的具体形态(用途是打赏 / 生成额度 / 会员权益、与档位和订阅漏斗怎么挂、单价怎么定、要不要落独立账本表与幂等扣减)目前**待单独设计**,这里只占一个位:trade 现有的 TIP(打赏)入账入口是真的,但"积分 / 绘豆"这层产品形态尚未落库,设计成形前不要在 trade 或别处提前建绘豆相关的表或枚举。绘豆与预算即产品(档位决定生成级别与额度)、订阅漏斗一脉,设计时一并考虑。
|