feat: improve attendance scheduling and API validation
This commit is contained in:
@@ -462,3 +462,90 @@ describe('ScheduleSyncService — all shifts fail', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('ScheduleSyncService — multiple lessons per student per day', () => {
|
||||
it('combines daily lessons into one DingTalk shift with multiple sections', async () => {
|
||||
const scheduleRepo = {
|
||||
find: jest.fn().mockResolvedValue([
|
||||
{
|
||||
id: 2,
|
||||
classId: 10,
|
||||
classroomId: 1,
|
||||
weekDay: 1,
|
||||
startTime: '22:10',
|
||||
endTime: '23:10',
|
||||
startDate: '2026-07-13',
|
||||
endDate: '2026-07-13',
|
||||
status: 'active',
|
||||
} as ClassSchedule,
|
||||
{
|
||||
id: 1,
|
||||
classId: 10,
|
||||
classroomId: 2,
|
||||
weekDay: 1,
|
||||
startTime: '20:00',
|
||||
endTime: '21:00',
|
||||
startDate: '2026-07-13',
|
||||
endDate: '2026-07-13',
|
||||
status: 'active',
|
||||
} as ClassSchedule,
|
||||
]),
|
||||
};
|
||||
const classStudentRepo = {
|
||||
find: jest.fn().mockResolvedValue([{ classId: 10, studentId: 20, status: 'active' }]),
|
||||
};
|
||||
const mappingRepo = {
|
||||
find: jest.fn().mockResolvedValue([{ studentId: 20, dingUserId: 'student-1' }]),
|
||||
};
|
||||
const classRepo = {
|
||||
find: jest.fn().mockResolvedValue([{ id: 10, name: '冲刺一班' }]),
|
||||
};
|
||||
const scheduleUsers = jest.fn().mockResolvedValue(undefined);
|
||||
const upsertShift = jest.fn().mockResolvedValue(2022);
|
||||
const dingTalkService = {
|
||||
queryShifts: jest.fn().mockResolvedValue([]),
|
||||
upsertShift,
|
||||
queryAttendanceGroups: jest.fn().mockResolvedValue([
|
||||
{ group_id: 1495610001, group_name: '排课_冲刺一班', type: 'TURN', member_count: 1 },
|
||||
]),
|
||||
updateAttendanceGroup: jest.fn().mockResolvedValue(undefined),
|
||||
createAttendanceGroup: jest.fn(),
|
||||
scheduleUsers,
|
||||
};
|
||||
|
||||
const service = new ScheduleSyncService(
|
||||
scheduleRepo as never,
|
||||
classStudentRepo as never,
|
||||
mappingRepo as never,
|
||||
classRepo as never,
|
||||
dingTalkService as never,
|
||||
);
|
||||
|
||||
const result = await service.syncAll('2026-07-13', 1);
|
||||
|
||||
expect(upsertShift).toHaveBeenCalledTimes(1);
|
||||
expect(upsertShift).toHaveBeenCalledWith(expect.objectContaining({
|
||||
name: '冲刺一班_20:00-21:00+22:10-23:10',
|
||||
sections: [
|
||||
expect.objectContaining({
|
||||
times: expect.arrayContaining([
|
||||
expect.objectContaining({ check_type: 'OnDuty', check_time: '1970-01-01 20:00:00' }),
|
||||
expect.objectContaining({ check_type: 'OffDuty', check_time: '1970-01-01 21:00:00' }),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
times: expect.arrayContaining([
|
||||
expect.objectContaining({ check_type: 'OnDuty', check_time: '1970-01-01 22:10:00' }),
|
||||
expect.objectContaining({ check_type: 'OffDuty', check_time: '1970-01-01 23:10:00' }),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
}));
|
||||
expect(scheduleUsers).toHaveBeenCalledTimes(1);
|
||||
expect(scheduleUsers.mock.calls[0][1]).toEqual([
|
||||
expect.objectContaining({ userid: 'student-1', shift_id: 2022 }),
|
||||
]);
|
||||
expect(result.syncedItems).toBe(1);
|
||||
expect(result.failedBatchCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user