feat: credit-based billing system

- New DB models: credit_packages, subscription_plans, user_credits, credit_consumptions, credit_purchases
- CreditService: balance, deduct, add_credits, grant_free_trial, history
- User API: /api/v1/credits/* (balance/history/packages/purchase/subscribe)
- Admin API: /api/v1/admin/credit-* (CRUD packages/plans, user credits, consumptions)
- PaymentService.create_credit_order + handle_callback for credit purchases
- Credit deduction on: discovery, translate, marketing, ai_chat, followup
- Free trial 30 credits on registration
- Documentation: docs/CREDIT_SYSTEM.md
This commit is contained in:
TradeMate Dev
2026-06-12 10:39:45 +08:00
parent 5d895ae12c
commit 2a107a42f3
21 changed files with 1528 additions and 33 deletions
+399
View File
@@ -0,0 +1,399 @@
# TradeMate Credit System — 信用计费系统设计方案
## 1. 业务模式变更
### 1.1 从工具订阅 → 结果付费
- **旧模式**:按月订阅解锁功能(Free/Pro/Enterprise
- **新模式**:按次付费购买"结果",所有 AI 功能统一消耗信用额度(Credits)
- **核心**:用户不为功能付费,为**产生的价值**(客户线索、分析报告、翻译字符)付费
### 1.2 目标用户群
- **国内**:中国外贸 SOHO/小团队 → 中文界面,¥ 计价
- **海外**:全球跨境小B → 英文界面,$ 计价
- 两端统一产品逻辑,分语言/货币展示
### 1.3 产品定位
| 维度 | 旧 | 新 |
|------|-----|-----|
| 定位 | 外贸工具套装 | 外贸客户发现引擎 |
| Slogan | 外贸小助手 | Customer Discovery Engine / 外贸获客引擎 |
| 核心价值 | 多功能的工具箱 | 输入产品,找到买家 |
| 免费策略 | 限功能免费版 | 注册送 N 次,用完即购 |
| 收费锚点 | 按月付费解锁 | 按产出结果扣次 |
## 2. 信用额度系统(Credits
### 2.1 定义
**1 Credit = 1 次消费单位。** 所有 AI 调用均以 Credits 计价。
用户持有的 Credits 通过两种方式获得:
- **一次性购买(Credit Pack)**:永不过期,用不完的余额留存
- **月订阅(Subscription)**:每月自动到账,月底未用完清零
两种方式的 Credits 统一存放在 `user_credits.balance` 中,消费时按 `source` 记录来源。
### 2.2 消费价格表
所有 AI 相关功能均消耗 Credits,覆盖 API 调用成本:
| 功能 | Credits | 说明 |
|------|---------|------|
| **客户搜索** (lead_search) | 10 | 多源搜索 + AI 匹配 + 联系方式提取 |
| **公司深度分析** (company_analysis) | 5 | 单家公司 AI 分析报告 |
| **市场情报报告** (market_intel) | 20 | 多维度市场研究 |
| **翻译** (translate, 5000 chars) | 5 | 按字符计费,不足按比例折算 |
| **回复建议** (reply_suggest) | 2 | AI 生成 3 条回复建议 |
| **开发信生成** (outreach) | 3 | 单封定制开发信 |
| **营销素材** (marketing_content) | 5 | 多风格营销文案 |
| **竞品分析** (competitor_analysis) | 10 | 多个竞品对比分析 |
| **AI 助手对话** (ai_chat, 10条消息) | 1 | 普通 AI 对话 |
| **信息提取** (info_extract) | 1 | 询盘/邮件信息提取 |
| **报价单生成** (quotation) | 2 | AI 辅助报价 |
| **跟进策略** (followup_scan) | 2 | AI 扫描跟进建议 |
### 2.3 计费规则
- **按次扣减**:功能调用成功后扣减,失败不扣
- **余额不足**:返回 `402 Payment Required` + 当前余额 + 所需额度
- **不足扣减**:如余额不足单次消费,允许扣至负数(欠费模式),欠费部分下次充值自动抵扣
- **日免费额度**:翻译每日免费 1000 字符(防流失),超出部分扣 Credits
- **免费试用**:新用户注册自动赠送 30 Credits(≈ 3 次搜索)
### 2.4 消费记录
每次扣减记录在 `credit_consumptions` 表,包含:
- user_id, result_type(功能类型)
- reference_id(关联业务记录 ID
- credits_change(正数 = 充值,负数 = 消费)
- balance_after(扣减后余额)
- sourcepackage/subscription/admin_grant
- metadata(Jsonb,保存上下文,如搜索关键词、翻译字符数)
## 3. 定价体系
### 3.1 次数包(一次性,永不过期)
| 次数 | 价格(¥) | 次均 | 价格($) |
|------|---------|------|---------|
| 20 | ¥19 | ¥0.95 | $3 |
| 100 | ¥79 | ¥0.79 | $12 |
| 500 | ¥299 | ¥0.60 | $45 |
| 2000 | ¥899 | ¥0.45 | $138 |
### 3.2 订阅套餐(每月重置)
| 套餐 | 月次数 | 月费(¥) | 月费($) | 次均 |
|------|-------|---------|---------|------|
| Starter | 100 | ¥69 | $10 | ¥0.69 |
| Pro | 500 | ¥269 | $40 | ¥0.54 |
| Enterprise | 2000 | ¥699 | $105 | ¥0.35 |
### 3.3 新旧过渡方案
- 现有有效订阅用户 → 按剩余天数折算为 Credits 存入余额
- Pro 剩余 15 天 ≈ 100 × 0.5 ≈ 50 Credits 一次性赠送
- 同时保留原订阅到期日
- 新用户 → 走新信用系统
- 过渡期两种支付方式并存
## 4. 数据库设计
### 4.1 新增表
```sql
-- 次数包定义(一次性购买)
CREATE TABLE credit_packages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL, -- 中文名
name_en VARCHAR(100) NOT NULL, -- 英文名
credits INTEGER NOT NULL, -- 额定量
price NUMERIC(10,2) NOT NULL, -- 人民币价格
price_usd NUMERIC(10,2), -- 美元价格
original_price NUMERIC(10,2), -- 原价(划线价)
is_active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- 订阅套餐定义(周期性)
CREATE TABLE subscription_plans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
name_en VARCHAR(100) NOT NULL,
credits_per_month INTEGER NOT NULL,
price NUMERIC(10,2) NOT NULL,
price_usd NUMERIC(10,2),
duration_days INTEGER NOT NULL DEFAULT 30,
is_active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- 用户信用余额
CREATE TABLE user_credits (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL UNIQUE REFERENCES users(id),
balance NUMERIC(12,1) NOT NULL DEFAULT 0, -- 当前余额(支持小数)
total_purchased NUMERIC(12,1) NOT NULL DEFAULT 0,
total_used NUMERIC(12,1) NOT NULL DEFAULT 0,
-- 订阅关联
subscription_plan_id UUID REFERENCES subscription_plans(id),
subscription_expires_at TIMESTAMP,
subscription_auto_renew BOOLEAN DEFAULT FALSE,
-- 免费赠送标记
free_trial_used BOOLEAN DEFAULT FALSE,
daily_translate_chars INTEGER DEFAULT 0,
daily_translate_date DATE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- 信用消费日志
CREATE TABLE credit_consumptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
result_type VARCHAR(50) NOT NULL, -- lead_search / translate / outreach / ...
reference_id UUID, -- 关联业务记录
credits_change NUMERIC(10,1) NOT NULL, -- 负=消费,正=充值
balance_after NUMERIC(12,1) NOT NULL,
source VARCHAR(30) NOT NULL, -- package / subscription / admin_grant / free_trial / daily_free
description VARCHAR(500),
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
-- 次数包购买记录
CREATE TABLE credit_purchases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
package_id UUID REFERENCES credit_packages(id),
subscription_plan_id UUID REFERENCES subscription_plans(id),
credits INTEGER NOT NULL,
amount NUMERIC(10,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'CNY',
payment_method VARCHAR(20), -- alipay / wechat / stripe
status VARCHAR(20) DEFAULT 'pending', -- pending / paid / refunded
payment_transaction_id UUID REFERENCES payment_transactions(id),
created_at TIMESTAMP DEFAULT NOW(),
paid_at TIMESTAMP
);
```
### 4.2 索引
```sql
CREATE INDEX idx_credit_consumptions_user ON credit_consumptions(user_id, created_at DESC);
CREATE INDEX idx_credit_consumptions_type ON credit_consumptions(result_type);
CREATE INDEX idx_credit_purchases_user ON credit_purchases(user_id);
CREATE INDEX idx_credit_purchases_status ON credit_purchases(status);
```
### 4.3 配置项
```json
{
"free_trial_credits": {"value": 30, "desc": "新用户免费赠送次数"},
"daily_free_translate_chars": {"value": 1000, "desc": "每日免费翻译字符数"},
"credit_consumption_rates": {
"value": {
"lead_search": 10,
"company_analysis": 5,
"market_intel": 20,
"translate_per_1000chars": 1,
"reply_suggest": 2,
"outreach": 3,
"marketing_content": 5,
"competitor_analysis": 10,
"ai_chat_per_10msg": 1,
"info_extract": 1,
"quotation": 2,
"followup_scan": 2
},
"desc": "各功能信用消耗速率"
}
}
```
## 5. API 设计
### 5.1 信用系统
```
GET /api/v1/credits/balance → 余额+订阅信息
GET /api/v1/credits/history?page=&size= → 消费历史
POST /api/v1/credits/packages → 购买次数包(走支付网关)
POST /api/v1/credits/subscribe → 开通订阅
POST /api/v1/credits/cancel-subscription → 取消订阅
GET /api/v1/credits/packages → 次数包列表
GET /api/v1/credits/subscription-plans → 订阅套餐列表
GET /api/v1/credits/rates → 各功能消耗速率
```
### 5.2 现有功能增加额度扣减
所有 AI 功能在返回结果后调用 `CreditService.deduct()`
```python
# 在每个 AI 功能服务层中(discovery/discovery.py, translate/translation.py, etc.)
from app.services.credit import CreditService
svc = CreditService(db)
success, balance = await svc.deduct(user_id, "lead_search", reference_id=record_id)
if not success:
raise HTTPException(status_code=402, detail="次数不足")
```
### 5.3 管理端
```
GET /api/v1/admin/credit-packages → CRUD
POST /api/v1/admin/credit-packages
PUT /api/v1/admin/credit-packages/{id}
DELETE /api/v1/admin/credit-packages/{id}
GET /api/v1/admin/subscription-plans → CRUD
POST /api/v1/admin/subscription-plans
PUT /api/v1/admin/subscription-plans/{id}
GET /api/v1/admin/user-credits → 用户余额列表
POST /api/v1/admin/user-credits/adjust → 手动调整余额
GET /api/v1/admin/credit-consumptions → 消费流水
GET /api/v1/admin/credit-stats → 统计数据
```
## 6. 前端改造
### 6.1 导航栏重构
```
新用户侧边栏(按优先级):
【核心】
★ 发现客户 /discovery ← 首页
市场情报 /market-intel ← 新增
我的线索 /my-leads ← 新增(收藏/保存的线索)
【工具(增值)】
客户管理 /customers
产品库 /products
报价单 /quotations
智能翻译 /translate
开发信 /outreach
营销素材 /marketing
【查看】
工作台 /workspace ← 仪表盘(用量统计/最近操作)
团队协作 /team
个人中心 /profile
```
### 6.2 Topbar 改造
顶部始终显示 `余额: X.X 次` 按钮,点击跳转购买页。
```html
<el-button class="credit-balance" @click="$router.push('/credits')">
<el-icon><Coin /></el-icon>
{{ balance }} 次
</el-button>
```
### 6.3 购买页面
替代现有 `/upgrade` 路由为 `/credits`
- Tab1: **次数包** — 多卡片展示(20/100/500/2000次),支付弹窗
- Tab2: **订阅套餐** — Starter/Pro/Enterprise,月付/年付
- Tab3: **消费明细** — 近期消费流水
### 6.4 余额不足引导
所有 AI 功能检测到余额不足时,前端弹窗:
```
"次数不足,当前剩余 X 次,本次操作需要 Y 次
[去购买] [取消]"
```
### 6.5 免费额度提示
新用户注册后首页显示:
```
"欢迎使用 TradeMate!您有 30 次免费搜索额度,用完即止。"
```
## 7. 国际化
### 7.1 第一阶段(核心页面)
| 页面 | 优先级 |
|------|--------|
| 登录/注册 | P0 |
| 导航栏 | P0 |
| 发现客户 | P0 |
| 购买页面 | P0 |
| 工作台 | P1 |
| 翻译 | P1 |
| 个人中心 | P1 |
### 7.2 技术方案
- 使用 vue-i18n + JSON locale files
- `zh-CN.json`(默认)+ `en.json`
- 通过 URL query `?lang=en` 或 Cookie 切换
- 管理后台跟随用户语言偏好
```json
// en.json
{
"nav": {
"discovery": "Find Buyers",
"translate": "Translate",
"customers": "Customers",
"credits": "Buy Credits"
},
"discovery": {
"search_placeholder": "e.g. LED lighting, solar panels...",
"search_button": "Search Buyers",
"credits_cost": "This search costs 10 credits"
}
}
```
## 8. 实施计划
### Phase 1 — 信用系统后端(当前)
1. 数据库模型 + migration
2. CreditService
3. 信用系统 API
4. 管理端 CRUD
### Phase 2 — 功能接入扣次
1. Discovery 接入(搜索/分析/市场报告)
2. Translate 接入
3. Marketing 接入
4. AI Chat / Outreach 接入
### Phase 3 — 前端改造
1. 新导航 + 余额显示
2. 购买/订阅页面
3. 余额不足引导
4. 工作台改仪表盘
### Phase 4 — 国际化 + 定价上线
1. 中英文切换
2. 海外支付(Stripe
3. 定价正式发布
4. 老用户数据迁移
## 9. 关键风险
| 风险 | 缓解 |
|------|------|
| 用户觉得按次付费贵 | 免费 30 次让用户先体验价值;套餐价需低于用户感知价值 |
| 搜索结果质量不稳 | 多搜索源回退(DB → Bing → Google CSE → AI),持续优化 |
| 老用户不满过渡方案 | 老用户按剩余天数高折抵换算,并保留原有套餐期限内权益 |
| 翻译用量大成本高 | 每日免费额兜底 + Credits 定价覆盖 API 成本 |