feat: M4-02 — admin learning data views (sessions, AI analysis, AI usage logs)
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 44s
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 44s
- Add AdminLearningController with 3 endpoints: GET /admin-api/learning/sessions — learning sessions list GET /admin-api/learning/analysis — AI analysis results GET /admin-api/learning/ai-usage — AI usage logs Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
5816ddf488
commit
90e921366a
100
src/modules/learning-session/admin-learning.controller.ts
Normal file
100
src/modules/learning-session/admin-learning.controller.ts
Normal file
@ -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 };
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user