273 lines
7.5 KiB
TypeScript
273 lines
7.5 KiB
TypeScript
import {
|
|
Controller,
|
|
Get,
|
|
Post,
|
|
Put,
|
|
Delete,
|
|
Body,
|
|
Param,
|
|
Query,
|
|
UseGuards,
|
|
Request,
|
|
Res,
|
|
} from '@nestjs/common';
|
|
import type { Response } from 'express';
|
|
import { ClassesService } from './classes.service';
|
|
import {
|
|
CreateClassDto,
|
|
UpdateClassDto,
|
|
QueryClassDto,
|
|
AddStudentsDto,
|
|
AddTeacherDto,
|
|
} from './dto/class.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';
|
|
import { NotificationsService } from '../notifications/notifications.service';
|
|
import { NotificationType } from '../entities/notification.entity';
|
|
import * as ExcelJS from 'exceljs';
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@Controller('classes')
|
|
export class ClassesController {
|
|
constructor(
|
|
private readonly service: ClassesService,
|
|
private readonly logService: OperationLogsService,
|
|
private readonly notificationsService: NotificationsService,
|
|
) {}
|
|
|
|
@Get()
|
|
@RequirePermission('class:view')
|
|
findAll(@Query() query: QueryClassDto) {
|
|
return this.service.findAll(query);
|
|
}
|
|
|
|
@Get(':id')
|
|
@RequirePermission('class:view')
|
|
findOne(@Param('id') id: string) {
|
|
return this.service.findOne(+id);
|
|
}
|
|
|
|
@Post()
|
|
@RequirePermission('class:create')
|
|
async create(@Body() dto: CreateClassDto, @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: 'class',
|
|
detail: `班级${result.code} ${result.name}`,
|
|
ipAddress,
|
|
userAgent,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Put(':id')
|
|
@RequirePermission('class:edit')
|
|
async update(
|
|
@Param('id') id: string,
|
|
@Body() dto: UpdateClassDto,
|
|
@Request() req: any,
|
|
) {
|
|
const { ipAddress, userAgent } = extractRequestInfo(req);
|
|
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: 'class',
|
|
detail: JSON.stringify(dto),
|
|
ipAddress,
|
|
userAgent,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Delete(':id')
|
|
@RequirePermission('class:delete')
|
|
async remove(@Param('id') id: string, @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: 'class',
|
|
ipAddress,
|
|
userAgent,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Get(':id/roster/export')
|
|
@RequirePermission('class:view')
|
|
async exportRoster(@Param('id') id: string, @Res() res: Response) {
|
|
const classEntity = await this.service.findOne(+id);
|
|
const classStudents = await this.service.getStudents(+id);
|
|
|
|
const workbook = new ExcelJS.Workbook();
|
|
const ws = workbook.addWorksheet('班级花名册');
|
|
ws.columns = [
|
|
{ header: '姓名', key: 'name', width: 15 },
|
|
{ header: '学号', key: 'studentNo', width: 15 },
|
|
{ header: '加入日期', key: 'joinDate', width: 15 },
|
|
{ header: '状态', key: 'status', width: 10 },
|
|
];
|
|
ws.getRow(1).font = { bold: true };
|
|
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
|
|
|
|
for (const cs of classStudents) {
|
|
ws.addRow({
|
|
name: cs.student?.name || '',
|
|
studentNo: cs.student?.idNumber || '',
|
|
joinDate: cs.joinDate || '',
|
|
status: cs.status || '',
|
|
});
|
|
}
|
|
|
|
res.setHeader(
|
|
'Content-Type',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
);
|
|
res.setHeader(
|
|
'Content-Disposition',
|
|
`attachment; filename=${encodeURIComponent(`班级花名册-${classEntity.name}`)}.xlsx`,
|
|
);
|
|
await workbook.xlsx.write(res);
|
|
res.end();
|
|
}
|
|
|
|
@Get(':id/students')
|
|
@RequirePermission('class:view')
|
|
getStudents(@Param('id') id: string) {
|
|
return this.service.getStudents(+id);
|
|
}
|
|
|
|
@Post(':id/students')
|
|
@RequirePermission('class:edit')
|
|
async addStudents(
|
|
@Param('id') id: string,
|
|
@Body() dto: AddStudentsDto,
|
|
@Request() req: any,
|
|
) {
|
|
const { ipAddress, userAgent } = extractRequestInfo(req);
|
|
const result = await this.service.addStudents(+id, dto.studentIds);
|
|
await this.logService.log({
|
|
userId: req.user?.id,
|
|
username: req.user?.username,
|
|
module: '班级管理',
|
|
action: '添加学生',
|
|
targetId: +id,
|
|
targetType: 'class',
|
|
detail: `新增${result.added}名学生`,
|
|
ipAddress,
|
|
userAgent,
|
|
});
|
|
try {
|
|
const cls = await this.service.findOne(+id);
|
|
if (cls.headTeacherId) {
|
|
void this.notificationsService.create({
|
|
recipientIds: [cls.headTeacherId],
|
|
type: NotificationType.CLASS_CHANGE,
|
|
title: '学员变动',
|
|
content: `班级新增${result.added}名学生`,
|
|
});
|
|
}
|
|
} catch {}
|
|
return result;
|
|
}
|
|
|
|
@Delete(':id/students/:studentId')
|
|
@RequirePermission('class:edit')
|
|
async removeStudent(
|
|
@Param('id') id: string,
|
|
@Param('studentId') studentId: string,
|
|
@Request() req: any,
|
|
) {
|
|
const { ipAddress, userAgent } = extractRequestInfo(req);
|
|
const result = await this.service.removeStudent(+id, +studentId);
|
|
await this.logService.log({
|
|
userId: req.user?.id,
|
|
username: req.user?.username,
|
|
module: '班级管理',
|
|
action: '移除学生',
|
|
targetId: +id,
|
|
targetType: 'class',
|
|
detail: `移除学生${studentId}`,
|
|
ipAddress,
|
|
userAgent,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Get(':id/teachers')
|
|
@RequirePermission('class:view')
|
|
getTeachers(@Param('id') id: string) {
|
|
return this.service.getTeachers(+id);
|
|
}
|
|
|
|
@Post(':id/teachers')
|
|
@RequirePermission('class:edit')
|
|
async addTeacher(
|
|
@Param('id') id: string,
|
|
@Body() dto: AddTeacherDto,
|
|
@Request() req: any,
|
|
) {
|
|
const { ipAddress, userAgent } = extractRequestInfo(req);
|
|
const result = await this.service.addTeacher(+id, dto);
|
|
await this.logService.log({
|
|
userId: req.user?.id,
|
|
username: req.user?.username,
|
|
module: '班级管理',
|
|
action: '添加教师',
|
|
targetId: +id,
|
|
targetType: 'class',
|
|
detail: `教师${dto.userId} 角色${dto.roleType}`,
|
|
ipAddress,
|
|
userAgent,
|
|
});
|
|
try {
|
|
void this.notificationsService.create({
|
|
recipientIds: [dto.userId],
|
|
type: NotificationType.CLASS_CHANGE,
|
|
title: '班级分配',
|
|
content: `您已被分配到班级担任${dto.roleType}角色`,
|
|
});
|
|
} catch {}
|
|
return result;
|
|
}
|
|
|
|
@Delete(':id/teachers/:userId')
|
|
@RequirePermission('class:edit')
|
|
async removeTeacher(
|
|
@Param('id') id: string,
|
|
@Param('userId') userId: string,
|
|
@Request() req: any,
|
|
) {
|
|
const { ipAddress, userAgent } = extractRequestInfo(req);
|
|
const result = await this.service.removeTeacher(+id, +userId);
|
|
await this.logService.log({
|
|
userId: req.user?.id,
|
|
username: req.user?.username,
|
|
module: '班级管理',
|
|
action: '移除教师',
|
|
targetId: +id,
|
|
targetType: 'class',
|
|
detail: `移除教师${userId}`,
|
|
ipAddress,
|
|
userAgent,
|
|
});
|
|
return result;
|
|
}
|
|
}
|