import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards, Request, UseInterceptors, UploadedFile, Res, ParseIntPipe, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import type { Request as ExpressRequest, Response } from 'express'; import * as fs from 'fs'; import { ArchiveReportService } from './archive-report.service'; import { ArchiveService } from './archive.service'; import { UpsertProfileDto, CreateEnrollmentDto, UpdateEnrollmentDto, CreateExamScoreDto, UpdateExamScoreDto, CreateLearningRecordDto, UpdateLearningRecordDto, UpsertResultDto, } from './dto/archive.dto'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { OperationLogsService } from '../operation-logs/operation-logs.service'; import { extractRequestInfo } from '../common/request-utils'; import { RequirePermission } from '../auth/decorators/permission.decorator'; interface AuthenticatedRequest extends ExpressRequest { user?: { id: number; username?: string }; } @UseGuards(JwtAuthGuard) @Controller('archive') export class ArchiveController { constructor( private readonly archiveService: ArchiveService, private readonly logService: OperationLogsService, private readonly reportService: ArchiveReportService, ) {} @Get(':studentId') @RequirePermission('student:view') async getProfile(@Param('studentId', ParseIntPipe) studentId: number, @Request() req: AuthenticatedRequest) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.getProfile(studentId); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '查看档案', targetId: studentId, targetType: 'archive', ipAddress, userAgent, }); return result; } @Put(':studentId/profile') @RequirePermission('student:edit') async upsertProfile( @Param('studentId', ParseIntPipe) studentId: number, @Body() dto: UpsertProfileDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.upsertProfile(studentId, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '更新档案信息', targetId: studentId, targetType: 'student_profile', detail: JSON.stringify(dto), ipAddress, userAgent, }); return result; } @Post(':studentId/enrollments') @RequirePermission('student:edit') async addEnrollment( @Param('studentId', ParseIntPipe) studentId: number, @Body() dto: CreateEnrollmentDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.addEnrollment(studentId, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '添加报名记录', targetId: result.id, targetType: 'student_enrollment', detail: `${dto.courseCategory} - ${dto.classType}`, ipAddress, userAgent, }); return result; } @Put('enrollments/:id') @RequirePermission('student:edit') async updateEnrollment( @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateEnrollmentDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.updateEnrollment(id, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '编辑报名记录', targetId: id, targetType: 'student_enrollment', detail: JSON.stringify(dto), ipAddress, userAgent, }); return result; } @Delete('enrollments/:id') @RequirePermission('student:edit') async deleteEnrollment(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.deleteEnrollment(id); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '归档报名记录', targetId: id, targetType: 'student_enrollment', ipAddress, userAgent, }); return result; } @Post(':studentId/exam-scores') @RequirePermission('student:edit') async addExamScore( @Param('studentId', ParseIntPipe) studentId: number, @Body() dto: CreateExamScoreDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.addExamScore(studentId, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '添加考试成绩', targetId: result.id, targetType: 'exam_score', detail: `${dto.examType} - ${dto.subject}: ${dto.score}`, ipAddress, userAgent, }); return result; } @Put('exam-scores/:id') @RequirePermission('student:edit') async updateExamScore( @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateExamScoreDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.updateExamScore(id, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '编辑考试成绩', targetId: id, targetType: 'exam_score', detail: JSON.stringify(dto), ipAddress, userAgent, }); return result; } @Delete('exam-scores/:id') @RequirePermission('student:edit') async deleteExamScore(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.deleteExamScore(id); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '归档考试成绩', targetId: id, targetType: 'exam_score', ipAddress, userAgent, }); return result; } @Post(':studentId/learning-records') @RequirePermission('student:edit') async addLearningRecord( @Param('studentId', ParseIntPipe) studentId: number, @Body() dto: CreateLearningRecordDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.addLearningRecord(studentId, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '添加学习记录', targetId: result.id, targetType: 'learning_record', detail: `${dto.recordType}: ${dto.content.substring(0, 50)}`, ipAddress, userAgent, }); return result; } @Put('learning-records/:id') @RequirePermission('student:edit') async updateLearningRecord( @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateLearningRecordDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.updateLearningRecord(id, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '编辑学习记录', targetId: id, targetType: 'learning_record', detail: JSON.stringify(dto), ipAddress, userAgent, }); return result; } @Delete('learning-records/:id') @RequirePermission('student:edit') async deleteLearningRecord(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.deleteLearningRecord(id); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '归档学习记录', targetId: id, targetType: 'learning_record', ipAddress, userAgent, }); return result; } @Put(':studentId/result') @RequirePermission('student:edit') async upsertResult( @Param('studentId', ParseIntPipe) studentId: number, @Body() dto: UpsertResultDto, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.upsertResult(studentId, dto); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '更新录取结果', targetId: studentId, targetType: 'result_archive', detail: JSON.stringify(dto), ipAddress, userAgent, }); return result; } @Post(':studentId/attachments') @RequirePermission('student:edit') @UseInterceptors(FileInterceptor('file')) async uploadAttachment( @Param('studentId', ParseIntPipe) studentId: number, @UploadedFile() file: Express.Multer.File, @Body('category') category: string, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.addAttachment(studentId, file, category || 'other'); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '上传附件', targetId: result.id, targetType: 'archive_attachment', detail: `${file.originalname} (${category || 'other'})`, ipAddress, userAgent, }); return result; } @Get(':studentId/attachments/:id') @RequirePermission('student:view') async downloadAttachment( @Param('studentId', ParseIntPipe) studentId: number, @Param('id', ParseIntPipe) id: number, @Res() res: Response, ) { const { fullPath, fileName, mimeType } = await this.archiveService.getAttachmentFile( studentId, id, ); res.setHeader('Content-Type', mimeType); res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(fileName)}"`); const stream = fs.createReadStream(fullPath); stream.pipe(res); } @Delete('attachments/:id') @RequirePermission('student:edit') async deleteAttachment(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) { const { ipAddress, userAgent } = extractRequestInfo(req); const result = await this.archiveService.deleteAttachment(id); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生档案', action: '归档附件', targetId: id, targetType: 'archive_attachment', ipAddress, userAgent, }); return result; } @Get(':studentId/report-html') @RequirePermission('student:view') async generateReportHtml( @Param('studentId', ParseIntPipe) studentId: number, @Request() req: AuthenticatedRequest, ) { const { ipAddress, userAgent } = extractRequestInfo(req); await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: 'archive', action: 'generate_report_html', targetId: studentId, targetType: 'student', ipAddress, userAgent, }); const html = await this.reportService.generateReportHtml(studentId); return { html }; } }