Files
new-api-key-usage/docs/design/payments表字段设计.md
T
skycurtain db20dcb611 docs: 交互设计定稿——八篇按功能分篇,经作者逐篇审阅定稿
- 八篇:总览与共性 / 登录与会话 / 全员总览 / key 管理 / 支付记录 / 成员管理 / 审计日志 / 成员视图
- 关键定案:R12 携带形态(来源徽章 + 展开行,两种空值场景区分)、纯用量模式(部门实例——无支付记录的完整子集,共性 §3.8)、响应式只做布局响应、组件能力需求清单(选型验收标准)、审计日志保留操作者列(前向兼容多管理员)、Key 与成员管理分页 + 「+ 新建成员」缝合流程
- 审阅期修订全记录见需求清单 §9「交互设计八篇定稿」
2026-10-01 23:58:28 +08:00

7.6 KiB
Raw Blame History

payments 表字段设计

数据模型第三张表:支付记录——每笔支付事件(订阅购买 / 充值)一行。平台只存事实(支付金额、换得额度、来源参数),「支付金额与额度之间的比率」是查询时的派生视图,不存储、不做任何分摊计算。本文记录字段清单、语义与决策理由。

项目 内容
状态 定稿(2026-10-01)
关联 docs/design/usage_logs表字段设计.md 3.6(subscription_id 直连与本表)· docs/design/qpu_history表字段设计.md(quota → 额度美元的换算时间线)· docs/references/NewAPI-接口范围.md §2.⑨(other.subscription_* 字段依据)
范围 列结构 + 语义 + 写入时机;方言映射归数据访问层设计
字段数 9 = 1 主键 + 8 人工录入(支付 4 · 期间 2 · 订阅 2)

1. 表定位

  • 一行 = 一笔支付事件(订阅购买 / 充值)——同一模型兼容两种形态(kind 列区分)
  • 本表是「只有作者知道」的信息的唯一落点:支付金额、额度、订阅 ID——上游没有免 auth 接口可查,全靠作者手工抄录
  • 币种无关:paid_amount 是支付金额数值,系统不预设币种——币种是使用者之间的约定;「金额 ↔ 额度」的比率同样是派生视图(查询时相除即得)
  • 不做分摊计算:平台不把支付金额按用量比例分摊到日志或成员——分账是社交层问题;本表与日志的交汇只发生在事实关联(subscription_id 直连)与参数展示(订阅信息对使用者完全公开透明)
  • 数据规模:年个位数到十位数行——无需索引设计

2. 字段清单

2.1 主键与支付字段(5 个)

列 类型 值来源 说明
id bigserial PK 自增
kind text 人工选择 subscription(订阅)/ topup(充值)
paid_at timestamptz 人工填写 支付时间
paid_amount numeric 人工必填 支付金额(币种无关——数值本身,币种由使用者约定)
remark text 人工填写 备注

2.2 期间与订阅字段(4 个)

