zouyu
2026-04-14 948194840fc18c00617cbf3cf72f9e81bb0ee5b3
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年MM月dd日");
    /** 质量部id */
    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年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 "";
        }
    }
    /**
     * 自定义分页方法
     * 
@@ -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);
    }
    /**