From e02347c992c4ed8d716b95962c8ca9fccc080a44 Mon Sep 17 00:00:00 2001 From: aikai Date: Mon, 27 May 2024 19:07:15 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E6=8C=89=E5=91=A8=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/attendance/AttendanceController.java | 2 + .../attendance/vo/AttendanceStatisticsVO.java | 51 ++- .../punchrecord/AttendancePunchRecordDO.java | 2 +- .../attendance/AttendanceServiceImpl.java | 339 +++++++++++++++--- 4 files changed, 331 insertions(+), 63 deletions(-) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/AttendanceController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/AttendanceController.java index c16d7215..2db60a8b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/AttendanceController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/AttendanceController.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.system.controller.app.attendance; +import cn.hutool.json.JSON; +import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchDTO; import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/vo/AttendanceStatisticsVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/vo/AttendanceStatisticsVO.java index 9a003d78..4159c795 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/vo/AttendanceStatisticsVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/vo/AttendanceStatisticsVO.java @@ -5,15 +5,54 @@ import lombok.Data; import lombok.experimental.Accessors; import java.util.List; -import java.util.Map; @Data @Accessors(chain = true) public class AttendanceStatisticsVO { - @Schema(description = "按天响应对象 key为日期 yyyy-MM-dd value为状态 0正常 1异常") - private Map statisticsStatusByDayMap; - - @Schema(description = "按天响应对象 key为日期 yyyy-MM-dd value为状态 0正常 1异常") - private Map> statisticsDataByDayMap; + @Schema(description = "平均工作时间") + private List averageWorkingHours; + @Schema(description = "出勤天数") + private List attendanceDays; + @Schema(description = "迟到次数") + private List beLateNumber; + @Schema(description = "早退次数") + private List earlyDeparturesNumber; + @Schema(description = "缺卡次数") + private List missingCardsNumber; + @Schema(description = "矿工日") + private List minerDays; + @Schema(description = "外勤次数") + private List fieldServiceNumber; + @Schema(description = "休息日") + private List restDays; + @Schema(description = "头部次数") + private CalculateNum calculateNum; + @Data + public static class CalculateNum { + // 总考勤时间 + long totalWorkingHours = 0L; + // 总出勤天数 + int totalAttendanceDays = 0; + // 总休息天数 + int totalRestDays = 0; + // 迟到总次数 + int totalLateArrivalsNumber = 0; + // 迟到总时间 + long totalLateArrivalsTime = 0L; + // 早退总次数 + int totalEarlyDeparturesNumber = 0; + // 早退总时间 + long totalEarlyDeparturesTime = 0L; + // 缺卡总次数 + int totalMissingCardsNumber = 0; + // 矿工总天数 + int totalMinerDays = 0; + // 外勤次数 + int totalFieldServiceNumber = 0; + // 平均工时 + long averageWorkingHours = 0L; + // 平均工时 + String averageWorkingHoursStr; + } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/attendance/punchrecord/AttendancePunchRecordDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/attendance/punchrecord/AttendancePunchRecordDO.java index 590f7c8d..5a406b88 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/attendance/punchrecord/AttendancePunchRecordDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/attendance/punchrecord/AttendancePunchRecordDO.java @@ -112,7 +112,7 @@ public class AttendancePunchRecordDO extends BaseDO { private Long lateTime; /** - * 迟到时长时间戳 + * 早退时长时间戳 */ private Long leaveEarlyTime; } \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceServiceImpl.java index 2babeba3..e7dc1c6d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceServiceImpl.java @@ -33,6 +33,7 @@ import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnT import cn.iocoder.yudao.module.system.service.attendance.punchrecord.AttendancePunchRecordService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.StringRedisTemplate; @@ -42,6 +43,7 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Date; @@ -407,6 +409,7 @@ public class AttendanceServiceImpl implements AttendanceService { @Override public AttendanceStatisticsVO statisticsByCycle(AttendanceStatisticsByCycleDTO dto) { + AttendanceStatisticsVO vo = new AttendanceStatisticsVO(); Date thisTime = new Date(); Date beginTime = dto.getStartTime(); Date endTime = dto.getEndTime(); @@ -426,75 +429,299 @@ public class AttendanceServiceImpl implements AttendanceService { .orderByAsc(AttendancePunchRecordDO::getLevel)); // --- Map> map = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime)); - - List averageWorkingHours = new ArrayList<>(); List attendanceDays = new ArrayList<>(); - List RestDays = new ArrayList<>(); - // 总考勤时间 - long totalWorkingHours = 0L; - // 总出勤天数 - int totalAttendanceDays = 0; - // 总休息天数 - int totalRestDays = 0; + List beLateNumber = new ArrayList<>(); + List earlyDeparturesNumber = new ArrayList<>(); + List missingCardsNumber = new ArrayList<>(); + List minerDays = new ArrayList<>(); + List fieldServiceNumber = new ArrayList<>(); + AttendanceStatisticsVO.CalculateNum calculateNum = new AttendanceStatisticsVO.CalculateNum(); - // 迟到总次数 - int totalBeLateFrequencys = 0; - // 迟到总时间 - long totalBeLateTime = 0L; for (Map.Entry> entry : map.entrySet()) { - //计算平均工时 - AttendancePunchStatisticsVO averageWorkingHourVO = new AttendancePunchStatisticsVO(); - averageWorkingHourVO.setDay(entry.getKey()); + String day = entry.getKey(); String weekChinese = dayOfWeekEnum(DateUtil.parse(entry.getKey())).toChinese(); - averageWorkingHourVO.setWeek(weekChinese); - // -- 按照班次子表分组 Map> workMap = entry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftItemId)); - for (Map.Entry> workEntry : workMap.entrySet()) { - if (CollectionUtil.isNotEmpty(workEntry.getValue())) { - // -- 出勤天数计算 -- 有打卡记录就算出勤了 - List attendanceList = workEntry.getValue().stream().filter(a -> ObjectUtil.isNotEmpty(a.getPunchTime())).collect(Collectors.toList()); - if (CollectionUtil.isNotEmpty(attendanceList)) { - AttendancePunchStatisticsVO attendanceDayVO = new AttendancePunchStatisticsVO(); - averageWorkingHourVO.setDay(entry.getKey()); - averageWorkingHourVO.setWeek(weekChinese); - attendanceDays.add(attendanceDayVO); - totalAttendanceDays += 1; - } - // -- 工时计算 - 有上班和下班两个卡 才计算到工时里面 - if (workEntry.getValue().size() >= 2) { - AttendancePunchRecordDO first = CollectionUtil.getFirst(workEntry.getValue()); - AttendancePunchRecordDO last = CollectionUtil.getLast(workEntry.getValue()); - if (ObjectUtil.isNotEmpty(first.getPunchTime()) && ObjectUtil.isNotEmpty(last.getPunchTime())) { - long time = Math.abs(LocalDateTimeUtil.between(first.getPunchTime(), last.getPunchTime(), ChronoUnit.MILLIS)); - totalWorkingHours += time; - averageWorkingHourVO.setTime(DateUtil.formatBetween(time, BetweenFormatter.Level.HOUR)); - } - } - // -- 迟到计算 - List beLateList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_LATE.equals(a.getStatus())).collect(Collectors.toList()); - if (CollectionUtil.isNotEmpty(beLateList)) { - totalBeLateFrequencys += beLateList.size(); - totalBeLateTime += beLateList.stream().mapToLong(AttendancePunchRecordDO::getLateTime).sum(); - } + // -- 出勤天数计算 + AttendancePunchStatisticsVO attendanceDayVO = this.calculateAttendanceDays(day, weekChinese, entry.getValue(), calculateNum); + if (attendanceDayVO != null) { + attendanceDays.add(attendanceDayVO); + } + // -- 考勤工时计算 + AttendancePunchStatisticsVO averageWorkingHourVO = this.calculateAverageWorkingHour(day, weekChinese, workMap, calculateNum); + averageWorkingHours.add(averageWorkingHourVO); - } else { - // -- 当天不需要考勤 则记录为休息 - AttendancePunchStatisticsVO restDay = new AttendancePunchStatisticsVO(); - restDay.setDay(entry.getKey()); - restDay.setWeek(weekChinese); - RestDays.add(restDay); - totalRestDays += 1; + // -- 迟到计算 + List beLateList = this.calculateBeLate(day, weekChinese, workMap, calculateNum); + beLateNumber.addAll(beLateList); + // -- 早退计算 + List earlyDeparturesList = this.calculateEarlyDepartures(day, weekChinese, workMap, calculateNum); + earlyDeparturesNumber.addAll(earlyDeparturesList); + + // -- 缺卡计算 + List missingCardsList = this.calculateMissingCardsList(day, weekChinese, workMap, calculateNum); + missingCardsNumber.addAll(missingCardsList); + + // -- 矿工计算 + AttendancePunchStatisticsVO miner = this.calculateMiner(day, weekChinese, entry.getValue(), calculateNum); + if (miner != null) { + minerDays.add(miner); + } + // -- 外勤计算 + List fieldServiceList = this.calculateFieldService(day, weekChinese, workMap, calculateNum); + fieldServiceNumber.addAll(fieldServiceList); + } + // -- 休息计算 + List restDays = this.calculateRestDayList(dateList, map.keySet().stream().distinct().collect(Collectors.toList()), calculateNum); + + calculateNum.setAverageWorkingHours(calculateNum.getTotalAttendanceDays() == 0 ? 0 : calculateNum.getTotalWorkingHours() / calculateNum.getTotalAttendanceDays()); + calculateNum.setAverageWorkingHoursStr(DateUtil.formatBetween(calculateNum.getAverageWorkingHours(), BetweenFormatter.Level.MINUTE)); + vo.setAttendanceDays(attendanceDays); + vo.setAverageWorkingHours(averageWorkingHours); + vo.setBeLateNumber(beLateNumber); + vo.setEarlyDeparturesNumber(earlyDeparturesNumber); + vo.setMissingCardsNumber(missingCardsNumber); + vo.setMinerDays(minerDays); + vo.setFieldServiceNumber(fieldServiceNumber); + vo.setRestDays(restDays); + vo.setCalculateNum(calculateNum); + return vo; + } + + /** + * 外勤计算 + * + * @param day + * @param weekChinese + * @param workMap + * @param calculateNum + * @return + */ + private List calculateFieldService(String day, String weekChinese, Map> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) { + List list = new ArrayList<>(); + int todayFieldService = 0; + for (Map.Entry> workEntry : workMap.entrySet()) { + if (CollectionUtil.isNotEmpty(workEntry.getValue())) { + // -- 缺卡计算 + List fieldServiceList = workEntry.getValue().stream().filter(a -> Constants.TRUE.equals(a.getFieldServiceFlag())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(fieldServiceList)) { + todayFieldService += fieldServiceList.size(); + for (AttendancePunchRecordDO attendancePunchRecordDO : fieldServiceList) { + AttendancePunchStatisticsVO vo = new AttendancePunchStatisticsVO(); + vo.setDay(day); + vo.setWeek(weekChinese); + vo.setShouldPunchTime(attendancePunchRecordDO.getShouldPunchTime().format(DateTimeFormatter.ofPattern("HH:mm"))); + list.add(vo); + } } } - averageWorkingHours.add(averageWorkingHourVO); } - - - return null; + calculateNum.setTotalFieldServiceNumber(calculateNum.getTotalFieldServiceNumber() + todayFieldService); + return list; } + /** + * 计算矿工 + * + * @param day + * @param weekChinese + * @param calculateNum + * @return + */ + private AttendancePunchStatisticsVO calculateMiner(String day, String weekChinese, List list, AttendanceStatisticsVO.CalculateNum calculateNum) { + AttendancePunchStatisticsVO vo = null; + List collect = list.stream().filter(a -> a.getStatus().equals(AttendanceOnTheDayDTO.PUNCH_STATUS_MISS)).collect(Collectors.toList()); + if (collect.size() == list.size()) { + vo = new AttendancePunchStatisticsVO(); + vo.setDay(day); + vo.setWeek(weekChinese); + calculateNum.setTotalMinerDays(calculateNum.getTotalMinerDays() + 1); + } + return vo; + } + + /** + * 休息时间计算 + * + * @param dateList + * @param attendanceTime + * @param calculateNum + * @return + */ + private List calculateRestDayList(List dateList, List attendanceTime, AttendanceStatisticsVO.CalculateNum calculateNum) { + List list = new ArrayList<>(); + List subtract = new ArrayList<>(CollectionUtil.subtract(dateList, attendanceTime)); + for (String timeStr : subtract) { + AttendancePunchStatisticsVO vo = new AttendancePunchStatisticsVO(); + vo.setDay(timeStr); + vo.setWeek(dayOfWeekEnum(DateUtil.parse(timeStr)).toChinese()); + list.add(vo); + } + calculateNum.setTotalRestDays(list.size()); + return list; + } + + + /** + * @param day + * @param weekChinese + * @param workMap + * @param calculateNum + * @return + */ + private List calculateMissingCardsList(String day, String weekChinese, Map> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) { + List list = new ArrayList<>(); + int todayMissingCardsNumber = 0; + for (Map.Entry> workEntry : workMap.entrySet()) { + if (CollectionUtil.isNotEmpty(workEntry.getValue())) { + // -- 缺卡计算 + List missingCardsList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_MISS.equals(a.getStatus())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(missingCardsList)) { + todayMissingCardsNumber += missingCardsList.size(); + for (AttendancePunchRecordDO attendancePunchRecordDO : missingCardsList) { + AttendancePunchStatisticsVO earlyDepartures = new AttendancePunchStatisticsVO(); + earlyDepartures.setDay(day); + earlyDepartures.setWeek(weekChinese); + earlyDepartures.setShouldPunchTime(attendancePunchRecordDO.getShouldPunchTime().format(DateTimeFormatter.ofPattern("HH:mm"))); + list.add(earlyDepartures); + } + } + } + } + calculateNum.setTotalMissingCardsNumber(calculateNum.getTotalMissingCardsNumber() + todayMissingCardsNumber); + return list; + } + + /** + * 早退计算 + * + * @param day + * @param weekChinese + * @param workMap + * @param calculateNum + * @return + */ + private List calculateEarlyDepartures(String day, String weekChinese, Map> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) { + List list = new ArrayList<>(); + int todayEarlyDeparturesNumber = 0; + long todayEarlyDeparturesTime = 0L; + for (Map.Entry> workEntry : workMap.entrySet()) { + if (CollectionUtil.isNotEmpty(workEntry.getValue())) { + // -- 早退计算 + List earlyDeparturesList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_LEAVE_EARLY.equals(a.getStatus())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(earlyDeparturesList)) { + todayEarlyDeparturesNumber += earlyDeparturesList.size(); + for (AttendancePunchRecordDO attendancePunchRecordDO : earlyDeparturesList) { + AttendancePunchStatisticsVO earlyDepartures = new AttendancePunchStatisticsVO(); + earlyDepartures.setDay(day); + earlyDepartures.setWeek(weekChinese); + earlyDepartures.setShouldPunchTime(attendancePunchRecordDO.getShouldPunchTime().format(DateTimeFormatter.ofPattern("HH:mm"))); + earlyDepartures.setTime("下班早退" + DateUtil.formatBetween(attendancePunchRecordDO.getLeaveEarlyTime(), BetweenFormatter.Level.MINUTE)); + list.add(earlyDepartures); + } + todayEarlyDeparturesTime += earlyDeparturesList.stream().mapToLong(AttendancePunchRecordDO::getLeaveEarlyTime).sum(); + } + } + } + calculateNum.setTotalEarlyDeparturesNumber(calculateNum.getTotalEarlyDeparturesNumber() + todayEarlyDeparturesNumber); + calculateNum.setTotalEarlyDeparturesTime(calculateNum.getTotalEarlyDeparturesTime() + todayEarlyDeparturesTime); + return list; + } + + /** + * 计算迟到 + * + * @param day + * @param weekChinese + * @param workMap + * @param calculateNum + * @return + */ + private List calculateBeLate(String day, String weekChinese, Map> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) { + List list = new ArrayList<>(); + int todayLateArrivalsNumber = 0; + long todayLateArrivalsTime = 0L; + for (Map.Entry> workEntry : workMap.entrySet()) { + if (CollectionUtil.isNotEmpty(workEntry.getValue())) { + // -- 迟到计算 + List beLateList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_LATE.equals(a.getStatus())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(beLateList)) { + todayLateArrivalsNumber += beLateList.size(); + for (AttendancePunchRecordDO attendancePunchRecordDO : beLateList) { + AttendancePunchStatisticsVO beLateDay = new AttendancePunchStatisticsVO(); + beLateDay.setDay(day); + beLateDay.setWeek(weekChinese); + beLateDay.setShouldPunchTime(attendancePunchRecordDO.getShouldPunchTime().format(DateTimeFormatter.ofPattern("HH:mm"))); + beLateDay.setTime("上班迟到" + DateUtil.formatBetween(attendancePunchRecordDO.getLateTime(), BetweenFormatter.Level.MINUTE)); + //将时间戳lateTime 转换成 **小时**分格式 不满1小时的时间不显小时 + list.add(beLateDay); + } + todayLateArrivalsTime += beLateList.stream().mapToLong(AttendancePunchRecordDO::getLateTime).sum(); + } + } + } + calculateNum.setTotalLateArrivalsNumber(calculateNum.getTotalLateArrivalsNumber() + todayLateArrivalsNumber); + calculateNum.setTotalLateArrivalsTime(calculateNum.getTotalLateArrivalsTime() + todayLateArrivalsTime); + return list; + } + + /** + * 计算考勤工时 + * + * @param day + * @param weekChinese + * @param workMap + * @param calculateNum + * @return + */ + private AttendancePunchStatisticsVO calculateAverageWorkingHour(String day, String weekChinese, Map> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) { + AttendancePunchStatisticsVO vo = new AttendancePunchStatisticsVO(); + long todayWorkingHours = 0L; + for (Map.Entry> workEntry : workMap.entrySet()) { + if (CollectionUtil.isNotEmpty(workEntry.getValue())) { + // -- 工时计算 - 有上班和下班两个卡 才计算到工时里面 + if (workEntry.getValue().size() >= 2) { + AttendancePunchRecordDO first = CollectionUtil.getFirst(workEntry.getValue()); + AttendancePunchRecordDO last = CollectionUtil.getLast(workEntry.getValue()); + if (ObjectUtil.isNotEmpty(first.getPunchTime()) && ObjectUtil.isNotEmpty(last.getPunchTime())) { + long time = Math.abs(LocalDateTimeUtil.between(first.getPunchTime(), last.getPunchTime(), ChronoUnit.MILLIS)); + todayWorkingHours += time; + DateUtil.formatBetween(time, BetweenFormatter.Level.MINUTE); + } + } + } + } + vo.setDay(day); + vo.setWeek(weekChinese); + vo.setTime(DateUtil.formatBetween(todayWorkingHours, BetweenFormatter.Level.MINUTE)); + calculateNum.setTotalWorkingHours(calculateNum.getTotalWorkingHours() + todayWorkingHours); + return vo; + } + + /** + * 计算出勤天数 + * + * @param day + * @param weekChinese + * @param list + * @param calculateNum + * @return + */ + private AttendancePunchStatisticsVO calculateAttendanceDays(String day, String weekChinese, List list, AttendanceStatisticsVO.CalculateNum calculateNum) { + AttendancePunchStatisticsVO averageWorkingHourVO = null; + List collect = list.stream().filter(a -> ObjectUtil.isNotEmpty(a.getPunchTime())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(collect)) { + averageWorkingHourVO = new AttendancePunchStatisticsVO(); + averageWorkingHourVO.setDay(day); + averageWorkingHourVO.setWeek(weekChinese); + calculateNum.setTotalAttendanceDays(calculateNum.getTotalAttendanceDays() + 1); + } + return averageWorkingHourVO; + } + + + }