列 类型 值来源 说明
period_start timestamptz? 人工录入(钱包页面) 仅订阅:该笔覆盖的用量期间起点;左闭
period_end timestamptz? 人工录入(钱包页面) 期间终点;左开;未结束可空
subscription_id bigint? 人工录入(钱包页面「订阅 #N」) 上游订阅实例 ID;与日志 subscription_id 直连(事实关联,不推算)
quota_granted bigint? 人工录入(钱包页面「总额度」/ 充值记录) 本笔换得的额度——单位钉死为原始 quota 整数(如 $25 额度 = 12,500,000;见 3.6)

3. 决策记录(含理由)

3.1 登记 = 手工抄录(来源只有两处)

支付记录全部由作者手工录入,来源仅两处:支付凭证(kind / paid_at / paid_amount)与钱包页面(订阅 ID / 期间 / 总额度)。订阅关键信息只有作者可见(没有免 auth 接口可查)——系统不做回填与推断,日志观测仅用于核对提示(见 §4);必填 / 可空的校验细节归技术稿。

3.2 只存事实,不存派生数

  • paid_amount 与 quota_granted 是不可替代的事实;「金额 ↔ 额度」的比率是派生数——查询时相除即得,事后核对永远可用;只存比率会丢失核对依据
  • 日志同样不加任何金额 / 比率列(usage_logs 零分摊字段):把支付金额按用量分摊到日志行的任何计算都不做——分摊口径是社交层问题,账本上只有客观事实
  • 关联维度(billing_source / subscription_id)已在日志侧提为独立列(见 usage_logs表字段设计.md 3.6),统计按事实关联

3.3 为什么额度也手工录入(不采用日志感知)

日志逐条自带 subscription_total(实测),但回填它需要「订阅 ID → 该 ID 日志 total」的关联逻辑;钱包页面已直接给出总额度——手工抄录一步到位。系统对支付记录零回填、零推断,观测仅用于核对(见 §4)。

3.4 币种无关(不预设人民币)

paid_amount 是纯数值。作者记账用什么货币是他的事,展示层的货币符号是界面约定——数据层与币种彻底解耦,更换支付渠道 / 币种时零 schema 变化。

3.5 命名与类型

  • 表名 payments(支付记录):订阅与充值同构,purchases(购买)只覆盖前者,payments 是两者的上位词
  • 金额列 numeric(金额不用浮点);时间列统一 timestamptz
  • 期间左闭右开([period_start, period_end))——与全项目期间惯例一致
  • 订阅实例 ID 与日志同名 subscription_id——同一体系、同一含义(直连即用此列)

3.6 quota_granted 的单位:原始 quota,不是美元

  • 存真值,不存换算快照:上游订阅套餐的额度本体是 quota 整数,「$25」只是界面显示形态(quota ÷ quota_per_unit,随 quota_display_type 而变)——本列存本体
  • 域一致性:日志的 quota 同为原始整数——SUM(usage_logs.quota) 与 quota_granted 同域直接可比,零换算;若存美元则每次比较都要过 ÷QPU,而 QPU 是时间线变量(qpu_history),凭空引入取值时刻问题
  • 类型精确:quota 恒为整数;美元额度可能有小数,bigint 装不下
  • 录入体验归交互层:钱包页面显示美元形态——管理面表单支持输入美元、按当时 QPU 换算落库(或展示两态),换算逻辑不进数据模型
  • 美元形态随时可派生:quota_granted ÷ QPU(展示层)
  • 与 qpu_history 无概念重复:quota 是量,QPU 是换算率——记录量不等于记录单位定义(记「¥170」不涵盖「1 美元 = 6.8 人民币」)。真值域(quota)不携带任何换算,换算率只在一处(qpu_history 时间线),美元永远现算现用;反过来若直接存美元,每个值都是某次换算的快照、各自隐含一个换算时刻——那才是真正的冗余,且上游改 QPU 后与日志统计失去同域可比性

4. 写入时机与维护

时机 动作
人工登记(管理通道) INSERT:录入支付信息(时间 / 金额)与订阅信息(ID / 期间 / 总额度,抄自钱包页面)→ 确认
系统观测(仅核对) 日志观测到的订阅 ID / subscription_total 仅作核对提示(如「观测到 #N 未对应任何支付记录」——即「未关联」分组的健康信号),不参与回填
编辑 / 删除 数据为人工登记,改错即修

5. 展示层边界(界面设计的输入)

  • 订阅与支付参数对使用者完全公开透明(访问主体模型裁决):期间、额度、支付金额、订阅 ID——成员视图与管理面同源可见;不存在「管理面独占的金额信息」
  • 「按订阅维度统计」(某笔订阅的消耗占比等)数据天然支持——按 subscription_id 直连聚合即可
  • 成员视角的日志行携带所属订阅/充值信息的具体形态,交互设计期定(见 docs/design/需求清单.md R12)

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

事项 归属
核对提示(观测到的订阅 ID ↔ 已录入记录比对)的交互 交互设计(待立)
充值(topup)的 quota_granted 抄录来源(充值记录页)核实 实现期真实数据核查