diff --git a/src/app.module.ts b/src/app.module.ts index 8aba25e..bd7a120 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { JwtModule } from '@nestjs/jwt'; @@ -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 { AdminAiChatModule } from './modules/admin-ai-chat/admin-ai-chat.module'; import { AdminAuditLogModule } from './modules/admin-audit-log/admin-audit-log.module'; import { UsersModule } from './modules/users/users.module'; import { KnowledgeBaseModule } from './modules/knowledge-base/knowledge-base.module'; @@ -87,6 +88,7 @@ import appleConfig from './config/apple.config'; AdminAuthModule, AdminDashboardModule, AdminUsersModule, + AdminAiChatModule, AdminAuditLogModule, UsersModule, KnowledgeBaseModule, diff --git a/src/modules/admin-ai-chat/admin-ai-chat.controller.ts b/src/modules/admin-ai-chat/admin-ai-chat.controller.ts new file mode 100644 index 0000000..c987fed --- /dev/null +++ b/src/modules/admin-ai-chat/admin-ai-chat.controller.ts @@ -0,0 +1,23 @@ +import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; +import { Controller, Post, Body, UseGuards } from '@nestjs/common'; +import { AdminAiChatService } from './admin-ai-chat.service'; +import { AiChatDto } from './dto/ai-chat.dto'; +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/role.enum'; + +@ApiTags('admin-ai-chat') +@Controller('admin-api/ai') +@UseGuards(AdminAuthGuard, AdminRolesGuard) +export class AdminAiChatController { + constructor(private readonly aiChatService: AdminAiChatService) {} + + @Post('chat') + @AdminRoles('SUPER_ADMIN' as AdminRole) + @ApiBearerAuth() + @ApiOperation({ summary: 'AI 对话(仅超级管理员)' }) + async chat(@Body() dto: AiChatDto) { + return this.aiChatService.chat(dto); + } +} \ No newline at end of file diff --git a/src/modules/admin-ai-chat/admin-ai-chat.module.ts b/src/modules/admin-ai-chat/admin-ai-chat.module.ts new file mode 100644 index 0000000..f1c1bfc --- /dev/null +++ b/src/modules/admin-ai-chat/admin-ai-chat.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { AdminAiChatController } from './admin-ai-chat.controller'; +import { AdminAiChatService } from './admin-ai-chat.service'; +import { DeepSeekProvider } from '../ai/providers/deepseek.provider'; +import { AdminAuthGuard } from '../../common/guards/admin-auth.guard'; +import { AdminRolesGuard } from '../../common/guards/admin-roles.guard'; + +@Module({ + controllers: [AdminAiChatController], + providers: [AdminAiChatService, DeepSeekProvider, AdminAuthGuard, AdminRolesGuard], +}) +export class AdminAiChatModule {} \ No newline at end of file diff --git a/src/modules/admin-ai-chat/admin-ai-chat.service.ts b/src/modules/admin-ai-chat/admin-ai-chat.service.ts new file mode 100644 index 0000000..6c2aed6 --- /dev/null +++ b/src/modules/admin-ai-chat/admin-ai-chat.service.ts @@ -0,0 +1,37 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { DeepSeekProvider } from '../ai/providers/deepseek.provider'; +import type { AiChatDto } from './dto/ai-chat.dto'; + +@Injectable() +export class AdminAiChatService { + private readonly logger = new Logger(AdminAiChatService.name); + + constructor(private readonly deepseek: DeepSeekProvider) {} + + async chat(dto: AiChatDto) { + const systemMessages = dto.messages.filter(m => m.role === 'system'); + const hasSystemPrompt = systemMessages.length > 0; + + const messages = hasSystemPrompt + ? dto.messages + : [ + { role: 'system' as const, content: '你是知习管理后台的 AI 任务助理,帮助管理员处理日常任务、解答问题。请用简洁专业的中文回复。' }, + ...dto.messages, + ]; + + const start = Date.now(); + const result = await this.deepseek.generate({ + model: 'deepseek-chat', + messages, + temperature: 0.7, + maxTokens: 4096, + }); + + this.logger.log(`AI chat completed in ${Date.now() - start}ms, tokens: ${result.usage?.inputTokens ?? 0}/${result.usage?.outputTokens ?? 0}`); + + return { + content: result.rawText, + usage: result.usage, + }; + } +} \ No newline at end of file diff --git a/src/modules/admin-ai-chat/dto/ai-chat.dto.ts b/src/modules/admin-ai-chat/dto/ai-chat.dto.ts new file mode 100644 index 0000000..aca8768 --- /dev/null +++ b/src/modules/admin-ai-chat/dto/ai-chat.dto.ts @@ -0,0 +1,27 @@ +import { IsString, IsArray, ValidateNested, IsOptional, IsIn, MinLength } from 'class-validator'; +import { Type } from 'class-transformer'; +import { ApiProperty } from '@nestjs/swagger'; + +export class ChatMessageDto { + @ApiProperty({ enum: ['user', 'assistant', 'system'] }) + @IsString() + @IsIn(['user', 'assistant', 'system']) + role: 'user' | 'assistant' | 'system'; + + @ApiProperty() + @IsString() + @MinLength(1) + content: string; +} + +export class AiChatDto { + @ApiProperty({ type: [ChatMessageDto] }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ChatMessageDto) + messages: ChatMessageDto[]; + + @ApiProperty({ required: false, default: false }) + @IsOptional() + stream?: boolean; +} \ No newline at end of file diff --git a/src/modules/admin-users/admin-users.module.ts b/src/modules/admin-users/admin-users.module.ts index fd54d0d..8681e6a 100644 --- a/src/modules/admin-users/admin-users.module.ts +++ b/src/modules/admin-users/admin-users.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common'; import { AdminUsersController } from './admin-users.controller'; import { AdminUsersService } from './admin-users.service'; import { AdminAuthModule } from '../admin-auth/admin-auth.module'; +import { PasswordService } from '../../common/utils/password.service'; @Module({ imports: [AdminAuthModule], controllers: [AdminUsersController], - providers: [AdminUsersService], + providers: [AdminUsersService, PasswordService], }) export class AdminUsersModule {}