feat: add billing pricing rules

This commit is contained in:
QiuSW
2026-07-02 15:28:19 +08:00
parent cbc56ee15d
commit 826f5f685b
14 changed files with 548 additions and 34 deletions
+25 -10
View File
@@ -70,8 +70,8 @@
| AiModel | 迁移自 `cmbot/config/ai_models.json` | name、url、model、`api_type`、`capabilities`、`timeout_seconds`、`connect_timeout_seconds`、`extra_body`、`is_active`、**api_key_encrypted(Fernet 加密存储)** |
| ModelAlias | 运营配置 | `operation_type + alias` → 映射到一个具体 AiModel;支持每个操作一个默认别名;调用方只见别名,不见 SKU |
| AiConfigAuditLog | 后台自动写入 | AiModel / ModelAlias / api_key 变更审计;记录 actor、action、target、changed_fields、changes、created_at;只读 |
| PricingRule | 运营配置 | 操作类型 × **能力别名**(+ 可选分辨率)→ 点数单价 |
| ExchangeRate | 运营配置 | 金额 → 点数 的汇率(如 1 元 = N 点),可带生效时间 |
| PricingRule | 运营配置 | 操作类型 × **能力别名字符串**(+ 可选分辨率)→ 点数单价;不外键到具体 AiModel |
| ExchangeRate | 运营配置 | 金额 → 点数 的汇率(如 1 元 = N 点),按 `currency + effective_from` 取当前有效值 |
关键事实:
@@ -136,16 +136,31 @@ CREATE TABLE ai_config_audit_log (
-- 计费规则(按别名定价,换底层模型不影响计费)
CREATE TABLE pricing_rule (
id INTEGER PRIMARY KEY,
operation_type TEXT NOT NULL, -- title / image
alias TEXT NOT NULL REFERENCES model_alias(alias),
resolution TEXT, -- 可空:按档位区分价格时使用
points_cost BIGINT NOT NULL,
UNIQUE(alias, resolution)
id BIGINT PRIMARY KEY,
operation_type VARCHAR(32) NOT NULL, -- title / image
alias VARCHAR(64) NOT NULL, -- 能力别名字符串,不绑具体模型
resolution VARCHAR(32) NOT NULL DEFAULT '', -- 空字符串表示该别名默认价
points_cost BIGINT NOT NULL, -- > 0
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL,
UNIQUE(operation_type, alias, resolution)
);
-- 金额换点数汇率(充值下单时锁定)
CREATE TABLE exchange_rate (
id BIGINT PRIMARY KEY,
currency VARCHAR(10) NOT NULL DEFAULT 'CNY',
points_per_unit DECIMAL(12,4) NOT NULL, -- 1 currency unit = N points
is_active BOOLEAN NOT NULL DEFAULT TRUE,
effective_from DATETIME(6) NOT NULL,
note VARCHAR(255) NOT NULL DEFAULT '',
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL
);
```
T-102 已实现 `AiModel` / `ModelAlias` 的 Django models、admin、迁移与别名解析。T-103 已补 `AiConfigAuditLog`,admin 里保存/删除模型配置或能力别名时自动写审计日志。默认别名唯一性由 model validation、admin 与导入器保证;MySQL 不支持通用 partial unique index,后续若要强制数据库层约束可在 T-401 评估触发器或约束表。
T-102 已实现 `AiModel` / `ModelAlias` 的 Django models、admin、迁移与别名解析。T-103 已补 `AiConfigAuditLog`,admin 里保存/删除模型配置或能力别名时自动写审计日志。T-202 已实现 `PricingRule` / `ExchangeRate` 与 `apps.billing.pricing` 计算函数:定价按 `operation_type + alias + resolution` 查 active 规则,优先 exact resolution,再回退到空 resolution 默认价;缺规则抛 `NoPricingRuleError(code="no_pricing_rule")`。默认别名唯一性由 model validation、admin 与导入器保证;MySQL 不支持通用 partial unique index,后续若要强制数据库层约束可在 T-401 评估触发器或约束表。
> 可选增强(接口预留、MVP 不实现):`account_alias_permission`(按账号授权可用别名,防止调用方点用未授权/昂贵模型);别名按比例分流到多个模型(灰度/AB/故障转移)。适配器接口需为此留口子。
@@ -238,7 +253,7 @@ CREATE TABLE call_record (
需要说明:
- 主键自增;`api_key.key_hash`、`recharge_order.order_no`、`user.username` 唯一。
- 主键自增;`api_key.key_hash`、`pricing_rule(operation_type, alias, resolution)`、`recharge_order.order_no`、`user.username` 唯一。
- 重要索引:`call_record(user_id, created_at)`、`points_ledger(user_id, created_at)`、`recharge_order(order_no)`、`api_key(key_hash)`。
- 不软删除业务流水;用户/账号可标记 `disabled` 而非物理删;API Key 用 `revoked` 状态而非物理删。
- 服务端生成字段:`api_key.key_hash`/`key_prefix`、`points_balance`、`balance_after`、各 `created_at` / `updated_at`。