diff --git a/src/app.module.ts b/src/app.module.ts index 9bb7fa0..5ab2834 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -15,6 +15,7 @@ import { AuthModule } from './modules/auth/auth.module'; import { AdminAuthModule } from './modules/admin-auth/admin-auth.module'; import { AdminDashboardModule } from './modules/admin-dashboard/admin-dashboard.module'; import { AdminUsersModule } from './modules/admin-users/admin-users.module'; +import { AdminBillingModule } from './modules/admin-billing/admin-billing.module'; import { AdminServersModule } from './modules/admin-servers/admin-servers.module'; import { AdminConversationModule } from './modules/admin-conversation/admin-conversation.module'; import { AdminAiChatModule } from './modules/admin-ai-chat/admin-ai-chat.module'; @@ -90,6 +91,7 @@ import appleConfig from './config/apple.config'; AdminAuthModule, AdminDashboardModule, AdminUsersModule, + AdminBillingModule, AdminServersModule, AdminConversationModule, AdminAiChatModule, diff --git a/src/modules/admin-billing/admin-billing.controller.ts b/src/modules/admin-billing/admin-billing.controller.ts new file mode 100644 index 0000000..4e2105d --- /dev/null +++ b/src/modules/admin-billing/admin-billing.controller.ts @@ -0,0 +1,22 @@ +import { Controller, Get, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; +import { AdminBillingService } from './admin-billing.service'; +import { AdminAuthGuard } from '../../common/guards/admin-auth.guard'; +import { AdminRolesGuard } from '../../common/guards/admin-roles.guard'; +import { AdminRoles } from '../../common/decorators/admin-roles.decorator'; +import type { AdminRole } from '../../common/types/admin-role.enum'; + +@ApiTags('admin-billing') +@Controller('admin-api/billing') +@UseGuards(AdminAuthGuard, AdminRolesGuard) +@ApiBearerAuth() +export class AdminBillingController { + constructor(private readonly billingService: AdminBillingService) {} + + @Get() + @AdminRoles('SUPER_ADMIN' as AdminRole) + @ApiOperation({ summary: 'API 用量与费用(仅超级管理员)' }) + async getBilling() { + return this.billingService.getAllBilling(); + } +} diff --git a/src/modules/admin-billing/admin-billing.module.ts b/src/modules/admin-billing/admin-billing.module.ts new file mode 100644 index 0000000..2cc5a34 --- /dev/null +++ b/src/modules/admin-billing/admin-billing.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { AdminBillingController } from './admin-billing.controller'; +import { AdminBillingService } from './admin-billing.service'; +import { AdminAuthGuard } from '../../common/guards/admin-auth.guard'; +import { AdminRolesGuard } from '../../common/guards/admin-roles.guard'; + +@Module({ + controllers: [AdminBillingController], + providers: [AdminBillingService, AdminAuthGuard, AdminRolesGuard], +}) +export class AdminBillingModule {} diff --git a/src/modules/admin-billing/admin-billing.service.ts b/src/modules/admin-billing/admin-billing.service.ts new file mode 100644 index 0000000..32006cf --- /dev/null +++ b/src/modules/admin-billing/admin-billing.service.ts @@ -0,0 +1,98 @@ +import { Injectable, Logger } from '@nestjs/common'; + +export interface BillingInfo { + name: string; + model: string; + balance: string; + currency: string; + status: 'ok' | 'unknown'; + consoleUrl: string; + note: string; +} + +@Injectable() +export class AdminBillingService { + private readonly logger = new Logger(AdminBillingService.name); + + async getAllBilling(): Promise<{ providers: BillingInfo[] }> { + const [deepseek, siliconflow] = await Promise.all([ + this.getDeepSeek(), + this.getSiliconFlow(), + ]); + + return { + providers: [ + deepseek, + siliconflow, + this.getMiniMax(), + this.getBaiduOcr(), + ], + }; + } + + private async getDeepSeek(): Promise { + try { + const resp = await fetch('https://api.deepseek.com/user/balance', { + headers: { Authorization: 'Bearer sk-ddddea4986d843be978ced9e82988fa0' }, + }); + const data = await resp.json(); + const bal = data?.balance_infos?.[0]?.total_balance; + return { + name: 'DeepSeek', + model: 'deepseek-v4-flash / deepseek-chat', + balance: bal ? `¥${bal}` : '—', + currency: 'CNY', + status: 'ok', + consoleUrl: 'https://platform.deepseek.com', + note: '充值余额', + }; + } catch { + return { name: 'DeepSeek', model: 'v4-flash', balance: '—', currency: 'CNY', status: 'unknown', consoleUrl: 'https://platform.deepseek.com', note: '查询失败' }; + } + } + + private async getSiliconFlow(): Promise { + try { + const resp = await fetch('https://api.siliconflow.cn/v1/user/info', { + headers: { Authorization: 'Bearer sk-jdtqzgrlneklatdmymscrnvljvzlkkxcrzylznufpgggswjz' }, + }); + const data = await resp.json(); + const bal = data?.data?.totalBalance; + return { + name: '硅基流动', + model: 'BGE-M3 / BGE-Reranker', + balance: bal ? `¥${bal}` : '—', + currency: 'CNY', + status: 'ok', + consoleUrl: 'https://cloud.siliconflow.cn', + note: '充值 + 赠送余额', + }; + } catch { + return { name: '硅基流动', model: 'Embedding/Rerank', balance: '—', currency: 'CNY', status: 'unknown', consoleUrl: 'https://cloud.siliconflow.cn', note: '查询失败' }; + } + } + + private getMiniMax(): BillingInfo { + return { + name: 'MiniMax', + model: 'MiniMax 2.7', + balance: '点数制', + currency: 'points', + status: 'unknown', + consoleUrl: 'https://platform.minimaxi.com', + note: '按点数计费,需登录控制台查看', + }; + } + + private getBaiduOcr(): BillingInfo { + return { + name: '百度 OCR', + model: '通用文字识别', + balance: '次数制', + currency: 'calls', + status: 'unknown', + consoleUrl: 'https://console.bce.baidu.com', + note: '按调用次数计费,需登录控制台查看', + }; + } +}