import { UseGuards } from '@nestjs/common'; import dayjs from '../common/dayjs'; import { AttendanceService } from './attendance.service'; import { AttendanceImportService } from './attendance-import.service'; import { OperationLogsService } from '../operation-logs/operation-logs.service'; import { AuthorizationService, CaslAction, SubjectName } from '../authorization'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; /** Minimal request user shape for type safety */ export interface RequestUser { id: number; username: string; permissions: string[]; isSuperAdmin: boolean; roles: string[]; } /** SSE event shape for @Sse() decorator */ export interface SseEvent { data: string | Record; id?: string; type?: string; retry?: number; } @UseGuards(JwtAuthGuard) export abstract class AttendanceControllerBase { constructor( protected readonly service: AttendanceService, protected readonly importService: AttendanceImportService, protected readonly logService: OperationLogsService, protected readonly authz: AuthorizationService, ) {} protected getTodayDateOnly(): string { return dayjs().utcOffset(8).format('YYYY-MM-DD'); } protected canManageAllAttendance(req: { user: RequestUser }): boolean { return ( this.authz.can(req, CaslAction.Manage, SubjectName.Attendance) || // Legacy: class:edit grants broad attendance access for teacher scoping this.authz.can(req, CaslAction.Update, SubjectName.Class) ); } protected getAccessibleClassIds(req: { user: RequestUser }) { return this.service.getAccessibleClassIds(req.user.id, this.canManageAllAttendance(req)); } protected assertClassAccess(req: { user: RequestUser }, classId: number) { return this.service.assertClassAccess(req.user.id, classId, this.canManageAllAttendance(req)); } }