Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 45 additions & 25 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# AITokenPool 架构设计

> v0.3(2026-08-18)· 落地 P0(A/B/C)与 P1 后端实现:axum 网关(OpenAI/Anthropic 双协议 + SSE 流式)、7 条路由规则落地、计量账本闭环、AES-256-GCM 上游 key 加密、点数规则(每日赠送/有效期/先赠后永扣减)、管理员充值 API;补齐 API 一览与数据库实表结构
> v0.2(2026-08-15)· 新增 4.2.1 路由与故障转移策略(随机初始选择 / 粘性 / 静默故障转移 / 3 次切换上限 / 5 秒健康冷却 / 健康优先)
> v0.1(2026-08-13)· 基于 openlocalrouter 演进

Expand Down Expand Up @@ -54,16 +55,26 @@
└──────────────────────────────────────────────────────┘
```

### 4.1 网关层(复用 openlocalrouter)
- 统一 API 端点(OpenAI/Anthropic 兼容)
- 流式转发(SSE/WebSocket)、协议转换
- 多 Provider 路由、负载均衡
### 4.1 网关层(P0-B/C 已实现,Rust axum 自研)
- 统一 API 端点:**OpenAI Chat Completions**(`POST /v1/chat/completions`)+ **Anthropic Messages**(`POST /anthropic/v1/messages`),覆盖国内主流 Coding Plan 与下游工具(Cursor / Claude Code / Cline / OpenCode)
- **流式转发(SSE)**:`stream:true` 分支逐块透传(`text/event-stream`),OpenAI 流尾 usage / Anthropic `message_start`+`message_delta` 计入账本;客户端断开不入账(finalize 随 body drop)
- **余额预检**:上游调用前检查**可用余额**(赠送 + 永久,P1 口径),≤ 0 → 402「余额不足」
- 多 Provider 路由、故障转移(见 4.2.1,`src/router.rs` 落地:粘性 / 健康冷却 / 3 次切换上限)
- **API 一览**(`src/routes/mod.rs`):
- `GET /healthz` — 健康检查(返回版本号)
- `POST /api/auth/login` — 邮箱 + argon2 口令 → 分发 key(`atk_live_` 前缀,get-or-create)
- `POST/GET /api/api-keys` — 分发 key 生成 / 脱敏列表(Bearer 认证)
- `POST /v1/chat/completions` / `POST /anthropic/v1/messages` — 网关(非流式 + SSE)
- `GET /api/models` — 模型市场(含可用性/价格)
- `POST/GET /api/sharings` + `PATCH /api/sharings/:id` — key 上架 / 列表(脱敏)/ 暂停·下线
- `GET /api/wallet` / `GET /api/transactions?type=` / `GET /api/dashboard` — 钱包(双余额)/ 交易分页 / 仪表盘聚合
- `POST /api/admin/credits` / `GET /api/admin/users` / `GET /api/admin/usage` — 管理员充值 / 成员列表 / 用量报表(role=admin)

### 4.2 Key 池
- 上游 key 加密存储(argon2/加密 at rest)
- 上游 key **AES-256-GCM 加密存储**(P0-C 实现,`src/crypto.rs`):密文格式 `v1:<nonce>:<cipher>`;主密钥来源 env `ATP_MASTER_KEY` → config `server.master_key`(hex 32 字节)→ 缺省 dev 随机密钥并告警(重启后旧密文不可解);启动时 `migrate_key_encryption` 自动迁移历史明文
- 企业:管理员配 key → 分发子 key 给员工
- 公共:分享者上传 key → 进共享池
- key 状态:可用/失效/限额/撤销
- key 状态:可用/失效/限额/撤销;转发前解密,解密失败判 key 不可用
- **管理入口(v1.11 定案)**:无独立 Key 池管理界面——管理员与普通用户一样通过**「上架」**(共享管理页:选厂商 → Plan → 模型、填 key、声明额度、可用时间段)配置上游 key;共享列表即 key 池视图(可暂停 / 删除)。「Key 池」保留为数据层概念(平台持有的上游 key 集合),UI 侧不再单独暴露。

#### 4.2.1 路由与故障转移策略(v0.2,宿主 2026-08-14 定案)
Expand All @@ -80,14 +91,18 @@

**示例流程**:A 请求模型 M → 随机选 B1(健康)→ A 的后续请求粘性复用 B1 → B1 报错(额度用尽)→ 静默切换 B2(第 1 次)→ B2 也报错 → 切换 B3(第 2 次)→ 成功返回;若 B1/B2/B3 均失败(第 3 次也失败)→ 返回「该模型暂无可用 key」。B1 报错后标记非健康 5 秒,冷却后可重新参与选择。

### 4.3 计量引擎
- 每次调用记录:用户、模型、输入/输出 token、成本
- 换算点数(按模型单价)
### 4.3 计量引擎(P0-B 已实现,`src/billing.rs`)
- 每次调用记录:用户、模型、输入/输出 token、成本(`usage_records`)
- **点数计算**:`成本 = 输入token×input_per_m/1e6 + 输出token×output_per_m/1e6`(models 表单价,`data/models.example.json` 价格大表 + config 官方价覆盖)→ 折算到锚定货币(USD/CNY,`CNY_PER_USD=7.2`)→ × `points_per_unit`(默认 1000 点/USD)
- **入账(事务性)**:`settle` 在成功响应后调用——扣消费者 → 加分享者 90% → 两条 transactions(consume/earn)→ usage_records → keys.used;任一步失败整体回滚(失败不入账)

### 4.4 账本层
- 企业:IT 发点数配额,员工消费扣减
- 公共:分享者 key 被用 → 赚点数;消费者用点数 → 扣减
- 交易流水(可对账)
### 4.4 账本层(P1 点数规则已落地)
- **点数账户拆分**:`quotas.balance` = 永久点数(分享收益 / 管理员充值);`quotas.gift_balance` = 当前有效赠送点数总额
- **新人每日赠送**(`src/gift.rs`):注册(users.created_at)起**连续 10 天**每天 1 点,**当日有效**(expires_at = 当天 23:59:59);懒加载 `ensure_daily_gift`(wallet/dashboard/网关预检前)补发当日点数;**惰性过期清理**——查询时把过期 active 记录标 expired 并从 gift_balance 扣减(无需定时任务),余额自愈对齐
- **扣减顺序**:消费**先扣最早到期的赠送点数**(gift_grants 按 expires_at ASC 逐条扣,标 used),不足再扣永久 balance;可用余额 = gift + permanent(网关预检与 settle 同口径)
- **分享收益**:分享者得 **90%**(平台抽成 10%,`SHARE_RATIO=0.9`),永久有效
- **管理员充值**:`POST /api/admin/credits` → 永久 balance 增加 + 写 transactions(type=topup,counterpart=管理员)
- 交易流水(可对账):`transactions` 全量记录 consume / earn / topup

### 4.5 市场层(公共版)
- 分享者上架闲置 key(声明额度/价格)
Expand All @@ -98,30 +113,35 @@
### 4.6 管理台
- 企业:key 管理、员工点数、用量报表
- 公共:市场浏览、钱包(点数余额)、交易记录
- **管理员 API(P1 已实现,`src/routes/admin.rs`)**:角色经 `AuthUser.role` 判定(Bearer key 关联 users.role),非 admin → 403;种子账号 `admin@aitokenpool.local / admin1234`(demo 账号 `demo@aitokenpool.local / demo1234`,余额 12471)
- role=ops 端点(平台运营者)留 P2,暂与 admin 合并权限位

## 5. 数据库设计(核心表)
## 5. 数据库设计(实表,`src/db.rs` 迁移 v3)

```
users — 用户/员工/分享者/消费者
keys — 上游 key(provider、额度、状态、属主)
api_keys — 分发 key(绑定用户、限额)
models — 模型(名称、单价[点数/token]、provider)
quotas — 点数账户(余额、流水)
transactions — 交易(谁→谁、key、token 数、点数、时间)
usage_records— 调用明细(用户、模型、token、成本)
users — 用户(email/password_hash[argon2]/name/role/created_at)
keys — 上游 key(provider/plan/model/状态/属主/加密密文/额度/已用/可用时间段/备注)
api_keys — 分发 key(绑定用户、atk_live_ 前缀、状态、last_used)
models — 模型价格(provider/model/currency/input_per_m/output_per_m)
quotas — 点数账户(balance 永久 + gift_balance 有效赠送)
gift_grants — 赠送明细(amount/granted_at/expires_at/status: active|used|expired)
transactions — 交易(counterpart/key_id/model/tokens/pts/type: consume|earn|topup/status/time)
usage_records— 调用明细(api_key_id/key_id/model/tokens/cost/time)
schema_version— 迁移版本(SCHEMA_VERSION=3,幂等迁移:ensure_column 补列 + CREATE TABLE IF NOT EXISTS)
```

## 6. 部署

- 企业版:Docker 单机(网关+账本+管理台),内网
- 公共版:云部署 + 多地节点(地域路由)+ 前端市场
- 配置:`config/config.toml`(server addr/db_path/master_key、points 锚定货币与粒度、providers/plans 端点、价格覆盖);`cargo run -- --config <path>`

## 7. 路线

1. **P0**:从 openlocalrouter 复用核心(网关/key 分发/用量追踪)
2. **P1**:点数/账本系统(企业版配额)
3. **P2**:Web 管理台
4. **P3**:公共版共享市场(上架/撮合/结算)
1. ~~**P0**:从 openlocalrouter 复用核心~~ → **已完成(v0.1.0→v0.2.2,PR #71–#74)**:Rust axum 自研骨架 + 认证 + API Key + 网关双协议 + 路由故障转移 + 计量账本 + SSE 流式 + key 加密 + 共享/钱包/交易 API
2. ~~**P1**:点数/账本系统~~ → **已完成(v0.3.0,PR #75)**:每日赠送/有效期/先赠后永扣减/管理员充值
3. **P2**:Web 管理台(UI 原型已 v1.20,前端静态页先行走在前面;后端角色 ops 端点)
4. **P3**:公共版共享市场深化(上架/撮合/结算)
5. **P4**:多地节点/地域路由

## 8. 竞品(2026-08-13 调研)
Expand Down
Loading