feat: 重构各业务模块管理页面与服务
This commit is contained in:
@@ -12,7 +12,6 @@ import {
|
||||
Res,
|
||||
UseInterceptors,
|
||||
UploadedFile,
|
||||
Inject,
|
||||
ParseIntPipe,
|
||||
UsePipes,
|
||||
ValidationPipe,
|
||||
@@ -20,17 +19,20 @@ import {
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Organization } from '../entities/organization.entity';
|
||||
import { ClassTeacher } from '../entities/class-teacher.entity';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import type { Response } from 'express';
|
||||
import { StudentsService } from './students.service';
|
||||
import { CreateStudentDto, QueryStudentDto, UpdateStudentDto } from './dto/student.dto';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { OperationLogsService } from '../operation-logs/operation-logs.service';
|
||||
import { extractRequestInfo } from '../common/request-utils';
|
||||
import { logAudit } from '../common/with-audit-log';
|
||||
import { RequirePermission } from '../auth/decorators/permission.decorator';
|
||||
import { AuthorizationService, CaslAction, SubjectName } from '../authorization';
|
||||
import type { AuthenticatedUser } from '../authorization';
|
||||
import {
|
||||
AuthorizationService,
|
||||
CaslAction,
|
||||
SubjectName,
|
||||
type AuthenticatedUser,
|
||||
} from '../authorization';
|
||||
import * as ExcelJS from 'exceljs';
|
||||
import {
|
||||
createStudentImportTemplateWorkbook,
|
||||
@@ -79,27 +81,17 @@ export class StudentsController {
|
||||
|
||||
@Get()
|
||||
@RequirePermission('student:view')
|
||||
async findAll(
|
||||
@Query() query: QueryStudentDto,
|
||||
@Request() req: AuthenticatedRequest,
|
||||
) {
|
||||
async findAll(@Query() query: QueryStudentDto, @Request() req: AuthenticatedRequest) {
|
||||
const classIds = await this.service.getAccessibleClassIds(
|
||||
req.user.id,
|
||||
this.canManageAllStudents(req),
|
||||
);
|
||||
return this.service.findAll(
|
||||
query,
|
||||
classIds,
|
||||
);
|
||||
return this.service.findAll(query, classIds);
|
||||
}
|
||||
|
||||
@Get('export')
|
||||
@RequirePermission('student:export')
|
||||
async exportExcel(
|
||||
@Query() query: QueryStudentDto,
|
||||
@Res() res?: Response,
|
||||
@Request() req?: any,
|
||||
) {
|
||||
async exportExcel(@Query() query: QueryStudentDto, @Res() res?: Response, @Request() req?: any) {
|
||||
const classIds = await this.service.getAccessibleClassIds(
|
||||
req.user.id,
|
||||
this.canManageAllStudents(req),
|
||||
@@ -142,15 +134,8 @@ export class StudentsController {
|
||||
admittedMajor: result?.admittedMajor || '',
|
||||
});
|
||||
}
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '导出学生',
|
||||
detail: `导出 ${students.length} 名学生`,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '导出学生', detail: `导出 ${students.length} 名学生`,
|
||||
});
|
||||
res!.setHeader(
|
||||
'Content-Type',
|
||||
@@ -183,18 +168,9 @@ export class StudentsController {
|
||||
@Post()
|
||||
@RequirePermission('student:create')
|
||||
async create(@Body() dto: CreateStudentDto, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.create(dto);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '新增学生',
|
||||
targetId: result.id,
|
||||
targetType: 'student',
|
||||
detail: `姓名: ${dto.name}, 电话: ${dto.phone || '无'}, 学号: ${dto.idNumber || '无'}`,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '新增学生', targetId: result.id, targetType: 'student', detail: `姓名: ${dto.name}, 电话: ${dto.phone || '无'}, 学号: ${dto.idNumber || '无'}`,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -203,35 +179,23 @@ export class StudentsController {
|
||||
@RequirePermission('student:edit')
|
||||
@UsePipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true }))
|
||||
async batchRestore(@Body() dto: BatchIdsDto, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.batchRestore(dto.ids);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '批量恢复学生',
|
||||
detail: `IDs: ${dto.ids.join(',')}`,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '批量恢复学生', detail: `IDs: ${dto.ids.join(',')}`,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@RequirePermission('student:edit')
|
||||
async update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateStudentDto, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
async update(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() dto: UpdateStudentDto,
|
||||
@Request() req: any,
|
||||
) {
|
||||
const result = await this.service.update(id, dto);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '编辑学生',
|
||||
targetId: id,
|
||||
targetType: 'student',
|
||||
detail: JSON.stringify(dto),
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '编辑学生', targetId: id, targetType: 'student', detail: JSON.stringify(dto),
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -239,17 +203,9 @@ export class StudentsController {
|
||||
@Delete(':id')
|
||||
@RequirePermission('student:delete')
|
||||
async remove(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.remove(id);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '归档学生',
|
||||
targetId: id,
|
||||
targetType: 'student',
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '归档学生', targetId: id, targetType: 'student',
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -257,16 +213,29 @@ export class StudentsController {
|
||||
@Post('batch-delete')
|
||||
@RequirePermission('student:delete')
|
||||
async batchRemove(@Body() body: { ids: number[] }, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.batchRemove(body.ids || []);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '批量归档学生',
|
||||
detail: `IDs: ${(body.ids || []).join(',')}`,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '批量归档学生', detail: `IDs: ${(body.ids || []).join(',')}`,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Delete(':id/permanent')
|
||||
@RequirePermission('student:purge')
|
||||
async purge(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
|
||||
const result = await this.service.purge(id);
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '永久删除学生', targetId: id, targetType: 'student', detail: '物理删除,不可恢复',
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Post('batch-permanent-delete')
|
||||
@RequirePermission('student:purge')
|
||||
async batchPurge(@Body() body: { ids: number[] }, @Request() req: any) {
|
||||
const result = await this.service.batchPurge(body.ids || []);
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '批量永久删除学生', detail: `IDs: ${(body.ids || []).join(',')}`,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -274,17 +243,9 @@ export class StudentsController {
|
||||
@Put(':id/restore')
|
||||
@RequirePermission('student:edit')
|
||||
async restore(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.restore(id);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '恢复学生',
|
||||
targetId: id,
|
||||
targetType: 'student',
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '恢复学生', targetId: id, targetType: 'student',
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -293,9 +254,8 @@ export class StudentsController {
|
||||
@RequirePermission('student:import')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async importExcel(@UploadedFile() file: Express.Multer.File, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(file.buffer as any);
|
||||
await workbook.xlsx.load(file.buffer.buffer as ArrayBuffer);
|
||||
const importData = parseStudentImportWorkbook(workbook);
|
||||
// Resolve organization names to IDs
|
||||
for (const row of importData.students) {
|
||||
@@ -309,14 +269,8 @@ export class StudentsController {
|
||||
}
|
||||
}
|
||||
const result = await this.service.batchImport(importData);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '导入学生',
|
||||
detail: result.message,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '导入学生', detail: result.message,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -325,9 +279,8 @@ export class StudentsController {
|
||||
@RequirePermission('student:import')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async matchImport(@UploadedFile() file: Express.Multer.File, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(file.buffer as unknown as ArrayBuffer);
|
||||
await workbook.xlsx.load(file.buffer.buffer as ArrayBuffer);
|
||||
const importData = parseStudentImportWorkbook(workbook);
|
||||
// Resolve organization names to IDs
|
||||
for (const row of importData.students) {
|
||||
@@ -339,14 +292,8 @@ export class StudentsController {
|
||||
}
|
||||
}
|
||||
const result = await this.service.matchImport(importData);
|
||||
await this.logService.log({
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '更新已有学生资料',
|
||||
detail: result.message,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
await logAudit(this.logService, req, {
|
||||
module: '学生管理', action: '更新已有学生资料', detail: result.message,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user