fix permissions and teacher attendance workflows

This commit is contained in:
2026-07-10 20:40:52 +08:00
parent 247879f276
commit 8ed1682b90
95 changed files with 4745 additions and 1237 deletions

View File

@@ -31,6 +31,16 @@ import { NotificationsService } from '../notifications/notifications.service';
import { NotificationType } from '../entities/notification.entity';
import * as ExcelJS from 'exceljs';
interface RequestUser {
id: number;
permissions?: string[];
isSuperAdmin?: boolean;
}
interface AuthenticatedRequest {
user: RequestUser;
}
@UseGuards(JwtAuthGuard)
@Controller('classes')
export class ClassesController {
@@ -40,30 +50,48 @@ export class ClassesController {
private readonly notificationsService: NotificationsService,
) {}
private assertReadAccess(req: AuthenticatedRequest, classId: number) {
const canManageAll =
req.user.isSuperAdmin === true || req.user.permissions?.includes('class:edit') === true;
return this.service.assertClassAccess(req.user.id, classId, canManageAll);
}
@Get()
@RequirePermission('class:view')
findAll(@Query() query: QueryClassDto) {
return this.service.findAll(query);
async findAll(@Query() query: QueryClassDto, @Request() req: AuthenticatedRequest) {
const classIds = await this.service.getAccessibleClassIds(
req.user.id,
req.user.isSuperAdmin === true || req.user.permissions?.includes('class:edit') === true,
);
return this.service.findAll(query, classIds);
}
@Get(':id')
@RequirePermission('class:view')
findOne(@Param('id') id: string) {
async findOne(@Param('id') id: string, @Request() req: AuthenticatedRequest) {
await this.assertReadAccess(req, +id);
return this.service.findOne(+id);
}
@Get(':id/schedule')
@RequirePermission('class:view')
getSchedule(@Param('id') id: string, @Query() query: QueryClassScheduleDto) {
async getSchedule(
@Param('id') id: string,
@Query() query: QueryClassScheduleDto,
@Request() req: AuthenticatedRequest,
) {
await this.assertReadAccess(req, +id);
return this.service.getSchedule(+id, query);
}
@Get(':id/attendance-summary')
@RequirePermission('class:view')
getAttendanceSummary(
async getAttendanceSummary(
@Param('id') id: string,
@Query() query: QueryClassAttendanceSummaryDto,
@Request() req: AuthenticatedRequest,
) {
await this.assertReadAccess(req, +id);
return this.service.getAttendanceSummary(+id, query);
}
@@ -86,14 +114,10 @@ export class ClassesController {
return result;
}
/** 批量导入学生到班级通过钉钉用户ID */
@Post(':id/students/import')
@RequirePermission('class:edit')
async batchImportStudents(
@Param('id') id: string,
@Body() dto: BatchImportStudentsDto,
) {
async batchImportStudents(@Param('id') id: string, @Body() dto: BatchImportStudentsDto) {
return this.service.batchImportStudents(+id, dto.users);
}
@@ -113,11 +137,7 @@ export class ClassesController {
@Put(':id')
@RequirePermission('class:edit')
async update(
@Param('id') id: string,
@Body() dto: UpdateClassDto,
@Request() req: any,
) {
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({
@@ -154,7 +174,12 @@ export class ClassesController {
@Get(':id/roster/export')
@RequirePermission('class:view')
async exportRoster(@Param('id') id: string, @Res() res: Response) {
async exportRoster(
@Param('id') id: string,
@Res() res: Response,
@Request() req: AuthenticatedRequest,
) {
await this.assertReadAccess(req, +id);
const classEntity = await this.service.findOne(+id);
const classStudents = await this.service.getStudents(+id);
@@ -192,17 +217,14 @@ export class ClassesController {
@Get(':id/students')
@RequirePermission('class:view')
getStudents(@Param('id') id: string) {
async getStudents(@Param('id') id: string, @Request() req: AuthenticatedRequest) {
await this.assertReadAccess(req, +id);
return this.service.getStudents(+id);
}
@Post(':id/students')
@RequirePermission('class:edit')
async addStudents(
@Param('id') id: string,
@Body() dto: AddStudentsDto,
@Request() req: any,
) {
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({
@@ -255,17 +277,14 @@ export class ClassesController {
@Get(':id/teachers')
@RequirePermission('class:view')
getTeachers(@Param('id') id: string) {
async getTeachers(@Param('id') id: string, @Request() req: AuthenticatedRequest) {
await this.assertReadAccess(req, +id);
return this.service.getTeachers(+id);
}
@Post(':id/teachers')
@RequirePermission('class:edit')
async addTeacher(
@Param('id') id: string,
@Body() dto: AddTeacherDto,
@Request() req: any,
) {
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({
@@ -290,6 +309,29 @@ export class ClassesController {
return result;
}
@Delete(':id/teacher-assignments/:assignmentId')
@RequirePermission('class:edit')
async removeTeacherAssignment(
@Param('id') id: string,
@Param('assignmentId') assignmentId: string,
@Request() req: any,
) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.removeTeacherAssignment(+id, +assignmentId);
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '班级管理',
action: '移除教师角色',
targetId: +id,
targetType: 'class',
detail: `移除教师分配${assignmentId}`,
ipAddress,
userAgent,
});
return result;
}
@Delete(':id/teachers/:userId')
@RequirePermission('class:edit')
async removeTeacher(