diff --git a/src/modules/learning-session/admin-learning.controller.ts b/src/modules/learning-session/admin-learning.controller.ts new file mode 100644 index 0000000..6f9cd58 --- /dev/null +++ b/src/modules/learning-session/admin-learning.controller.ts @@ -0,0 +1,100 @@ +import { Controller, Get, Query, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation, ApiQuery } from '@nestjs/swagger'; +import { PrismaService } from '../../infrastructure/database/prisma.service'; +import { AdminAuthGuard } from '../../common/guards/admin-auth.guard'; +import { AdminRolesGuard } from '../../common/guards/admin-roles.guard'; + +@ApiTags('admin-learning') +@ApiBearerAuth() +@Controller('admin-api/learning') +@UseGuards(AdminAuthGuard, AdminRolesGuard) +export class AdminLearningController { + constructor(private readonly prisma: PrismaService) {} + + @Get('sessions') + @ApiOperation({ summary: '学习会话列表' }) + @ApiQuery({ name: 'userId', required: false }) + @ApiQuery({ name: 'page', required: false }) + @ApiQuery({ name: 'limit', required: false }) + async listSessions( + @Query('userId') userId?: string, + @Query('page') page?: string, + @Query('limit') limit?: string, + ) { + const take = Math.min(Number(limit) || 20, 100); + const skip = (Math.max(Number(page) || 1, 1) - 1) * take; + const where: any = {}; + if (userId) where.userId = userId; + + const [items, total] = await Promise.all([ + this.prisma.learningSession.findMany({ + where, + orderBy: { startedAt: 'desc' }, + take, + skip, + select: { id: true, userId: true, knowledgeBaseId: true, knowledgeItemId: true, mode: true, status: true, startedAt: true, endedAt: true, durationSeconds: true, focusMinutes: true }, + }), + this.prisma.learningSession.count({ where }), + ]); + return { items, total }; + } + + @Get('analysis') + @ApiOperation({ summary: 'AI 分析结果列表' }) + @ApiQuery({ name: 'userId', required: false }) + @ApiQuery({ name: 'page', required: false }) + @ApiQuery({ name: 'limit', required: false }) + async listAnalysis( + @Query('userId') userId?: string, + @Query('page') page?: string, + @Query('limit') limit?: string, + ) { + const take = Math.min(Number(limit) || 20, 100); + const skip = (Math.max(Number(page) || 1, 1) - 1) * take; + const where: any = {}; + if (userId) where.userId = userId; + + const [items, total] = await Promise.all([ + this.prisma.aiAnalysisResult.findMany({ + where, + orderBy: { createdAt: 'desc' }, + take, + skip, + select: { id: true, userId: true, jobId: true, summary: true, masteryScore: true, weaknesses: true, strengths: true, createdAt: true }, + }), + this.prisma.aiAnalysisResult.count({ where }), + ]); + return { items, total }; + } + + @Get('ai-usage') + @ApiOperation({ summary: 'AI 调用日志' }) + @ApiQuery({ name: 'userId', required: false }) + @ApiQuery({ name: 'model', required: false }) + @ApiQuery({ name: 'page', required: false }) + @ApiQuery({ name: 'limit', required: false }) + async listAiUsage( + @Query('userId') userId?: string, + @Query('model') model?: string, + @Query('page') page?: string, + @Query('limit') limit?: string, + ) { + const take = Math.min(Number(limit) || 20, 100); + const skip = (Math.max(Number(page) || 1, 1) - 1) * take; + const where: any = {}; + if (userId) where.userId = userId; + if (model) where.model = { contains: model }; + + const [items, total] = await Promise.all([ + this.prisma.aiUsageLog.findMany({ + where, + orderBy: { createdAt: 'desc' }, + take, + skip, + select: { id: true, userId: true, model: true, provider: true, inputTokens: true, outputTokens: true, estimatedCost: true, success: true, createdAt: true }, + }), + this.prisma.aiUsageLog.count({ where }), + ]); + return { items, total }; + } +} diff --git a/src/modules/learning-session/learning-session.module.ts b/src/modules/learning-session/learning-session.module.ts index 3f82fe2..c68c1e7 100644 --- a/src/modules/learning-session/learning-session.module.ts +++ b/src/modules/learning-session/learning-session.module.ts @@ -1,10 +1,11 @@ import { Module } from '@nestjs/common'; import { LearningSessionController } from './learning-session.controller'; +import { AdminLearningController } from './admin-learning.controller'; import { LearningSessionService } from './learning-session.service'; import { LearningSessionRepository } from './learning-session.repository'; @Module({ - controllers: [LearningSessionController], + controllers: [LearningSessionController, AdminLearningController], providers: [LearningSessionService, LearningSessionRepository], exports: [LearningSessionService], })