package com.ruoyi.performance.service.impl; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.util.concurrent.AtomicDouble; import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.core.domain.entity.User; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.performance.dto.PerformanceShiftAddDto; import com.ruoyi.performance.dto.PerformanceShiftMapDto; import com.ruoyi.performance.dto.StaffAttendanceDTO; import com.ruoyi.performance.excel.PerformanceShiftAnnotationTextExcelData; import com.ruoyi.performance.excel.PerformanceShiftExcelData; import com.ruoyi.performance.excel.handler.performance.CommentWriteHandler; import com.ruoyi.performance.mapper.PerformanceShiftMapper; import com.ruoyi.performance.pojo.PerformanceShift; import com.ruoyi.performance.pojo.StaffAttendanceTrackingRecord; import com.ruoyi.performance.service.PerformanceShiftService; import com.ruoyi.performance.service.StaffAttendanceTrackingRecordService; import com.ruoyi.performance.vo.StaffAttendanceVO; import com.ruoyi.system.mapper.UserMapper; import com.ruoyi.system.service.ISysDictTypeService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.ibatis.annotations.Param; import org.apache.poi.ss.usermodel.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; /** *

* 绩效管理-班次 服务实现类 *

* * @author 江苏鵷雏网络科技有限公司 * @since 2024-05-08 09:12:04 */ @Slf4j @Service public class PerformanceShiftServiceImpl extends ServiceImpl implements PerformanceShiftService { @Autowired private ISysDictTypeService dictTypeService; @Autowired private StaffAttendanceTrackingRecordService trackingRecordService; @Autowired UserMapper userMapper; private DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private DateTimeFormatter yyyyMMddHHmmss = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private DateTimeFormatter yyyMMStr = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); private final int LIST_MAX_COUNT = 1000; private static final String morningShiftKeyword = "早";//早班班次关键字 private static final String dayShiftKeyword = "中";//中班班次关键字 private static final String nightShiftKeyword = "夜";//夜班班次关键字 private static final String holidayLeaveKeyword = "休";//休假,调休假班次关键字 private static final String officialTripKeyword = "公";//公差班次关键字 private static final String personalLeaveKeyword = "事";//事假班次关键字 private static final String sickLeaveKeyword = "病";//病假班次关键字 private static final String annualLeaveKeyword = "年";//年假班次关键字 private static final String marriageLeaveKeyword = "婚";//婚假班次关键字 private static final String maternityLeaveKeyword = "产";//产假班次关键字 private static final String bereavementLeaveKeyword = "丧";//丧假班次关键字 private static final List shiftSoreList = Arrays.asList("早","中","夜","休","公","事","病","年","婚","产","丧"); @Transactional(rollbackFor = Exception.class) @Override public void performanceShiftAdd(PerformanceShiftAddDto performanceShiftAddDto) { //1.查询所选周次时间范围内已排班的数据 List shiftList = baseMapper.selectList(Wrappers.lambdaQuery() .between(ObjectUtils.allNotNull(performanceShiftAddDto.getStartWeek(), performanceShiftAddDto.getEndWeek()), PerformanceShift::getWorkTime, performanceShiftAddDto.getStartWeek(), performanceShiftAddDto.getEndWeek()) .in(!performanceShiftAddDto.getUserIdList().isEmpty(), PerformanceShift::getUserId, performanceShiftAddDto.getUserIdList()) ); List timeList = getLocalDateTimesBetween(performanceShiftAddDto.getStartWeek(), performanceShiftAddDto.getEndWeek()); //处理选中人员 List newShiftList = new ArrayList<>(); performanceShiftAddDto.getUserIdList().forEach(userId->{ List oldShifts = shiftList.stream().filter(f -> Objects.equals(f.getUserId(), userId)).collect(Collectors.toList()); timeList.forEach(time->{ PerformanceShift performanceShift = oldShifts.stream().filter(f -> f.getWorkTime().isEqual(time)).findFirst().orElse(new PerformanceShift(userId, time)); if(Objects.isNull(performanceShift.getId())||StringUtils.isBlank(performanceShift.getShift())){ performanceShift.setShift(performanceShiftAddDto.getShift()); newShiftList.add(performanceShift); } }); if(newShiftList.size()>LIST_MAX_COUNT){ this.saveBatch(newShiftList); newShiftList.clear(); } }); if(!newShiftList.isEmpty())this.saveOrUpdateBatch(newShiftList); } private List saveMonth (LocalDateTime week,String userId,List list){ LocalDate firstDayOfMonth = week.toLocalDate().withDayOfMonth(1); LocalDate lastDayOfMonth = week.toLocalDate().with(TemporalAdjusters.lastDayOfMonth()); List localDateTimesBetween = getLocalDateTimesBetween(firstDayOfMonth.atStartOfDay(), lastDayOfMonth.atStartOfDay()); localDateTimesBetween.forEach(i -> { PerformanceShift performanceShift = new PerformanceShift(); performanceShift.setUserId(Integer.valueOf(userId)); performanceShift.setWorkTime(i); performanceShift.setShift(""); list.add(performanceShift); if (list.size() >= 1000) { baseMapper.insertBatchSomeColumn(list); list.clear(); } }); return list; } @Override public Map performanceShift( String time, String userName, String laboratory) { //查询人员架构 List userList = userMapper.selectUserListByPerformance(false); List userIdList = userList.stream().map(User::getId).collect(Collectors.toList()); //班次时间范围为上个月的26号到本月的25号 LocalDateTime localDateTime = LocalDateTime.parse(time, yyyyMMddHHmmss); LocalDate firstDayOfMonth = localDateTime.toLocalDate().minusMonths(1L).withDayOfMonth(26); LocalDate lastDayOfMonth = localDateTime.toLocalDate().withDayOfMonth(25); //人员排班详情 List mapIPage = baseMapper.performanceShift(firstDayOfMonth,lastDayOfMonth, userName, laboratory); Map> groupByUserId = mapIPage.stream().collect(Collectors.groupingBy(PerformanceShiftMapDto::getUserId)); List newRecords = new ArrayList<>(); groupByUserId.keySet().forEach(key->{ PerformanceShiftMapDto shiftMapDto = new PerformanceShiftMapDto(); List shiftMapDtos = groupByUserId.get(key); //统计各班次天数 Map countShift = countShift(shiftMapDtos); shiftMapDto.setMonthlyAttendance(countShift); shiftMapDto.setMonthlyAttendanceStr(formateMap(countShift)); shiftMapDto.setList(shiftMapDtos); shiftMapDto.setUserName(shiftMapDtos.isEmpty()?"":shiftMapDtos.get(0).getUserName()); shiftMapDto.setUserId(key); newRecords.add(shiftMapDto); }); newRecords.sort(Comparator.comparing(r->userIdList.indexOf(r.getUserId()))); Map resultMap = new HashMap<>(); resultMap.put("page", newRecords); resultMap.put("headerList", getYearHeaderTimeList(firstDayOfMonth,lastDayOfMonth)); return resultMap; } public String formateMap(Map map){ List stringList = new ArrayList<>(); map.forEach((k,v)->{ if(shiftSoreList.contains(k)){ stringList.add(k+":"+v); } }); return String.join(",",stringList); } /** * 统计班次 * @param shiftMapDtos 班次列表 */ private Map countShift(List shiftMapDtos){ TreeMap targetMap = new TreeMap<>(Comparator.comparing(shiftSoreList::indexOf)); //汇总早班、中班、夜班、休息、请假、出差的天数,以及总出勤天数 Map groupByShiftName = shiftMapDtos.stream().filter(f-> StringUtils.isNotBlank(f.getShiftName())).collect(Collectors.groupingBy(PerformanceShiftMapDto::getShiftName,Collectors.counting())); AtomicLong morningShiftCount = new AtomicLong(0);//早班 AtomicLong dayShiftCount = new AtomicLong(0);//中班 AtomicLong nightShiftCount = new AtomicLong(0);//晚班 AtomicLong holidayShiftCount = new AtomicLong(0);//休 AtomicLong totalCount = new AtomicLong(0);//总出勤天数 groupByShiftName.keySet().forEach(key->{ if(key.contains(morningShiftKeyword)){ morningShiftCount.getAndAdd(groupByShiftName.get(key)); totalCount.getAndAdd(groupByShiftName.get(key)); }else if(key.contains(dayShiftKeyword)){ dayShiftCount.getAndAdd(groupByShiftName.get(key)); totalCount.getAndAdd(groupByShiftName.get(key)); }else if(key.contains(nightShiftKeyword)){ nightShiftCount.getAndAdd(groupByShiftName.get(key)); totalCount.getAndAdd(groupByShiftName.get(key)); }else if(key.contains(holidayLeaveKeyword)){ holidayShiftCount.getAndAdd(groupByShiftName.get(key)); }else if(key.contains(officialTripKeyword)){ targetMap.put(key,groupByShiftName.get(key)); totalCount.getAndAdd(groupByShiftName.get(key)); }else{ targetMap.put(key,groupByShiftName.get(key)); } }); targetMap.put("早",morningShiftCount.get()); targetMap.put("中",dayShiftCount.get()); targetMap.put("夜",nightShiftCount.get()); targetMap.put("休",holidayShiftCount.get()); targetMap.put("totalCount",totalCount.get()); return targetMap; } /** * 班次分页查询:获取月度日期表头列表 * @param firstDayOfMonth * @param lastDayOfMonth * @return */ private List getYearHeaderTimeList(LocalDate firstDayOfMonth,LocalDate lastDayOfMonth){ List localDateTimesBetween = getLocalDateTimesBetween(firstDayOfMonth.atStartOfDay(), lastDayOfMonth.atStartOfDay()); List list = new ArrayList<>(); for (LocalDateTime dateTime : localDateTimesBetween) { Map hashMap = new HashMap<>(); DateTime parse = DateUtil.parse(dateTime.format(yyyyMMdd)); hashMap.put("weekly", DateUtil.weekOfYear(DateUtil.offsetDay(parse, 1))); hashMap.put("headerTime", getWeek(dateTime.format(yyyyMMddHHmmss))); list.add(hashMap); } return list; } @Override public void performanceShiftUpdate(PerformanceShift performanceShift) { baseMapper.update(new PerformanceShift(), Wrappers.lambdaUpdate() .eq(PerformanceShift::getId, performanceShift.getId()) .set(PerformanceShift::getShift, performanceShift.getShift())); } @Override public List> performanceShiftPageYear(LocalDateTime time, String userName, String laboratory) { //查询人员架构 List userList = userMapper.selectUserListByPerformance(false); List userIdList = userList.stream().map(User::getId).collect(Collectors.toList()); if(ObjectUtil.isEmpty(time)){ throw new RuntimeException("查询日期不能为空"); } LocalDateTime startDateTime = time.minusMonths(1L).withDayOfMonth(26); LocalDateTime endDateTime = time.plusMonths(11L).withDayOfMonth(25); List shiftYearList = baseMapper.performanceShiftYear(startDateTime,endDateTime, userName, laboratory); //按人员分组,统计每个人的年度班次 Map> groupByUserId = shiftYearList.stream().collect(Collectors.groupingBy(PerformanceShift::getUserId)); List> returnList = new ArrayList<>(); groupByUserId.keySet().forEach(userIdKey->{ //按月份分组,统计每个月的班次详情 Map returnMap = new HashMap<>(); List shiftMapDtos = groupByUserId.get(userIdKey); Map sidebarAnnualAttendance = countShift(shiftMapDtos);//年度班次统计 List> monthlyAttendances = new ArrayList<>(); if(!shiftMapDtos.isEmpty()){ returnMap.put("userName",shiftMapDtos.get(0).getUserName()); returnMap.put("userId",shiftMapDtos.get(0).getUserId()); //遍历12个月的班次信息 for (int i = 0; i < 11; i++) { LocalDateTime firstDayOfMonth = startDateTime.plusMonths(i); LocalDateTime lastDayOfMonth = firstDayOfMonth.plusMonths(1L).withDayOfMonth(25); Map monthlyAttendanceMap = new HashMap<>(); List monthShiftDtos = shiftMapDtos.stream().filter(f->!f.getWorkTime().isBefore(firstDayOfMonth)&&!f.getWorkTime().isAfter(lastDayOfMonth)).collect(Collectors.toList()); if(CollectionUtils.isEmpty(monthShiftDtos)){ monthlyAttendanceMap.put("monthlyAttendanceStr",formateMap(countShift(new ArrayList<>())));//月度班次统计 monthlyAttendanceMap.put("monthlyAttendance",countShift(new ArrayList<>()));//月度班次统计 }else{ monthlyAttendanceMap.put("monthlyAttendanceStr",formateMap(countShift(monthShiftDtos)));//月度班次统计 monthlyAttendanceMap.put("monthlyAttendance",countShift(monthShiftDtos));//月度班次统计 } monthlyAttendances.add(monthlyAttendanceMap); } returnMap.put("monthlyAttendances",monthlyAttendances); returnMap.put("sidebarAnnualAttendance",sidebarAnnualAttendance); returnMap.put("sidebarAnnualAttendanceStr",formateMap(sidebarAnnualAttendance)); } returnList.add(returnMap); }); returnList.sort(Comparator.comparing(r->userIdList.indexOf(Integer.parseInt(r.get("userId").toString())))); return returnList; } @Override public boolean editAnnotationText(PerformanceShift performanceShift) { return this.updateById(performanceShift); } @Override public void exportToExcel(LocalDateTime time, String userName, String laboratory, Boolean isMonth, HttpServletResponse response) { response.reset(); try{ String fileName = "中天耐丝质量部班次信息"+ ExcelTypeEnum.XLSX; fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()); response.setContentType("application/vnd.ms-excel"); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName); response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); Map data; LocalDateTime startDate; LocalDateTime endDate; if(!isMonth){ //年度统计,时间区间取一整年 startDate = time.minusMonths(1L).withDayOfMonth(26); endDate = time.plusMonths(11L).withDayOfMonth(25); }else{ //月度统计,时间区间取上个月26号到本月25号 startDate = time.minusMonths(1L).withDayOfMonth(26); endDate = time.withDayOfMonth(25); } List performanceShiftDateList = buildPerformanceShiftDateList(startDate,endDate,isMonth); //批注信息坐标信息 List annotationTextList = new ArrayList<>(); // 查询班次 List performanceShifts = baseMapper.selectListByWorkTime(startDate, endDate, userName); //获取考勤数据 StaffAttendanceDTO staffAttendanceDTO = new StaffAttendanceDTO(); staffAttendanceDTO.setStartDate(startDate); staffAttendanceDTO.setEndDate(endDate); staffAttendanceDTO.setKeyword(userName); List attendanceRecords = trackingRecordService.getAttendanceRecord(performanceShifts,staffAttendanceDTO); //组装导出数据 List excelData = new ArrayList<>(); Map> groupByUserId = performanceShifts.stream().collect(Collectors.groupingBy(PerformanceShiftMapDto::getUserId)); List userIdKeys = groupByUserId.keySet().stream().sorted().collect(Collectors.toList()); for (int i = 0; i < userIdKeys.size(); i++) { List shiftMapDtos = groupByUserId.get(userIdKeys.get(i)); PerformanceShiftExcelData performanceShiftExcelData = new PerformanceShiftExcelData(); List shiftList = new ArrayList<>(); performanceShiftExcelData.setPersonName(shiftMapDtos.get(0).getUserName()); performanceShiftExcelData.setExcelIndex(i+1); AtomicInteger morningShiftCount = new AtomicInteger(0);//早班天数 AtomicInteger dayShiftCount = new AtomicInteger(0);//中班天数 AtomicInteger nightShiftCount = new AtomicInteger(0);//夜班天数 AtomicInteger holidayCount = new AtomicInteger(0);//休息天数 AtomicInteger personalLeaveCount = new AtomicInteger(0);//事假天数 AtomicInteger annualLeaveCount = new AtomicInteger(0);//年假天数 AtomicInteger officialTripCount = new AtomicInteger(0);//公差天数 AtomicInteger marriageLeaveCount = new AtomicInteger(0);//婚假天数 AtomicInteger bereavementLeaveCount = new AtomicInteger(0);//丧假天数 AtomicInteger sickLeaveCount = new AtomicInteger(0);//病假天数 AtomicInteger maternityLeaveCount = new AtomicInteger(0);//产假天数 AtomicInteger totalCount = new AtomicInteger(0);//出勤天数 AtomicDouble attendanceWorkHourCount = new AtomicDouble(0D);//出勤总时间 for (int j = 0; j < shiftMapDtos.size(); j++) { PerformanceShiftMapDto shiftMapDto = shiftMapDtos.get(j); //统计各班次的天数 if(StringUtils.contains(shiftMapDto.getShiftName(),morningShiftKeyword)){ morningShiftCount.getAndIncrement(); totalCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),dayShiftKeyword)){ dayShiftCount.getAndIncrement(); totalCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),nightShiftKeyword)){ nightShiftCount.getAndIncrement(); totalCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),holidayLeaveKeyword)){ holidayCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),personalLeaveKeyword)){ personalLeaveCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),annualLeaveKeyword)){ annualLeaveCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),officialTripKeyword)){ officialTripCount.getAndIncrement(); totalCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),marriageLeaveKeyword)){ marriageLeaveCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),bereavementLeaveKeyword)){ bereavementLeaveCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),sickLeaveKeyword)){ sickLeaveCount.getAndIncrement(); }else if(StringUtils.contains(shiftMapDto.getShiftName(),maternityLeaveKeyword)){ maternityLeaveCount.getAndIncrement(); } shiftList.add(shiftMapDto.getShiftName()); if(!StringUtils.isAllBlank(shiftMapDto.getStartTime(),shiftMapDto.getEndTime())){ //过滤当前人员的班次信息 StaffAttendanceVO vo = attendanceRecords.stream().filter(f-> StringUtils.isNotBlank(f.getPersonCode())).filter(f -> StringUtils.equals(f.getPersonCode(), shiftMapDto.getPersonCode()) && f.getSwingDate().isEqual(shiftMapDto.getWorkTime())).findFirst().orElse(null); if(ObjectUtils.isNotEmpty(vo)){ String actualWorkHours = Objects.toString(vo.getActualWorkHours(), ""); if (StringUtils.isNotBlank(actualWorkHours)) { attendanceWorkHourCount.getAndAdd(Double.parseDouble(actualWorkHours)); } } } //月度统计才插入批注数据 if(isMonth){ if(StringUtils.isNotBlank(shiftMapDto.getAnnotationText())){ annotationTextList.add(new PerformanceShiftAnnotationTextExcelData(i,j,shiftMapDto.getAnnotationText())); } } } if(isMonth){ performanceShiftExcelData.setShiftNameList(shiftList); } performanceShiftExcelData.setTotalCount(totalCount.get()); //班次考勤天数 performanceShiftExcelData.setMorningShiftCount(morningShiftCount.get()); performanceShiftExcelData.setDayShiftCount(dayShiftCount.get()); performanceShiftExcelData.setNightShiftCount(nightShiftCount.get()); performanceShiftExcelData.setHolidayLeaveCount(holidayCount.get()); performanceShiftExcelData.setPersonalLeaveCount(personalLeaveCount.get()); performanceShiftExcelData.setAnnualLeaveCount(annualLeaveCount.get()); performanceShiftExcelData.setOfficialTripCount(officialTripCount.get()); performanceShiftExcelData.setMarriageLeaveCount(marriageLeaveCount.get()); performanceShiftExcelData.setBereavementLeaveCount(bereavementLeaveCount.get()); performanceShiftExcelData.setSickLeaveCount(sickLeaveCount.get()); performanceShiftExcelData.setMaternityLeaveCount(maternityLeaveCount.get()); performanceShiftExcelData.setTotalWorkHourCount(attendanceWorkHourCount.get()); excelData.add(performanceShiftExcelData); } //导出 InputStream resourceAsStream = buildPerformanceShiftTemplate(performanceShiftDateList,isMonth); try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(resourceAsStream).registerWriteHandler(new CommentWriteHandler(excelData, annotationTextList)).relativeHeadRowIndex(4).build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); excelWriter.fill(excelData, writeSheet); if(!isMonth){ String startDateStr = startDate.format(yyyMMStr); String endDateStr = endDate.format(yyyMMStr); Map dateMap = new HashMap<>(); dateMap.put("startDate",startDateStr); dateMap.put("endDate",endDateStr); excelWriter.fill(dateMap, writeSheet); } } }catch (Exception e){ throw new RuntimeException("班次导出异常"); } } private InputStream buildPerformanceShiftTemplate(List attendanceDateList,Boolean isMonth) throws IOException { String templateName = "/static/performance_shift_month_template.xlsx"; if(!isMonth){ templateName = "/static/performance_shift_year_template.xlsx"; } try (InputStream templateStream = this.getClass().getResourceAsStream(templateName)) { assert templateStream != null; try (Workbook workbook = WorkbookFactory.create(templateStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { Sheet sheet = workbook.getSheetAt(0); if(isMonth){ fillPerformanceShiftHeader(sheet, attendanceDateList); } workbook.write(outputStream); return new ByteArrayInputStream(outputStream.toByteArray()); } } catch (Exception e) { throw new IOException("构建班次导出模板失败", e); } } private void fillPerformanceShiftHeader(Sheet sheet, List attendanceDateList) { if (sheet == null || attendanceDateList == null || attendanceDateList.isEmpty()) { return; } Row titleRow = sheet.getRow(1); if (titleRow != null) { Cell titleCell = titleRow.getCell(0); if (titleCell != null) { titleCell.setCellValue(attendanceDateList.get(attendanceDateList.size() - 1).format(DateTimeFormatter.ofPattern("yyyy年M月"))); } } Row weekRow = sheet.getRow(2); Row dayRow = sheet.getRow(3); if (weekRow == null || dayRow == null) { return; } final int startColumnIndex = 2; final int maxDateColumnCount = 31; for (int i = 0; i < maxDateColumnCount; i++) { Cell weekCell = getOrCreateCell(weekRow, startColumnIndex + i, startColumnIndex); Cell dayCell = getOrCreateCell(dayRow, startColumnIndex + i, startColumnIndex); if (i < attendanceDateList.size()) { LocalDate currentDate = attendanceDateList.get(i); weekCell.setCellValue(resolveWeekOfYear(currentDate)); dayCell.setCellValue(currentDate.getDayOfMonth()); } else { weekCell.setBlank(); dayCell.setBlank(); } } } private Cell getOrCreateCell(Row row, int cellIndex, int templateCellIndex) { Cell cell = row.getCell(cellIndex); if (cell != null) { return cell; } Cell templateCell = row.getCell(templateCellIndex); cell = row.createCell(cellIndex); if (templateCell != null && templateCell.getCellStyle() != null) { cell.setCellStyle(templateCell.getCellStyle()); } return cell; } private String resolveWeekOfYear(LocalDate date) { switch (date.getDayOfWeek()) { case MONDAY: return "一"; case TUESDAY: return "二"; case WEDNESDAY: return "三"; case THURSDAY: return "四"; case FRIDAY: return "五"; case SATURDAY: return "六"; case SUNDAY: return "日"; default: return ""; } } private List buildPerformanceShiftDateList(LocalDateTime startDateTime,LocalDateTime endDateTime,Boolean isMonth) { if (startDateTime == null || endDateTime == null) { throw new IllegalArgumentException("导出时间范围不能为空"); } LocalDate startDate = startDateTime.toLocalDate(); LocalDate endDate = endDateTime.toLocalDate(); if (startDate.isAfter(endDate)) { throw new IllegalArgumentException("开始时间不能晚于结束时间"); } List attendanceDateList = new ArrayList<>(); for (LocalDate currentDate = startDate; !currentDate.isAfter(endDate); currentDate = currentDate.plusDays(1)) { attendanceDateList.add(currentDate); } if (attendanceDateList.size() > 31 && isMonth) { throw new IllegalArgumentException("导出时间范围不能超过31天"); } return attendanceDateList; } // 获取两个localDateTime的每一天 public static List getLocalDateTimesBetween(LocalDateTime start, LocalDateTime end) { List localDateTimes = new ArrayList<>(); LocalDate currentDate = start.toLocalDate(); LocalDateTime currentLocalDateTime = start; while (!currentDate.isAfter(end.toLocalDate())) { localDateTimes.add(currentLocalDateTime); currentLocalDateTime = currentLocalDateTime.plusDays(1); currentDate = currentDate.plusDays(1); } return localDateTimes; } public static String getWeek(String dayStr) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { Date date = sdf.parse(dayStr); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); int day = calendar.get(Calendar.DAY_OF_MONTH); return day + " " + getWeekDay(dayOfWeek); } catch (Exception e) { e.printStackTrace(); } return null; } public static String getWeekDay(int dayOfWeek) { switch (dayOfWeek) { case Calendar.MONDAY: return "周一"; case Calendar.TUESDAY: return "周二"; case Calendar.WEDNESDAY: return "周三"; case Calendar.THURSDAY: return "周四"; case Calendar.FRIDAY: return "周五"; case Calendar.SATURDAY: return "周六"; case Calendar.SUNDAY: return "周日"; default: return "未知"; } } /** * 返回表头 *

