feat: integrate CampusScope.filter() into all business services (12 services + 12 modules)

This commit is contained in:
2026-07-05 23:58:51 +08:00
parent eeae06fe30
commit cd6364268d
40 changed files with 949 additions and 172 deletions

View File

@@ -18,6 +18,7 @@ import {
QueryAttendanceRecordsDto,
QueryDingRawDto,
MatchDingRecordDto,
AttendanceReportQueryDto,
} from './dto/attendance.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
@@ -56,7 +57,7 @@ export class AttendanceController {
// ── Export attendance records ──
@Get('attendance-records/export')
@RequirePermission('attendance:view')
@RequirePermission('attendance:export')
async exportRecords(
@Query() query: QueryAttendanceRecordsDto,
@Res() res: Response,
@@ -167,4 +168,83 @@ export class AttendanceController {
});
return result;
}
}
// ── Attendance class-based report export ──
@Get('attendance-records/report')
@RequirePermission('attendance:export')
async exportReport(
@Query() query: AttendanceReportQueryDto,
@Res() res: Response,
@Request() req: any,
) {
const reportData = await this.service.getReport(query);
const workbook = new ExcelJS.Workbook();
const ws = workbook.addWorksheet('考勤统计报表');
ws.columns = [
{ header: '班级名称', key: 'className', width: 30 },
{ header: '总记录数', key: 'total', width: 12 },
{ header: '出勤', key: 'present', width: 10 },
{ header: '出勤率', key: 'presentRate', width: 10 },
{ header: '缺勤', key: 'absent', width: 10 },
{ header: '缺勤率', key: 'absentRate', width: 10 },
{ header: '迟到', key: 'late', width: 10 },
{ header: '迟到率', key: 'lateRate', width: 10 },
{ header: '请假', key: 'leave', width: 10 },
{ header: '请假率', key: 'leaveRate', width: 10 },
];
ws.getRow(1).font = { bold: true };
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
for (const row of reportData) {
ws.addRow({
className: row.className,
total: row.total,
present: row.present,
presentRate: `${row.presentRate}%`,
absent: row.absent,
absentRate: `${row.absentRate}%`,
late: row.late,
lateRate: `${row.lateRate}%`,
leave: row.leave,
leaveRate: `${row.leaveRate}%`,
});
}
// Audit log
const { ipAddress, userAgent } = extractRequestInfo(req);
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '考勤管理',
action: '导出考勤报表',
detail: `classId=${query.classId || '全部'} ${query.dateFrom || ''}~${query.dateTo || ''}`,
ipAddress,
userAgent,
});
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader('Content-Disposition', 'attachment; filename=attendance-report.xlsx');
await workbook.xlsx.write(res);
res.end();
}
// ── Abnormal attendance alerts ──
@Get('attendance-records/alerts')
@RequirePermission('attendance:view')
getAlerts(
@Query('days') days?: string,
@Query('threshold') threshold?: string,
) {
return this.service.getAlerts(
days ? +days : 14,
threshold ? +threshold : 3,
);
}
@Post('ding-attendance-raw/auto-match')
@RequirePermission('attendance:edit')
async autoMatch() {
return this.service.autoMatchDingRecords();
}
}