tuqin
7 小时以前 52feee3e55403d2858da59605173ec055f601bfa
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRuleServiceImpl.java
@@ -2,6 +2,7 @@
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;
@@ -11,6 +12,7 @@
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;
@@ -19,7 +21,10 @@
/**
 * 考勤规则配置 Service 实现类
 *
 * @author 芋道源码
 * 说明:同一部门允许配置多条规则,按「生效开始日期 / 生效结束日期」区间区分;
 * 匹配时取覆盖目标日期、且生效开始日期最晚的那条(无日期的视为长期有效,优先级最低)。
 *
 * @author 超级管理员
 */
@Service
@Validated
@@ -30,12 +35,9 @@
    @Override
    public Long createAttendanceRule(HrmAttendanceRuleSaveReqVO createReqVO) {
        // 校验规则名称是否重复
        // 校验规则名称是否重复(同一部门允许多条规则,不再限制部门唯一)
        validateAttendanceRuleNameUnique(null, createReqVO.getName());
        // 校验部门是否已配置规则(如果指定了部门)
        if (createReqVO.getDeptId() != null) {
            validateAttendanceRuleDeptUnique(null, createReqVO.getDeptId());
        }
        validateEffectiveDateRange(createReqVO.getEffectiveStartDate(), createReqVO.getEffectiveEndDate());
        HrmAttendanceRuleDO attendanceRule = BeanUtils.toBean(createReqVO, HrmAttendanceRuleDO.class);
        // 设置默认值
        if (attendanceRule.getLateAllowMinutes() == null) {
@@ -59,10 +61,7 @@
        validateAttendanceRuleExists(updateReqVO.getId());
        // 校验规则名称是否重复
        validateAttendanceRuleNameUnique(updateReqVO.getId(), updateReqVO.getName());
        // 校验部门是否已配置规则(如果指定了部门)
        if (updateReqVO.getDeptId() != null) {
            validateAttendanceRuleDeptUnique(updateReqVO.getId(), updateReqVO.getDeptId());
        }
        validateEffectiveDateRange(updateReqVO.getEffectiveStartDate(), updateReqVO.getEffectiveEndDate());
        HrmAttendanceRuleDO updateObj = BeanUtils.toBean(updateReqVO, HrmAttendanceRuleDO.class);
        attendanceRuleMapper.updateById(updateObj);
    }
@@ -92,15 +91,12 @@
        }
    }
    private void validateAttendanceRuleDeptUnique(Long id, Long deptId) {
        List<HrmAttendanceRuleDO> rules = attendanceRuleMapper.selectListByDeptId(deptId);
        if (rules.isEmpty()) {
            return;
        }
        for (HrmAttendanceRuleDO rule : rules) {
            if (id == null || !rule.getId().equals(id)) {
                throw exception(ATTENDANCE_RULE_DEPT_EXISTS);
            }
    /**
     * 校验生效日期区间:开始日期不能晚于结束日期
     */
    private void validateEffectiveDateRange(LocalDate startDate, LocalDate endDate) {
        if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
            throw exception(ATTENDANCE_RULE_EFFECTIVE_DATE_INVALID);
        }
    }
@@ -120,22 +116,47 @@
    }
    @Override
    public HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId) {
        // 1. 先查找部门专属规则
        List<HrmAttendanceRuleDO> deptRules = attendanceRuleMapper.selectList(new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<HrmAttendanceRuleDO>()
                .eq(HrmAttendanceRuleDO::getDeptId, deptId)
                .eq(HrmAttendanceRuleDO::getStatus, 0));
        if (!deptRules.isEmpty()) {
            return deptRules.get(0);
    public HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId, LocalDate date) {
        LocalDate targetDate = date != null ? date : LocalDate.now();
        // 1. 先查找部门专属规则(覆盖目标日期的,生效开始日期最晚的优先)
        if (deptId != null) {
            List<HrmAttendanceRuleDO> deptRules = selectEffectiveRules(deptId, targetDate);
            if (!deptRules.isEmpty()) {
                return deptRules.get(0);
            }
        }
        // 2. 查找全局规则(deptId 为 null)
        List<HrmAttendanceRuleDO> globalRules = attendanceRuleMapper.selectList(new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<HrmAttendanceRuleDO>()
                .isNull(HrmAttendanceRuleDO::getDeptId)
                .eq(HrmAttendanceRuleDO::getStatus, 0));
        List<HrmAttendanceRuleDO> globalRules = selectEffectiveRules(null, targetDate);
        if (!globalRules.isEmpty()) {
            return globalRules.get(0);
        }
        return null;
    }
}
    /**
     * 查询在指定日期生效的规则
     *
     * @param deptId 部门ID,null 表示全局规则
     * @param date   目标日期
     */
    private List<HrmAttendanceRuleDO> selectEffectiveRules(Long deptId, LocalDate date) {
        LambdaQueryWrapperX<HrmAttendanceRuleDO> 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);
    }
}