|
|
@@ -37,6 +37,8 @@ import static com.management.platform.constant.Constant.*;
|
|
|
@Service
|
|
|
public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMapper, AttendanceStaff> implements AttendanceStaffService {
|
|
|
|
|
|
+ private static final BigDecimal STANDARD_WORK_HOUR = BigDecimal.valueOf(8);
|
|
|
+
|
|
|
@Resource
|
|
|
private AttendanceService attendanceService;
|
|
|
|
|
|
@@ -78,7 +80,7 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
LocalDate nextMonthDate = startDate.plusMonths(1);//下月开始第一天
|
|
|
ArrayList<LocalDate> localDates = new ArrayList<>();//本月所有日期
|
|
|
for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
|
|
|
- localDates.add(date);
|
|
|
+ localDates.add(date);
|
|
|
}
|
|
|
List<AttendanceRule> ruleList = attendanceRuleService.list();
|
|
|
|
|
|
@@ -90,6 +92,8 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
System.out.println("开始获取applyFormList==="+new Date().toString());
|
|
|
//获取申请单数据
|
|
|
List<ApplyForm> applyFormList=applyFormService.getAllListByFirstDateAndLastDate(startDate,nextMonthDate);
|
|
|
+ Set<LocalDate> specialWorkDays = getSpecialDates(startDate, endDate, 1);
|
|
|
+ Set<LocalDate> specialRestDays = getSpecialDates(startDate, endDate, 0);
|
|
|
|
|
|
List<AttendanceStaff> addList = new ArrayList<>();
|
|
|
Map<String, List<Attendance>> listMap = attendanceList.stream().collect(Collectors.groupingBy(Attendance::getJobNumber));
|
|
|
@@ -125,6 +129,7 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
staff.setWorkHour(BigDecimal.valueOf(8));
|
|
|
staff.setAttendanceType(QING_JIA);
|
|
|
staff.setAttendanceTypeName(applyForm.getApplyBillName());
|
|
|
+ markNoOvertime(staff);
|
|
|
|
|
|
addList.add(staff);
|
|
|
continue;
|
|
|
@@ -147,7 +152,10 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
else if (applyForm.getType()==4){
|
|
|
//加班以实际打卡的时间和时长为准
|
|
|
List<Attendance> dayAttendance = attendances.stream().filter(a -> a.getClockTime().toLocalDate().equals(date)).collect(Collectors.toList());
|
|
|
- dayAttendance = resolveDayClockAttendances(date, dayAttendance, attendances);
|
|
|
+ //7点之前的算是前一天的打卡时间
|
|
|
+ dayAttendance = dayAttendance.stream().filter(a->a.getClockTime().toLocalTime().isAfter(LocalTime.of(6,0)))
|
|
|
+ .sorted(Comparator.comparing(Attendance::getClockTime))
|
|
|
+ .collect(Collectors.toList());
|
|
|
if (dayAttendance.size() > 0) {
|
|
|
if (dayAttendance.size() == 1) {
|
|
|
staff.setClockStartTime(dayAttendance.get(0).getClockTime());
|
|
|
@@ -155,6 +163,7 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
staff.setWorkHour(new BigDecimal(0));
|
|
|
staff.setAttendanceType(YI_CHANG);
|
|
|
staff.setAttendanceTypeName("班次异常");
|
|
|
+ markNoOvertime(staff);
|
|
|
} else {
|
|
|
//取上下班时间;按时间排序后取第一个和最后一个时间
|
|
|
if (attendances.size() > 0) {
|
|
|
@@ -175,10 +184,11 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
staff.setAttendanceTypeName("班次异常");
|
|
|
continue;
|
|
|
} else {
|
|
|
- BigDecimal bigDecimal = calculateWorkHours(startClockTime, endClockTime, workType.getAttendanceType());
|
|
|
+ BigDecimal bigDecimal = calculateWorkHours(startClockTime, endClockTime, workType.getAttendanceType(), specialWorkDays, specialRestDays);
|
|
|
staff.setWorkHour(bigDecimal.compareTo(new BigDecimal(0)) > 0 ? bigDecimal : new BigDecimal(0));
|
|
|
staff.setAttendanceType(workType.getAttendanceType());
|
|
|
- staff.setAttendanceTypeName("加班");
|
|
|
+ staff.setAttendanceTypeName(workType.getAttendanceTypeName());
|
|
|
+ applyOvertime(staff, specialWorkDays, specialRestDays);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -262,6 +272,7 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
staff.setAttendanceType(XIAO_YE_BAN_YI_CHANG_2);
|
|
|
staff.setAttendanceTypeName("异常小夜班2");
|
|
|
}
|
|
|
+ markNoOvertime(staff);
|
|
|
addList.add(staff);
|
|
|
continue;
|
|
|
}
|
|
|
@@ -285,21 +296,22 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
|
|
|
AttendanceStaff workTimeType = getWorkTimeType(ruleList, curDateAttendances);
|
|
|
if (workTimeType != null) {
|
|
|
+ //上面处理了请假的数据 存在请假的考勤就不需要判断了
|
|
|
staff.setClockStartTime(workTimeType.getClockStartTime());
|
|
|
staff.setAttendanceType(workTimeType.getAttendanceType());
|
|
|
staff.setAttendanceTypeName(workTimeType.getAttendanceTypeName());
|
|
|
// ruleList.stream().filter(r -> r.getId().intValue() == workTimeType.getAttendanceType().intValue()).findFirst().ifPresent(r -> staff.setAttendanceTypeName(r.getName()));
|
|
|
//白班或者异常小夜班,下班时间是当天
|
|
|
LocalDateTime endClockTime = null;
|
|
|
- if (workTimeType.getAttendanceType() == JIA_BAN) {
|
|
|
- List<Attendance> effectiveDayAttendances = resolveDayClockAttendances(date, curDateAttendances, attendances);
|
|
|
- if (effectiveDayAttendances.isEmpty()) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- staff.setClockStartTime(effectiveDayAttendances.get(0).getClockTime());
|
|
|
- endClockTime = effectiveDayAttendances.get(effectiveDayAttendances.size() - 1).getClockTime();
|
|
|
- }
|
|
|
- else if (workTimeType.getAttendanceType().intValue() == BAI_BAN || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_1 || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_2 ||
|
|
|
+// if (workTimeType.getIsOver() == 1) {
|
|
|
+// curDateAttendances.stream().filter(a->a.getClockTime().toLocalTime().isAfter(LocalTime.of(6, 0))).findFirst()
|
|
|
+// .ifPresent(a->staff.setClockStartTime(a.getClockTime()));
|
|
|
+// if (staff.getClockStartTime() != null) {
|
|
|
+// endClockTime = curDateAttendances.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// else
|
|
|
+ if (workTimeType.getAttendanceType().intValue() == BAI_BAN || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_1 || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_2 ||
|
|
|
workTimeType.getAttendanceType().intValue() == ZHONG_BAN || workTimeType.getAttendanceType().intValue() == XIAO_YE_BAN_YI_CHANG_1 || workTimeType.getAttendanceType().intValue() == XIAO_YE_BAN_YI_CHANG_2) {
|
|
|
endClockTime = curDateAttendances.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime();
|
|
|
} else {
|
|
|
@@ -312,24 +324,29 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
}
|
|
|
}
|
|
|
staff.setClockEndTime(endClockTime);
|
|
|
- if (staff.getClockEndTime() == null) {
|
|
|
- staff.setClockEndTime(staff.getClockStartTime());
|
|
|
- }
|
|
|
+// if (staff.getClockEndTime() == null) {
|
|
|
+// staff.setClockEndTime(staff.getClockStartTime());
|
|
|
+// }
|
|
|
//仅仅一次打卡,为异常班次
|
|
|
- if (staff.getClockEndTime().equals(staff.getClockStartTime())) {
|
|
|
+ if (staff.getClockEndTime()==null||staff.getClockStartTime()==null) {
|
|
|
staff.setClockStartTime(staff.getClockStartTime());
|
|
|
staff.setAttendanceType(YI_CHANG);
|
|
|
staff.setAttendanceTypeName("班次异常");
|
|
|
staff.setWorkHour(new BigDecimal(0));
|
|
|
+ markNoOvertime(staff);
|
|
|
} else {
|
|
|
//有上下班打卡时间,计算时长
|
|
|
- BigDecimal bigDecimal = calculateWorkHours(staff.getClockStartTime(), endClockTime, workTimeType.getAttendanceType());
|
|
|
+ BigDecimal bigDecimal = calculateWorkHours(staff.getClockStartTime(), endClockTime, workTimeType.getAttendanceType(), specialWorkDays, specialRestDays);
|
|
|
staff.setWorkHour(bigDecimal);
|
|
|
+ applyOvertime(staff, specialWorkDays, specialRestDays);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//过滤掉空数据
|
|
|
if (!StringUtils.isEmpty(staff.getJobNumber()) && (staff.getClockStartTime()!=null || staff.getClockEndTime()!=null)) {
|
|
|
+ if (staff.getIsOver() == null) {
|
|
|
+ markNoOvertime(staff);
|
|
|
+ }
|
|
|
addList.add(staff);
|
|
|
}
|
|
|
}
|
|
|
@@ -339,56 +356,6 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
return msg;
|
|
|
}
|
|
|
|
|
|
- private static final LocalTime EARLY_MORNING_CUTOFF = LocalTime.of(7, 0);
|
|
|
- private static final LocalTime NIGHT_SHIFT_START_THRESHOLD = LocalTime.of(16, 0);
|
|
|
-
|
|
|
- /**
|
|
|
- * 解析当天有效打卡:同日≥2次保留全部;仅1次凌晨卡且前日未闭合夜班则归前日下班。
|
|
|
- */
|
|
|
- private List<Attendance> resolveDayClockAttendances(LocalDate date, List<Attendance> dayAttendances, List<Attendance> allAttendances) {
|
|
|
- if (dayAttendances == null || dayAttendances.isEmpty()) {
|
|
|
- return Collections.emptyList();
|
|
|
- }
|
|
|
- List<Attendance> sorted = dayAttendances.stream()
|
|
|
- .sorted(Comparator.comparing(Attendance::getClockTime))
|
|
|
- .collect(Collectors.toList());
|
|
|
- if (sorted.size() >= 2) {
|
|
|
- return sorted;
|
|
|
- }
|
|
|
- Attendance only = sorted.get(0);
|
|
|
- if (!only.getClockTime().toLocalTime().isAfter(EARLY_MORNING_CUTOFF)
|
|
|
- && isPreviousDayUnclosedNightShift(date, allAttendances, only.getClockTime())) {
|
|
|
- return Collections.emptyList();
|
|
|
- }
|
|
|
- return sorted;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 前一日末卡为晚班/夜班且未形成完整班次时,当日凌晨单卡视为前一日下班。
|
|
|
- */
|
|
|
- private boolean isPreviousDayUnclosedNightShift(LocalDate date, List<Attendance> allAttendances, LocalDateTime earlyPunch) {
|
|
|
- LocalDate prevDate = date.minusDays(1);
|
|
|
- List<Attendance> prevDay = allAttendances.stream()
|
|
|
- .filter(a -> a.getClockTime().toLocalDate().equals(prevDate))
|
|
|
- .sorted(Comparator.comparing(Attendance::getClockTime))
|
|
|
- .collect(Collectors.toList());
|
|
|
- if (prevDay.isEmpty()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (prevDay.size() >= 2) {
|
|
|
- long hours = Duration.between(prevDay.get(0).getClockTime(), prevDay.get(prevDay.size() - 1).getClockTime()).toHours();
|
|
|
- if (hours >= 8) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- Attendance lastPrev = prevDay.get(prevDay.size() - 1);
|
|
|
- if (lastPrev.getClockTime().toLocalTime().isBefore(NIGHT_SHIFT_START_THRESHOLD)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- long hoursSinceLastPrev = Duration.between(lastPrev.getClockTime(), earlyPunch).toHours();
|
|
|
- return hoursSinceLastPrev < 8;
|
|
|
- }
|
|
|
-
|
|
|
private AttendanceStaff getWorkTimeType(List<AttendanceRule> ruleList, List<Attendance> attendanceList) {
|
|
|
AttendanceRule baiBanRule = ruleList.get(0);
|
|
|
AttendanceRule yichangBaiBan1Rule = ruleList.get(1);
|
|
|
@@ -402,12 +369,6 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
AttendanceStaff staff = new AttendanceStaff();
|
|
|
for (Attendance attendance : attendanceList) {
|
|
|
staff.setClockStartTime(attendance.getClockTime());
|
|
|
- //如果是非工作日,就是加班
|
|
|
- if (!WorkDayCalculateUtils.isWorkDay(attendance.getClockTime().toLocalDate())) {
|
|
|
- staff.setAttendanceType(JIA_BAN);
|
|
|
- staff.setAttendanceTypeName("加班");
|
|
|
- return staff;
|
|
|
- }
|
|
|
LocalTime workTime = attendance.getClockTime().toLocalTime();
|
|
|
if (!workTime.isBefore(baiBanRule.getBeginWorkStartTime()) && !workTime.isAfter(baiBanRule.getBeginWorkEndTime())) {
|
|
|
staff.setAttendanceType(BAI_BAN);
|
|
|
@@ -450,6 +411,80 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ private void applyOvertime(AttendanceStaff staff, Set<LocalDate> specialWorkDays, Set<LocalDate> specialRestDays) {
|
|
|
+ BigDecimal workHour = staff.getWorkHour();
|
|
|
+ if (workHour == null || staff.getClockDate() == null) {
|
|
|
+ staff.setIsOver(0);
|
|
|
+ staff.setOvertimeHour(BigDecimal.ZERO);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal overtimeHour;
|
|
|
+ if (WorkDayCalculateUtils.isLegalHoliday(staff.getClockDate()) || !isWorkDay(staff.getClockDate(), specialWorkDays, specialRestDays)) {
|
|
|
+ overtimeHour = workHour;
|
|
|
+ } else {
|
|
|
+ overtimeHour = workHour.subtract(STANDARD_WORK_HOUR);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (overtimeHour.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ staff.setIsOver(0);
|
|
|
+ staff.setOvertimeHour(BigDecimal.ZERO);
|
|
|
+ } else {
|
|
|
+ staff.setIsOver(1);
|
|
|
+ staff.setOvertimeHour(overtimeHour.setScale(1, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean isWorkDay(LocalDate date, Set<LocalDate> specialWorkDays, Set<LocalDate> specialRestDays) {
|
|
|
+ if (specialWorkDays != null && specialWorkDays.contains(date)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (specialRestDays != null && specialRestDays.contains(date)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return WorkDayCalculateUtils.isWorkDay(date);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Set<LocalDate> getSpecialDates(LocalDate startDate, LocalDate endDate, int type) {
|
|
|
+ return specialDateSetService.list(new QueryWrapper<SpecialDateSet>()
|
|
|
+ .between("special_date", startDate, endDate)
|
|
|
+ .eq("type", type))
|
|
|
+ .stream()
|
|
|
+ .map(SpecialDateSet::getSpecialDate)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void markNoOvertime(AttendanceStaff staff) {
|
|
|
+ staff.setIsOver(0);
|
|
|
+ staff.setOvertimeHour(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getAttendanceColor(AttendanceStaff staff) {
|
|
|
+ Integer attendanceType = staff.getAttendanceType();
|
|
|
+ if (attendanceType == null) {
|
|
|
+ return "#2e2e2e";
|
|
|
+ }
|
|
|
+ if (attendanceType == YI_CHANG) {
|
|
|
+ return "#F56C6C";
|
|
|
+ }
|
|
|
+ if (staff.getIsOver() != null && staff.getIsOver() == 1) {
|
|
|
+ return "#E6A23C";
|
|
|
+ }
|
|
|
+ if (attendanceType == DA_YE_BAN
|
|
|
+ || attendanceType == XIAO_YE_BAN
|
|
|
+ || attendanceType == XIAO_YE_BAN_2
|
|
|
+ || attendanceType == XIAO_YE_BAN_YI_CHANG_1
|
|
|
+ || attendanceType == XIAO_YE_BAN_YI_CHANG_2) {
|
|
|
+ return "#67C23A";
|
|
|
+ }
|
|
|
+ if (attendanceType == ZHONG_BAN
|
|
|
+ || attendanceType == QING_JIA
|
|
|
+ || attendanceType == TI_QIAN_XIA_BAN) {
|
|
|
+ return "#909399";
|
|
|
+ }
|
|
|
+ return "#2e2e2e";
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public HttpRespMsg getListData(String month,String userId,Integer pageIndex ,Integer pageSize,HttpServletRequest request) {
|
|
|
HttpRespMsg msg = new HttpRespMsg();
|
|
|
@@ -464,51 +499,17 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
}else {
|
|
|
wrapper.eq("job_number", u.getJobNumber());
|
|
|
}
|
|
|
- List<HighTemperatureSet> dateSetList = highTemperatureSetService.list();
|
|
|
-
|
|
|
- // 定义午休时间段的开始和结束
|
|
|
- LocalTime noonBreakStart = LocalTime.of(12, 0);
|
|
|
- LocalTime noonBreakEndHotDay = LocalTime.of(13, 30); // 高温日结束时间
|
|
|
- LocalTime noonBreakEndNormalDay = LocalTime.of(13, 0); // 普通日结束时间
|
|
|
-
|
|
|
IPage<AttendanceStaff> iPage = page(new Page(pageIndex, pageSize), wrapper);
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
map.put("total", iPage.getTotal());
|
|
|
List<AttendanceStaff> records = iPage.getRecords();
|
|
|
for (AttendanceStaff record : records) {
|
|
|
- boolean isHotDay = dateSetList.stream().anyMatch(d -> d != null && !d.getStartDate().isAfter(record.getClockDate())&&!d.getEndDate().isBefore(record.getClockDate()));
|
|
|
- // 获取当天的打卡时间(转换为当天的时间)
|
|
|
- LocalDateTime clockStart = record.getClockStartTime();
|
|
|
- LocalDateTime clockEnd = record.getClockEndTime();
|
|
|
- // 检查是否跨过午休时间
|
|
|
- if (clockStart != null && clockEnd != null) {
|
|
|
- if (clockStart.toLocalTime().isBefore(noonBreakStart) &&
|
|
|
- clockEnd.toLocalTime().isAfter(isHotDay ? noonBreakEndHotDay : noonBreakEndNormalDay)) {
|
|
|
-
|
|
|
- // 完整午休时间扣除
|
|
|
- if (isHotDay) {
|
|
|
- record.setWorkHour(record.getWorkHour().subtract(BigDecimal.valueOf(1.5))); // 高温日扣1.5小时
|
|
|
- } else {
|
|
|
- record.setWorkHour(record.getWorkHour().subtract(BigDecimal.valueOf(1))); // 普通日扣1小时
|
|
|
- }
|
|
|
- }
|
|
|
- // 部分重叠的情况
|
|
|
- else {
|
|
|
- // 计算重叠的午休时间(分钟)
|
|
|
- long overlapMinutes = calculateOverlapMinutes(
|
|
|
- clockStart.toLocalTime(),
|
|
|
- clockEnd.toLocalTime(),
|
|
|
- noonBreakStart,
|
|
|
- isHotDay ? noonBreakEndHotDay : noonBreakEndNormalDay
|
|
|
- );
|
|
|
-
|
|
|
- if (overlapMinutes > 0) {
|
|
|
- double overlapHours = overlapMinutes / 60.0;
|
|
|
- record.setWorkHour(record.getWorkHour().subtract(BigDecimal.valueOf(overlapHours)));
|
|
|
- }
|
|
|
- }
|
|
|
+ if (record.getWorkHour() != null) {
|
|
|
record.setWorkHour(record.getWorkHour().setScale(1, RoundingMode.HALF_UP));
|
|
|
}
|
|
|
+ if (record.getOvertimeHour() != null) {
|
|
|
+ record.setOvertimeHour(record.getOvertimeHour().setScale(1, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
}
|
|
|
map.put("records",records );
|
|
|
msg.setData(map);
|
|
|
@@ -548,21 +549,24 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
List<LocalDate> workdays = getWorkDays(yearMonth);
|
|
|
// 2.2 获取自定义调休日列表
|
|
|
List<LocalDate> freeDays = getFreeDays(yearMonth);
|
|
|
+ Set<LocalDate> specialWorkDays = new HashSet<>(workdays);
|
|
|
+ Set<LocalDate> specialRestDays = new HashSet<>(freeDays);
|
|
|
|
|
|
for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
|
|
|
AttendanceStaff staff = new AttendanceStaff();
|
|
|
staff.setClockDate(d);
|
|
|
- // 判断是否为休息日(周末或自定义假日)
|
|
|
- if (d.getDayOfWeek() == DayOfWeek.SATURDAY
|
|
|
- || d.getDayOfWeek() == DayOfWeek.SUNDAY
|
|
|
- || customHolidays.contains(d)) {
|
|
|
+ if (WorkDayCalculateUtils.isLegalHoliday(d)) {
|
|
|
+ staff.setAttendanceType(XIU_XI);//法定节假日
|
|
|
+ staff.setAttendanceTypeName("法定节假日");
|
|
|
+ staff.setColor("#8e44ad");
|
|
|
+ } else if (!isWorkDay(d, specialWorkDays, specialRestDays) || customHolidays.contains(d)) {
|
|
|
staff.setAttendanceType(XIU_XI);//休息
|
|
|
- staff.setAttendanceTypeName("休息");
|
|
|
+ staff.setAttendanceTypeName("休息日");
|
|
|
staff.setColor("#2e2e2e");
|
|
|
}
|
|
|
if (freeDays.contains(d)) {
|
|
|
staff.setAttendanceType(TIAO_XIU);//调休
|
|
|
- staff.setAttendanceTypeName("调休");
|
|
|
+ staff.setAttendanceTypeName("休息日");
|
|
|
staff.setColor("#2e2e2e");
|
|
|
}
|
|
|
if (workdays.contains(d)){
|
|
|
@@ -593,42 +597,7 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
if (first.isPresent()) {
|
|
|
AttendanceStaff s = first.get();
|
|
|
BeanUtils.copyProperties(s,staff);
|
|
|
- if (staff.getAttendanceType()==BAI_BAN){
|
|
|
- staff.setColor("#2e2e2e");
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==BAI_BAN_YI_CHANG_1){
|
|
|
- staff.setColor("#2e2e2e");//绿色
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==BAI_BAN_YI_CHANG_2){
|
|
|
- staff.setColor("#2e2e2e");//绿色
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==DA_YE_BAN){
|
|
|
- staff.setColor("#67C23A");//绿色
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==XIAO_YE_BAN){
|
|
|
- staff.setColor("#67C23A");//绿色
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==XIAO_YE_BAN_2){
|
|
|
- staff.setColor("#67C23A");//绿色
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==XIAO_YE_BAN_YI_CHANG_1){
|
|
|
- staff.setColor("#67C23A");//绿色
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==XIAO_YE_BAN_YI_CHANG_2){
|
|
|
- staff.setColor("#67C23A");//绿色
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==ZHONG_BAN){
|
|
|
- staff.setColor("#909399");
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==QING_JIA){
|
|
|
- staff.setColor("#909399");
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==JIA_BAN){
|
|
|
- staff.setColor("#909399");
|
|
|
- }
|
|
|
- else if (staff.getAttendanceType()==TI_QIAN_XIA_BAN){
|
|
|
- staff.setColor("#909399");
|
|
|
- }
|
|
|
+ staff.setColor(getAttendanceColor(staff));
|
|
|
}else {
|
|
|
if (staff.getId()==null&&staff.getAttendanceType()==null) {
|
|
|
staff.setAttendanceType(YI_CHANG);//班次异常
|
|
|
@@ -641,6 +610,7 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
}
|
|
|
for (AttendanceStaff staff : allList) {
|
|
|
Integer type = staff.getAttendanceType();
|
|
|
+ Integer isOver = staff.getIsOver();
|
|
|
LocalDate clockDate = staff.getClockDate();
|
|
|
String format = clockDate.format(formatter1);
|
|
|
List<ApplyForm> applyFormList= applyFormService.getListByDate(format,userId==null|| StringUtils.isEmpty(userId)?request.getHeader("Token"):userId);
|
|
|
@@ -655,7 +625,34 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
String startStr = startTime.format(timeFormatter);
|
|
|
String endStr = endTime.format(timeFormatter);
|
|
|
List<HashMap<String, Object>> maplist = new ArrayList<>();
|
|
|
- if (type == BAI_BAN) {
|
|
|
+ // 周末加班(非法定节假日)不判迟到早退;法定节假日加班按班次规则正常判断
|
|
|
+ boolean isLegalOvertime = isOver != null && isOver == 1 && WorkDayCalculateUtils.isLegalHoliday(clockDate);
|
|
|
+ boolean isRestOvertime = isOver != null && isOver == 1 && !WorkDayCalculateUtils.isLegalHoliday(clockDate) && !isWorkDay(clockDate, specialWorkDays, specialRestDays);
|
|
|
+ if (isLegalOvertime) {
|
|
|
+ HashMap<String, Object> startMap = new HashMap<>();
|
|
|
+ startMap.put("res", "正常");
|
|
|
+ startMap.put("msg", startStr + "上班考勤打卡");
|
|
|
+ maplist.add(startMap);
|
|
|
+ HashMap<String, Object> endMap = new HashMap<>();
|
|
|
+ endMap.put("res", "正常");
|
|
|
+ endMap.put("msg", endStr + "下班考勤打卡");
|
|
|
+ maplist.add(endMap);
|
|
|
+ staff.setAttendanceTypeName("法定节假日加班");
|
|
|
+ staff.setColor(getAttendanceColor(staff));
|
|
|
+ }
|
|
|
+ else if (isRestOvertime) {
|
|
|
+ HashMap<String, Object> startMap = new HashMap<>();
|
|
|
+ startMap.put("res", "正常");
|
|
|
+ startMap.put("msg", startStr + "上班考勤打卡");
|
|
|
+ maplist.add(startMap);
|
|
|
+ HashMap<String, Object> endMap = new HashMap<>();
|
|
|
+ endMap.put("res", "正常");
|
|
|
+ endMap.put("msg", endStr + "下班考勤打卡");
|
|
|
+ maplist.add(endMap);
|
|
|
+ staff.setAttendanceTypeName("休息日加班");
|
|
|
+ staff.setColor(getAttendanceColor(staff));
|
|
|
+ }
|
|
|
+ else if (type == BAI_BAN) {
|
|
|
if (startTime.isAfter(LocalTime.of(8, 0, 0))) {
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
map.put("res", "晚点");
|
|
|
@@ -1147,14 +1144,16 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
maplist.add(map);
|
|
|
}
|
|
|
staff.setAttendanceTypeName("异常小夜班2");
|
|
|
- } else if (type == JIA_BAN) {
|
|
|
- //提取加班数据
|
|
|
+ }
|
|
|
+ if (isOver != null && isOver == 1) {
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
+ BigDecimal overtimeHour = staff.getOvertimeHour() == null ? BigDecimal.ZERO : staff.getOvertimeHour();
|
|
|
String str = staff.getClockDate().format(formatter1) + " " + staff.getClockStartTime().format(hhMMFormatter)
|
|
|
- + "-" + staff.getClockEndTime().format(hhMMFormatter) + ", 共" + staff.getWorkHour().toString() + "小时";
|
|
|
+ + "-" + staff.getClockEndTime().format(hhMMFormatter) + ", 加班" + overtimeHour.toString() + "小时";
|
|
|
map.put("res", "加班");
|
|
|
map.put("msg", str);
|
|
|
maplist.add(map);
|
|
|
+ staff.setHasOverTime(overtimeHour.compareTo(BigDecimal.ZERO) > 0);
|
|
|
}
|
|
|
if (!maplist.isEmpty()) {
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
@@ -1376,12 +1375,13 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
|
|
|
// 2025年中国法定节假日列表
|
|
|
String[] holidayDates = {
|
|
|
- "2025-01-01", // 元旦
|
|
|
- "2025-01-28", "2025-01-29", "2025-01-30", "2025-01-31", "2025-02-03", "2025-02-04", // 春节
|
|
|
- "2025-04-04", // 清明节
|
|
|
- "2025-05-01", "2025-05-02", "2025-05-05", // 劳动节
|
|
|
- "2025-06-02", // 端午节
|
|
|
- "2025-10-01", "2025-10-02", "2025-10-03", "2025-10-06", "2025-10-07", "2025-10-08" // 国庆节
|
|
|
+ "2026-01-01","2026-01-02",//元旦
|
|
|
+ "2026-02-16", "2026-02-17", "2026-02-18", "2026-02-19", "2026-02-20","2026-02-23",//春节
|
|
|
+ "2026-04-06",//清明节
|
|
|
+ "2026-05-01","2026-05-02",//劳动节
|
|
|
+ "2026-06-19",//端午节
|
|
|
+ "2026-09-25",//中秋节
|
|
|
+ "2026-10-01","2026-10-02","2026-10-03",//国庆节
|
|
|
};
|
|
|
// 将字符串日期转换为LocalDate并添加到列表中
|
|
|
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
@@ -1402,11 +1402,15 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
}
|
|
|
|
|
|
public static BigDecimal calculateWorkHours(LocalDateTime start, LocalDateTime end, int workType) {
|
|
|
+ return calculateWorkHours(start, end, workType, Collections.emptySet(), Collections.emptySet());
|
|
|
+ }
|
|
|
+
|
|
|
+ public static BigDecimal calculateWorkHours(LocalDateTime start, LocalDateTime end, int workType, Set<LocalDate> specialWorkDays, Set<LocalDate> specialRestDays) {
|
|
|
String startTime = start.format(DateTimeFormatter.ofPattern("HH:mm"));
|
|
|
String endTime = end.format(DateTimeFormatter.ofPattern("HH:mm"));
|
|
|
|
|
|
LocalDate startDate = start.toLocalDate();
|
|
|
- boolean isWorkDay = WorkDayCalculateUtils.isWorkDay(startDate);
|
|
|
+ boolean isWorkDay = isWorkDay(startDate, specialWorkDays, specialRestDays);
|
|
|
|
|
|
// 12:00-13:00为午休,中间来的得从下午上班时间开始算
|
|
|
if (startTime.compareTo("12:00") > 0 && startTime.compareTo("13:00") < 0) {
|
|
|
@@ -1431,8 +1435,8 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
hours -= 1;
|
|
|
}
|
|
|
if (workType == BAI_BAN) {
|
|
|
- //正常白班减去晚休时间,17:30-18:00
|
|
|
- if (startHour <= 17 && endHour >= 18) {
|
|
|
+ //正常白班下班后半小时不计入加班时间。
|
|
|
+ if (startHour < 18 && (endHour > 17 || (endHour == 17 && endMinute > 0))) {
|
|
|
hours -= 0.5;
|
|
|
}
|
|
|
}
|
|
|
@@ -1485,9 +1489,8 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
|
|
|
}
|
|
|
}
|
|
|
long totalMinutes = workDuration.toMinutes();
|
|
|
- if (totalMinutes < 60) return 0;
|
|
|
- long overtimeMinutes = totalMinutes;
|
|
|
- return (int) Math.floor(overtimeMinutes / 30.0);
|
|
|
+ if (totalMinutes < 30) return 0;
|
|
|
+ return (int) Math.floor(totalMinutes / 30.0);
|
|
|
}
|
|
|
|
|
|
|