3 天以前 b852254d90e7d1955db31db154193ec8ee84d8b3
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
@@ -173,8 +173,59 @@
                    .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
            record.setWorkHours(workHours);
        }
        // 7. 计算加班时长:超过规则下班时间的部分
        record.setOvertimeHours(computeOvertimeHours(rule.getWorkEndTime(), clockTimeOnly));
        attendanceRecordMapper.updateById(record);
        return record;
    }
    /**
     * 计算加班时长(小时):clockTimeOnly 晚于 workEndTime 的部分,否则为 0
     */
    private BigDecimal computeOvertimeHours(LocalTime workEndTime, LocalTime clockTimeOnly) {
        if (workEndTime == null || clockTimeOnly == null || !clockTimeOnly.isAfter(workEndTime)) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(Duration.between(workEndTime, clockTimeOnly).toMinutes())
                .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recordOvertime(HrmAttendanceExceptionDO exception) {
        // 仅处理加班类型
        if (!HrmAttendanceExceptionTypeEnum.OVERTIME.getType().equals(exception.getType())
                || exception.getExceptionTime() == null) {
            return;
        }
        // 1. 取员工考勤规则,用规则下班时间到异常时间(实际下班点)折算加班时长;无规则则无法折算,跳过
        BigDecimal overtimeHours = BigDecimal.ZERO;
        HrmEmployeeDO employee = employeeService.getEmployee(exception.getUserId());
        HrmAttendanceRuleDO rule = employee == null ? null
                : attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId());
        if (rule != null) {
            overtimeHours = computeOvertimeHours(rule.getWorkEndTime(), exception.getExceptionTime().toLocalTime());
        }
        if (overtimeHours.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        // 2. 查询当日考勤记录,不存在则补建
        HrmAttendanceRecordDO record = attendanceRecordMapper.selectByUserIdAndDate(exception.getUserId(), exception.getExceptionDate());
        if (record == null) {
            record = HrmAttendanceRecordDO.builder()
                    .userId(exception.getUserId())
                    .deptId(employee != null ? employee.getDeptId() : exception.getDeptId())
                    .date(exception.getExceptionDate())
                    .dataSource(HrmAttendanceDataSourceEnum.SUPPLEMENT.getType())
                    .build();
            record.setOvertimeHours(overtimeHours);
            attendanceRecordMapper.insert(record);
            return;
        }
        // 3. 已有记录则累加加班时长
        BigDecimal existed = record.getOvertimeHours() != null ? record.getOvertimeHours() : BigDecimal.ZERO;
        record.setOvertimeHours(existed.add(overtimeHours));
        attendanceRecordMapper.updateById(record);
    }
    @Override
@@ -187,11 +238,13 @@
        LocalDate exceptionDate = exception.getExceptionDate();
        LocalDateTime exceptionTime = exception.getExceptionTime();
        Long userId = exception.getUserId();
        HrmEmployeeDO employee = employeeService.getEmployee(userId);
        HrmAttendanceRuleDO rule = employee == null ? null
                : attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId());
        // 查询考勤记录
        HrmAttendanceRecordDO record = attendanceRecordMapper.selectByUserIdAndDate(userId, exceptionDate);
        if (record == null) {
            // 如果没有记录,创建一条
            HrmEmployeeDO employee = employeeService.getEmployee(userId);
            record = HrmAttendanceRecordDO.builder()
                    .userId(userId)
                    .deptId(employee != null ? employee.getDeptId() : exception.getDeptId())
@@ -210,6 +263,8 @@
            // 下班补卡
            record.setClockOutTime(exceptionTime);
            record.setClockOutType(HrmAttendanceClockTypeEnum.NORMAL.getType());
            // 下班补卡同时折算加班时长(超过规则下班时间部分)
            record.setOvertimeHours(computeOvertimeHours(rule == null ? null : rule.getWorkEndTime(), time));
        }
        // 更新数据来源为补卡审批
        record.setDataSource(HrmAttendanceDataSourceEnum.SUPPLEMENT.getType());