| | |
| | | /** |
| | | * 考勤记录 Service 实现类 |
| | | * |
| | | * @author 芋道源码 |
| | | * @author 超级管理员 |
| | | */ |
| | | @Service |
| | | @Validated |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void createImportedRecord(HrmAttendanceRecordDO record) { |
| | | // 按考勤规则重算打卡类型与时长,不信任 Excel 填写的值 |
| | | fillClockTypes(record); |
| | | fillCalculatedHours(record); |
| | | attendanceRecordMapper.insert(record); |
| | | } |
| | | |
| | | @Override |
| | | public void updateImportedRecord(HrmAttendanceRecordDO record) { |
| | | fillClockTypes(record); |
| | | fillCalculatedHours(record); |
| | | attendanceRecordMapper.updateById(record); |
| | | } |
| | | |
| | | /** |
| | | * 按考勤规则重算上下班打卡类型(正常/迟到/早退),与打卡逻辑保持一致 |
| | | */ |
| | | private void fillClockTypes(HrmAttendanceRecordDO record) { |
| | | if (record.getDeptId() == null) { |
| | | return; |
| | | } |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(record.getDeptId()); |
| | | if (rule == null) { |
| | | return; |
| | | } |
| | | // 上班打卡类型:晚于 上班时间+迟到允许 记为迟到 |
| | | if (record.getClockInTime() != null && rule.getWorkStartTime() != null) { |
| | | int lateAllowMinutes = rule.getLateAllowMinutes() != null ? rule.getLateAllowMinutes() : 0; |
| | | LocalTime lateThreshold = rule.getWorkStartTime().plusMinutes(lateAllowMinutes); |
| | | LocalTime clockInOnly = record.getClockInTime().toLocalTime(); |
| | | record.setClockInType(clockInOnly.isAfter(lateThreshold) |
| | | ? HrmAttendanceClockTypeEnum.LATE.getType() |
| | | : HrmAttendanceClockTypeEnum.NORMAL.getType()); |
| | | } |
| | | // 下班打卡类型:早于 下班时间-早退允许 记为早退 |
| | | if (record.getClockOutTime() != null && rule.getWorkEndTime() != null) { |
| | | int earlyAllowMinutes = rule.getEarlyAllowMinutes() != null ? rule.getEarlyAllowMinutes() : 0; |
| | | LocalTime earlyThreshold = rule.getWorkEndTime().minusMinutes(earlyAllowMinutes); |
| | | LocalTime clockOutOnly = record.getClockOutTime().toLocalTime(); |
| | | record.setClockOutType(clockOutOnly.isBefore(earlyThreshold) |
| | | ? HrmAttendanceClockTypeEnum.EARLY.getType() |
| | | : HrmAttendanceClockTypeEnum.NORMAL.getType()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据上下班打卡时间和考勤规则,计算工作时长与加班时长 |
| | | */ |
| | | private void fillCalculatedHours(HrmAttendanceRecordDO record) { |
| | | // 工作时长 = 下班打卡时间 - 上班打卡时间(与打卡逻辑一致) |
| | | if (record.getClockInTime() != null && record.getClockOutTime() != null) { |
| | | Duration duration = Duration.between(record.getClockInTime(), record.getClockOutTime()); |
| | | BigDecimal workHours = BigDecimal.valueOf(duration.toMinutes()) |
| | | .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); |
| | | record.setWorkHours(workHours); |
| | | } |
| | | // 加班时长:按考勤规则的标准下班时间计算超额部分 |
| | | if (record.getClockOutTime() != null && record.getDeptId() != null) { |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(record.getDeptId()); |
| | | if (rule != null && rule.getWorkEndTime() != null) { |
| | | LocalTime workEnd = rule.getWorkEndTime(); |
| | | LocalDate outDate = record.getClockOutTime().toLocalDate(); |
| | | LocalDateTime workEndDateTime = LocalDateTime.of(outDate, workEnd); |
| | | if (record.getClockOutTime().isAfter(workEndDateTime)) { |
| | | Duration overtimeDuration = Duration.between(workEndDateTime, record.getClockOutTime()); |
| | | BigDecimal overtimeThreshold = rule.getOvertimeThreshold() != null |
| | | ? rule.getOvertimeThreshold() : BigDecimal.ZERO; |
| | | BigDecimal overtimeHours = BigDecimal.valueOf(overtimeDuration.toMinutes()) |
| | | .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); |
| | | record.setOvertimeHours(overtimeHours.compareTo(overtimeThreshold) > 0 ? overtimeHours : BigDecimal.ZERO); |
| | | } else { |
| | | record.setOvertimeHours(BigDecimal.ZERO); |
| | | } |
| | | } else { |
| | | record.setOvertimeHours(BigDecimal.ZERO); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public HrmAttendanceRecordDO clockIn(Long userId, LocalDateTime clockTime, String location) { |
| | | LocalDate clockDate = clockTime.toLocalDate(); |