| | |
| | | |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | 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.conditions.Wrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.core.domain.Result; |
| | | import com.google.common.util.concurrent.AtomicDouble; |
| | | import com.ruoyi.common.enums.ClockInState; |
| | | import com.ruoyi.common.enums.EnterOrExitType; |
| | | import com.ruoyi.common.enums.StaffAttendanceReportType; |
| | | import com.ruoyi.common.enums.SyncStatus; |
| | | import com.ruoyi.common.utils.api.icc.IccApiUtil; |
| | | import com.ruoyi.common.utils.api.icc.model.GetResultPageRequest; |
| | | import com.ruoyi.common.utils.api.icc.model.GetResultPageResponse; |
| | | import com.ruoyi.inspect.dto.StaffAttendanceDTO; |
| | | import com.ruoyi.inspect.excel.StaffAttendanceAnnotationTextExcelData; |
| | | import com.ruoyi.inspect.excel.StaffAttendanceExcelData; |
| | | import com.ruoyi.inspect.excel.handler.CommentWriteHandler; |
| | | import com.ruoyi.inspect.mapper.StaffAttendanceTrackingRecordMapper; |
| | | import com.ruoyi.inspect.pojo.StaffAttendanceTrackingRecord; |
| | | import com.ruoyi.inspect.service.StaffAttendanceTrackingRecordService; |
| | | import com.ruoyi.inspect.util.HourDiffCalculator; |
| | | import com.ruoyi.inspect.util.TimeDiffCalculator; |
| | | import com.ruoyi.inspect.vo.StaffAttendanceVO; |
| | | import com.ruoyi.inspect.vo.StaffClockInVO; |
| | | import com.ruoyi.performance.dto.PerformanceShiftMapDto; |
| | | import com.ruoyi.performance.mapper.PerformanceShiftMapper; |
| | | import com.ruoyi.performance.mapper.ShiftTimeMapper; |
| | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.ObjectUtils; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | 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 javax.servlet.http.HttpServletResponse; |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.ByteArrayOutputStream; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | private DateTimeFormatter HHmm = DateTimeFormatter.ofPattern("HH:mm"); |
| | | |
| | | private DateTimeFormatter yyyMMddHHmmss = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | private DateTimeFormatter yyyMMStr = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); |
| | | /** 质量部id */ |
| | | private final static String deptIds = "6"; |
| | | |
| | |
| | | |
| | | // 数字部分固定长度 |
| | | private static final int DIGIT_LENGTH = 6; |
| | | |
| | | private static final String holidayLeaveKeyword = "休";//休假,调休假班次关键字 |
| | | private static final String personalLeaveKeyword = "事";//事假班次关键字 |
| | | private static final String annualLeaveKeyword = "年";//年假班次关键字 |
| | | private static final String officialTripKeyword = "公";//公差班次关键字 |
| | | private static final String marriageLeaveKeyword = "婚";//婚假班次关键字 |
| | | private static final String bereavementLeaveKeyword = "丧";//丧假班次关键字 |
| | | private static final String sickLeaveKeyword = "病";//病假班次关键字 |
| | | |
| | | /** |
| | | * 同步的门禁设备列表 |
| | |
| | | */ |
| | | private final static List<String> syncDeviceCode = Arrays.asList("1001538", "1001539", "1001540", "1001541", |
| | | "1001626", "1001627", "1001628", "1001629"); |
| | | |
| | | /** |
| | | * 上班时间判定边界小时数 |
| | | */ |
| | | private final static long WORK_TIME_BOUNDARY = 2L; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public IPage<StaffAttendanceVO> pageAttendanceRecord(Page<StaffAttendanceTrackingRecord> page, |
| | | StaffAttendanceDTO staffAttendanceDTO) { |
| | | /** |
| | | * 查询考勤记录 |
| | | * @param performanceShifts 班次信息 |
| | | * @param staffAttendanceDTO 考勤查询条件 |
| | | * @return |
| | | */ |
| | | public List<StaffAttendanceVO> getAttendanceRecord(List<PerformanceShiftMapDto> performanceShifts,StaffAttendanceDTO staffAttendanceDTO) { |
| | | // 查询打卡记录 |
| | | System.out.println(staffAttendanceDTO.getStartDate()); |
| | | System.out.println(staffAttendanceDTO.getEndDate()); |
| | | System.out.println(ObjectUtils.allNotNull(staffAttendanceDTO.getStartDate(),staffAttendanceDTO.getEndDate())); |
| | | Wrapper<StaffAttendanceTrackingRecord> queryWrapper = Wrappers.<StaffAttendanceTrackingRecord>lambdaQuery() |
| | | .eq(StaffAttendanceTrackingRecord::getEnableReport, Boolean.TRUE) |
| | | .between(ObjectUtils.allNotNull(staffAttendanceDTO.getStartDate(),staffAttendanceDTO.getEndDate()), |
| | |
| | | .or() |
| | | .like(StaffAttendanceTrackingRecord::getPersonName, staffAttendanceDTO.getKeyword())); |
| | | List<StaffAttendanceTrackingRecord> recordList = baseMapper.selectList(queryWrapper); |
| | | // 查询班次 |
| | | List<PerformanceShiftMapDto> performanceShifts = performanceShiftMapper.selectListByWorkTime( |
| | | staffAttendanceDTO.getStartDate(), staffAttendanceDTO.getEndDate(), staffAttendanceDTO.getKeyword()); |
| | | // 组装数据 |
| | | List<StaffAttendanceVO> resultList = new ArrayList<>(); |
| | | for (int i = 0; i < performanceShifts.size(); i++) { |
| | |
| | | * 正常:当前班次结束后第一次出门时间 |
| | | * 异常(早退):无当前班次结束到下一班次开始前的出门记录并且最后一次出门在当前班次时间范围内,取当前班次最后一次出门时间 |
| | | */ |
| | | //当前时间 |
| | | LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai")); |
| | | // 当前班次开始天0点时间 |
| | | LocalDateTime startDateTime = LocalDateTime.of(p.getWorkTime().toLocalDate(), LocalTime.MIN); |
| | | // 当前班次结束天24点时间 |
| | |
| | | LocalTime currentShiftStartTime = LocalTime.parse(p.getStartTime(), HHmm); |
| | | LocalDateTime currentShiftStartDateTime = LocalDateTime.of(p.getWorkTime().toLocalDate(), |
| | | currentShiftStartTime); |
| | | // 当前班次结束时间 |
| | | LocalTime currentShiftEndTime = LocalTime.parse(p.getEndTime(), HHmm); |
| | | LocalDateTime currentShiftEndDateTime = LocalDateTime.of(endDateTime.toLocalDate(), |
| | | currentShiftEndTime); |
| | | // 下一班次开始时间 |
| | | LocalDateTime nextShiftStartDateTime = getShiftStartDateTime(i + 1, performanceShifts, |
| | | startDateTime.plusDays(1L)); |
| | | LocalDateTime nextShiftStartDateTime = getShiftStartDateTime(p.getPersonCode(), performanceShifts, startDateTime.plusDays(1L)); |
| | | if (Double.compare(hourDiff, 0) == -1) { |
| | | // 如果小时差为负数,表示跨天,结束时间需加一 |
| | | endDateTime = endDateTime.plusDays(1L); |
| | | } |
| | | // 当前班次结束时间 |
| | | LocalTime currentShiftEndTime = LocalTime.parse(p.getEndTime(), HHmm); |
| | | LocalDateTime currentShiftEndDateTime = LocalDateTime.of(endDateTime.toLocalDate(), |
| | | currentShiftEndTime); |
| | | // 过滤出当前人员当前班次的进/出记录 |
| | | LocalDateTime workDateTime = null; |
| | | LocalDateTime offWorkDateTime = null; |
| | | List<StaffAttendanceTrackingRecord> enterRecords = filterAttendanceRecord(p.getPersonCode(), |
| | | EnterOrExitType.ENTER.getValue(), startDateTime, endDateTime, recordList); |
| | | //上班时间判定边界 |
| | | LocalDateTime boundaryTime = currentShiftStartDateTime.minusHours(WORK_TIME_BOUNDARY); |
| | | if (!enterRecords.isEmpty()) { |
| | | // 上班时间和状态 |
| | | StaffAttendanceTrackingRecord enterRecord = enterRecords.stream() |
| | | .filter(s -> !s.getSwingTime().isAfter(currentShiftStartDateTime)) |
| | | .filter(s -> !s.getSwingTime().isAfter(currentShiftStartDateTime) && !s.getSwingTime().isBefore(boundaryTime)) |
| | | .max(Comparator.comparing(StaffAttendanceTrackingRecord::getSwingTime)) |
| | | .orElse(new StaffAttendanceTrackingRecord()); |
| | | if (BeanUtil.isEmpty(enterRecord)) { |
| | |
| | | && s.getSwingTime().isBefore(nextShiftStartDateTime)) |
| | | .min(Comparator.comparing(StaffAttendanceTrackingRecord::getSwingTime)) |
| | | .orElse(new StaffAttendanceTrackingRecord()); |
| | | if (BeanUtil.isEmpty(exitRecord)) { |
| | | if (BeanUtil.isEmpty(exitRecord) && !now.isBefore(currentShiftEndDateTime)) { |
| | | exitRecord = exitRecords.stream() |
| | | .filter(s -> (s.getSwingTime().isAfter(currentShiftStartDateTime) |
| | | && s.getSwingTime().isBefore(currentShiftEndDateTime))) |
| | |
| | | |
| | | } |
| | | if (ObjectUtils.allNotNull(workDateTime, offWorkDateTime)) { |
| | | vo.setActualWorkHours(HourDiffCalculator.getHourDiff(workDateTime.toLocalTime().format(HHmm), |
| | | offWorkDateTime.toLocalTime().format(HHmm))); |
| | | vo.setActualWorkHours(TimeDiffCalculator.getHourDiff(workDateTime, |
| | | offWorkDateTime)); |
| | | } |
| | | // 赋值 |
| | | vo.setShiftId(p.getShift()); |
| | | vo.setPersonCode(p.getPersonCode()); |
| | | vo.setPersonName(p.getUserName()); |
| | | vo.setPlannedWorkHours(hourDiff); |
| | | //应勤时长 |
| | | double plannedWorkHours = Math.abs(hourDiff); |
| | | vo.setDiffHour(hourDiff); |
| | | vo.setPlannedWorkHours(plannedWorkHours); |
| | | vo.setSwingDate(startDateTime); |
| | | vo.setWorkDateTime(workDateTime); |
| | | vo.setOffWorkDateTime(offWorkDateTime); |
| | |
| | | vo.setCreateTime(recordList.get(0).getCreateTime()); |
| | | vo.setUpdateUser(recordList.get(0).getUpdateUser()); |
| | | vo.setUpdateTime(recordList.get(0).getUpdateTime()); |
| | | vo.setResult(ClockInState.ABNORMAL.getValue()); |
| | | if(ObjectUtils.allNotNull(vo.getWorkClockInState(),vo.getOffClockInState())){ |
| | | vo.setResult(Integer.min(vo.getWorkClockInState(),vo.getOffClockInState())); |
| | | //当前时间在下班时间过后才判断考勤结果 |
| | | if(!now.isBefore(currentShiftEndDateTime)){ |
| | | if(ObjectUtils.allNotNull(vo.getWorkClockInState(),vo.getOffClockInState())){ |
| | | vo.setResult(Integer.min(vo.getWorkClockInState(),vo.getOffClockInState())); |
| | | }else{ |
| | | vo.setResult(ClockInState.ABNORMAL.getValue()); |
| | | } |
| | | } |
| | | // 计算缺勤时长 |
| | | if (ObjectUtils.allNotNull(vo.getActualWorkHours(), vo.getPlannedWorkHours())) { |
| | |
| | | resultList.add(vo); |
| | | } |
| | | } |
| | | return limitPages(page, resultList); |
| | | return resultList; |
| | | } |
| | | |
| | | @Override |
| | | public IPage<StaffAttendanceVO> pageAttendanceRecord(Page<StaffAttendanceTrackingRecord> page, |
| | | StaffAttendanceDTO staffAttendanceDTO) { |
| | | // 查询班次 |
| | | List<PerformanceShiftMapDto> performanceShifts = performanceShiftMapper.selectListByWorkTime( |
| | | staffAttendanceDTO.getStartDate(), staffAttendanceDTO.getEndDate(), staffAttendanceDTO.getKeyword()); |
| | | return limitPages(page, getAttendanceRecord(performanceShifts,staffAttendanceDTO)); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return this.saveOrUpdateBatch(records); |
| | | } |
| | | |
| | | @Override |
| | | public void exportStaffAttendanceRecords(HttpServletResponse response, StaffAttendanceDTO staffAttendanceDTO) { |
| | | response.reset(); |
| | | try{ |
| | | List<LocalDate> attendanceDateList = buildAttendanceDateList(staffAttendanceDTO); |
| | | //批注信息坐标信息 |
| | | List<StaffAttendanceAnnotationTextExcelData> annotationTextList = new ArrayList<>(); |
| | | // 查询班次 |
| | | List<PerformanceShiftMapDto> performanceShifts = performanceShiftMapper.selectListByWorkTime( |
| | | staffAttendanceDTO.getStartDate(), staffAttendanceDTO.getEndDate(), staffAttendanceDTO.getKeyword()); |
| | | //获取考勤数据 |
| | | List<StaffAttendanceVO> attendanceRecords = getAttendanceRecord(performanceShifts,staffAttendanceDTO); |
| | | //组装导出数据 |
| | | List<StaffAttendanceExcelData> excelData = new ArrayList<>(); |
| | | Map<Integer, List<PerformanceShiftMapDto>> groupByUserId = performanceShifts.stream().collect(Collectors.groupingBy(PerformanceShiftMapDto::getUserId)); |
| | | List<Integer> userIdKeys = groupByUserId.keySet().stream().sorted().collect(Collectors.toList()); |
| | | for (int i = 0; i < userIdKeys.size(); i++) { |
| | | List<PerformanceShiftMapDto> shiftMapDtos = groupByUserId.get(userIdKeys.get(i)); |
| | | StaffAttendanceExcelData attendanceExcelData = new StaffAttendanceExcelData(); |
| | | List<String> shiftList = new ArrayList<>(); |
| | | attendanceExcelData.setPersonName(shiftMapDtos.get(0).getUserName()); |
| | | attendanceExcelData.setExcelIndex(i+1); |
| | | 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 attendanceDayCount = 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(),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(); |
| | | }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(); |
| | | } |
| | | if(StringUtils.isAllBlank(shiftMapDto.getStartTime(),shiftMapDto.getEndTime())){ |
| | | shiftList.add(shiftMapDto.getShiftName()); |
| | | }else{ |
| | | //过滤当前人员的班次信息 |
| | | 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.isEmpty(vo)){ |
| | | shiftList.add(""); |
| | | }else{ |
| | | String actualWorkHours = Objects.toString(vo.getActualWorkHours(), ""); |
| | | Double diffHour = ObjectUtils.defaultIfNull(vo.getDiffHour(), 0D); |
| | | if (StringUtils.isBlank(actualWorkHours)) { |
| | | shiftList.add(""); |
| | | } else { |
| | | shiftList.add(Double.compare(diffHour, 0D) < 0 ? "-" + actualWorkHours : actualWorkHours); |
| | | attendanceDayCount.getAndIncrement(); |
| | | attendanceWorkHourCount.getAndAdd(Double.parseDouble(actualWorkHours)); |
| | | } |
| | | } |
| | | } |
| | | //月度统计才插入批注数据 |
| | | if(StringUtils.isNoneBlank(staffAttendanceDTO.getAttendanceReportType()) && StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(), StaffAttendanceReportType.MONTH.name())){ |
| | | if(StringUtils.isNotBlank(shiftMapDto.getAnnotationText())){ |
| | | annotationTextList.add(new StaffAttendanceAnnotationTextExcelData(i,j,shiftMapDto.getAnnotationText())); |
| | | } |
| | | } |
| | | } |
| | | if(StringUtils.isNoneBlank(staffAttendanceDTO.getAttendanceReportType()) && StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(), StaffAttendanceReportType.MONTH.name())){ |
| | | attendanceExcelData.setShiftList(shiftList); |
| | | } |
| | | attendanceExcelData.setAttendanceDayCount(attendanceDayCount.get()); |
| | | attendanceExcelData.setAttendanceWorkHourCount(attendanceWorkHourCount.get()); |
| | | //班次考勤天数 |
| | | attendanceExcelData.setHolidayCount(holidayCount.get()); |
| | | attendanceExcelData.setPersonalLeaveCount(personalLeaveCount.get()); |
| | | attendanceExcelData.setAnnualLeaveCount(annualLeaveCount.get()); |
| | | attendanceExcelData.setOfficialTripCount(officialTripCount.get()); |
| | | attendanceExcelData.setMarriageLeaveCount(marriageLeaveCount.get()); |
| | | attendanceExcelData.setBereavementLeaveCount(bereavementLeaveCount.get()); |
| | | attendanceExcelData.setSickLeaveCount(sickLeaveCount.get()); |
| | | excelData.add(attendanceExcelData); |
| | | } |
| | | //导出 |
| | | 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"); |
| | | InputStream resourceAsStream = buildAttendanceTemplate(attendanceDateList,staffAttendanceDTO.getAttendanceReportType()); |
| | | 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(StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(),StaffAttendanceReportType.YEAR.name())){ |
| | | String startDate = staffAttendanceDTO.getStartDate().format(yyyMMStr); |
| | | String endDate = staffAttendanceDTO.getEndDate().format(yyyMMStr); |
| | | Map<String, String> dateMap = new HashMap<>(); |
| | | dateMap.put("startDate",startDate); |
| | | dateMap.put("endDate",endDate); |
| | | excelWriter.fill(dateMap, writeSheet); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | |
| | | private List<LocalDate> buildAttendanceDateList(StaffAttendanceDTO staffAttendanceDTO) { |
| | | if (staffAttendanceDTO == null || staffAttendanceDTO.getStartDate() == null || staffAttendanceDTO.getEndDate() == null) { |
| | | throw new IllegalArgumentException("导出时间范围不能为空"); |
| | | } |
| | | LocalDate startDate = staffAttendanceDTO.getStartDate().toLocalDate(); |
| | | LocalDate endDate = staffAttendanceDTO.getEndDate().toLocalDate(); |
| | | if (startDate.isAfter(endDate)) { |
| | | throw new IllegalArgumentException("开始时间不能晚于结束时间"); |
| | | } |
| | | List<LocalDate> attendanceDateList = new ArrayList<>(); |
| | | for (LocalDate currentDate = startDate; !currentDate.isAfter(endDate); currentDate = currentDate.plusDays(1)) { |
| | | attendanceDateList.add(currentDate); |
| | | } |
| | | if (attendanceDateList.size() > 31 && StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(),StaffAttendanceReportType.MONTH.name())) { |
| | | throw new IllegalArgumentException("导出时间范围不能超过31天"); |
| | | } |
| | | return attendanceDateList; |
| | | } |
| | | |
| | | private InputStream buildAttendanceTemplate(List<LocalDate> attendanceDateList,String attendanceReportType) throws IOException { |
| | | String templateName = "/static/staff_attendance_month_template.xlsx"; |
| | | if(StringUtils.equals(attendanceReportType,StaffAttendanceReportType.YEAR.name())){ |
| | | templateName = "/static/staff_attendance_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(StringUtils.equals(attendanceReportType,StaffAttendanceReportType.MONTH.name())){ |
| | | fillAttendanceHeader(sheet, attendanceDateList); |
| | | } |
| | | workbook.write(outputStream); |
| | | return new ByteArrayInputStream(outputStream.toByteArray()); |
| | | } |
| | | } catch (Exception e) { |
| | | throw new IOException("构建考勤导出模板失败", e); |
| | | } |
| | | } |
| | | |
| | | private void fillAttendanceHeader(Sheet sheet, List<LocalDate> 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 ""; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 自定义分页方法 |
| | | * |
| | |
| | | long pages = getPages(size,total); |
| | | int startIndex = Math.toIntExact((current - 1) * size >= total ? (pages - 1) * size : (current - 1) * size); |
| | | int endIndex = Math.toIntExact(Math.min(current * size, total)); |
| | | List<StaffAttendanceVO> records = resultList.subList(startIndex, endIndex); |
| | | List<StaffAttendanceVO> records; |
| | | if(resultList.isEmpty()){ |
| | | records = new ArrayList<>(); |
| | | }else{ |
| | | records = resultList.subList(startIndex, endIndex); |
| | | } |
| | | resultPage.setRecords(records); |
| | | resultPage.setTotal(total); |
| | | resultPage.setSize(size); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取指定下标的班次开始时间 |
| | | * |
| | | * @param index |
| | | * @param dtoList |
| | | * 获取当前班次日期的下一班次开始时间 |
| | | * @param personCode 人员编号 |
| | | * @param dtoList 班次列表 |
| | | * @param nextShiftTime 下一班次时间 |
| | | * @return |
| | | */ |
| | | private LocalDateTime getShiftStartDateTime(int index, List<PerformanceShiftMapDto> dtoList, |
| | | LocalDateTime nextShiftTime) { |
| | | if (dtoList.isEmpty() || index >= dtoList.size()) { |
| | | private LocalDateTime getShiftStartDateTime(String personCode,List<PerformanceShiftMapDto> dtoList, LocalDateTime nextShiftTime) { |
| | | if (dtoList.isEmpty()) { |
| | | return LocalDateTime.of(nextShiftTime.toLocalDate(), LocalTime.MAX); |
| | | } |
| | | LocalTime localTime = ObjectUtil.isNull(dtoList.get(index).getStartTime()) ? LocalTime.MAX |
| | | : LocalTime.parse(dtoList.get(index).getStartTime(), HHmm); |
| | | return LocalDateTime.of(nextShiftTime.toLocalDate(), localTime); |
| | | //过滤当前人员的下一班次信息 |
| | | PerformanceShiftMapDto shiftMapDto = dtoList.stream().filter(f -> StringUtils.equals(f.getPersonCode(), personCode) && f.getWorkTime().isEqual(nextShiftTime)).findFirst().orElse(new PerformanceShiftMapDto()); |
| | | if(StringUtils.isEmpty(shiftMapDto.getStartTime())){ |
| | | return LocalDateTime.of(nextShiftTime.toLocalDate(), LocalTime.MAX); |
| | | } |
| | | LocalTime nextShiftStartTime = LocalTime.parse(shiftMapDto.getStartTime(), HHmm); |
| | | return LocalDateTime.of(nextShiftTime.toLocalDate(), nextShiftStartTime); |
| | | } |
| | | |
| | | /** |