* 外层List代表行内层 List代表列 相同的列数据会被主动合并 * 构造双列表头 * * @return List> */ private static List> getYearHeader(String year, List enums) { List> line = new ArrayList<>(); line.add(Arrays.asList("考勤汇总", "序号", "序号")); line.add(Arrays.asList("考勤汇总", "工号", "工号")); line.add(Arrays.asList("考勤汇总", "姓名", "姓名")); line.add(Arrays.asList("出勤详情", year, "出勤")); // 年 header for (SysDictData anEnum : enums) { if (!anEnum.getDictValue().equals("5")) { line.add(Arrays.asList("考勤汇总", year, anEnum.getDictLabel())); } } // 月header for (int i = 1; i < 13; i++) { line.add(Arrays.asList("出勤详情", i + " 月", "出勤")); for (SysDictData anEnum : enums) { if (!anEnum.getDictValue().equals("5")) { line.add(Arrays.asList("出勤详情", i + " 月", anEnum.getDictLabel())); } } } return line; } private static List> getMonthHeader(LocalDateTime localDateTimeYear, List enums) { String year = localDateTimeYear.getYear() + " 年人员班次"; List> line = new ArrayList<>(); line.add(Arrays.asList(year, "序号", "序号", "序号")); line.add(Arrays.asList(year, "姓名", "姓名", "姓名")); line.add(Arrays.asList(year, "实验室", "实验室", "实验室")); line.add(Arrays.asList(year, localDateTimeYear.getYear() + "", localDateTimeYear.getYear() + "", "出勤")); line.add(Arrays.asList(year, localDateTimeYear.getYear() + "", localDateTimeYear.getYear() + "", enums.get(3).getDictLabel())); line.add(Arrays.asList(year, "年", "年", enums.get(4).getDictLabel())); line.add(Arrays.asList(year, localDateTimeYear.getMonthValue() + "", localDateTimeYear.getMonthValue() + "", enums.get(0).getDictLabel())); line.add(Arrays.asList(year, "月", "月", enums.get(1).getDictLabel())); line.add(Arrays.asList(year, "", "", enums.get(2).getDictLabel())); line.add(Arrays.asList(year, "周次", "星期", "出差")); LocalDate firstDayOfMonth = localDateTimeYear.toLocalDate().withDayOfMonth(1); LocalDate lastDayOfMonth = localDateTimeYear.toLocalDate().with(TemporalAdjusters.lastDayOfMonth()); List timeList = getLocalDateTimesBetween(firstDayOfMonth.atStartOfDay(), lastDayOfMonth.atStartOfDay()); timeList.forEach(i -> { int dayOfYear = i.getDayOfMonth(); Date from = Date.from(i.atZone(ZoneId.systemDefault()).toInstant()); String weekDay = getWeekDay(i.getDayOfWeek().getValue()); line.add(Arrays.asList(year, DateUtil.weekOfYear(DateUtil.offsetDay(from, 1)) + "", weekDay, dayOfYear + "")); }); return line; } public List> dataRequiredForProcessingIntoExcelMonth(List list, List enums) { List> data = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { List excelRowList = new ArrayList<>(); excelRowList.add(i + 1); excelRowList.add(list.get(i).getUserName()); excelRowList.add(list.get(i).getDepartment()); excelRowList.add(list.get(i).getMonthlyAttendance().get("totalAttendance")); excelRowList.add(list.get(i).getMonthlyAttendance().get(enums.get(3).getDictLabel())); // 休 excelRowList.add(list.get(i).getMonthlyAttendance().get(enums.get(4).getDictLabel())); // 假 excelRowList.add(list.get(i).getMonthlyAttendance().get(enums.get(0).getDictLabel())); // 早 excelRowList.add(list.get(i).getMonthlyAttendance().get(enums.get(1).getDictLabel())); // 中 excelRowList.add(list.get(i).getMonthlyAttendance().get(enums.get(2).getDictLabel())); // 夜 excelRowList.add(list.get(i).getMonthlyAttendance().get(enums.get(6).getDictLabel())); // 差 data.add(excelRowList); } return data; } }