mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat(wallet): 钱包系统替代配额系统,新增支付与退款机制
- 新增钱包余额管理、充值、扣费、退款完整流程 - 新增支付网关抽象层(支持手动/支付宝/微信) - 用量计费从配额系统迁移到钱包余额扣费 - 新增管理员钱包管理与支付订单管理页面 - 新增用户钱包中心页面 - 移除独立 Key 锁定机制,统一由钱包余额控制 - 新增相关 API 路由、序列化器与数据库迁移 - 新增钱包、支付、退款相关测试
This commit is contained in:
@@ -12,15 +12,47 @@ import type { ProviderWithEndpointsSummary, GlobalModelResponse } from '@/api/en
|
||||
|
||||
// ========== 用户数据 ==========
|
||||
|
||||
const MOCK_ADMIN_BILLING = {
|
||||
id: 'wallet-demo-admin',
|
||||
balance: 0,
|
||||
recharge_balance: 0,
|
||||
gift_balance: 0,
|
||||
refundable_balance: 0,
|
||||
currency: 'USD',
|
||||
status: 'active',
|
||||
limit_mode: 'unlimited' as const,
|
||||
unlimited: true,
|
||||
total_recharged: 0,
|
||||
total_consumed: 1234.56,
|
||||
total_refunded: 0,
|
||||
total_adjusted: 0,
|
||||
updated_at: new Date().toISOString(),
|
||||
}
|
||||
|
||||
const MOCK_USER_BILLING = {
|
||||
id: 'wallet-demo-user',
|
||||
balance: 54.68,
|
||||
recharge_balance: 40,
|
||||
gift_balance: 14.68,
|
||||
refundable_balance: 40,
|
||||
currency: 'USD',
|
||||
status: 'active',
|
||||
limit_mode: 'finite' as const,
|
||||
unlimited: false,
|
||||
total_recharged: 100,
|
||||
total_consumed: 45.32,
|
||||
total_refunded: 0,
|
||||
total_adjusted: 0,
|
||||
updated_at: new Date().toISOString(),
|
||||
}
|
||||
|
||||
export const MOCK_ADMIN_USER: User = {
|
||||
id: 'demo-admin-uuid-0001',
|
||||
username: 'Demo Admin',
|
||||
email: 'admin@demo.aether.io',
|
||||
role: 'admin',
|
||||
is_active: true,
|
||||
quota_usd: null,
|
||||
used_usd: 156.78,
|
||||
total_usd: 1234.56,
|
||||
billing: MOCK_ADMIN_BILLING,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -34,9 +66,7 @@ export const MOCK_NORMAL_USER: User = {
|
||||
email: 'user@demo.aether.io',
|
||||
role: 'user',
|
||||
is_active: true,
|
||||
quota_usd: 100,
|
||||
used_usd: 45.32,
|
||||
total_usd: 245.32,
|
||||
billing: MOCK_USER_BILLING,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -74,9 +104,7 @@ export const MOCK_ADMIN_PROFILE: Profile = {
|
||||
username: MOCK_ADMIN_USER.username,
|
||||
role: 'admin',
|
||||
is_active: true,
|
||||
quota_usd: null,
|
||||
used_usd: 156.78,
|
||||
total_usd: 1234.56,
|
||||
billing: MOCK_ADMIN_BILLING,
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
updated_at: new Date().toISOString(),
|
||||
last_login_at: new Date().toISOString(),
|
||||
@@ -92,9 +120,7 @@ export const MOCK_USER_PROFILE: Profile = {
|
||||
username: MOCK_NORMAL_USER.username,
|
||||
role: 'user',
|
||||
is_active: true,
|
||||
quota_usd: 100,
|
||||
used_usd: 45.32,
|
||||
total_usd: 245.32,
|
||||
billing: MOCK_USER_BILLING,
|
||||
created_at: '2024-06-01T00:00:00Z',
|
||||
updated_at: new Date().toISOString(),
|
||||
last_login_at: new Date().toISOString(),
|
||||
@@ -269,10 +295,8 @@ export const MOCK_ALL_USERS: AdminUser[] = [
|
||||
username: 'Demo Admin',
|
||||
email: 'admin@demo.aether.io',
|
||||
role: 'admin',
|
||||
unlimited: true,
|
||||
is_active: true,
|
||||
quota_usd: null,
|
||||
used_usd: 156.78,
|
||||
total_usd: 1234.56,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -283,10 +307,8 @@ export const MOCK_ALL_USERS: AdminUser[] = [
|
||||
username: 'Demo User',
|
||||
email: 'user@demo.aether.io',
|
||||
role: 'user',
|
||||
unlimited: false,
|
||||
is_active: true,
|
||||
quota_usd: 100,
|
||||
used_usd: 45.32,
|
||||
total_usd: 245.32,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -297,10 +319,8 @@ export const MOCK_ALL_USERS: AdminUser[] = [
|
||||
username: 'Alice Wang',
|
||||
email: 'alice@example.com',
|
||||
role: 'user',
|
||||
unlimited: false,
|
||||
is_active: true,
|
||||
quota_usd: 50,
|
||||
used_usd: 23.45,
|
||||
total_usd: 123.45,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -311,10 +331,8 @@ export const MOCK_ALL_USERS: AdminUser[] = [
|
||||
username: 'Bob Zhang',
|
||||
email: 'bob@example.com',
|
||||
role: 'user',
|
||||
unlimited: false,
|
||||
is_active: true,
|
||||
quota_usd: 200,
|
||||
used_usd: 89.12,
|
||||
total_usd: 589.12,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -325,10 +343,8 @@ export const MOCK_ALL_USERS: AdminUser[] = [
|
||||
username: 'Charlie Li',
|
||||
email: 'charlie@example.com',
|
||||
role: 'user',
|
||||
unlimited: false,
|
||||
is_active: false,
|
||||
quota_usd: 30,
|
||||
used_usd: 30.00,
|
||||
total_usd: 30.00,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -387,8 +403,6 @@ export const MOCK_ADMIN_API_KEYS: AdminApiKeysResponse = {
|
||||
key_display: 'sk-sa...abc1',
|
||||
is_active: true,
|
||||
is_standalone: true,
|
||||
balance_used_usd: 25.50,
|
||||
current_balance_usd: 74.50,
|
||||
total_requests: 500,
|
||||
total_tokens: 1500000,
|
||||
total_cost_usd: 25.50,
|
||||
@@ -404,8 +418,6 @@ export const MOCK_ADMIN_API_KEYS: AdminApiKeysResponse = {
|
||||
key_display: 'sk-sa...def2',
|
||||
is_active: true,
|
||||
is_standalone: true,
|
||||
balance_used_usd: 45.00,
|
||||
current_balance_usd: 55.00,
|
||||
total_requests: 800,
|
||||
total_tokens: 2400000,
|
||||
total_cost_usd: 45.00,
|
||||
@@ -808,8 +820,7 @@ export const MOCK_USAGE_RESPONSE: UsageResponse = {
|
||||
total_cost: 45.67,
|
||||
total_actual_cost: 33.33,
|
||||
avg_response_time: 1.23,
|
||||
quota_usd: 100,
|
||||
used_usd: 45.32,
|
||||
billing: MOCK_USER_BILLING,
|
||||
summary_by_model: [
|
||||
{ model: 'claude-sonnet-4-5-20250929', requests: 456, input_tokens: 650000, output_tokens: 250000, total_tokens: 900000, total_cost_usd: 18.50, actual_total_cost_usd: 13.50 },
|
||||
{ model: 'gpt-5.1', requests: 312, input_tokens: 480000, output_tokens: 180000, total_tokens: 660000, total_cost_usd: 12.30, actual_total_cost_usd: 9.20 },
|
||||
|
||||
@@ -588,8 +588,22 @@ const mockHandlers: Record<string, (config: AxiosRequestConfig) => Promise<Axios
|
||||
total_cost: Number((totalCost * 20).toFixed(2)),
|
||||
total_actual_cost: Number((totalActualCost * 20).toFixed(2)),
|
||||
avg_response_time: Number(avgResponseTime.toFixed(2)) || 1.23,
|
||||
quota_usd: 100,
|
||||
used_usd: Number((totalCost * 20).toFixed(2)),
|
||||
billing: {
|
||||
id: 'wallet-demo-user',
|
||||
balance: Number((100 - totalCost * 20).toFixed(2)),
|
||||
recharge_balance: Number((100 - totalCost * 20).toFixed(2)),
|
||||
gift_balance: 0,
|
||||
refundable_balance: Number((100 - totalCost * 20).toFixed(2)),
|
||||
currency: 'USD',
|
||||
status: 'active',
|
||||
limit_mode: 'finite',
|
||||
unlimited: false,
|
||||
total_recharged: 100,
|
||||
total_consumed: Number((totalCost * 20).toFixed(2)),
|
||||
total_refunded: 0,
|
||||
total_adjusted: 0,
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
activity_heatmap: heatmap,
|
||||
summary_by_model: Array.from(modelStats.entries()).map(([model, stats]) => ({
|
||||
model,
|
||||
@@ -726,10 +740,8 @@ const mockHandlers: Record<string, (config: AxiosRequestConfig) => Promise<Axios
|
||||
username: body.username,
|
||||
email: body.email,
|
||||
role: body.role || 'user',
|
||||
unlimited: Boolean(body.unlimited),
|
||||
is_active: true,
|
||||
quota_usd: body.quota_usd || null,
|
||||
used_usd: 0,
|
||||
total_usd: 0,
|
||||
allowed_providers: null,
|
||||
allowed_api_formats: null,
|
||||
allowed_models: null,
|
||||
@@ -757,8 +769,6 @@ const mockHandlers: Record<string, (config: AxiosRequestConfig) => Promise<Axios
|
||||
key_display: 'sk-sa...demo',
|
||||
is_active: true,
|
||||
is_standalone: true,
|
||||
balance_used_usd: 0,
|
||||
current_balance_usd: body.initial_balance_usd || 100,
|
||||
total_requests: 0,
|
||||
created_at: new Date().toISOString()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user