统计按周期

This commit is contained in:
aikai 2024-05-27 19:07:15 +08:00
parent fb77318851
commit e02347c992
4 changed files with 331 additions and 63 deletions

View File

@ -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;

View File

@ -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<String, Integer> statisticsStatusByDayMap;
@Schema(description = "按天响应对象 key为日期 yyyy-MM-dd value为状态 0正常 1异常")
private Map<String, List<AttendancePunchVO>> statisticsDataByDayMap;
@Schema(description = "平均工作时间")
private List<AttendancePunchStatisticsVO> averageWorkingHours;
@Schema(description = "出勤天数")
private List<AttendancePunchStatisticsVO> attendanceDays;
@Schema(description = "迟到次数")
private List<AttendancePunchStatisticsVO> beLateNumber;
@Schema(description = "早退次数")
private List<AttendancePunchStatisticsVO> earlyDeparturesNumber;
@Schema(description = "缺卡次数")
private List<AttendancePunchStatisticsVO> missingCardsNumber;
@Schema(description = "矿工日")
private List<AttendancePunchStatisticsVO> minerDays;
@Schema(description = "外勤次数")
private List<AttendancePunchStatisticsVO> fieldServiceNumber;
@Schema(description = "休息日")
private List<AttendancePunchStatisticsVO> 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;
}
}

View File

@ -112,7 +112,7 @@ public class AttendancePunchRecordDO extends BaseDO {
private Long lateTime;
/**
* 迟到时长时间戳
* 早退时长时间戳
*/
private Long leaveEarlyTime;
}

View File

@ -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<String, List<AttendancePunchRecordDO>> map = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime));
List<AttendancePunchStatisticsVO> averageWorkingHours = new ArrayList<>();
List<AttendancePunchStatisticsVO> attendanceDays = new ArrayList<>();
List<AttendancePunchStatisticsVO> RestDays = new ArrayList<>();
// 总考勤时间
long totalWorkingHours = 0L;
// 总出勤天数
int totalAttendanceDays = 0;
// 总休息天数
int totalRestDays = 0;
List<AttendancePunchStatisticsVO> beLateNumber = new ArrayList<>();
List<AttendancePunchStatisticsVO> earlyDeparturesNumber = new ArrayList<>();
List<AttendancePunchStatisticsVO> missingCardsNumber = new ArrayList<>();
List<AttendancePunchStatisticsVO> minerDays = new ArrayList<>();
List<AttendancePunchStatisticsVO> fieldServiceNumber = new ArrayList<>();
AttendanceStatisticsVO.CalculateNum calculateNum = new AttendanceStatisticsVO.CalculateNum();
// 迟到总次数
int totalBeLateFrequencys = 0;
// 迟到总时间
long totalBeLateTime = 0L;
for (Map.Entry<String, List<AttendancePunchRecordDO>> 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<Long, List<AttendancePunchRecordDO>> workMap = entry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftItemId));
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 出勤天数计算 -- 有打卡记录就算出勤了
List<AttendancePunchRecordDO> 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<AttendancePunchRecordDO> 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<AttendancePunchStatisticsVO> beLateList = this.calculateBeLate(day, weekChinese, workMap, calculateNum);
beLateNumber.addAll(beLateList);
// -- 早退计算
List<AttendancePunchStatisticsVO> earlyDeparturesList = this.calculateEarlyDepartures(day, weekChinese, workMap, calculateNum);
earlyDeparturesNumber.addAll(earlyDeparturesList);
// -- 缺卡计算
List<AttendancePunchStatisticsVO> 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<AttendancePunchStatisticsVO> fieldServiceList = this.calculateFieldService(day, weekChinese, workMap, calculateNum);
fieldServiceNumber.addAll(fieldServiceList);
}
// -- 休息计算
List<AttendancePunchStatisticsVO> 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<AttendancePunchStatisticsVO> calculateFieldService(String day, String weekChinese, Map<Long, List<AttendancePunchRecordDO>> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) {
List<AttendancePunchStatisticsVO> list = new ArrayList<>();
int todayFieldService = 0;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 缺卡计算
List<AttendancePunchRecordDO> 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<AttendancePunchRecordDO> list, AttendanceStatisticsVO.CalculateNum calculateNum) {
AttendancePunchStatisticsVO vo = null;
List<AttendancePunchRecordDO> 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<AttendancePunchStatisticsVO> calculateRestDayList(List<String> dateList, List<String> attendanceTime, AttendanceStatisticsVO.CalculateNum calculateNum) {
List<AttendancePunchStatisticsVO> list = new ArrayList<>();
List<String> 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<AttendancePunchStatisticsVO> calculateMissingCardsList(String day, String weekChinese, Map<Long, List<AttendancePunchRecordDO>> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) {
List<AttendancePunchStatisticsVO> list = new ArrayList<>();
int todayMissingCardsNumber = 0;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 缺卡计算
List<AttendancePunchRecordDO> 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<AttendancePunchStatisticsVO> calculateEarlyDepartures(String day, String weekChinese, Map<Long, List<AttendancePunchRecordDO>> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) {
List<AttendancePunchStatisticsVO> list = new ArrayList<>();
int todayEarlyDeparturesNumber = 0;
long todayEarlyDeparturesTime = 0L;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 早退计算
List<AttendancePunchRecordDO> 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<AttendancePunchStatisticsVO> calculateBeLate(String day, String weekChinese, Map<Long, List<AttendancePunchRecordDO>> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) {
List<AttendancePunchStatisticsVO> list = new ArrayList<>();
int todayLateArrivalsNumber = 0;
long todayLateArrivalsTime = 0L;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 迟到计算
List<AttendancePunchRecordDO> 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<Long, List<AttendancePunchRecordDO>> workMap, AttendanceStatisticsVO.CalculateNum calculateNum) {
AttendancePunchStatisticsVO vo = new AttendancePunchStatisticsVO();
long todayWorkingHours = 0L;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> 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<AttendancePunchRecordDO> list, AttendanceStatisticsVO.CalculateNum calculateNum) {
AttendancePunchStatisticsVO averageWorkingHourVO = null;
List<AttendancePunchRecordDO> 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;
}
}