4.9 KiB
4.9 KiB
Data Model And State
真相源原则
这套服务端最关键的状态归属如下:
Postgres- 用户认证数据
- 角色、聊天、Provider 配置
- Flux 余额与账本
- Stripe 业务镜像
- LLM 请求日志
Redis- Flux 余额缓存
- 服务配置 KV
- 聊天跨实例广播 (Pub/Sub)
- Sub-Flux 计量债务账本(TTS 字符等,详见
flux-meter.md) - TTS voices 上游响应缓存
如果要判断”改哪个地方才算真的改成功”,大多数场景答案都是 Postgres。Redis Streams 已全部移除,没有”计费事件队列”这层抽象。
主要表分组
认证
usersessionaccountverification
来源文件:
src/schemas/accounts.ts
说明:
better-auth直接用这组表- 由
pnpm -F @proj-airi/server auth:generate自动产物,手改会被覆盖
角色与用户交互
characterscharacter_coversavatar_modelcharacter_capabilitiescharacter_i18ncharacter_promptsuser_character_likesuser_character_bookmarks
来源文件:
src/schemas/characters.tssrc/schemas/user-character.ts
说明:
- 角色实体采用软删除
- 点赞与收藏通过中间表建模
- 计数值冗余保存在
characters表上
聊天
chatschat_membersmessagesmediastickerssticker_packs
来源文件:
src/schemas/chats.ts
说明:
messages.seq是会话内顺序字段- 写消息时通过
SELECT ... FOR UPDATE锁 chat 以串行生成 seq senderId是宽松字段,不强制外键
Provider 配置
user_provider_configssystem_provider_configs
来源文件:
src/schemas/providers.ts
说明:
- 运行时查询时会把系统配置和用户配置拼接成一个结果集
config是jsonb
Flux / 账本
user_fluxflux_transaction
来源文件:
src/schemas/flux.tssrc/schemas/flux-transaction.ts
职责边界:
user_flux- 当前余额快照(单行/用户)
flux_transaction- append-only 账本流水(type: credit / debit / initial / promo)
- 同时承担系统真相源和用户可见历史,
/api/v1/flux/history直接读这张表
关键约束:
flux_transaction对(userId, requestId) WHERE requestId IS NOT NULL有部分唯一索引- 用来做扣费 / 充值幂等(含 admin promo grant 的
idempotencyKey)
Stripe 业务镜像
stripe_customerstripe_checkout_sessionstripe_subscriptionstripe_invoice
来源文件:
src/schemas/stripe.ts
说明:
- 这些表是 Stripe 状态的本地镜像
- 真正的余额变化仍由
billingService写入user_flux + flux_transaction fluxCredited字段用于避免重复入账
LLM 请求日志
llm_request_log
来源文件:
src/schemas/llm-request-log.ts
说明:
- 只做追加写入
- 明确不加 user 外键,以避免高并发写入的额外约束成本
服务与状态写入边界
createFluxService()
负责:
- 余额读取
- 新用户首次读取时初始化
user_flux - Redis cache-aside
不负责:
- 扣费
- 充值
- transaction 写入
createBillingService()
负责:
- 所有余额写操作
- DB 事务
- debitFlux / credit 方法:事务内 lock → check → update
user_flux→ insertflux_transactionledger - 事务提交后 best-effort
redis.set更新 Flux 余额缓存
这是所有 Flux 写路径应收敛到的中心。
createStripeService()
负责:
- Stripe 实体 upsert
不负责:
- 最终 Flux 入账
真正入账通过 billingService.creditFluxFromStripeCheckout() 或相关 credit 方法完成。
Redis 中的数据类型
Flux 缓存
- key:
flux:<userId> - value: 字符串化整数
写入来源:
fluxService.getFlux()cache miss 后回填billingService余额事务提交后 best-effortredis.set直接更新(API 进程内同步)
配置 KV
- key:
config:<CONFIG_NAME>
由 config-kv.ts 管理,支持:
- 数值
- 字符串
FLUX_PACKAGESJSON
聊天跨实例广播
- channel:
chat:broadcast:<userId>
幂等与并发控制
余额并发
billingService 在事务中:
- (可选)按
(userId, requestId)命中 ledger → 命中即返回,跳过余下步骤 SELECT user_flux FOR UPDATE- 计算新余额
- 写
user_flux+ 写flux_transactionledger - 事务提交后 best-effort
redis.set
这保证同一用户余额更新是串行化的,并且 ledger 行与余额变更在同一原子提交里。
Stripe 幂等
主要依赖:
stripe_checkout_session.fluxCreditedstripe_invoice.fluxCreditedflux_transaction(userId, requestId)唯一约束
现有代码中的结构信号
src/schemas/flux-grant-batch.ts是已废弃的旧 admin batch 设计 schema,没有被app.ts装配也没有 migration 在用,是 dead code,改这块前直接删除。当前 admin 发 FLUX 走/api/admin/flux-grants同步路径,不写新表。