1. ER 总览
┌──────────┐ 1:N ┌────────────┐
│ users │────▶│ identities │ (platform+openid → 统一账号)
└────┬─────┘ └────────────┘
┌──────────┼─────────────┬──────────────┬─────────────┐
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────────┐
│ saves │ │ wallets │ │ currency_txns │ │ orders │ │ share_records │
│ N per │ │ 1:1 │ │ 流水 N │ │ N │ │ N ──▶ share_visits
│ (game) │ └─────────┘ └───────────────┘ └────┬─────┘ └───────────────┘
└───┬────┘ │ product_id
│ game_id ▼
│ ┌─────────┐ ┌──────────────┐
└─────▶│ games │◀─────────────│ products │
└────┬────┘ └──────────────┘
│ game_id
┌───────────┼────────────────┬────────────────┐
▼ ▼ ▼ ▼
┌──────────────────┐ ┌─────────┐ ┌───────────┐ ┌───────────┐
│leaderboard_scores│ │ seasons │ │ ad_events │ │ events │(埋点,短保留)
└──────────────────┘ └─────────┘ └───────────┘ └───────────┘
用户日常域: signins · streaks · tasks · task_progress
管理域: admin_users · audit_logs · app_configs
2. 账号域
2.1 users — 统一用户
| 字段 | 类型 | 说明 |
| id | bigserial PK | 全平台唯一 user_id,所有业务表外键 |
| nickname / avatar | text | 用户授权后写入;昵称入库前过内容安全检查 |
| province | text | 地域攀比(省榜)用;来源 IP 归属或用户授权 |
| status | smallint | 0 正常 / 1 禁言(榜单匿名)/ 2 封禁 |
| risk_level | smallint | 风控分层:0 正常 / 1 观察 / 2 高危(作弊嫌疑,奖励降权) |
| created_at / last_login_at | timestamptz | 注册与活跃;last_login_at 支撑回归奖励判定(≥3/7 天未登录) |
2.2 identities — 平台身份(双平台打通的关键)
| 字段 | 类型 | 说明 |
| id | bigserial PK | — |
| user_id | bigint FK→users | 一个用户可挂多个平台身份 |
| platform | text | wx / tt(预留海外平台) |
| appid | text | 合集与独立爆款是不同 appid,都映射到同一 user(靠 unionid) |
| openid | text | UNIQUE(platform, appid, openid) |
| unionid | text,可空,索引 | 微信同主体多小游戏合并账号的依据;留意 拆独立爆款前必须在开放平台绑定主体拿到 unionid,否则合集与独立款账号无法打通 |
| session_key_enc | text | 加密存储,用于解密平台数据(如需要);短期有效 |
3. 游戏库域
3.1 games — 游戏库(registry.html 的数据库化)
| 字段 | 类型 | 说明 |
| game_id | text PK | 如 g01_dieta(全民叠塔)…g15_juecha;见 04 映射表 |
| title / emoji / engine_tag | text | 名称、图标、心理引擎标签(沿用 registry 分类) |
| status | text | dev / test / online / offline——大厅只下发 online;灰度用 test+白名单 |
| entry | text | 合集内分包名/入口路径;独立版记录其 appid |
| sort / is_new | int / bool | 大厅排序与 NEW 角标(运营可调) |
| config | jsonb | 逐款游戏参数:{ energyMax, energyRegenSec, scoreMax, sessionMinSec, adScenes:[...], metaVersion }。scoreMax/sessionMinSec 是服务端防作弊校验的依据(见 03) |
| updated_at | timestamptz | 客户端以此做配置缓存失效 |
4. 存档域(M1 的落点)
4.1 saves — 统一云存档
| 字段 | 类型 | 说明 |
| user_id / game_id / slot | 复合 PK | slot 默认 0;预留多存档位。特殊 game_id:_hub 存合集大厅级数据(用户设置、新手引导标记) |
| data | jsonb | 游戏自定义结构,SDK 透传;单档上限 64KB(服务端硬校验,防滥用) |
| version | int | 乐观锁:客户端提交 version=n,服务端仅当 db.version==n 时写入并 +1,否则返回 409 与云端档,由 SDK 走冲突策略(见 04) |
| updated_at | timestamptz | 冲突提示与运营排查用 |
存档内容约定:货币(金币/钻石/体力)不放 saves.data,一律走 wallets(服务端记账,客户端只读);saves 只放玩法进度(关卡、天赋等级、图鉴、局内统计)。这样"改存档刷钱"在结构上不可能。
5. 经济域(M2 的落点)
5.1 wallets — 钱包(服务端权威)
| 字段 | 类型 | 说明 |
| user_id | bigint PK | 1:1 users |
| coin | bigint | 软货币(全游戏通用——合集的统一金币,是"一个总系统"的核心体验) |
| gem | bigint | 硬货币(主要靠付费;签到/成就少量赠送) |
| energy / energy_updated_at | int / timestamptz | 体力惰性结算:读取时按 games.config.energyRegenSec 补算,不跑定时器 |
5.2 currency_txns — 货币流水(审计与对账)
| 字段 | 类型 | 说明 |
| id | bigserial PK | — |
| user_id / game_id | bigint / text | 索引 (user_id, created_at) |
| currency / delta / balance_after | text / bigint / bigint | 每笔变动记余额快照,客服排查零成本 |
| reason | text | signin / task / ad_reward / share_reward / pay_grant / consume_revive / consume_gacha / admin_adjust…——直接支撑 v2 的 sink/source 报表 |
| ref_type / ref_id | text | 关联单据(order_id / share_id / task_id),保证每笔发放可溯源、可幂等 |
5.3 orders — 支付订单(M8 的落点)
| 字段 | 类型 | 说明 |
| order_id | text PK | 自有单号(雪花/ULID),下单时生成并传给平台 |
| user_id / platform / product_id | — | — |
| amount_fen | int | 以分计价,对账基准 |
| status | text | 状态机:created → paid → delivered;分支 refunded / failed。回调幂等靠状态机:paid 重复回调直接 200 |
| platform_txn_id / raw_callback | text / jsonb | 平台流水号与回调原文(争议仲裁用) |
| paid_at / delivered_at | timestamptz | 发货延迟监控(正常应 <1s,同事务) |
5.4 products — 商品配置(管理后台维护)
| 字段 | 类型 | 说明 |
| product_id | text PK | 如 first_charge_6 / monthly_card / gem_30 / gift_newbie |
| type | text | first_charge / monthly / gift / gem / skin —— 对应 v2 3.3 定价梯度 |
| price_fen / grants | int / jsonb | grants 示例:{gem:60, skin:"s01", monthlyDays:30};发货逻辑读 grants 通用执行 |
| limit_per_user / platform / status | — | 首充限 1 次;平台定向(iOS 政策差异);上下架 |
6. 社交域(分享 / 排行榜)
6.1 share_records + share_visits — 分享与回流(M4/P 分享通道的落点)
| 表 | 字段 | 说明 |
| share_records | share_id(PK, 短随机码) · user_id · game_id · scene · created_at | 用户点「喊好友帮忙」即创建;share_id 编进分享卡 query |
| share_visits | id · share_id · visitor_user_id · is_new_user · rewarded · created_at | UNIQUE(share_id, visitor_user_id) 同一好友只计一次;visitor==分享者本人不计;rewarded 标记是否已给分享者发奖。回流判定与限额策略见 05 |
6.2 leaderboard_scores + seasons — 排行榜(R8 的服务端化)
| 表 | 字段 | 说明 |
| leaderboard_scores | game_id · season · user_id · score · extra(jsonb) · updated_at,UNIQUE(game_id, season, user_id) | Postgres 为持久层;Redis ZSET lb:{game}:{season} 为读缓存(topN/我的排名 O(logN))。写入必过服务端校验(score ≤ games.config.scoreMax 等) |
| seasons | season_id · game_id(可空=全局) · start_at · end_at · status · rewards(jsonb) | 赛季化支撑 M7 长线;结算由 VPS cron 执行:榜单快照→发奖入 currency_txns→开新季 |
7. 日常域(签到 / 任务)
| 表 | 字段 | 说明 |
| streaks | user_id PK · current_streak · longest_streak · last_date · freeze_count | R6 落点;freeze_count = 断签保护卡余量(广告/付费获得) |
| signins | (user_id, date) PK · streak_after · reward(jsonb) | 7 日/30 日阶梯奖励在服务端按 streak_after 计算发放 |
| tasks | task_id PK · type(daily/weekly/achievement) · title · target(jsonb) · reward(jsonb) · status | target 示例:{event:"game_finish", gameId:"any", count:3}——由埋点事件驱动进度,新任务零代码 |
| task_progress | (user_id, task_id, period_key) PK · progress · claimed_at | period_key = 日期/周号(achievement 为 'all');claimed_at 空 = 可领未领(蔡格尼克 R12 的红点数据源) |
8. 埋点与广告事件
| 表 | 字段 | 说明 |
| ad_events | id · user_id · game_id · scene · platform · status(show/complete/abort) · created_at | 激励视频漏斗:人均次数、完播率——07 看板直接消费 |
| events | id · user_id · game_id · name · props(jsonb) · created_at | 通用埋点(session_start/game_finish/unlock_choice/…,事件字典见 05)。留意 按月分区、只保留 90 天明细;日级聚合结果另存 stats_daily 供看板长期查询 |
9. 管理域
| 表 | 字段 | 说明 |
| admin_users | id · email · role(superadmin/ops/readonly) · status · last_login_at | RBAC 见 07;CF Access 白名单 + 本表双重校验 |
| audit_logs | id · admin_id · action · target · detail(jsonb) · created_at | 所有写操作(改配置/调余额/封号)强制落审计 |
| app_configs | key PK · value(jsonb) · updated_at · updated_by | 全局开关与活动配置:首充弹窗开关、分享每日限额、公告、卡点参数(M6 日程的运营侧调参入口) |
10. 一致性与幂等要点(跨表规则)
- 发货事务:orders 置 paid → wallets 入账 → currency_txns 记账 → orders 置 delivered,单事务完成;任何一步失败整体回滚,订单停在 paid 由补偿任务重试。
- 发奖幂等:一切奖励发放(分享/任务/签到/广告)以
currency_txns(ref_type, ref_id, user_id) 唯一约束兜底,重复请求天然拒绝。
- 体力惰性结算:不跑全表定时器;读钱包时按时间差补算,写回 energy_updated_at。
- Redis 可丢:榜单 ZSET、限频计数、配置缓存全部可从 Postgres 重建,Redis 故障只降级不丢数据。