10 小时以前 571ccc18671ef45c6403496e8d99efec82168083
src/main/java/com/ruoyi/staff/service/impl/PersonalShiftServiceImpl.java
@@ -1,25 +1,23 @@
package com.ruoyi.staff.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.project.system.domain.SysDictData;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.staff.dto.PerformanceShiftAddDto;
import com.ruoyi.staff.dto.PerformanceShiftMapDto;
import com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper;
import com.ruoyi.staff.mapper.PersonalShiftMapper;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.ruoyi.staff.pojo.PersonalShift;
import com.ruoyi.staff.mapper.PersonalShiftMapper;
import com.ruoyi.staff.service.PersonalAttendanceLocationConfigService;
import com.ruoyi.staff.service.PersonalShiftService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.staff.utils.JackSonUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import cn.hutool.core.date.DateUtil;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
@@ -27,11 +25,11 @@
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -44,10 +42,10 @@
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class PersonalShiftServiceImpl extends ServiceImpl<PersonalShiftMapper, PersonalShift> implements PersonalShiftService {
    @Autowired
    private PersonalAttendanceLocationConfigMapper personalAttendanceLocationConfigMapper;
    private final PersonalAttendanceLocationConfigMapper personalAttendanceLocationConfigMapper;
    @Override
    public int performanceShiftAdd(PerformanceShiftAddDto performanceShiftAddDto) {
@@ -119,47 +117,146 @@
        IPage<PerformanceShiftMapDto> mapIPage = baseMapper.performanceShiftPage(page, time, userName, sysDeptId);
        //查询所有班次(打卡规则)
        List<PersonalAttendanceLocationConfig> personalAttendanceLocationConfigs = personalAttendanceLocationConfigMapper.selectList(null);
        mapIPage.getRecords().forEach(i -> {
            String[] shiftTimes = i.getShiftTime().split(";");
        // 获取当月时间范围
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter formatters = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.parse(time, formatters);
        LocalDate firstDayOfMonth = localDateTime.toLocalDate().withDayOfMonth(1);
        LocalDate lastDayOfMonth = localDateTime.toLocalDate().with(TemporalAdjusters.lastDayOfMonth());
        // 收集所有员工ID
        List<Long> staffIds = mapIPage.getRecords().stream()
                .filter(dto -> dto.getUserId() != null)
                .map(dto -> Long.valueOf(dto.getUserId()))
                .distinct()
                .collect(Collectors.toList());
        // 查询当月审核通过的请假记录(从approval_instance的form_config解析)
        Map<Long, List<String>> staffHolidayMap = new HashMap<>();
        if (!staffIds.isEmpty()) {
            // 通过关联查询获取员工请假信息
            List<Map<String, Object>> holidayRecords = baseMapper.selectStaffHolidayDates(staffIds);
            // 构建员工-请假日期映射(从form_config JSON解析)
            for (Map<String, Object> record : holidayRecords) {
                Long staffId = ((Number) record.get("staff_id")).longValue();
                String formConfig = (String) record.get("form_config");
                if (formConfig != null && !formConfig.isEmpty()) {
                    try {
                        // 解析form_config JSON
                        Map<String, Object> formMap = JackSonUtil.unmarshal(formConfig, Map.class);
                        Object formPayloadObj = formMap.get("formPayload");
                        if (formPayloadObj instanceof Map) {
                            Map<String, Object> formPayload = (Map<String, Object>) formPayloadObj;
                            Object dateRangeObj = formPayload.get("00"); // 请假日期字段key为"00"
                            if (dateRangeObj instanceof List) {
                                List<String> dateRange = (List<String>) dateRangeObj;
                                if (dateRange.size() >= 2) {
                                    String startDateTime = dateRange.get(0);
                                    String endDateTime = dateRange.get(1);
                                    // 解析日期(格式:yyyy-MM-dd HH:mm:ss)
                                    LocalDate startDate = LocalDate.parse(startDateTime.substring(0, 10));
                                    LocalDate endDate = LocalDate.parse(endDateTime.substring(0, 10));
                                    // 过滤当月范围内的请假日期
                                    List<String> dates = getDatesBetween(startDate, endDate).stream()
                                            .filter(date -> {
                                                LocalDate d = LocalDate.parse(date);
                                                return !d.isBefore(firstDayOfMonth) && !d.isAfter(lastDayOfMonth);
                                            })
                                            .collect(Collectors.toList());
                                    if (!dates.isEmpty()) {
                                        staffHolidayMap.computeIfAbsent(staffId, k -> new ArrayList<>()).addAll(dates);
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        // JSON解析失败,跳过此记录
                    }
                }
            }
        }
        for (PerformanceShiftMapDto i : mapIPage.getRecords()) {
            // 设置请假日期列表
            if (i.getUserId() != null) {
                Long staffId = Long.valueOf(i.getUserId());
                List<String> holidayDates = staffHolidayMap.getOrDefault(staffId, new ArrayList<>());
                i.setHolidayDates(holidayDates);
            }
            List<String> shiftTimes = StrUtil.split(i.getShiftTime(), ";");
            if(CollUtil.isEmpty(shiftTimes)){
                continue;
            }
            double totalAttendance = 0;//总出勤天数
            List<Map<String, Object>> map = new ArrayList<>();
            // 获取该员工的请假日期集合
            Set<String> holidayDateSet = new HashSet<>(i.getHolidayDates());
            // 分割日期
            for (String shiftTime : shiftTimes) {
                i.setShiftTime(null);
                Map<String, Object> hashMap = new HashMap<>();
                String[] shiftTimeAndShift = shiftTime.split(":");
                List<String> shiftTimeAndShift = StrUtil.split(shiftTime, ":");
                if(CollUtil.isEmpty(shiftTimeAndShift) || shiftTimeAndShift.size() != 3){
                    continue;
                }
                //排班详细数据
                hashMap.put("id", shiftTimeAndShift[2]);
                hashMap.put("shift", shiftTimeAndShift[1]);
                hashMap.put("time", shiftTimeAndShift[0]);
                hashMap.put("id", shiftTimeAndShift.get(2));
                hashMap.put("shift", shiftTimeAndShift.get(1));
                hashMap.put("time", shiftTimeAndShift.get(0));
                // 提取日期部分(格式:yyyy-MM-dd HH:mm:ss -> yyyy-MM-dd)
                String workTimeStr = shiftTimeAndShift.get(0);
                String workDate = workTimeStr.length() >= 10 ? workTimeStr.substring(0, 10) : workTimeStr;
                // 如果该日期有审核通过的请假记录,设置isHoliday为true,并将班次设为休息
                if (holidayDateSet.contains(workDate)) {
                    hashMap.put("isHoliday", true);
                    hashMap.put("shift", "休息");
                } else {
                    hashMap.put("isHoliday", false);
                }
                map.add(hashMap);
                i.setList(map);
                //汇总的各班次统计数据
                for (PersonalAttendanceLocationConfig personalAttendanceLocationConfig : personalAttendanceLocationConfigs) {
                    if (!i.getMonthlyAttendance().containsKey(personalAttendanceLocationConfig.getShift())){
                        i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), 0);
                // 统计班次数据
                if (holidayDateSet.contains(workDate)) {
                    // 如果是请假日期,统计为休息(只统计一次)
                    if (!i.getMonthlyAttendance().containsKey("休息")){
                        i.getMonthlyAttendance().put("休息", 0);
                    }
                    if (personalAttendanceLocationConfig.getShift().equals(shiftTimeAndShift[1])) {
                        BigDecimal bigDecimal = new BigDecimal(i.getMonthlyAttendance().get(personalAttendanceLocationConfig.getShift()).toString());
                        i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), bigDecimal.add(new BigDecimal("1")));
                    i.getMonthlyAttendance().put("休息", new BigDecimal(i.getMonthlyAttendance().get("休息").toString()).add(new BigDecimal("1")));
                } else {
                    // 不是请假日期,统计原班次
                    for (PersonalAttendanceLocationConfig personalAttendanceLocationConfig : personalAttendanceLocationConfigs) {
                        if (!i.getMonthlyAttendance().containsKey(personalAttendanceLocationConfig.getShift())){
                            i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), 0);
                        }
                        if (personalAttendanceLocationConfig.getShift().equals(shiftTimeAndShift.get(1))) {
                            BigDecimal bigDecimal = new BigDecimal(i.getMonthlyAttendance().get(personalAttendanceLocationConfig.getShift()).toString());
                            i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), bigDecimal.add(new BigDecimal("1")));
                        }
                    }
                }
                //统计总出勤天数(早/中/晚/夜)都算出勤,其余都是休息
                if (shiftTimeAndShift[1].contains("早") ||
                        shiftTimeAndShift[1].contains("中") ||
                        shiftTimeAndShift[1].contains("晚") ||
                        shiftTimeAndShift[1].contains("夜")) {
                    i.getMonthlyAttendance().put("totalAttendance", totalAttendance += 1);
                    // 统计总出勤天数(早/中/晚/夜)都算出勤
                    if (shiftTimeAndShift.get(1).contains("早") ||
                        shiftTimeAndShift.get(1).contains("中") ||
                        shiftTimeAndShift.get(1).contains("晚") ||
                        shiftTimeAndShift.get(1).contains("夜")) {
                        i.getMonthlyAttendance().put("totalAttendance", totalAttendance += 1);
                    }
                }
            }
        });
        }
        // 获取header时间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter formatters = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 将字符串时间转换为 LocalDateTime 类型时间
        LocalDateTime localDateTime = LocalDateTime.parse(time, formatters);
        LocalDate firstDayOfMonth = localDateTime.toLocalDate().withDayOfMonth(1);
        LocalDate lastDayOfMonth = localDateTime.toLocalDate().with(TemporalAdjusters.lastDayOfMonth());
        List<LocalDateTime> localDateTimesBetween = getLocalDateTimesBetween(firstDayOfMonth.atStartOfDay(), lastDayOfMonth.atStartOfDay());
        List<Object> list1 = new ArrayList<>();
        for (LocalDateTime dateTime : localDateTimesBetween) {
@@ -173,6 +270,21 @@
        resultMap.put("page", mapIPage);
        resultMap.put("headerList", list1);
        return resultMap;
    }
    /**
     * 获取两个日期之间的所有日期(格式:yyyy-MM-dd)
     * 注意:结束日期不包含在内(请假日期范围是开始日期到结束日期,不含结束日期)
     */
    private List<String> getDatesBetween(LocalDate startDate, LocalDate endDate) {
        List<String> dates = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate current = startDate;
        while (current.isBefore(endDate)) {
            dates.add(current.format(formatter));
            current = current.plusDays(1);
        }
        return dates;
    }
    @Override
@@ -220,6 +332,9 @@
            for (String shiftTime : shiftTimes) {
                Map<String, Object> hashMap = new HashMap<>();
                String[] shiftTimeAndShift = shiftTime.split(":");
                if(shiftTimeAndShift.length != 3){
                    continue;
                }
                for (PersonalAttendanceLocationConfig personalAttendanceLocationConfig : personalAttendanceLocationConfigs) {
                    if (!i.getMonthlyAttendance().containsKey(personalAttendanceLocationConfig.getShift())) {
                        i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), 0);
@@ -250,6 +365,16 @@
        LocalDateTime localDateTime = LocalDateTime.parse(time, formatters);
        map.put("header", getMonthHeader(localDateTime));
        List<List<Object>> lists = dataRequiredForProcessingIntoExcelMonth(mapIPage, personalAttendanceLocationConfigs);
        int maxSize = lists.stream()
                .mapToInt(List::size)
                .max()
                .orElse(0);
        for (List<Object> row : lists) {
            while (row.size() < maxSize) {
                row.add("-");
            }
        }
        map.put("data", lists);
        return map;
    }