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:新版设计语境(项目沿革、重开设计动因、协作约定、文档自立原则)
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# 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` 抄录来源(充值记录页)核实 | 实现期真实数据核查 |
|
||||
Reference in New Issue
Block a user