feat: 考勤模块重构与钉钉考勤同步
This commit is contained in:
118
apps/server/src/attendance/attendance-calendar.service.ts
Normal file
118
apps/server/src/attendance/attendance-calendar.service.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Between } from 'typeorm';
|
||||
import { AttendanceRecord, ClassSchedule } from '../entities';
|
||||
import type { AttendanceCalendarQueryDto } from './dto/attendance.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AttendanceCalendarService {
|
||||
constructor(
|
||||
@InjectRepository(AttendanceRecord)
|
||||
private attendanceRepo: Repository<AttendanceRecord>,
|
||||
@InjectRepository(ClassSchedule)
|
||||
private scheduleRepo: Repository<ClassSchedule>,
|
||||
) {}
|
||||
|
||||
async getCalendar(query: AttendanceCalendarQueryDto) {
|
||||
const { classId, weekStart } = query;
|
||||
|
||||
if (!weekStart) {
|
||||
// Default to the Monday of the current week
|
||||
const now = new Date();
|
||||
const day = now.getDay();
|
||||
const diff = day === 0 ? -6 : 1 - day; // Monday offset
|
||||
const monday = new Date(now);
|
||||
monday.setDate(now.getDate() + diff);
|
||||
const mondayStr = monday.toISOString().slice(0, 10);
|
||||
|
||||
return this.buildCalendar(classId, mondayStr);
|
||||
}
|
||||
|
||||
return this.buildCalendar(classId, weekStart);
|
||||
}
|
||||
|
||||
private getWeekDayForDate(date: string): number {
|
||||
const day = new Date(`${date}T00:00:00+08:00`).getUTCDay();
|
||||
return day === 0 ? 7 : day;
|
||||
}
|
||||
|
||||
async getScheduleOptionsForAttendance(classId: number, date: string) {
|
||||
const weekDay = this.getWeekDayForDate(date);
|
||||
const { entities, raw } = await this.scheduleRepo
|
||||
.createQueryBuilder('cs')
|
||||
.leftJoin('cs.teacher', 'teacher')
|
||||
.addSelect('cs.id', 'scheduleIdForTeacherMap')
|
||||
.addSelect('teacher.username', 'teacherUsername')
|
||||
.addSelect('teacher.name', 'teacherName')
|
||||
.where('cs.classId = :classId', { classId })
|
||||
.andWhere('cs.weekDay = :weekDay', { weekDay })
|
||||
.andWhere('cs.startDate <= :date', { date })
|
||||
.andWhere('cs.endDate >= :date', { date })
|
||||
.andWhere('cs.status = :status', { status: 'active' })
|
||||
.orderBy('cs.startTime', 'ASC')
|
||||
.addOrderBy('cs.subject', 'ASC')
|
||||
.getRawAndEntities();
|
||||
|
||||
const teacherByScheduleId = new Map(
|
||||
raw.map((row: { scheduleIdForTeacherMap: string; teacherName: string | null; teacherUsername: string | null }) => [
|
||||
Number(row.scheduleIdForTeacherMap),
|
||||
{
|
||||
teacherName: row.teacherName || null,
|
||||
teacherUsername: row.teacherUsername || null,
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
return entities.map((schedule) => {
|
||||
const teacher = teacherByScheduleId.get(schedule.id) ?? {
|
||||
teacherName: null,
|
||||
teacherUsername: null,
|
||||
};
|
||||
return { ...schedule, ...teacher };
|
||||
});
|
||||
}
|
||||
|
||||
private async buildCalendar(classId: number, weekStart: string) {
|
||||
// Compute weekEnd (Sunday = weekStart + 6 days)
|
||||
const start = new Date(weekStart);
|
||||
const end = new Date(start);
|
||||
end.setDate(start.getDate() + 6);
|
||||
const endStr = end.toISOString().slice(0, 10);
|
||||
|
||||
const records = await this.attendanceRepo.find({
|
||||
where: {
|
||||
classId,
|
||||
attendanceDate: Between(weekStart, endStr),
|
||||
},
|
||||
relations: ['student'],
|
||||
order: { attendanceDate: 'ASC', session: 'ASC' },
|
||||
});
|
||||
|
||||
// Group by studentId
|
||||
const studentMap = new Map<
|
||||
number,
|
||||
{
|
||||
studentId: number;
|
||||
studentName: string;
|
||||
days: Array<{ date: string; session: string; status: string }>;
|
||||
}
|
||||
>();
|
||||
|
||||
for (const r of records) {
|
||||
if (!studentMap.has(r.studentId)) {
|
||||
studentMap.set(r.studentId, {
|
||||
studentId: r.studentId,
|
||||
studentName: r.student?.name ?? `Student#${r.studentId}`,
|
||||
days: [],
|
||||
});
|
||||
}
|
||||
studentMap.get(r.studentId)!.days.push({
|
||||
date: r.attendanceDate,
|
||||
session: r.session,
|
||||
status: r.status,
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(studentMap.values());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user