zouyu
2026-08-13 e1aacd2adff671b44658f476722f4c4fc30c7eb5
performance-server/src/main/java/com/ruoyi/performance/service/impl/StaffAttendanceTrackingRecordServiceImpl.java
@@ -12,27 +12,28 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.enums.*;
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.performance.dto.PerformanceShiftMapDto;
import com.ruoyi.performance.dto.StaffAttendanceDTO;
import com.ruoyi.performance.excel.StaffAttendanceAnnotationTextExcelData;
import com.ruoyi.performance.excel.StaffAttendanceExcelData;
import com.ruoyi.performance.excel.handler.attendance.CommentWriteHandler;
import com.ruoyi.performance.mapper.StaffAttendanceTrackingRecordMapper;
import com.ruoyi.performance.pojo.StaffAttendanceTrackingRecord;
import com.ruoyi.performance.service.StaffAttendanceTrackingRecordService;
import com.ruoyi.performance.dto.PerformanceShiftMapDto;
import com.ruoyi.performance.mapper.PerformanceShiftMapper;
import com.ruoyi.performance.mapper.ShiftTimeMapper;
import com.ruoyi.performance.mapper.StaffAttendanceTrackingRecordMapper;
import com.ruoyi.performance.pojo.ShiftTime;
import com.ruoyi.performance.pojo.StaffAttendanceTrackingRecord;
import com.ruoyi.performance.service.StaffAttendanceTrackingRecordService;
import com.ruoyi.performance.utils.HourDiffCalculator;
import com.ruoyi.performance.utils.TimeDiffCalculator;
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.commons.lang3.StringUtils;
@@ -50,10 +51,7 @@
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.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
@@ -72,6 +70,9 @@
    @Autowired
    private IccApiUtil iccApiUtil;
    @Autowired
    private ISysDictTypeService sysDictTypeService;
    @Autowired
    private PerformanceShiftMapper performanceShiftMapper;
@@ -108,21 +109,6 @@
    private static final String maternityLeaveKeyword = "产";//产假班次关键字
    /**
     * 同步的门禁设备列表
     * channel_name device_code
     * 10.100.22.2_门禁通道_1 1001538
     * 10.100.22.3_门禁通道_1 1001539
     * 10.100.22.4_门禁通道_1 1001540
     * 10.100.22.5_门禁通道_1 1001541
     * 10.100.22.12_门禁通道_1 1001626
     * 10.100.22.13_门禁通道_1 1001627
     * 10.100.22.14_门禁通道_1 1001628
     * 10.100.22.15_门禁通道_1 1001629
     */
    private final static List<String> syncDeviceCode = Arrays.asList("1001538", "1001539", "1001540", "1001541",
            "1001626", "1001627", "1001628", "1001629");
    /**
     * 上班时间判定边界小时数
     */
    private final static long WORK_TIME_BOUNDARY = 2L;
