forked from wangziqi/gongxue-base
feat: integrate CampusScope.filter() into all business services (12 services + 12 modules)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user