Files
new-api-key-usage/docs/design/qpu_history表字段设计.md
T
skycurtain e4d25526f3 docs: 设计阶段定稿——需求清单(15 项全定案)与七篇表设计定稿
- 需求清单:独立计费平台定位,R1-R15 全部定案(12 项裁决 + R10 裁撤 + R3/R4 延后),裁决档案含访问主体模型(作者登录体系 + 成员长期令牌)、分摊哲学(平台不做价值判断只提供事实服务)、双形态交付(完整前后端先行,CFW 抽取版后续)
- 数据模型定稿(一表一文档):api_keys(16 列,401 状态机裁撤——引擎记录不决策)/ usage_logs(25 列,billing_source 与 subscription_id 提列,非消费行去重含 content 指纹)/ payments(币种无关支付记录,只存事实)/ qpu_history(按日志时刻取值)/ members(成员实体与长期令牌)/ users(空库双通道初始化与重置)/ sessions(服务端会话,否决 JWT)
- 接口参考:NewAPI-接口范围融合字段级实证,定位 docs/references/(计费平台可用接口参考,唯一上游参考)
- README / AGENTS:新版设计语境(项目沿革、重开设计动因、协作约定、文档自立原则)
2026-10-01 14:54:06 +08:00

5.2 KiB
Raw Blame History

qpu_history 表字段设计

数据模型第四张表:换算参数时间线(表名 qpu_history,QPU = QuotaPerUnit 缩写)——quota_per_unit(quota → 额度美元)的历史取值。历史值在 sk-key 视角无从观测,本表由同步引擎自动采样、只记变化点;纯只读(唯一写入者是同步引擎)。本文记录字段清单、语义与决策理由。

项目 内容
状态 定稿(2026-10-01)
关联 docs/design/usage_logs表字段设计.md(消费方:quota → 美元的展示换算)· docs/references/NewAPI-接口范围.md(采集来源 /api/status)
范围 列结构 + 语义 + 写入规则;采样调度(同步引擎)不在此文;方言映射归数据访问层设计
字段数 3 = 1 主键 + 2 业务字段

1. 表定位

  • 一行 = 一次「换算单位取值变化」事件(值 + 生效时间)——正常情况全表仅 1 行(当前站点为出厂默认值,从未变过)
  • 它是 quota → 额度美元这一单位换算的时间线:任何时刻的换算取值 = 「该时刻之前最近一条」
  • 为什么要自己攒:/api/status 只能看到当前值,历史值无从观测(上游不留历史、管理审计不记值)——没有本表,历史日志只能用今天的单位换算,账会串
  • 与日志零耦合:换算全部发生在展示 / 聚合层——本表任何变动都不触发 usage_logs 写入

2. 字段清单

列 类型 值来源 说明
id bigserial PK 自增
qpu numeric /api/status.quota_per_unit 当时的 quota_per_unit 值
effective_from timestamptz 首次采样或检测到变化的时间 生效时间;采样误差 ≤ 同步周期

3. 决策记录(含理由)

3.1 为什么自动采样(而非人工登记)

它是「可登记的事实」的反面——历史值无从观测(只能看到当前值);自动采样(每轮同步)是唯一能沿时间轴捕获它的方式。采样误差 ≤ 同步周期(边界影响可忽略)。

3.2 只记变化点,不记每次采样

部署时插基线;此后每轮同步顺带读 /api/status,值变化才追加一行——行数 = 调价次数 + 1(正常为 1)。

3.3 取值语义:按日志自身时刻取值

  • 一条日志的美元换算 = 该日志 created_at 时刻的当值 QPU(「该时刻之前最近一条」)——这是纯粹的历史单位换算:这条日志发生时一单位 quota 值多少美元额度,就用当时的换算率
  • 为什么按日志时刻:换算的唯一目的是「这条日志当时值多少美元额度」——历史正确性要求用历史当值;不存在「一笔订阅一把尺」的分摊自洽诉求(分摊计算已整体裁撤)
  • 边界:日志时刻早于本表最早记录时(如补录历史数据),回退用「最早已知值」(近似规则,正常部署起点的日志不会触发)

3.4 quota_per_unit 的可变性说明(本表存在的理由)

  • 它是「全站计价单位的定义」——改它 ≈ 重定义货币单位(全站一致重定标);管理员极少动(当前站点为出厂默认值)。本设计以「按时间线换算」从根上免疫它的任何变更
  • 存在必要性审视(2026-09-29,作者裁决保留):改动概率极低(历史上零次)× 权限极高(Root,深思熟虑的定价动作而非误操作)× 不可逆(一旦发生且未被捕捉,「当时的率」永久不存在)——风险结构与日志 1000 条窗口同构;备选方案「砍表 + 保留观测告警、事后人工重建」被否决——不可逆的历史由自动化保障,不依赖人工响应;全部代价 = 每轮一次 /api/status 采样 + 十行时点查询(qpuAt(t),3 行内存缓存 + 区间判断)

3.5 为什么不做人工修正(只读设计)

  • 人工介入的场景不成立——quota_per_unit 在上游无编辑入口(前台默认隐藏且只读)、管理审计不记值、历史上从未变化(见 docs/references/NewAPI-接口范围.md)
  • 本表由此定型为纯只读时间线:唯一写入者是同步引擎(值变化才追加);管理通道仅提供只读查看
  • 未来若真出现需要人工介入的场景(如运营方主动告知一次精确切换时刻),再评估启用

3.6 命名:表名 qpu_history、值列 qpu

  • 表名:quota_per_unit_history 过长、逐处引用啰嗦 → qpu_history——QPU 是 QuotaPerUnit 的通用缩写,短且醒目
  • 值列:value 过泛("什么的值"不自明)→ 改为 qpu,与表名一体、一眼即知
  • 通用约定:表名 / 列名用 QPU 缩写,引用上游字段 quota_per_unit 时保持全称

4. 写入时机(唯一写入者:同步引擎)

时机 动作
部署(基线) INSERT 基线一行(当时 /api/status 取值)
每轮同步 读 /api/status:值变化才追加一行
  • 除追加外无 UPDATE / DELETE——数据不可变

5. 待定事项(关联后续环节)

事项 归属
采样调度(读取频率、失败重试) 同步引擎设计(待立)