@@ -133,7 +119,9 @@
        if (ObjectUtil.isAllEmpty(startDate, endDate)) {
            throw new RuntimeException("同步日期不能为空");
        }
        //查询门禁设备字典列表
        List<SysDictData> performanceIccDevice = sysDictTypeService.selectDictDataByType("performance_icc_device");
        Set<String> syncDeviceCode = performanceIccDevice.stream().map(SysDictData::getDictValue).collect(Collectors.toSet());
        // 查询已同步的记录
        List<Long> trackingRecordIccIdList = baseMapper.selectIccIdList();
        try {
@@ -412,10 +400,37 @@
        return this.saveOrUpdateBatch(records);
    }
    /**
     * 根据导出日期格式化导出时间范围
     * @param staffAttendanceDTO
     */
    private static void formatExportDateRange(StaffAttendanceDTO staffAttendanceDTO){
        if(StringUtils.isNoneBlank(staffAttendanceDTO.getReportDate())){
            LocalTime startTime = LocalTime.of(0,0,0);
            LocalTime endTime = LocalTime.of(23,59,59);
            YearMonth yearMonth;
            if(StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(),CalendarType.MONTH.name())){
                yearMonth = YearMonth.parse(staffAttendanceDTO.getReportDate(),DateTimeFormatter.ofPattern("yyyy-MM"));
                LocalDateTime startDateTime = LocalDateTime.of(yearMonth.minusMonths(1L).atDay(26),startTime);
                LocalDateTime endDateTime = LocalDateTime.of(yearMonth.atDay(25),endTime);
                staffAttendanceDTO.setStartDate(startDateTime);
                staffAttendanceDTO.setEndDate(endDateTime);
            }else if(StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(),CalendarType.YEAR.name())){
                Year year = Year.parse(staffAttendanceDTO.getReportDate(),DateTimeFormatter.ofPattern("yyyy"));
                yearMonth = year.atMonth(1);
                LocalDateTime startDateTime = LocalDateTime.of(yearMonth.minusMonths(1L).atDay(26),startTime);
                LocalDateTime endDateTime = LocalDateTime.of(yearMonth.withMonth(12).atDay(25),endTime);
                staffAttendanceDTO.setStartDate(startDateTime);
                staffAttendanceDTO.setEndDate(endDateTime);
            }
        }
    }
    @Override
    public void exportStaffAttendanceRecords(HttpServletResponse response, StaffAttendanceDTO staffAttendanceDTO) {
        response.reset();
        try{
            formatExportDateRange(staffAttendanceDTO);
            //查询人员架构
            List<User> userList = userMapper.selectUserListByPerformance(false);
            List<Integer> userIdList = userList.stream().map(User::getId).collect(Collectors.toList());
@@ -423,6 +438,7 @@
            //批注信息坐标信息
            List<StaffAttendanceAnnotationTextExcelData> annotationTextList = new ArrayList<>();
            // 查询班次
            List<PerformanceShiftMapDto> performanceShifts = performanceShiftMapper.selectListByWorkTime(
                    staffAttendanceDTO.getStartDate(), staffAttendanceDTO.getEndDate(), staffAttendanceDTO.getKeyword());
            //获取考勤数据
@@ -447,50 +463,55 @@
                AtomicInteger maternityLeaveCount = 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();
                    }else if(StringUtils.contains(shiftMapDto.getShiftName(),maternityLeaveKeyword)){
                        maternityLeaveCount.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("");
                for (int j = 0; j < attendanceDateList.size(); j++) {
                    LocalDate localDate = attendanceDateList.get(j);
                    PerformanceShiftMapDto shiftMapDto = shiftMapDtos.stream().filter(f -> f.getWorkTime().toLocalDate().equals(localDate)).findFirst().orElse(null);
                    if(Objects.nonNull(shiftMapDto)){
                        //统计各假期和公差的天数
                        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();
                        }else if(StringUtils.contains(shiftMapDto.getShiftName(),maternityLeaveKeyword)){
                            maternityLeaveCount.getAndIncrement();
                        }
                        if(StringUtils.isAllBlank(shiftMapDto.getStartTime(),shiftMapDto.getEndTime())){
                            shiftList.add(shiftMapDto.getShiftName());
                        }else{
                            String actualWorkHours = Objects.toString(vo.getActualWorkHours(), "");
                            Double diffHour = ObjectUtils.defaultIfNull(vo.getDiffHour(), 0D);
                            if (StringUtils.isBlank(actualWorkHours)) {
                            //过滤当前人员的班次信息
                            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 {
                                shiftList.add(Double.compare(diffHour, 0D) < 0 ? "-" + actualWorkHours : actualWorkHours);
                                attendanceDayCount.getAndIncrement();
                                attendanceWorkHourCount.getAndAdd(Double.parseDouble(actualWorkHours));
                            }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(), CalendarType.MONTH.name())){
                        if(StringUtils.isNotBlank(shiftMapDto.getAnnotationText())){
                            annotationTextList.add(new StaffAttendanceAnnotationTextExcelData(i,j,shiftMapDto.getAnnotationText()));
                        //月度统计才插入批注数据
                        if(StringUtils.isNoneBlank(staffAttendanceDTO.getAttendanceReportType()) && StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(), CalendarType.MONTH.name())){
                            if(StringUtils.isNotBlank(shiftMapDto.getAnnotationText())){
                                annotationTextList.add(new StaffAttendanceAnnotationTextExcelData(i,j,shiftMapDto.getAnnotationText()));
                            }
                        }
                    }else{
                        shiftList.add("");
                    }
                }
                if(StringUtils.isNoneBlank(staffAttendanceDTO.getAttendanceReportType()) && StringUtils.equals(staffAttendanceDTO.getAttendanceReportType(), CalendarType.MONTH.name())){
@@ -738,22 +759,34 @@
    public static String restorePersonCode(Object number) {
        // 1. 空值校验
        if (number == null) {
            throw new IllegalArgumentException("传入数字不能为空");
            return "";
        }
        String original = number.toString();
        try {
        // 2. 统一转换为字符串并去除首尾空格
        String numStr = number.toString().trim();
            String numStr = original.trim();
        // 3. 校验是否为纯数字(排除负数、非数字字符)
        if (!numStr.matches("\\d+")) {
            throw new IllegalArgumentException("传入的不是有效正整数:" + numStr);
        }
            String digitStr;
            if (numStr.matches("\\d+")) {
                digitStr = numStr;
            } else if (numStr.matches("(?i)^ZT\\d+$")) {
                digitStr = numStr.substring(2);
            } else if (numStr.matches("(?i)^ZT-\\d+$")) {
                digitStr = numStr.substring(3);
            } else {
                return original;
            }
        // 4. 补前置0到指定长度(6位),超出则保留原数字
        String paddedNum = String.format("%0" + DIGIT_LENGTH + "d", Long.parseLong(numStr));
            String paddedNum = StringUtils.leftPad(digitStr, DIGIT_LENGTH, '0');
        // 5. 拼接前缀返回
        return PREFIX + paddedNum;
            return PREFIX + paddedNum;
        } catch (Exception e) {
            return original;
        }
    }
}