fix(server): 考勤自动结算支持空班级直接完成,补充自动匹配测试

This commit is contained in:
2026-08-08 15:05:42 +08:00
parent a0829f17ce
commit f96c1c26c3
3 changed files with 168 additions and 8 deletions

View File

@@ -1,4 +1,4 @@
import { Injectable, Logger } from '@nestjs/common';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { In, LessThan, LessThanOrEqual, MoreThanOrEqual, Repository } from 'typeorm';
@@ -150,13 +150,91 @@ export class AttendanceSettlementService {
true,
);
} catch (error: unknown) {
if (session) await this.sessionRepo.update({ id: session.id, status: 'settling' }, { status: 'in_progress' });
if (this.isNoActiveStudentsError(error)) {
try {
await this.finalizeEmptyLesson(schedule, lessonDate, session);
this.logger.log(`课程${schedule.id} ${lessonDate}班级无在读学生,已直接完成结算`);
} catch (finalizeError: unknown) {
this.logger.error(
`课程${schedule.id} ${lessonDate}空班级完成结算落库失败: ${finalizeError instanceof Error ? finalizeError.message : String(finalizeError)}`,
);
if (session) {
await this.sessionRepo.update(
{ id: session.id, status: 'settling' },
{ status: 'in_progress' },
);
}
}
return;
}
if (session) {
await this.sessionRepo.update({ id: session.id, status: 'settling' }, { status: 'in_progress' });
}
this.logger.error(
`课程${schedule.id} ${lessonDate}自动结算失败: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
private isNoActiveStudentsError(error: unknown): boolean {
return (
error instanceof BadRequestException &&
error.message.includes('该班级暂无在读学生')
);
}
private async finalizeEmptyLesson(
schedule: ClassSchedule,
lessonDate: string,
session?: AttendanceSession,
): Promise<void> {
const completedAt = new Date();
const completedBy = schedule.teacherId;
if (session) {
await this.sessionRepo.update(
{ id: session.id, status: 'settling' },
{ status: 'completed', completedBy, completedAt },
);
return;
}
const existing = await this.sessionRepo.findOne({
where: { scheduleId: schedule.id, lessonDate },
});
if (existing) {
if (existing.status !== 'completed') {
await this.sessionRepo.update(
{ id: existing.id },
{ status: 'completed', completedBy, completedAt },
);
}
return;
}
try {
await this.sessionRepo.save(
this.sessionRepo.create({
scheduleId: schedule.id,
classId: schedule.classId!,
lessonDate,
status: 'completed',
startedBy: completedBy,
startedAt: completedAt,
completedBy,
completedAt,
}),
);
} catch (error: unknown) {
const code = (error as Record<string, unknown>).code;
const errno = (error as Record<string, unknown>).errno;
if (code !== 'ER_DUP_ENTRY' && errno !== 1062) throw error;
await this.sessionRepo.update(
{ scheduleId: schedule.id, lessonDate, status: 'in_progress' },
{ status: 'completed', completedBy, completedAt },
);
}
}
private getEndedOccurrenceDate(
schedule: ClassSchedule,
clock: { weekDay: number; minutes: number },