package cn.iocoder.yudao.module.hrm.service.attendance; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRulePageReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRuleSaveReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO; import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceRuleMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; /** * 考勤规则配置 Service 实现类 * * 说明:同一部门允许配置多条规则,按「生效开始日期 / 生效结束日期」区间区分; * 匹配时取覆盖目标日期、且生效开始日期最晚的那条(无日期的视为长期有效,优先级最低)。 * * @author 超级管理员 */ @Service @Validated public class HrmAttendanceRuleServiceImpl implements HrmAttendanceRuleService { @Resource private HrmAttendanceRuleMapper attendanceRuleMapper; @Override public Long createAttendanceRule(HrmAttendanceRuleSaveReqVO createReqVO) { // 校验规则名称是否重复(同一部门允许多条规则,不再限制部门唯一) validateAttendanceRuleNameUnique(null, createReqVO.getName()); validateEffectiveDateRange(createReqVO.getEffectiveStartDate(), createReqVO.getEffectiveEndDate()); HrmAttendanceRuleDO attendanceRule = BeanUtils.toBean(createReqVO, HrmAttendanceRuleDO.class); // 设置默认值 if (attendanceRule.getLateAllowMinutes() == null) { attendanceRule.setLateAllowMinutes(0); } if (attendanceRule.getEarlyAllowMinutes() == null) { attendanceRule.setEarlyAllowMinutes(0); } if (attendanceRule.getOvertimeThreshold() == null) { attendanceRule.setOvertimeThreshold(BigDecimal.ONE); } if (attendanceRule.getStatus() == null) { attendanceRule.setStatus(0); } attendanceRuleMapper.insert(attendanceRule); return attendanceRule.getId(); } @Override public void updateAttendanceRule(HrmAttendanceRuleSaveReqVO updateReqVO) { validateAttendanceRuleExists(updateReqVO.getId()); // 校验规则名称是否重复 validateAttendanceRuleNameUnique(updateReqVO.getId(), updateReqVO.getName()); validateEffectiveDateRange(updateReqVO.getEffectiveStartDate(), updateReqVO.getEffectiveEndDate()); HrmAttendanceRuleDO updateObj = BeanUtils.toBean(updateReqVO, HrmAttendanceRuleDO.class); attendanceRuleMapper.updateById(updateObj); } @Override public void deleteAttendanceRule(Long id) { validateAttendanceRuleExists(id); attendanceRuleMapper.deleteById(id); } private void validateAttendanceRuleExists(Long id) { if (attendanceRuleMapper.selectById(id) == null) { throw exception(ATTENDANCE_RULE_NOT_EXISTS); } } private void validateAttendanceRuleNameUnique(Long id, String name) { HrmAttendanceRuleDO rule = attendanceRuleMapper.selectOne(HrmAttendanceRuleDO::getName, name); if (rule == null) { return; } if (id == null) { throw exception(ATTENDANCE_RULE_NAME_EXISTS); } if (!rule.getId().equals(id)) { throw exception(ATTENDANCE_RULE_NAME_EXISTS); } } /** * 校验生效日期区间:开始日期不能晚于结束日期 */ private void validateEffectiveDateRange(LocalDate startDate, LocalDate endDate) { if (startDate != null && endDate != null && startDate.isAfter(endDate)) { throw exception(ATTENDANCE_RULE_EFFECTIVE_DATE_INVALID); } } @Override public HrmAttendanceRuleDO getAttendanceRule(Long id) { return attendanceRuleMapper.selectById(id); } @Override public PageResult getAttendanceRulePage(HrmAttendanceRulePageReqVO pageReqVO) { return attendanceRuleMapper.selectPage(pageReqVO); } @Override public List getAttendanceRuleList() { return attendanceRuleMapper.selectListByStatus(0); } @Override public HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId, LocalDate date) { LocalDate targetDate = date != null ? date : LocalDate.now(); // 1. 先查找部门专属规则(覆盖目标日期的,生效开始日期最晚的优先) if (deptId != null) { List deptRules = selectEffectiveRules(deptId, targetDate); if (!deptRules.isEmpty()) { return deptRules.get(0); } } // 2. 查找全局规则(deptId 为 null) List globalRules = selectEffectiveRules(null, targetDate); if (!globalRules.isEmpty()) { return globalRules.get(0); } return null; } /** * 查询在指定日期生效的规则 * * @param deptId 部门ID,null 表示全局规则 * @param date 目标日期 */ private List selectEffectiveRules(Long deptId, LocalDate date) { LambdaQueryWrapperX wrapper = new LambdaQueryWrapperX<>(); if (deptId == null) { wrapper.isNull(HrmAttendanceRuleDO::getDeptId); } else { wrapper.eq(HrmAttendanceRuleDO::getDeptId, deptId); } wrapper.eq(HrmAttendanceRuleDO::getStatus, 0); // 生效开始日期为空(不限)或 <= 目标日期 wrapper.and(w -> w.isNull(HrmAttendanceRuleDO::getEffectiveStartDate) .or().le(HrmAttendanceRuleDO::getEffectiveStartDate, date)); // 生效结束日期为空(不限)或 >= 目标日期 wrapper.and(w -> w.isNull(HrmAttendanceRuleDO::getEffectiveEndDate) .or().ge(HrmAttendanceRuleDO::getEffectiveEndDate, date)); // 生效开始日期越晚优先级越高;同开始日期时取先创建的 wrapper.orderByDesc(HrmAttendanceRuleDO::getEffectiveStartDate); wrapper.orderByAsc(HrmAttendanceRuleDO::getId); return attendanceRuleMapper.selectList(wrapper); } }