import { Body, Controller, Get, Param, ParseIntPipe, Post, Put, Query, Request, UseGuards, UsePipes, ValidationPipe, } from '@nestjs/common'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { RequirePermission } from '../auth/decorators/permission.decorator'; import { extractRequestInfo } from '../common/request-utils'; import { OperationLogsService } from '../operation-logs/operation-logs.service'; import type { AuthenticatedUser } from '../authorization'; import { CreateExamDto, QueryExamDto, UpdateExamScoreValueDto } from './dto/exam.dto'; import { ExamsService } from './exams.service'; interface AuthenticatedRequest { user: AuthenticatedUser; ip?: string; headers?: Record; } @UseGuards(JwtAuthGuard) @UsePipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true })) @Controller('exams') export class ExamsController { constructor( private readonly service: ExamsService, private readonly logService: OperationLogsService, ) {} private canManageAll(req: AuthenticatedRequest) { return req.user.isSuperAdmin || req.user.permissions.includes('exam:edit'); } @Get() @RequirePermission('exam:view') async findAll(@Query() query: QueryExamDto, @Request() req: AuthenticatedRequest) { const classIds = await this.service.getAccessibleClassIds(req.user.id, this.canManageAll(req)); return this.service.findAll(query, classIds); } @Get(':id') @RequirePermission('exam:view') findOne(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) { return this.service.findOne(id, req.user.id, this.canManageAll(req)); } @Post() @RequirePermission('exam:view') async create(@Body() dto: CreateExamDto, @Request() req: AuthenticatedRequest) { const result = await this.service.create(dto, req.user.id, this.canManageAll(req)); const { ipAddress, userAgent } = extractRequestInfo(req); await this.logService.log({ userId: req.user.id, username: req.user.username, module: '考试管理', action: '创建考试', targetId: result.id, targetType: 'exam', detail: `${dto.examName} - ${dto.subject}`, ipAddress, userAgent, }); return result; } @Put(':id/archive') @RequirePermission('exam:view') async archive( @Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest, ) { const result = await this.service.archive(id, req.user.id, this.canManageAll(req)); const { ipAddress, userAgent } = extractRequestInfo(req); await this.logService.log({ userId: req.user.id, username: req.user.username, module: '考试管理', action: '归档考试', targetId: id, targetType: 'exam', ipAddress, userAgent, }); return result; } @Put(':id/restore') @RequirePermission('exam:view') async restore( @Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest, ) { const result = await this.service.restore(id, req.user.id, this.canManageAll(req)); const { ipAddress, userAgent } = extractRequestInfo(req); await this.logService.log({ userId: req.user.id, username: req.user.username, module: '考试管理', action: '恢复考试', targetId: id, targetType: 'exam', ipAddress, userAgent, }); return result; } @Put(':examId/scores/:scoreId') @RequirePermission('exam:view') async updateScore( @Param('examId', ParseIntPipe) examId: number, @Param('scoreId', ParseIntPipe) scoreId: number, @Body() dto: UpdateExamScoreValueDto, @Request() req: AuthenticatedRequest, ) { const result = await this.service.updateScore( examId, scoreId, dto.score, req.user.id, this.canManageAll(req), ); const { ipAddress, userAgent } = extractRequestInfo(req); await this.logService.log({ userId: req.user.id, username: req.user.username, module: '考试管理', action: dto.score === null || dto.score === undefined ? '清空成绩' : '录入成绩', targetId: scoreId, targetType: 'exam_score', detail: dto.score === null || dto.score === undefined ? '成绩已清空' : `成绩:${dto.score}`, ipAddress, userAgent, }); return result; } }