forked from wangziqi/gongxue-base
feat: add attendance device SN classroom bindings
This commit is contained in:
@@ -4,6 +4,7 @@ import { Repository, In, Between, LessThanOrEqual, MoreThanOrEqual, DataSource }
|
||||
import {
|
||||
AttendanceRecord,
|
||||
AttendanceSession,
|
||||
AttendanceDevice,
|
||||
DingAttendanceRaw,
|
||||
Class,
|
||||
Student,
|
||||
@@ -66,11 +67,71 @@ export class AttendanceService {
|
||||
private classTeacherRepo: Repository<ClassTeacher>,
|
||||
@InjectRepository(AttendanceSession)
|
||||
private attendanceSessionRepo: Repository<AttendanceSession>,
|
||||
@InjectRepository(AttendanceDevice)
|
||||
private attendanceDeviceRepo: Repository<AttendanceDevice>,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
private sessionMutex = new SessionMutex();
|
||||
|
||||
private formatDeviceDetail(device: AttendanceDevice): string {
|
||||
const classroomName = device.classroom?.name;
|
||||
return classroomName ? `${device.deviceName} · ${classroomName}` : device.deviceName;
|
||||
}
|
||||
|
||||
private async attachAttendanceDeviceMappings<T extends AttendanceRecord>(
|
||||
records: T[],
|
||||
classroomId?: number | null,
|
||||
): Promise<T[]> {
|
||||
if (records.length === 0) return records;
|
||||
const sns = [...new Set(records.map((record) => record.punchDeviceId?.trim()).filter(Boolean) as string[])];
|
||||
const devicesBySn = new Map<string, AttendanceDevice>();
|
||||
if (sns.length > 0) {
|
||||
const devices = await this.attendanceDeviceRepo.find({
|
||||
where: { deviceSn: In(sns), status: 'active' },
|
||||
relations: ['classroom'],
|
||||
});
|
||||
for (const device of devices) devicesBySn.set(device.deviceSn, device);
|
||||
}
|
||||
|
||||
const classroomIds = [...new Set([
|
||||
...records.map((record) => record.classId).filter((id): id is number => id != null),
|
||||
...(classroomId != null ? [classroomId] : []),
|
||||
])];
|
||||
const devicesByClassroom = new Map<number, AttendanceDevice>();
|
||||
if (classroomIds.length > 0) {
|
||||
const devices = await this.attendanceDeviceRepo.find({
|
||||
where: { classroomId: In(classroomIds), status: 'active' },
|
||||
relations: ['classroom'],
|
||||
order: { id: 'ASC' },
|
||||
});
|
||||
for (const device of devices) {
|
||||
if (!devicesByClassroom.has(device.classroomId)) devicesByClassroom.set(device.classroomId, device);
|
||||
}
|
||||
}
|
||||
|
||||
for (const record of records) {
|
||||
const sn = record.punchDeviceId?.trim();
|
||||
const mappedBySn = sn ? devicesBySn.get(sn) : undefined;
|
||||
if (mappedBySn) {
|
||||
record.punchDeviceName = this.formatDeviceDetail(mappedBySn);
|
||||
record.punchDeviceId = mappedBySn.deviceSn;
|
||||
continue;
|
||||
}
|
||||
const source = (record.punchSource || '').trim().toUpperCase();
|
||||
const isMachine = ['ATM', 'ATTENDANCE_MACHINE', 'MACHINE', 'DEVICE'].some(
|
||||
(value) => source === value || source.includes(value),
|
||||
);
|
||||
const fallbackClassroomId = record.classId ?? classroomId ?? undefined;
|
||||
const mappedByClassroom = fallbackClassroomId ? devicesByClassroom.get(fallbackClassroomId) : undefined;
|
||||
if (isMachine && mappedByClassroom && !record.punchDeviceName) {
|
||||
record.punchDeviceName = this.formatDeviceDetail(mappedByClassroom);
|
||||
record.punchDeviceId = record.punchDeviceId || mappedByClassroom.deviceSn;
|
||||
}
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
async getAccessibleClassIds(userId: number, canManageAll = false): Promise<number[] | undefined> {
|
||||
if (canManageAll) return undefined;
|
||||
const assignments = await this.classTeacherRepo.find({ where: { userId } });
|
||||
@@ -172,7 +233,7 @@ export class AttendanceService {
|
||||
order: { studentId: 'ASC' },
|
||||
})
|
||||
: [];
|
||||
return { schedule, session, records };
|
||||
return { schedule, session, records: await this.attachAttendanceDeviceMappings(records, schedule.classId) };
|
||||
}
|
||||
|
||||
private getLessonAttendanceWindow(
|
||||
@@ -299,7 +360,7 @@ export class AttendanceService {
|
||||
relations: ['student'],
|
||||
order: { studentId: 'ASC' },
|
||||
});
|
||||
return { schedule, session: existing, records };
|
||||
return { schedule, session: existing, records: await this.attachAttendanceDeviceMappings(records, schedule.classId) };
|
||||
}
|
||||
if (existing.status !== 'in_progress' && !(finalize && existing.status === 'settling')) {
|
||||
throw new BadRequestException('课程考勤正在结算');
|
||||
@@ -385,7 +446,7 @@ export class AttendanceService {
|
||||
existing.completedAt = new Date();
|
||||
await sessionRepo.save(existing);
|
||||
}
|
||||
return { schedule, session: existing, records: saved };
|
||||
return { schedule, session: existing, records: await this.attachAttendanceDeviceMappings(saved, schedule.classId) };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -428,7 +489,7 @@ export class AttendanceService {
|
||||
relations: ['student'],
|
||||
order: { studentId: 'ASC' },
|
||||
});
|
||||
return { schedule, session, records: existingRecords };
|
||||
return { schedule, session, records: await this.attachAttendanceDeviceMappings(existingRecords, schedule.classId) };
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
@@ -469,7 +530,7 @@ export class AttendanceService {
|
||||
session.completedAt = new Date();
|
||||
session = await sessionRepo.save(session);
|
||||
}
|
||||
return { schedule, session, records: saved };
|
||||
return { schedule, session, records: await this.attachAttendanceDeviceMappings(saved, schedule.classId) };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -516,7 +577,7 @@ export class AttendanceService {
|
||||
relations: ['student'],
|
||||
order: { studentId: 'ASC' },
|
||||
});
|
||||
return { session, records };
|
||||
return { session, records: await this.attachAttendanceDeviceMappings(records, session.classId) };
|
||||
}
|
||||
|
||||
const pendingRecords = await recordRepo.count({
|
||||
@@ -535,7 +596,7 @@ export class AttendanceService {
|
||||
relations: ['student'],
|
||||
order: { studentId: 'ASC' },
|
||||
});
|
||||
return { session: savedSession, records };
|
||||
return { session: savedSession, records: await this.attachAttendanceDeviceMappings(records, session.classId) };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user