考勤报表导出
This commit is contained in:
parent
b8beed61da
commit
2619fa3015
@ -49,4 +49,8 @@ public class Constants {
|
|||||||
* yyyy-MM-dd格式
|
* yyyy-MM-dd格式
|
||||||
*/
|
*/
|
||||||
public static final DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
public static final DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
/**
|
||||||
|
* yyyy-MM-dd HH:mm:ss格式
|
||||||
|
*/
|
||||||
|
public static final DateTimeFormatter FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -10,6 +10,7 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import javax.annotation.security.PermitAll;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -17,7 +17,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE
|
|||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class ExportAttendanceExcelDTO {
|
public class ExportAttendanceExcelDTO {
|
||||||
|
|
||||||
@Schema(description = "报表类型 1月度统计 2每日统计 3打卡记录")
|
@Schema(description = "报表类型 1月度统计 2每日统计")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||||
|
@ -7,6 +7,8 @@ import lombok.Data;
|
|||||||
public class CalculateNum {
|
public class CalculateNum {
|
||||||
@Schema(description = "总考勤时间")
|
@Schema(description = "总考勤时间")
|
||||||
long totalWorkingHours = 0L;
|
long totalWorkingHours = 0L;
|
||||||
|
@Schema(description = "总考勤时间中文")
|
||||||
|
String totalWorkingHoursStr;
|
||||||
@Schema(description = "总出勤天数")
|
@Schema(description = "总出勤天数")
|
||||||
int totalAttendanceDays = 0;
|
int totalAttendanceDays = 0;
|
||||||
@Schema(description = "总休息天数")
|
@Schema(description = "总休息天数")
|
||||||
@ -25,6 +27,10 @@ public class CalculateNum {
|
|||||||
String totalEarlyDeparturesTimeStr;
|
String totalEarlyDeparturesTimeStr;
|
||||||
@Schema(description = "缺卡总次数")
|
@Schema(description = "缺卡总次数")
|
||||||
int totalMissingCardsNumber = 0;
|
int totalMissingCardsNumber = 0;
|
||||||
|
@Schema(description = "上班缺卡总次数")
|
||||||
|
int totalUpMissingCardsNumber = 0;
|
||||||
|
@Schema(description = "下班缺卡总次数")
|
||||||
|
int totalDownMissingCardsNumber = 0;
|
||||||
@Schema(description = "矿工总天数")
|
@Schema(description = "矿工总天数")
|
||||||
int totalMinerDays = 0;
|
int totalMinerDays = 0;
|
||||||
@Schema(description = "外勤次数")
|
@Schema(description = "外勤次数")
|
||||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
@ -138,4 +139,7 @@ public class AttendancePunchRecordDO extends BaseDO {
|
|||||||
* 是否已提醒 0否 1是
|
* 是否已提醒 0否 1是
|
||||||
*/
|
*/
|
||||||
private Integer remindFlag;
|
private Integer remindFlag;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String deptName;
|
||||||
}
|
}
|
@ -5,8 +5,12 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
|
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户打卡记录 Mapper
|
* 用户打卡记录 Mapper
|
||||||
@ -35,4 +39,12 @@ public interface AttendancePunchRecordMapper extends BaseMapperX<AttendancePunch
|
|||||||
.orderByDesc(AttendancePunchRecordDO::getId));
|
.orderByDesc(AttendancePunchRecordDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计
|
||||||
|
*
|
||||||
|
* @param userList
|
||||||
|
* @param dateList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<AttendancePunchRecordDO> statistics(@Param("userList") List<Long> userList, @Param("dateList") List<String> dateList);
|
||||||
}
|
}
|
@ -21,9 +21,11 @@ import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.Attendance
|
|||||||
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshift.AttendanceGroupShiftDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshift.AttendanceGroupShiftDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.attendance.group.AttendanceGroupMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.attendance.group.AttendanceGroupMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.attendance.punchrecord.AttendancePunchRecordMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.attendance.punchrecord.AttendancePunchRecordMapper;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper;
|
||||||
import cn.iocoder.yudao.module.system.handler.PunchHandler;
|
import cn.iocoder.yudao.module.system.handler.PunchHandler;
|
||||||
import cn.iocoder.yudao.module.system.service.attendance.group.AttendanceGroupService;
|
import cn.iocoder.yudao.module.system.service.attendance.group.AttendanceGroupService;
|
||||||
import cn.iocoder.yudao.module.system.service.attendance.groupshift.AttendanceGroupShiftService;
|
import cn.iocoder.yudao.module.system.service.attendance.groupshift.AttendanceGroupShiftService;
|
||||||
@ -34,6 +36,7 @@ import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnT
|
|||||||
import cn.iocoder.yudao.module.system.service.attendance.punchrecord.AttendancePunchRecordService;
|
import cn.iocoder.yudao.module.system.service.attendance.punchrecord.AttendancePunchRecordService;
|
||||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
|
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -44,6 +47,9 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
@ -85,6 +91,8 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
private AttendanceGroupMapper attendanceGroupMapper;
|
private AttendanceGroupMapper attendanceGroupMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private AttendanceGroupUserService attendanceGroupUserService;
|
private AttendanceGroupUserService attendanceGroupUserService;
|
||||||
|
@Resource
|
||||||
|
private PostMapper postMapper;
|
||||||
|
|
||||||
|
|
||||||
// 定义一些常量以提高代码的可读性和可维护性
|
// 定义一些常量以提高代码的可读性和可维护性
|
||||||
@ -608,7 +616,6 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
@Override
|
@Override
|
||||||
public TeamAttendanceStatisticsByCycleVO tesmStatisticsByCycle(TeamAttendanceStatisticsByCycleDTO dto) {
|
public TeamAttendanceStatisticsByCycleVO tesmStatisticsByCycle(TeamAttendanceStatisticsByCycleDTO dto) {
|
||||||
TeamAttendanceStatisticsByCycleVO vo = new TeamAttendanceStatisticsByCycleVO();
|
TeamAttendanceStatisticsByCycleVO vo = new TeamAttendanceStatisticsByCycleVO();
|
||||||
TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO teamAttendanceStatisticsNumVO = new TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO();
|
|
||||||
//查询考勤组
|
//查询考勤组
|
||||||
AttendanceGroupDO attendanceGroupDO = attendanceGroupService.getGroup(dto.getGroupId());
|
AttendanceGroupDO attendanceGroupDO = attendanceGroupService.getGroup(dto.getGroupId());
|
||||||
// - 判断当前用户是否有权限查看
|
// - 判断当前用户是否有权限查看
|
||||||
@ -792,11 +799,189 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
} else {
|
} else {
|
||||||
userList = adminUserService.getAllList(CommonStatusEnum.ENABLE.getStatus(), null, dto.getTargetIds());
|
userList = adminUserService.getAllList(CommonStatusEnum.ENABLE.getStatus(), null, dto.getTargetIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- 统计
|
// -- 统计
|
||||||
List<String> dateList = DateUtils.betweenDayList(dto.getStartTime(), dto.getEndTime());
|
List<String> dateList = DateUtils.betweenDayList(dto.getStartTime(), dto.getEndTime());
|
||||||
if (dto.getType() == 1) {
|
String hh_mm = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm"));
|
||||||
this.monthlyStatistics(response, userList, dateList);
|
String first = CollectionUtil.getFirst(dateList);
|
||||||
|
String last = CollectionUtil.getLast(dateList);
|
||||||
|
//查询出数据 -- 时间周期 - 用户列表
|
||||||
|
List<AttendancePunchRecordDO> list = attendancePunchRecordMapper.statistics(userList.stream().map(AdminUserDO::getId).collect(Collectors.toList()), dateList);
|
||||||
|
//获取用户列表
|
||||||
|
Map<Long, AdminUserDO> userMap = new HashMap<>();
|
||||||
|
if (CollectionUtil.isNotEmpty(userList)) {
|
||||||
|
userMap = userList.stream().collect(Collectors.toMap(AdminUserDO::getId, Function.identity()));
|
||||||
}
|
}
|
||||||
|
List<PostDO> userPostList = postMapper.selectList();
|
||||||
|
// 根据id分组
|
||||||
|
Map<Long, PostDO> postMap = userPostList.stream().collect(Collectors.toMap(PostDO::getId, Function.identity()));
|
||||||
|
|
||||||
|
if (dto.getType() == 1) {
|
||||||
|
String headTitle = String.format("月度汇总 统计日期:%s 至 %s", first, last);
|
||||||
|
String detailedHead = String.format("月度汇总 统计日期:%s 至 %s %s", first, last, hh_mm);
|
||||||
|
this.monthlyStatistics(response, userList, dateList, headTitle, detailedHead, list);
|
||||||
|
} else {
|
||||||
|
String headTitle = String.format("每日统计 统计日期:%s 至 %s", first, last);
|
||||||
|
String detailedHead = String.format("报表生成时间:%s %s", last, hh_mm);
|
||||||
|
this.dayStatistics(response, userMap, postMap, dateList, headTitle, detailedHead, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日导出
|
||||||
|
*
|
||||||
|
* @param response
|
||||||
|
* @param userMap
|
||||||
|
* @param postMap
|
||||||
|
* @param dateList
|
||||||
|
* @param headTitle
|
||||||
|
* @param detailedHead
|
||||||
|
* @param list
|
||||||
|
*/
|
||||||
|
private void dayStatistics(HttpServletResponse response, Map<Long, AdminUserDO> userMap, Map<Long, PostDO> postMap, List<String> dateList, String headTitle, String detailedHead, List<AttendancePunchRecordDO> list) {
|
||||||
|
List<List<String>> data = new ArrayList<>();
|
||||||
|
// -- 根据部门分组 - 根据考勤组分组
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> userPunchMap = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getUserId));
|
||||||
|
|
||||||
|
// -- 先计算下要循环几次 - 一个用户一个部门一个考勤组一个班次一个日期 最大的打卡次数 -
|
||||||
|
Map<String, List<AttendancePunchRecordDO>> map = list.stream().collect(Collectors.groupingBy(
|
||||||
|
a -> a.getUserId() + "_"
|
||||||
|
+ a.getDeptId() + "_"
|
||||||
|
+ a.getAttendanceGroupId() + "_"
|
||||||
|
+ a.getAttendanceGroupShiftId() + "_"
|
||||||
|
+ a.getDayTime()));
|
||||||
|
|
||||||
|
int maxSize = map.values().stream().mapToInt(List::size).max().orElse(0);
|
||||||
|
// -- 如果膜2大于0
|
||||||
|
maxSize = (maxSize % 2) > 0 ? maxSize / 2 + 1 : maxSize / 2;
|
||||||
|
|
||||||
|
//先根据人员分组 - 再根据部门分组 - 再根据考勤组分组 - 再根据日期分组
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> entry : userPunchMap.entrySet()) {
|
||||||
|
AdminUserDO adminUserDO = userMap.get(entry.getKey());
|
||||||
|
List<String> postNames = this.getPostNames(adminUserDO, postMap);
|
||||||
|
|
||||||
|
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> deptMap = entry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDeptId));
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> deptEntry : deptMap.entrySet()) {
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> groupMap = deptEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupId));
|
||||||
|
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupEntry : groupMap.entrySet()) {
|
||||||
|
//按日期分组
|
||||||
|
Map<String, List<AttendancePunchRecordDO>> dayMap = groupEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime, TreeMap::new, Collectors.toList()));
|
||||||
|
|
||||||
|
for (String dateStr : dateList) {
|
||||||
|
List<AttendancePunchRecordDO> items = dayMap.get(dateStr);
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> groupShiftMap = new HashMap<>();
|
||||||
|
if (CollectionUtil.isNotEmpty(items)) {
|
||||||
|
groupShiftMap = items.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftId));
|
||||||
|
} else {
|
||||||
|
groupShiftMap.put(-1L, Collections.emptyList());
|
||||||
|
}
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupShiftEntry : groupShiftMap.entrySet()) {
|
||||||
|
List<String> row = new ArrayList<>();
|
||||||
|
row.add(adminUserDO.getNickname());
|
||||||
|
row.add(groupEntry.getValue().get(0).getAttendanceGroupName());
|
||||||
|
row.add(groupEntry.getValue().get(0).getDeptName());
|
||||||
|
row.add(String.join("/", postNames));
|
||||||
|
row.add(dateStr);
|
||||||
|
row.add(CollectionUtil.isEmpty(groupShiftEntry.getValue()) ? "休息" : groupShiftEntry.getValue().get(0).getAttendanceGroupShiftName());
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> workMap = groupShiftEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftItemId, TreeMap::new, Collectors.toList()));
|
||||||
|
// TODO: 2024/7/2 这里可能会有排序问题 具体看数据在调试
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupShiftItemEntry : workMap.entrySet()) {
|
||||||
|
for (AttendancePunchRecordDO attendancePunchRecordDO : groupShiftItemEntry.getValue()) {
|
||||||
|
row.add(attendancePunchRecordDO.getPunchTime() == null ? "/" : attendancePunchRecordDO.getPunchTime().format(Constants.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND));
|
||||||
|
row.add(this.statusToStr(attendancePunchRecordDO.getStatus()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxSize > workMap.entrySet().size()) {
|
||||||
|
for (int i = 0; i < maxSize - workMap.entrySet().size(); i++) {
|
||||||
|
row.add("/");
|
||||||
|
row.add("/");
|
||||||
|
row.add("/");
|
||||||
|
row.add("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//工时
|
||||||
|
String workHour = this.calculateAverageWorkingHour(workMap);
|
||||||
|
row.add(workHour);
|
||||||
|
//迟到时长
|
||||||
|
String beLate = this.calculateBeLate(workMap);
|
||||||
|
row.add(beLate);
|
||||||
|
//早退时长
|
||||||
|
String leaveEarly = this.calculateEarlyDepartures(workMap);
|
||||||
|
row.add(leaveEarly);
|
||||||
|
Map<Integer, Integer> missingCardsMap = this.calculateCommuteMissingCardsList(workMap);
|
||||||
|
row.add(missingCardsMap.get(Constants.ZERO).toString());
|
||||||
|
row.add(missingCardsMap.get(Constants.ONE).toString());
|
||||||
|
data.add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("考勤按日统计耗时:{}", (System.currentTimeMillis() - time));
|
||||||
|
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
|
||||||
|
|
||||||
|
try {
|
||||||
|
EasyExcel.write(response.getOutputStream())
|
||||||
|
.head(generateDailyHead(headTitle, detailedHead, maxSize))
|
||||||
|
.autoCloseStream(false)
|
||||||
|
.excelType(ExcelTypeEnum.XLS)
|
||||||
|
.registerWriteHandler(new CustomCellStyleHandler())
|
||||||
|
.sheet("考勤统计按日导出")
|
||||||
|
.doWrite(data);
|
||||||
|
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));
|
||||||
|
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取岗位名称
|
||||||
|
*
|
||||||
|
* @param adminUserDO
|
||||||
|
* @param postMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private List<String> getPostNames(AdminUserDO adminUserDO, Map<Long, PostDO> postMap) {
|
||||||
|
if (adminUserDO.getPostIds() != null) {
|
||||||
|
return adminUserDO.getPostIds().stream()
|
||||||
|
.map(postMap::get)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(PostDO::getName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态转换
|
||||||
|
*
|
||||||
|
* @param status
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String statusToStr(int status) {
|
||||||
|
if (status == 0) {
|
||||||
|
return "正常";
|
||||||
|
} else if (status == 1) {
|
||||||
|
return "迟到";
|
||||||
|
} else if (status == 2) {
|
||||||
|
return "早退";
|
||||||
|
} else if (status == 3) {
|
||||||
|
return "缺卡";
|
||||||
|
} else if (status == 4) {
|
||||||
|
return "未打卡";
|
||||||
|
} else if (status == 5) {
|
||||||
|
return "补卡";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -805,37 +990,104 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
* @param response
|
* @param response
|
||||||
* @param userList
|
* @param userList
|
||||||
* @param dateList
|
* @param dateList
|
||||||
|
* @param headTitle
|
||||||
|
* @param detailedHead
|
||||||
|
* @param list
|
||||||
*/
|
*/
|
||||||
private void monthlyStatistics(HttpServletResponse response, List<AdminUserDO> userList, List<String> dateList) {
|
private void monthlyStatistics(HttpServletResponse response, List<AdminUserDO> userList, List<String> dateList, String headTitle, String detailedHead, List<AttendancePunchRecordDO> list) {
|
||||||
//查询出数据 -- 时间周期 - 用户列表
|
List<List<String>> data = new ArrayList<>();
|
||||||
List<AttendancePunchRecordDO> list = attendancePunchRecordMapper.selectList(new LambdaQueryWrapper<AttendancePunchRecordDO>()
|
|
||||||
.in(AttendancePunchRecordDO::getUserId, userList)
|
|
||||||
.in(AttendancePunchRecordDO::getDayTime, dateList));
|
|
||||||
// -- 根据部门分组 - 根据考勤组分组
|
// -- 根据部门分组 - 根据考勤组分组
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> userPunchMap = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getUserId));
|
||||||
|
|
||||||
|
//获取用户列表
|
||||||
|
Map<Long, AdminUserDO> userMap = new HashMap<>();
|
||||||
|
if (CollectionUtil.isNotEmpty(userList)) {
|
||||||
|
userMap = userList.stream().collect(Collectors.toMap(AdminUserDO::getId, Function.identity()));
|
||||||
|
}
|
||||||
|
List<PostDO> userPostList = postMapper.selectList();
|
||||||
|
// 根据id分组
|
||||||
|
Map<Long, PostDO> postMap = userPostList.stream().collect(Collectors.toMap(PostDO::getId, Function.identity()));
|
||||||
|
|
||||||
|
//先根据人员分组 - 再根据部门分组 - 再根据考勤组分组 - 再根据日期分组
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> entry : userPunchMap.entrySet()) {
|
||||||
|
AdminUserDO adminUserDO = userMap.get(entry.getKey());
|
||||||
|
List<String> postNames = this.getPostNames(adminUserDO, postMap);
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> deptMap = entry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDeptId));
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> deptEntry : deptMap.entrySet()) {
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> groupMap = deptEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupId));
|
||||||
|
|
||||||
|
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupEntry : groupMap.entrySet()) {
|
||||||
|
CalculateNum calculateNum = new CalculateNum();
|
||||||
|
//按日期分组
|
||||||
|
Map<String, List<AttendancePunchRecordDO>> dayMap = groupEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime));
|
||||||
|
for (Map.Entry<String, List<AttendancePunchRecordDO>> dayEntry : dayMap.entrySet()) {
|
||||||
|
// -- 按照班次子表分组
|
||||||
|
Map<Long, List<AttendancePunchRecordDO>> workMap = dayEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftItemId));
|
||||||
|
this.calculateAverageWorkingHour(workMap, calculateNum);
|
||||||
|
// -- 出考勤天数
|
||||||
|
this.calculateAttendanceDays(dayEntry.getValue(), calculateNum);
|
||||||
|
// -- 迟到
|
||||||
|
this.calculateBeLate(workMap, calculateNum);
|
||||||
|
// -- 早退
|
||||||
|
this.calculateEarlyDepartures(workMap, calculateNum);
|
||||||
|
// -- 缺卡
|
||||||
|
this.calculateCommuteMissingCardsList(workMap, calculateNum);
|
||||||
|
// -- 旷工
|
||||||
|
this.calculateMiner(dayEntry.getValue(), calculateNum);
|
||||||
|
}
|
||||||
|
//休息天数
|
||||||
|
this.calculateRestDay(dateList, dayMap.keySet().stream().distinct().collect(Collectors.toList()), calculateNum);
|
||||||
|
//考勤总时间中文
|
||||||
|
calculateNum.setTotalWorkingHoursStr(DateUtil.formatBetween(calculateNum.getTotalWorkingHours(), BetweenFormatter.Level.MINUTE));
|
||||||
|
//迟到总时间中文
|
||||||
|
calculateNum.setTotalLateArrivalsTimeStr(DateUtil.formatBetween(calculateNum.getTotalLateArrivalsTime(), BetweenFormatter.Level.MINUTE));
|
||||||
|
//早退总时间中文
|
||||||
|
calculateNum.setTotalEarlyDeparturesTimeStr(DateUtil.formatBetween(calculateNum.getTotalEarlyDeparturesTime(), BetweenFormatter.Level.MINUTE));
|
||||||
|
|
||||||
|
List<String> row = new ArrayList<>();
|
||||||
|
row.add(adminUserDO.getNickname());
|
||||||
|
row.add(groupEntry.getValue().get(0).getAttendanceGroupName());
|
||||||
|
row.add(groupEntry.getValue().get(0).getDeptName());
|
||||||
|
row.add(String.join("/", postNames));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalAttendanceDays()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalRestDays()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalWorkingHoursStr()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalLateArrivalsNumber()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalLateArrivalsTimeStr()));
|
||||||
|
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesNumber()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesTimeStr()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalUpMissingCardsNumber()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalDownMissingCardsNumber()));
|
||||||
|
row.add(String.valueOf(calculateNum.getTotalMinerDays()));
|
||||||
|
data.add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
EasyExcel.write(response.getOutputStream())
|
||||||
|
.head(generateHead(headTitle, detailedHead))
|
||||||
|
.autoCloseStream(false)
|
||||||
|
.excelType(ExcelTypeEnum.XLS)
|
||||||
|
.registerWriteHandler(new CustomCellStyleHandler())
|
||||||
|
.sheet("考勤统计按月导出")
|
||||||
|
.doWrite(data);
|
||||||
|
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));
|
||||||
|
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
String fileName = "/Users/aikai/Downloads/" + System.currentTimeMillis() + ".xlsx";
|
|
||||||
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
|
|
||||||
EasyExcel.write(fileName)
|
|
||||||
.head(generateHead())
|
|
||||||
.registerWriteHandler(new CustomCellStyleHandler())
|
|
||||||
// .registerWriteHandler(new LoopMergeStrategy())
|
|
||||||
// .registerWriteHandler(loopMergeStrategy)
|
|
||||||
.sheet("模板")
|
|
||||||
.doWrite(generateData());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<List<String>> generateHead() {
|
public static List<List<String>> generateHead(String headTitle, String detailedHead) {
|
||||||
List<List<String>> head = new ArrayList<>();
|
List<List<String>> head = new ArrayList<>();
|
||||||
String headTitle = "月度汇总 统计日期:2024-06-01 至 2024-06-13";
|
|
||||||
String detailedHead = "月度汇总 统计日期:2024-06-01 至 2024-06-13 10:48";
|
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "姓名", "姓名"));
|
head.add(Arrays.asList(headTitle, detailedHead, "姓名", "姓名"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤组", "考勤组"));
|
head.add(Arrays.asList(headTitle, detailedHead, "考勤组", "考勤组"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "部门", "部门"));
|
head.add(Arrays.asList(headTitle, detailedHead, "部门", "部门"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "工号", "工号"));
|
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "职位", "职位"));
|
head.add(Arrays.asList(headTitle, detailedHead, "职位", "职位"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "出勤天数", "出勤天数"));
|
head.add(Arrays.asList(headTitle, detailedHead, "出勤天数", "出勤天数"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "休息天数", "休息天数"));
|
head.add(Arrays.asList(headTitle, detailedHead, "休息天数", "休息天数"));
|
||||||
@ -853,70 +1105,48 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "工作日加班"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "工作日加班"));
|
||||||
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "休息日加班"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "休息日加班"));
|
||||||
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "节假日加班"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "节假日加班"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "1"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "1"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "2"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "2"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "3"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "3"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "4"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "4"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "5"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "5"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
||||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
||||||
// 添加更多表头
|
// 添加更多表头
|
||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<List<Object>> generateData() {
|
public static List<List<String>> generateDailyHead(String headTitle, String detailedHead, int size) {
|
||||||
List<List<Object>> data = new ArrayList<>();
|
List<List<String>> head = new ArrayList<>();
|
||||||
|
head.add(Arrays.asList(headTitle, detailedHead, "姓名", "姓名"));
|
||||||
List<Object> row1 = new ArrayList<>();
|
head.add(Arrays.asList(headTitle, detailedHead, "考勤组", "考勤组"));
|
||||||
row1.add("艾楷");
|
head.add(Arrays.asList(headTitle, detailedHead, "部门", "部门"));
|
||||||
row1.add("测试考勤");
|
head.add(Arrays.asList(headTitle, detailedHead, "职位", "职位"));
|
||||||
row1.add("");
|
head.add(Arrays.asList(headTitle, detailedHead, "日期", "日期"));
|
||||||
row1.add("");
|
head.add(Arrays.asList(headTitle, detailedHead, "班次", "班次"));
|
||||||
row1.add("");
|
for (int i = 1; i <= size; i++) {
|
||||||
row1.add("22026829221062585");
|
head.add(Arrays.asList(headTitle, detailedHead, "上班" + i + "打卡时间", "上班" + i + "打卡时间"));
|
||||||
row1.add(1);
|
head.add(Arrays.asList(headTitle, detailedHead, "上班" + i + "打卡结果", "上班" + i + "打卡结果"));
|
||||||
row1.add(2);
|
head.add(Arrays.asList(headTitle, detailedHead, "下班" + i + "打卡时间", "下班" + i + "打卡时间"));
|
||||||
row1.add(3);
|
head.add(Arrays.asList(headTitle, detailedHead, "下班" + i + "打卡结果", "下班" + i + "打卡结果"));
|
||||||
row1.add(4);
|
}
|
||||||
row1.add(5);
|
// head.add(Arrays.asList(headTitle, detailedHead, "关联的审批单", "关联的审批单"));
|
||||||
row1.add(6);
|
// head.add(Arrays.asList(headTitle, detailedHead, "出勤天数", "出勤天数"));
|
||||||
row1.add(7);
|
// head.add(Arrays.asList(headTitle, detailedHead, "休息天数", "休息天数"));
|
||||||
row1.add(8);
|
head.add(Arrays.asList(headTitle, detailedHead, "工作时长", "工作时长"));
|
||||||
row1.add(9);
|
// head.add(Arrays.asList(headTitle, detailedHead, "迟到次数", "迟到次数"));
|
||||||
row1.add(10);
|
head.add(Arrays.asList(headTitle, detailedHead, "迟到时长", "迟到时长"));
|
||||||
row1.add(10);
|
// head.add(Arrays.asList(headTitle, detailedHead, "早退次数", "早退次数"));
|
||||||
row1.add(12);
|
head.add(Arrays.asList(headTitle, detailedHead, "早退时长", "早退时长"));
|
||||||
// 添加更多数据
|
head.add(Arrays.asList(headTitle, detailedHead, "上班缺卡次数", "上班缺卡次数"));
|
||||||
|
head.add(Arrays.asList(headTitle, detailedHead, "下班缺卡次数", "下班缺卡次数"));
|
||||||
List<Object> row2 = new ArrayList<>();
|
// head.add(Arrays.asList(headTitle, detailedHead, "旷工天数", "旷工天数"));
|
||||||
row2.add("谢鸿飞");
|
return head;
|
||||||
row2.add("测试考勤");
|
|
||||||
row2.add("");
|
|
||||||
row2.add("");
|
|
||||||
row2.add("");
|
|
||||||
row2.add("066557433135769889");
|
|
||||||
row2.add(1);
|
|
||||||
row2.add(2);
|
|
||||||
row2.add(3);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
row2.add(4);
|
|
||||||
// 添加更多数据
|
|
||||||
|
|
||||||
data.add(row1);
|
|
||||||
data.add(row2);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 外勤计算
|
* 外勤计算
|
||||||
*
|
*
|
||||||
@ -1012,6 +1242,18 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 休息时间计算
|
||||||
|
*
|
||||||
|
* @param dateList
|
||||||
|
* @param attendanceTime
|
||||||
|
* @param calculateNum
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private void calculateRestDay(List<String> dateList, List<String> attendanceTime, CalculateNum calculateNum) {
|
||||||
|
List<String> subtract = new ArrayList<>(CollectionUtil.subtract(dateList, attendanceTime));
|
||||||
|
calculateNum.setTotalRestDays(subtract.size());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param day
|
* @param day
|
||||||
@ -1057,6 +1299,51 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
calculateNum.setTotalMissingCardsNumber(calculateNum.getTotalMissingCardsNumber() + todayMissingCardsNumber);
|
calculateNum.setTotalMissingCardsNumber(calculateNum.getTotalMissingCardsNumber() + todayMissingCardsNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void calculateCommuteMissingCardsList(Map<Long, List<AttendancePunchRecordDO>> workMap, CalculateNum calculateNum) {
|
||||||
|
int todayUpMissingCardsNumber = 0;
|
||||||
|
int todayDownMissingCardsNumber = 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)) {
|
||||||
|
long upNum = missingCardsList.stream().filter(a -> Constants.ZERO.equals(a.getWorkType())).count();
|
||||||
|
long downNum = missingCardsList.stream().filter(a -> Constants.ONE.equals(a.getWorkType())).count();
|
||||||
|
todayUpMissingCardsNumber += upNum;
|
||||||
|
todayDownMissingCardsNumber += downNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calculateNum.setTotalUpMissingCardsNumber(calculateNum.getTotalUpMissingCardsNumber() + todayUpMissingCardsNumber);
|
||||||
|
calculateNum.setTotalDownMissingCardsNumber(calculateNum.getTotalDownMissingCardsNumber() + todayDownMissingCardsNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 迟到次数
|
||||||
|
*
|
||||||
|
* @param workMap
|
||||||
|
*/
|
||||||
|
private Map<Integer, Integer> calculateCommuteMissingCardsList(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||||
|
int todayUpMissingCardsNumber = 0;
|
||||||
|
int todayDownMissingCardsNumber = 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)) {
|
||||||
|
long upNum = missingCardsList.stream().filter(a -> Constants.ZERO.equals(a.getWorkType())).count();
|
||||||
|
long downNum = missingCardsList.stream().filter(a -> Constants.ONE.equals(a.getWorkType())).count();
|
||||||
|
todayUpMissingCardsNumber += upNum;
|
||||||
|
todayDownMissingCardsNumber += downNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(Constants.ZERO, todayUpMissingCardsNumber);
|
||||||
|
map.put(Constants.ONE, todayDownMissingCardsNumber);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 早退计算
|
* 早退计算
|
||||||
*
|
*
|
||||||
@ -1111,6 +1398,20 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
calculateNum.setTotalEarlyDeparturesTime(calculateNum.getTotalEarlyDeparturesTime() + todayEarlyDeparturesTime);
|
calculateNum.setTotalEarlyDeparturesTime(calculateNum.getTotalEarlyDeparturesTime() + todayEarlyDeparturesTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String calculateEarlyDepartures(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||||
|
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)) {
|
||||||
|
todayEarlyDeparturesTime += earlyDeparturesList.stream().mapToLong(AttendancePunchRecordDO::getLeaveEarlyTime).sum();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DateUtil.formatBetween(todayEarlyDeparturesTime, BetweenFormatter.Level.MINUTE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算迟到
|
* 计算迟到
|
||||||
*
|
*
|
||||||
@ -1166,6 +1467,26 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
calculateNum.setTotalLateArrivalsTime(calculateNum.getTotalLateArrivalsTime() + todayLateArrivalsTime);
|
calculateNum.setTotalLateArrivalsTime(calculateNum.getTotalLateArrivalsTime() + todayLateArrivalsTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 迟到时长
|
||||||
|
*
|
||||||
|
* @param workMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String calculateBeLate(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||||
|
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)) {
|
||||||
|
todayLateArrivalsTime += beLateList.stream().mapToLong(AttendancePunchRecordDO::getLateTime).sum();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DateUtil.formatBetween(todayLateArrivalsTime, BetweenFormatter.Level.MINUTE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算考勤工时
|
* 计算考勤工时
|
||||||
*
|
*
|
||||||
@ -1184,6 +1505,17 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算工时
|
||||||
|
*
|
||||||
|
* @param workMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String calculateAverageWorkingHour(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||||
|
long todayWorkingHours = this.calculateAverageWorkingHourDay(workMap);
|
||||||
|
return DateUtil.formatBetween(todayWorkingHours, BetweenFormatter.Level.MINUTE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算考勤工时
|
* 计算考勤工时
|
||||||
*
|
*
|
||||||
@ -1209,6 +1541,30 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
return todayWorkingHours;
|
return todayWorkingHours;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算工时 - 当天
|
||||||
|
*
|
||||||
|
* @param workMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private long calculateAverageWorkingHourDay(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return todayWorkingHours;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算出勤天数
|
* 计算出勤天数
|
||||||
|
@ -25,7 +25,7 @@ public class CustomCellStyleHandler implements CellWriteHandler {
|
|||||||
|
|
||||||
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||||
CellStyle cellStyle = workbook.createCellStyle();
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 5120);
|
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 7120);
|
||||||
// 设置水平和垂直居中对齐
|
// 设置水平和垂直居中对齐
|
||||||
cellStyle.setAlignment(HorizontalAlignment.CENTER);
|
cellStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||||
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||||
@ -44,12 +44,14 @@ public class CustomCellStyleHandler implements CellWriteHandler {
|
|||||||
// 头的策略
|
// 头的策略
|
||||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||||
// 背景设置为红色
|
// 背景设置为红色
|
||||||
headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
|
headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
|
||||||
WriteFont headWriteFont = new WriteFont();
|
WriteFont headWriteFont = new WriteFont();
|
||||||
headWriteFont.setFontHeightInPoints((short) 20);
|
headWriteFont.setFontHeightInPoints((short) 20);
|
||||||
headWriteCellStyle.setWriteFont(headWriteFont);
|
headWriteCellStyle.setWriteFont(headWriteFont);
|
||||||
WriteCellStyle.merge(headWriteCellStyle, cellDataList.get(0).getOrCreateStyle());
|
WriteCellStyle.merge(headWriteCellStyle, cellDataList.get(0).getOrCreateStyle());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
cell.setCellStyle(cellStyle);
|
cell.setCellStyle(cellStyle);
|
||||||
}
|
}
|
||||||
|
@ -9,4 +9,27 @@
|
|||||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<select id="statistics"
|
||||||
|
resultType="cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO">
|
||||||
|
select
|
||||||
|
a.*,
|
||||||
|
b.name as deptName
|
||||||
|
from kq_attendance_punch_record a
|
||||||
|
left join system_dept as b on a.dept_id = b.id
|
||||||
|
<where>
|
||||||
|
a.deleted = 0
|
||||||
|
<if test="userList != null and userList.size() > 0">
|
||||||
|
and a.user_id in
|
||||||
|
<foreach collection="userList" item="userId" separator="," open="(" close=")">
|
||||||
|
#{userId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="dateList != null and dateList.size() > 0">
|
||||||
|
and a.day_time in
|
||||||
|
<foreach collection="dateList" item="dayTime" separator="," open="(" close=")">
|
||||||
|
#{dayTime}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
Loading…
Reference in New Issue
Block a user