import { Controller, Get, Post, Body, Query, HttpCode, HttpStatus } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger'; import { IsBoolean } from 'class-validator'; import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service'; import { TradingConfigRepository } from '../../infrastructure/persistence/repositories/trading-config.repository'; import { OrderRepository } from '../../infrastructure/persistence/repositories/order.repository'; import { OrderType, OrderStatus, OrderSource } from '../../domain/aggregates/order.aggregate'; import { Public } from '../../shared/guards/jwt-auth.guard'; class SetBuyEnabledDto { @IsBoolean() enabled: boolean; } class SetDepthEnabledDto { @IsBoolean() enabled: boolean; } @ApiTags('Admin') @Controller('admin') export class AdminController { constructor( private readonly prisma: PrismaService, private readonly tradingConfigRepository: TradingConfigRepository, private readonly orderRepository: OrderRepository, ) {} @Get('accounts/sync') @Public() @ApiOperation({ summary: '获取所有交易账户用于同步' }) async getAllAccountsForSync() { const accounts = await this.prisma.tradingAccount.findMany({ select: { accountSequence: true, shareBalance: true, cashBalance: true, frozenShares: true, frozenCash: true, totalBought: true, totalSold: true, createdAt: true, updatedAt: true, }, }); return { accounts: accounts.map((acc) => ({ accountSequence: acc.accountSequence, shareBalance: acc.shareBalance.toString(), cashBalance: acc.cashBalance.toString(), frozenShares: acc.frozenShares.toString(), frozenCash: acc.frozenCash.toString(), totalBought: acc.totalBought.toString(), totalSold: acc.totalSold.toString(), createdAt: acc.createdAt, updatedAt: acc.updatedAt, })), total: accounts.length, }; } @Get('trading/status') @Public() @ApiOperation({ summary: '获取交易系统状态' }) @ApiResponse({ status: 200, description: '返回交易系统配置状态' }) async getTradingStatus() { const config = await this.tradingConfigRepository.getConfig(); if (!config) { return { initialized: false, isActive: false, buyEnabled: false, activatedAt: null, message: '交易系统未初始化', }; } return { initialized: true, isActive: config.isActive, buyEnabled: config.buyEnabled, activatedAt: config.activatedAt, totalShares: config.totalShares.toFixed(8), burnTarget: config.burnTarget.toFixed(8), burnPeriodMinutes: config.burnPeriodMinutes, minuteBurnRate: config.minuteBurnRate.toFixed(18), message: config.isActive ? '交易系统已激活' : '交易系统未激活', }; } @Get('trading/buy-enabled') @Public() @ApiOperation({ summary: '获取买入功能开关状态' }) @ApiResponse({ status: 200, description: '返回买入功能是否启用' }) async getBuyEnabled() { const config = await this.tradingConfigRepository.getConfig(); return { enabled: config?.buyEnabled ?? false, }; } @Post('trading/buy-enabled') @Public() // TODO: 生产环境应添加管理员权限验证 @HttpCode(HttpStatus.OK) @ApiOperation({ summary: '设置买入功能开关' }) @ApiResponse({ status: 200, description: '买入功能开关设置成功' }) async setBuyEnabled(@Body() dto: SetBuyEnabledDto) { await this.tradingConfigRepository.setBuyEnabled(dto.enabled); return { success: true, enabled: dto.enabled, message: dto.enabled ? '买入功能已开启' : '买入功能已关闭', }; } @Post('trading/activate') @Public() // TODO: 生产环境应添加管理员权限验证 @HttpCode(HttpStatus.OK) @ApiOperation({ summary: '激活交易/销毁系统' }) @ApiResponse({ status: 200, description: '交易系统已激活' }) @ApiResponse({ status: 400, description: '交易系统未初始化' }) async activateTrading() { const config = await this.tradingConfigRepository.getConfig(); if (!config) { return { success: false, message: '交易系统未初始化,请先启动服务', }; } if (config.isActive) { return { success: true, message: '交易系统已处于激活状态', activatedAt: config.activatedAt, }; } await this.tradingConfigRepository.activate(); const updatedConfig = await this.tradingConfigRepository.getConfig(); return { success: true, message: '交易系统已激活,每分钟销毁已开始运行', activatedAt: updatedConfig?.activatedAt, }; } @Post('trading/deactivate') @Public() // TODO: 生产环境应添加管理员权限验证 @HttpCode(HttpStatus.OK) @ApiOperation({ summary: '关闭交易/销毁系统' }) @ApiResponse({ status: 200, description: '交易系统已关闭' }) async deactivateTrading() { const config = await this.tradingConfigRepository.getConfig(); if (!config) { return { success: false, message: '交易系统未初始化', }; } if (!config.isActive) { return { success: true, message: '交易系统已处于关闭状态', }; } await this.tradingConfigRepository.deactivate(); return { success: true, message: '交易系统已关闭,每分钟销毁已暂停', }; } @Get('trading/depth-enabled') @Public() @ApiOperation({ summary: '获取深度显示开关状态' }) @ApiResponse({ status: 200, description: '返回深度显示是否启用' }) async getDepthEnabled() { const config = await this.tradingConfigRepository.getConfig(); return { enabled: config?.depthEnabled ?? false, }; } @Get('orders') @Public() @ApiOperation({ summary: '获取全局订单列表' }) @ApiQuery({ name: 'page', required: false, type: Number }) @ApiQuery({ name: 'pageSize', required: false, type: Number }) @ApiQuery({ name: 'type', required: false, enum: ['BUY', 'SELL'] }) @ApiQuery({ name: 'status', required: false, enum: ['PENDING', 'PARTIAL', 'FILLED', 'CANCELLED'] }) @ApiQuery({ name: 'source', required: false, enum: ['USER', 'MARKET_MAKER', 'DEX_BOT', 'SYSTEM'] }) @ApiQuery({ name: 'search', required: false, type: String }) @ApiQuery({ name: 'startDate', required: false, type: String }) @ApiQuery({ name: 'endDate', required: false, type: String }) async getOrders( @Query('page') page?: number, @Query('pageSize') pageSize?: number, @Query('type') type?: string, @Query('status') status?: string, @Query('source') source?: string, @Query('search') search?: string, @Query('startDate') startDate?: string, @Query('endDate') endDate?: string, ) { const p = page ? Number(page) : 1; const ps = pageSize ? Number(pageSize) : 20; const result = await this.orderRepository.findAllOrders({ type: type as OrderType | undefined, status: status as OrderStatus | undefined, source: source as OrderSource | undefined, search: search || undefined, startDate: startDate ? new Date(startDate) : undefined, endDate: endDate ? new Date(endDate) : undefined, page: p, pageSize: ps, }); return { data: result.data, total: result.total, page: p, pageSize: ps, totalPages: Math.ceil(result.total / ps), }; } @Get('trades') @Public() @ApiOperation({ summary: '获取全局成交记录' }) @ApiQuery({ name: 'page', required: false, type: Number }) @ApiQuery({ name: 'pageSize', required: false, type: Number }) @ApiQuery({ name: 'source', required: false, enum: ['USER', 'MARKET_MAKER', 'DEX_BOT', 'SYSTEM'] }) @ApiQuery({ name: 'search', required: false, type: String }) @ApiQuery({ name: 'startDate', required: false, type: String }) @ApiQuery({ name: 'endDate', required: false, type: String }) async getTrades( @Query('page') page?: number, @Query('pageSize') pageSize?: number, @Query('source') source?: string, @Query('search') search?: string, @Query('startDate') startDate?: string, @Query('endDate') endDate?: string, ) { const p = page ? Number(page) : 1; const ps = pageSize ? Number(pageSize) : 20; const result = await this.orderRepository.findAllTrades({ source: source || undefined, search: search || undefined, startDate: startDate ? new Date(startDate) : undefined, endDate: endDate ? new Date(endDate) : undefined, page: p, pageSize: ps, }); return { data: result.data, total: result.total, page: p, pageSize: ps, totalPages: Math.ceil(result.total / ps), }; } @Post('trading/depth-enabled') @Public() // TODO: 生产环境应添加管理员权限验证 @HttpCode(HttpStatus.OK) @ApiOperation({ summary: '设置深度显示开关' }) @ApiResponse({ status: 200, description: '深度显示开关设置成功' }) async setDepthEnabled(@Body() dto: SetDepthEnabledDto) { await this.tradingConfigRepository.setDepthEnabled(dto.enabled); return { success: true, enabled: dto.enabled, message: dto.enabled ? '深度显示已开启' : '深度显示已关闭', }; } }