From 948194840fc18c00617cbf3cf72f9e81bb0ee5b3 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期二, 14 四月 2026 16:50:00 +0800
Subject: [PATCH] 人员考勤调整:导出区分年度和月度

---
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/StaffAttendanceTrackingRecordServiceImpl.java |  332 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 300 insertions(+), 32 deletions(-)

diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/StaffAttendanceTrackingRecordServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/StaffAttendanceTrackingRecordServiceImpl.java
index 99d2aaf..6dbf68b 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/StaffAttendanceTrackingRecordServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/StaffAttendanceTrackingRecordServiceImpl.java
@@ -2,25 +2,33 @@
 
 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;
@@ -28,17 +36,27 @@
 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;
 
 /**
@@ -66,6 +84,8 @@
     private DateTimeFormatter HHmm = DateTimeFormatter.ofPattern("HH:mm");
 
     private DateTimeFormatter yyyMMddHHmmss = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    private DateTimeFormatter yyyMMStr = DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�");
     /** 璐ㄩ噺閮╥d */
     private final static String deptIds = "6";
 
@@ -74,6 +94,14 @@
 
     // 鏁板瓧閮ㄥ垎鍥哄畾闀垮害
     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 = "鐥�";//鐥呭亣鐝鍏抽敭瀛�
 
     /**
      * 鍚屾鐨勯棬绂佽澶囧垪琛�
@@ -89,6 +117,11 @@
      */
     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)
@@ -136,13 +169,14 @@
         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()),
@@ -152,9 +186,6 @@
                                 .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++) {
@@ -181,26 +212,27 @@
                 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)) {
@@ -244,14 +276,17 @@
 
                 }
                 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);
@@ -282,7 +317,16 @@
                     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
@@ -362,6 +406,227 @@
         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骞碝鏈�")));
+            }
+        }
+        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 "";
+        }
+    }
+
     /**
      * 鑷畾涔夊垎椤垫柟娉�
      * 
@@ -409,20 +674,23 @@
     }
 
     /**
-     * 鑾峰彇鎸囧畾涓嬫爣鐨勭彮娆″紑濮嬫椂闂�
-     * 
-     * @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);
     }
 
     /**

--
Gitblit v1.9.3