已由
migrations/000001_initial_schema.sql至migrations/000020_stats_aggregation.sql增量落地;后续迁移必须新增版本文件,不得重写已应用文件。 日期:2026-09-04 事实来源:PostgreSQL;Redis 只存可重建状态
- 所有主键使用项目统一的 UUIDv7(若实现阶段因驱动限制采用等价有序 UUID,必须在 ADR/迁移说明);时间统一 UTC
timestamptz; - 金额用
amount_minor bigint+currency char(3),或经核准的 PostgreSQLnumeric;禁止 float; - 账本、UsageEvent、PaymentEvent、AuditLog 追加式且不可破坏式删除;纠错新增 adjustment/reconciliation;Credential secret history 只允许追加新版本和设置 rotated_at,不允许删除/篡改密文;
- API Key 只存不可逆 hash、短 prefix、元数据;Credential secret 只存 AEAD ciphertext、12-byte nonce、key_version;
- 所有业务外键明确租户/所有者检查,禁止仅靠前端隔离;Credential pool 与 Credential 必须属于同一 Provider;
- route 和 price 在请求开始时形成不可变 snapshot,后续配置修改不重写历史。
erDiagram
USERS ||--o{ USER_ROLES : has
ROLES ||--o{ USER_ROLES : grants
USERS ||--o{ USER_SESSIONS : owns
USERS ||--o{ MFA_FACTORS : secures
USERS ||--o{ API_KEYS : owns
API_KEYS ||--|| API_KEY_POLICIES : uses
POLICIES ||--o{ API_KEY_POLICIES : assigned
POLICIES ||--o{ POLICY_MODEL_ENTITLEMENTS : grants
MODELS ||--o{ POLICY_MODEL_ENTITLEMENTS : allows
MODELS ||--o{ MODEL_ALIASES : resolves
MODELS ||--o{ MODEL_ROUTES : exposes
MODEL_ROUTES ||--o{ ROUTE_VERSIONS : versions
ROUTE_VERSIONS ||--o{ ROUTE_TARGETS : contains
PROVIDERS ||--o{ PROVIDER_MODELS : offers
PROVIDER_MODELS ||--o{ ROUTE_TARGETS : serves
PROVIDERS ||--o{ CREDENTIALS : owns
CREDENTIAL_POOLS ||--o{ POOL_MEMBERS : contains
CREDENTIALS ||--o{ POOL_MEMBERS : joins
USERS ||--o{ WALLETS : owns
WALLETS ||--o{ WALLET_LEDGER : records
USERS ||--o{ PAYMENT_ORDERS : places
PAYMENT_ORDERS ||--o{ PAYMENT_EVENTS : receives
PAYMENT_ORDERS ||--o{ PAYMENT_PROVIDER_OPERATIONS : executes
PAYMENT_ORDERS ||--o{ PAYMENT_EXTERNAL_REFUNDS : reconciles
PAYMENT_ORDERS ||--o{ PAYMENT_DISPUTES : disputes
WALLETS ||--o{ WALLET_LIABILITIES : recovers
WALLET_LIABILITIES ||--o| PAYMENT_EXTERNAL_REFUNDS : explains
WALLET_LIABILITIES ||--o| PAYMENT_DISPUTES : explains
USAGE_EVENTS }o--|| PRICE_VERSIONS : priced_by
USAGE_EVENTS }o--|| WALLETS : charges
USAGE_EVENTS }o--|| API_KEYS : attributed_to
USAGE_EVENTS }o--|| ROUTE_VERSIONS : resolved_by
CREDENTIALS ||--o{ QUOTA_SNAPSHOTS : observed
CREDENTIALS ||--o| QUOTA_PROBE_STATES : schedules
CREDENTIALS ||--|| CREDENTIAL_HEALTH : reports
REQUEST_RECORDS ||--o{ REQUEST_ATTEMPTS : includes
REQUEST_RECORDS ||--o| USAGE_EVENTS : settles
REQUEST_RECORDS ||--o{ SCHEDULER_DECISIONS : explains
users:id,email_normalized,password_hash,password_params_version,password_changed_at,status,created_at,updated_at;当前密码 hash 为 Argon2id PHC,参数策略单独版本化;roles、user_roles:至少admin、user;角色变更写 audit;user_sessions:id,user_id, uniquetoken_hash,csrf_token_hash,expires_at,revoked_at,mfa_verified_at,last_seen_at, device metadata;数据库不存明文 Session/CSRF token;mfa_factors:每个(user_id, factor_type)唯一;TOTP 保存 AEAD ciphertext/nonce/key_version、enabled/confirmed、last_totp_counter;恢复码只存 hash 和consumed_at,条件 UPDATE 单次消费;api_keys:id,user_id,name,key_prefix,secret_hash,secret_version,status,expires_at, canonical CIDR IP policy, RPM/TPM/daily/monthly budget,last_used_at,rotated_at,created_at,updated_at;Key 使用bablo_sk_+ 32-byte CSPRNG base64url,数据库只保存完整 Key 的 SHA-256 与展示 prefix,没有group_id/单一provider_id;policies、api_key_policies、policy_model_entitlements:表达 Key -> policy -> 多模型 entitlement;用户 Key 使用 metadata 标识的 managed policy 和 default deny,显式 deny 优先于 allow,最后才考虑 policy default action;授权替换与 Key 更新在同一事务内完成。
models:canonicalpublic_model_id、独立model_aliases(禁用后仍保留标识不可重分配)、canonical capabilities、visibility、billing class、enabled;providers:slug、display name、resource_type(official_api/enterprise_api/subscription/third_party)、commercial_allowed、endpoint policy;P0 对 subscription 强制不商业开放;provider_models:provider_id, upstream model ID, protocol/capabilities, enabled,review_status、discovery_status、discovered/last-seen timestamps;发现新增项默认 pending/disabled,发现消失不自动禁用已批准配置;credentials:provider、external stable ID、source kind、status、proxy/region metadata、pool state;credential_secrets:credential 一对一或版本化记录,ciphertext,nonce,key_version, secret kind;与普通 credential metadata 分离;credential_pools、pool_members:可供 route target 使用的资源池,成员有 priority/weight/enabled;credential_health:last success/error class、cooldown_until、observed_at;model_routes:public model、match type/value(P0 exact)、enabled、active version;route_versions:route、monotonic version、effective window、created_by、snapshot hash;route_targets:route version、provider model、credential pool、priority、weight、commercial policy、enabled;请求使用单一 version snapshot。
quota_snapshots:credential、provider/model、observation key、window kind、used/remaining/limit(可空)、reset_at、observed_at、source、confidence、error、bounded metadata、stale 计算信息;采集失败不刷新observed_at;quota_probe_states:每个 Credential 一行的 probe/status/last attempt/last observation/next attempt/failure/backoff/error state;仅为可重建 worker 状态,不是 quota 事实。price_versions:scope、version、effective_from/to、currency、status、created_by;只有 active/retired 且请求时刻位于 effective window 的已发布版本可创建非零 reservation;model_prices:price version、resolved provider model 或 billing scope、input/output/cache-read/cache-write/reasoning/per-request 单价;unit_price是主货币单位/一个维度单位的 decimal,缺价不得静默按 0。
-
request_records:request_id, user, API key, endpoint, requested model, stream flag, started/finished, terminal status;不存正文; -
request_attempts:request、attempt_no、route version、provider、credential、upstream status/error、latency/TTFT、started/finished;用于 fallback 与排障; -
usage_events:不可变结算事实,每个逻辑request_id至多一条,包含 request/user/key/wallet、requested/resolved model、provider/provider model/route version/credential、price version、started/finished、token breakdown、amount/currency、status/error、latency/TTFT、estimated/provenance、settlement key;非零金额要求 wallet,request_record_id若存在必须与request_id对应,usage_reconciliations只追加迟到差异,不覆盖原始事件。 -
scheduler_decisions:request/attempt、候选内部 ID、排除原因、score/priority、selected、fallback chain、strategy version;JSON 只含非敏感 metadata; -
scheduler_decisions的选中字段同时保存 route version、target、provider、credential;新决策必须满足三者成组一致,历史仅有 target 的记录保持可读。credentials.max_concurrency为 1–10000;Redis 仅保存带 TTL 的 lease、cursor、affinity,不是事实源。 -
outbox_events:与关键业务事务同库提交,worker 可重试、claim、幂等消费。
wallets:user、currency、available_balance_minor、reserved_balance_minor、status、financial_hold、version;两种余额都是 transaction 内维护、可由 ledger delta 重建的投影,均不得为负;open liability 或 pending settlement 时 hold 阻止新 reservation;wallet_reservations:request/wallet/user/key、request record、resolved model/provider model/route/provider/credential、已发布 price version、预估 token、预留金额、状态和最终 UsageEvent;同一 request 的载荷不可漂移;billing_settlements:reservation、UsageEvent、reserved/actual amount、estimated、status/error、幂等键、retry attempt 和短租约;pending保留 reservation,后续充值/worker 以 owner token 重试;wallet_ledger:wallet、entry type、signed amount、available/reserved delta、两种 balance-after snapshot、currency、reference、idempotency key、UsageEvent/operator/source、created_at;追加式,delta 是重建权威;payment_liability同时表达追偿扣款与胜诉反向入账;wallet_liabilities:wallet、payment_refund|payment_dispute、稳定 Provider reference、principal/recovered、currency、open|settled|reversed;允许 recovered 单调增加或 settled -> reversed,不允许删除/改写身份;payment_orders:全局 order no、user、amount/currency、provider、merchant/live mode、Checkout Session/trade、PaymentIntent、Charge、refund、safe checkout data、external refunded total 和本地账本引用;状态机覆盖 create/pay/close、Bablo 发起退款和外部全额退款;payment_events:provider event/trade/refund/payment-intent/charge/dispute ID、payload SHA-256(不保存支付正文)、merchant/live mode、验签来源、occurred/received time;verified fact 追加式,processing state 独立更新;payment_provider_operations:order + create/refund、payload hash、merchant/live mode、owner lease、attempt/backoff、Provider reference;持久化 Provider 调用单飞与崩溃恢复;payment_external_refunds:Provider refund immutable fact,关联 order 与 liability,记录金额/币种和 Provider object ID;payment_disputes:Provider dispute、order、liability、charge/payment-intent、金额/币种、open -> won|lost;胜诉反转 liability,败诉保留追偿;payment_funding_operations:人工充值全局幂等事实,绑定 operator、目标用户、币种、金额和唯一 ledger;payment_vouchers:code SHA-256/prefix、amount/currency、状态/过期、创建/兑换主体及版本化 AEAD 密文;明文创建响应可在未兑换前按同一幂等请求重放,兑换后清除密文;audit_logs:actor、action、target、before/after 摘要(脱敏)、request ID、result、created_at;stats_rollups:按小时/天和受控维度聚合,必须可由 Usage/Ledger 重建,不是事实源。
| 对象 | 必须唯一/幂等 |
|---|---|
| user | lower(email_normalized) |
| session | token_hash;有效 Session 同时必须有 csrf_token_hash,撤销不删除历史 |
| MFA | (user_id, factor_type);recovery (factor_id, code_hash),TOTP counter/恢复码消费在行锁事务内防重放 |
| API Key | secret_hash;prefix 仅展示索引,不代替 hash |
| entitlement | (policy_id, model_id) |
| provider/model | (provider_id, upstream_model_id);发现和人工映射共用稳定上游 identity |
| credential | (provider_id, external_stable_id)(无稳定 ID 时用受控 fingerprint) |
| quota snapshot | (credential_id, observation_key, window_kind);同一 observation key 的 payload 必须完全一致,冲突拒绝;probe state 每 Credential 一行且为可重建状态 |
| pool membership | (pool_id, credential_id) |
| route version | (route_id, version_no);target 顺序/目标 identity 在同一 version 内唯一 |
| public model/alias | lower(public_model_id)、lower(alias) 各自唯一且跨表互斥;禁用 alias 仍保留占位 |
| price version | (scope, version_no);同一 scope 的 published effective intervals 不重叠 |
| price | (price_version_id, pricing_scope, target, dimension);同一版本目标维度唯一 |
| request | request_id;若已有 request 记录,重试必须 metadata 完全一致 |
| usage settle | settlement_key 与 request_id 均唯一;P0 由服务端派生 usage:v1:<request_id>,重复 finalize 返回已有结果 |
| wallet reservation | request_id、(wallet_id, reservation_key)、非空 usage_event_id 均唯一;同一 request 的 owner/route/price/estimate/amount 必须一致 |
| billing settlement | reservation_id、usage_event_id、idempotency_key 分别唯一;重复 settle 返回同一状态 |
| wallet ledger | (wallet_id, idempotency_key);非空 usage_event_id 全局唯一,充值/退款/调账 reference 由调用方稳定派生 |
| payment order | order_no、服务端财务 idempotency key;(provider, trade/payment_intent/charge/refund) 在相应非空列上唯一;merchant/live mode 一旦赋值不可改变 |
| payment event | (payment_provider, provider_event_id);同 event ID 必须保持 payload hash、所有 Provider object ID、merchant/live mode 和业务事实完全一致 |
| payment provider operation | (payment_order_id, operation_type);payload hash + merchant/live mode 固定;owner token + lease 控制单飞重试 |
| external refund | (payment_provider, provider_refund_no);每条事实唯一绑定一个 liability |
| dispute | (payment_provider, provider_dispute_no);每条争议唯一绑定一个 liability,终态不可再次转换 |
| wallet liability | (reference_type, reference_id);恢复 ledger idempotency key 由 liability + credit source ledger 派生 |
| funding operation | 全局 idempotency_key;重放必须匹配 operator/user/currency/amount |
| payment voucher | code_hash 与 create idempotency key 唯一;redeem 在 voucher 行锁 + wallet 行锁事务内只成功一次 |
| scheduler decision | (request_id, attempt_no, decision_no) |
| audit | event_id 或 (request_id, actor, action, target, nonce) 按实现选定 |
| outbox | (aggregate_type, aggregate_id, event_type, idempotency_key);processing claim 绑定 claimed_by owner token |
所有幂等键必须由服务端生成或从受信任的 provider event/request identity 派生,不能接受客户端任意覆盖账务事实。
- reservation 使用 API-key advisory transaction lock 串行化 daily/monthly budget,再锁 wallet 行把 available 转入 reserved;
financial_hold=true时拒绝新 reservation; - 任何 ledger entry 成功提交都必须有唯一 idempotency key;重试不新增第二笔,管理员调账同时写 audit;
- reservation、usage charge、release、refund hold/reversal/liability 的 available/reserved delta 必须满足 entry-type 代数约束;
SUM(delta)必须重建钱包投影,数据库拒绝历史 ledger UPDATE/DELETE; - settle 少收释放、多收补扣;补扣余额不足时保留 reserved 并写 pending settlement/outbox。后续 credit 在 wallet 事务内按 FIFO 恢复 open liability;独立 worker 以 owner lease 重试 pending settlement,任一欠费存在时
financial_hold持续生效;不能静默免费或形成负余额; usage_events、payment_events、外部退款事实和audit_logs不 UPDATE 既有事实来“修正”;新增 adjustment/reconciliation/liability/reversal;- price version 切换只影响新请求;reservation 和 UsageEvent 必须绑定同一已发布版本、wallet、request 和 owner;
- Provider refund 调用前以 ledger hold 把 available 转为 reserved;verified success 消费,definitive failure 释放,timeout/未知结果保持 pending;Provider operation 的 merchant/live mode 与 payload hash 在所有重试中不变;
- 签名有效的支付事件必须核对金额、币种、merchant/live mode,并要求所有本地已有的 order/trade/refund/payment-intent/charge 标识同时一致;外部退款/争议通过 liability 追偿;
- Stripe browser redirect 不改变订单或钱包,只有验签 webhook 或经认证 Provider API reconciliation 可入账;
- Redis 丢失不改变 PostgreSQL 账务;Redis lease/限流重建后不能绕过 DB 权限、预算和 financial hold。
首批索引围绕真实查询:user_id + created_at、api_key_id + created_at、public/resolved_model + created_at、provider_id/credential_id + created_at、request_id、order_no、status + created_at、observed_at。高增长表按时间范围评估分区,但没有基准数据前不预先复杂分区。
Raw Usage、scheduler/audit、payment payload hash 和 rollup 的 retention 必须配置化并区分合规需要;账本与财务证据不得因 dashboard retention 被删除。所有结构变更走 SQL-first migration,要求空库 up、连续升级、重复启动安全和约束测试。当前首批 schema 已落地,高增长表仍保持未分区,待真实基准数据证明需要后再迁移。
migrations/000001_initial_schema.sql创建身份、授权、模型、Provider、Credential、Route、Quota、Price、Request、Usage、Wallet、Payment、Audit、Outbox 和 Stats 核心表。migrations/000002_fact_table_guards.sql为 Usage、reconciliation、Wallet Ledger、Payment Event、Scheduler Decision 和 Audit 建立数据库级 append-only 防护,并校验 pool/credential 与 route target/provider 的归属一致性。migrations/000003_wallet_payment_integrity.sql、000004_auth_security.sql、000005_api_key_security.sql分别补充账务/支付、Web Session/MFA 和 API Key 安全约束;已应用迁移保持不可变。migrations/000006_model_catalog_integrity.sql新增model_aliases、大小写不敏感且跨表互斥的 model identifier guards、provider model discovery/review 状态、published price entry/version guards 与生效区间互斥。migrations/000007_credential_security.sql增加 Credential runtime metadata、source-kind/secret ciphertext/key/rotation 约束、source identity guard、secret history append-only、pool identity guard 和 active-secret 索引。migrations/000008_route_integrity.sql增加 Route match/hash/metadata 约束;Route version 仅允许一次性关闭,Route target immutable,所有新 target 集合必须通过新 version 发布。migrations/000009_scheduler_integrity.sql增加 Credential 并发容量约束、Scheduler 决策选中路由/provider/credential 关联字段、历史兼容约束和数据库选择一致性 trigger;Scheduler 运行态仍由 Redis TTL 状态协调。migrations/000010_usage_integrity.sql增加 Usagerequest_id唯一索引、started/finished 时间快照、request-record 关联与 settlement/request/source/claim-owner 输入约束;从 v9 回填历史 Usage 时间并恢复 append-only trigger,outbox claim/retry/publish 由internal/usage以 owner token 和 stale lease 实现。migrations/000011_billing_integrity.sql增加 reservation/settlement、explicit available/reserved ledger delta、balance-after snapshot、published-price/owner/Usage cross-table guard、ledger immutability、API-key budget 与 settlement 查询索引;migration 已验证空 schema up、latest down 和重放。migrations/000012_payment_integrity.sql至000015_payment_merchant_mode.sql增加 Payment 状态机、event processing、voucher replay、Provider operation、退款 hold、merchant/live-mode 绑定与历史身份回填阻塞;migrations/000016_payment_financial_recovery.sql增加 pending settlement recovery lease、wallet financial hold、liability 和人工充值幂等事实;migrations/000017_payment_provider_recovery.sql增加 PaymentIntent/Charge/Dispute、external refund/dispute 表及 order-wallet-ledger 跨表约束;migrations/000018_billing_liability_integrity.sql以数据库唯一约束保证一个 Provider financial reference 只能产生一条 liability;migrations/000019_quota_observation.sql增加 provider/model/observation key、metadata、immutable quota snapshot 和可重建 probe state 约束。migrations/000020_stats_aggregation.sql扩展stats_rollups的 UTC 小时/日、受控维度、币种、完整计数器和可选财务投影列;stats_rollups可由 Usage/Ledger 删除重建,唯一身份为(bucket_kind, bucket_start, dimension_key, currency),不是真实账本。internal/stats从 UsageEvent、Wallet Ledger、Scheduler Decision、PaymentEvent/订单派生 overview、trend、dimensions、Usage/decision 分页、trace、payment summary、reconciliation 和受控 rollup rebuild;充值/收入/退款只在可安全归因的 Wallet Ledger 范围内统计。internal/model实现 canonical ID/alias 解析、public/admin 列表、能力/visibility/billing class 校验和 route readiness;alias 禁用后不被重新分配。internal/provider实现资源政策、上游模型映射和完整 discovery snapshot reconcile;新增发现 pending/disabled,缺失只改变 discovery signal,批准配置不被发现覆盖。internal/credential实现 non-secret DTO、AES-GCM secret create/rotate/reencrypt、active runtime source、monotonic health、Provider pool membership 和 opaque composite cursor;管理员 API 永不返回 secret value。internal/pricing使用 decimal string +numeric(30,12),实现 draft/activate/retire 与 provider_model -> model -> global 价格解析;缺价/禁用计费 fail closed。internal/route实现 P0 exact route、多 Provider/Pool candidate、snapshot hash、active version 原子切换、opaque cursor、preview/resolution 和管理员 API;resolver 只产出 scheduler candidates,不选择 Credential。internal/quota实现合法 Provider quota/health probe、被动 response observation、snapshot freshness/staleness、退避、Redis TTL lease 和 admin view;只向 Scheduler 提供 Bablo 标准化快照,不暴露 CPA 类型。internal/scheduler实现 target/member 硬过滤、429 cooldown、quota freshness/reset、priority/fill-first/round-robin/weighted-round-robin/quota-aware、有限 affinity、Redis/内存 TTL lease/cursor/affinity 和 immutable Decision Log;它只接收 Route resolution,不暴露 CPA 类型。internal/usage实现 request record 幂等、immutable UsageEvent、stream/cancel/no-usage 状态、late reconciliation、transactional outbox claim/ack/retry;Proxy 只提交领域输入,不暴露 CPA 类型或正文。internal/billing使用 decimal string +math/big汇总后一次换算最小货币单位,实现 Quote/Reserve/Settle/Release/Credit、payment refund hold、liability、pending settlement recovery、GetWallet/RebuildBalance;Proxy 在 CPA 执行前 reserve、UsageEvent 后 settle,missing usage 按 reservation 估算结算;internal/payment实现订单/退款/外部退款/争议、Stripe 与 fixture adapter、Provider operation/reconciliation/expiration worker、voucher 和人工充值;cmd/bablo接入支付 HTTP 与恢复 worker,应用启动不自动迁移。