package cn.iocoder.yudao.module.hrm.service.attendance;
|
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO;
|
import org.junit.jupiter.api.Test;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDateTime;
|
import java.time.LocalTime;
|
import java.util.List;
|
import java.util.stream.Collectors;
|
import java.util.stream.Stream;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
/**
|
* {@link HrmAttendanceRuleServiceImpl} 的单元测试
|
*
|
* 同一个部门允许配置多条考勤规则(多班次),这里覆盖「按打卡时间匹配班次」的选规则逻辑。
|
* 注意:断言匹配到的规则编号,而不是规则对象本身 —— 返回的是传入的同一个对象,比较编号更能说明选的是哪一条。
|
*/
|
public class HrmAttendanceRuleServiceImplTest {
|
|
private static HrmAttendanceRuleDO rule(Long id, String name, LocalTime start, LocalTime end) {
|
return HrmAttendanceRuleDO.builder().id(id).name(name)
|
.workStartTime(start).workEndTime(end)
|
.lateAllowMinutes(0).earlyAllowMinutes(0)
|
.overtimeThreshold(BigDecimal.ONE).status(0).build();
|
}
|
|
/** 早班 08:00-16:00、晚班 16:00-24:00 —— 用来覆盖同一个部门多个班次 */
|
private static List<HrmAttendanceRuleDO> dayAndNightRules() {
|
return Stream.of(
|
rule(1L, "早班", LocalTime.of(8, 0), LocalTime.of(16, 0)),
|
rule(2L, "晚班", LocalTime.of(16, 0), LocalTime.of(23, 59)))
|
.collect(Collectors.toList());
|
}
|
|
@Test
|
public void testMatchRuleByClockTime_singleRule() {
|
// 只有一条规则时,无论打卡时间是多少都用它
|
List<HrmAttendanceRuleDO> rules = Stream.of(rule(1L, "标准班", LocalTime.of(9, 0), LocalTime.of(18, 0)))
|
.collect(Collectors.toList());
|
assertEquals(1L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 2, 0)).getId());
|
}
|
|
/** 打卡时间落在哪个班次的时间窗口内,就用哪个班次 —— 不能因为返回顺序变了就选错 */
|
@Test
|
public void testMatchRuleByClockTime_clockTimeInWindow() {
|
List<HrmAttendanceRuleDO> rules = dayAndNightRules();
|
assertEquals(1L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 8, 5)).getId());
|
assertEquals(2L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 16, 5)).getId());
|
assertEquals(2L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 23, 30)).getId());
|
}
|
|
/** 班次顺序颠倒时结果不变(保证不随数据库返回顺序漂移) */
|
@Test
|
public void testMatchRuleByClockTime_orderIndependent() {
|
List<HrmAttendanceRuleDO> reversed = Stream.of(
|
rule(2L, "晚班", LocalTime.of(16, 0), LocalTime.of(23, 59)),
|
rule(1L, "早班", LocalTime.of(8, 0), LocalTime.of(16, 0)))
|
.collect(Collectors.toList());
|
assertEquals(1L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(reversed, LocalDateTime.of(2026, 10, 16, 7, 50)).getId());
|
}
|
|
/** 打卡时间不在任何窗口内(例如提前很久到岗),取窗口离得最近的一条 */
|
@Test
|
public void testMatchRuleByClockTime_outOfWindow() {
|
List<HrmAttendanceRuleDO> rules = dayAndNightRules();
|
assertEquals(1L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 6, 0)).getId()); // 离早班 2 小时
|
assertEquals(2L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 17, 1, 0)).getId()); // 离晚班下班 1 小时
|
}
|
|
/** 跨天夜班:22:00-06:00,凌晨的打卡属于这个班次 */
|
@Test
|
public void testMatchRuleByClockTime_crossDay() {
|
List<HrmAttendanceRuleDO> rules = Stream.of(
|
rule(1L, "白班", LocalTime.of(8, 0), LocalTime.of(16, 0)),
|
rule(3L, "夜班", LocalTime.of(22, 0), LocalTime.of(6, 0)))
|
.collect(Collectors.toList());
|
assertEquals(3L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 22, 10)).getId());
|
assertEquals(3L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 17, 5, 40)).getId());
|
assertEquals(1L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 9, 0)).getId());
|
}
|
|
/** 不关心班次(打卡时间为空)时,取上班时间最早的一条,结果稳定 */
|
@Test
|
public void testMatchRuleByClockTime_nullClockTime() {
|
List<HrmAttendanceRuleDO> rules = Stream.of(
|
rule(2L, "晚班", LocalTime.of(16, 0), LocalTime.of(23, 59)),
|
rule(1L, "早班", LocalTime.of(8, 0), LocalTime.of(16, 0)))
|
.collect(Collectors.toList());
|
assertEquals(1L, HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, null).getId());
|
}
|
|
/** 班次时间没配好(为空)时不能抛异常,仍要能选出一条规则 */
|
@Test
|
public void testMatchRuleByClockTime_ruleWithoutTime() {
|
List<HrmAttendanceRuleDO> rules = Stream.of(
|
rule(9L, "未配置班次时间的规则", null, null),
|
rule(1L, "早班", LocalTime.of(8, 0), LocalTime.of(16, 0)))
|
.collect(Collectors.toList());
|
HrmAttendanceRuleDO matched = HrmAttendanceRuleServiceImpl.matchRuleByClockTime(rules, LocalDateTime.of(2026, 10, 16, 9, 0));
|
assertNotNull(matched);
|
assertEquals(1L, matched.getId());
|
}
|
|
}
|