Files
gongxue-base/apps/server/src/archive/archive.controller.ts

262 lines
11 KiB
TypeScript

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 { withAuditLog } from '../common/with-audit-log';
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) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '查看档案', targetId: studentId, targetType: 'archive',
}), () => this.archiveService.getProfile(studentId));
}
@Put(':studentId/profile')
@RequirePermission('student:edit')
async upsertProfile(
@Param('studentId', ParseIntPipe) studentId: number,
@Body() dto: UpsertProfileDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '更新档案信息', targetId: studentId, targetType: 'student_profile', detail: JSON.stringify(dto),
}), () => this.archiveService.upsertProfile(studentId, dto));
}
@Post(':studentId/enrollments')
@RequirePermission('student:edit')
async addEnrollment(
@Param('studentId', ParseIntPipe) studentId: number,
@Body() dto: CreateEnrollmentDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (result) => ({
module: '学生档案', action: '添加报名记录', targetId: result.id, targetType: 'student_enrollment', detail: `${dto.courseCategory} - ${dto.classType}`,
}), () => this.archiveService.addEnrollment(studentId, dto));
}
@Put('enrollments/:id')
@RequirePermission('student:edit')
async updateEnrollment(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateEnrollmentDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '编辑报名记录', targetId: id, targetType: 'student_enrollment', detail: JSON.stringify(dto),
}), () => this.archiveService.updateEnrollment(id, dto));
}
@Delete('enrollments/:id')
@RequirePermission('student:edit')
async deleteEnrollment(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '归档报名记录', targetId: id, targetType: 'student_enrollment',
}), () => this.archiveService.deleteEnrollment(id));
}
@Delete('enrollments/:id/permanent')
@RequirePermission('archive:purge')
async purgeEnrollment(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '永久删除报名记录', targetId: id, targetType: 'student_enrollment', detail: '物理删除,不可恢复',
}), () => this.archiveService.purgeEnrollment(id));
}
@Post(':studentId/exam-scores')
@RequirePermission('student:edit')
async addExamScore(
@Param('studentId', ParseIntPipe) studentId: number,
@Body() dto: CreateExamScoreDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (result) => ({
module: '学生档案', action: '添加考试成绩', targetId: result.id, targetType: 'exam_score', detail: `${dto.examType} - ${dto.subject}: ${dto.score}`,
}), () => this.archiveService.addExamScore(studentId, dto));
}
@Put('exam-scores/:id')
@RequirePermission('student:edit')
async updateExamScore(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateExamScoreDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '编辑考试成绩', targetId: id, targetType: 'exam_score', detail: JSON.stringify(dto),
}), () => this.archiveService.updateExamScore(id, dto));
}
@Delete('exam-scores/:id')
@RequirePermission('student:edit')
async deleteExamScore(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '归档考试成绩', targetId: id, targetType: 'exam_score',
}), () => this.archiveService.deleteExamScore(id));
}
@Delete('exam-scores/:id/permanent')
@RequirePermission('archive:purge')
async purgeExamScore(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '永久删除考试成绩', targetId: id, targetType: 'exam_score', detail: '物理删除,不可恢复',
}), () => this.archiveService.purgeExamScore(id));
}
@Post(':studentId/learning-records')
@RequirePermission('student:edit')
async addLearningRecord(
@Param('studentId', ParseIntPipe) studentId: number,
@Body() dto: CreateLearningRecordDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (result) => ({
module: '学生档案', action: '添加学习记录', targetId: result.id, targetType: 'learning_record', detail: `${dto.recordType}: ${dto.content.substring(0, 50)}`,
}), () => this.archiveService.addLearningRecord(studentId, dto));
}
@Put('learning-records/:id')
@RequirePermission('student:edit')
async updateLearningRecord(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateLearningRecordDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '编辑学习记录', targetId: id, targetType: 'learning_record', detail: JSON.stringify(dto),
}), () => this.archiveService.updateLearningRecord(id, dto));
}
@Delete('learning-records/:id')
@RequirePermission('student:edit')
async deleteLearningRecord(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '归档学习记录', targetId: id, targetType: 'learning_record',
}), () => this.archiveService.deleteLearningRecord(id));
}
@Delete('learning-records/:id/permanent')
@RequirePermission('archive:purge')
async purgeLearningRecord(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '永久删除学习记录', targetId: id, targetType: 'learning_record', detail: '物理删除,不可恢复',
}), () => this.archiveService.purgeLearningRecord(id));
}
@Put(':studentId/result')
@RequirePermission('student:edit')
async upsertResult(
@Param('studentId', ParseIntPipe) studentId: number,
@Body() dto: UpsertResultDto,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '更新录取结果', targetId: studentId, targetType: 'result_archive', detail: JSON.stringify(dto),
}), () => this.archiveService.upsertResult(studentId, dto));
}
@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,
) {
return withAuditLog(this.logService, req, (result) => ({
module: '学生档案', action: '上传附件', targetId: result.id, targetType: 'archive_attachment', detail: `${file.originalname} (${category || 'other'})`,
}), () => this.archiveService.addAttachment(studentId, file, category || 'other'));
}
@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) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '归档附件', targetId: id, targetType: 'archive_attachment',
}), () => this.archiveService.deleteAttachment(id));
}
@Delete('attachments/:id/permanent')
@RequirePermission('archive:purge')
async purgeAttachment(@Param('id', ParseIntPipe) id: number, @Request() req: AuthenticatedRequest) {
return withAuditLog(this.logService, req, (_result) => ({
module: '学生档案', action: '永久删除附件', targetId: id, targetType: 'archive_attachment', detail: '物理删除,不可恢复',
}), () => this.archiveService.purgeAttachment(id));
}
@Get(':studentId/report-html')
@RequirePermission('student:view')
async generateReportHtml(
@Param('studentId', ParseIntPipe) studentId: number,
@Request() req: AuthenticatedRequest,
) {
return withAuditLog(this.logService, req, () => ({
module: 'archive', action: 'generate_report_html', targetId: studentId, targetType: 'student',
}), async () => {
const html = await this.reportService.generateReportHtml(studentId);
return { html };
});
}
}