package jnpf.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import jnpf.attendance.antifreeze.UserAntifreeze;
import jnpf.attendance.dto.GroupUpdateByUserDTO;
import jnpf.attendance.mapper.AttendanceGroupUserMapper;
import jnpf.attendance.mapper.AttendanceLeaveApproveMapper;
import jnpf.attendance.mapper.AttendanceManagerPermissionMapper;
import jnpf.attendance.service.*;
import jnpf.attendance.service.handle.notice.AttendanceNoticeHandler;
import jnpf.base.ActionResult;
import jnpf.base.service.SuperServiceImpl;
import jnpf.database.util.TenantDataSourceUtil;
import jnpf.entity.AttendanceGroup;
import jnpf.entity.AttendanceGroupUser;
import jnpf.entity.AttendanceManagerPermission;
import jnpf.enums.attendance.AttendanceNoticeEnum;
import jnpf.enums.attendance.GroupUserTypeEnum;
import jnpf.enums.attendance.StatisticsEnumUtil;
import jnpf.exception.HandleException;
import jnpf.exception.LoginException;
import jnpf.message.enums.PermissionOrgReUserEnum;
import jnpf.message.model.permission.PermissionRelationOrganizeUserListDTO;
import jnpf.model.attendance.dto.*;
import jnpf.model.attendance.model.UserChangeNoticeModel;
import jnpf.model.attendance.vo.AttendanceGroupUserVo;
import jnpf.model.attendance.vo.AttendanceSelfApproveVo;
import jnpf.model.attendance.vo.GroupUserLineScheduleVo;
import jnpf.model.attendance.vo.SecondmentDateVo;
import jnpf.model.attendance.vo.attendance.ClockInExportVo;
import jnpf.model.attendance.vo.attendance.JoinGroupVo;
import jnpf.model.common.DateRangeDto;
import jnpf.model.personnels.dto.turnover.FtbDepUserDTO;
import jnpf.permission.V2UserApi;
import jnpf.permission.eum.v2.UserWorkStatusEnums;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.personnels.FtbPersonnelsTurnoverManagementApi;
import jnpf.util.*;
import jnpf.util.attendance.DayStatisticsUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.tuple.MutablePair;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static jnpf.constants.RedisConstant.GROUP_JOIN_USERS_KEY;
import static jnpf.util.DateConvertUtil.setOverlap;

@Service
@Slf4j
public class AttendanceUserServiceImpl extends SuperServiceImpl<AttendanceGroupUserMapper, AttendanceGroupUser> implements AttendanceUserService {

    @Resource
    private AttendanceGroupUserMapper attendanceGroupUserMapper;
    @Autowired
    private UserAntifreeze userAntifreeze;
    @Autowired
    private RedissonClient redissonClient;
    @Resource
    private AttendanceDailyRuleService attendanceDailyRuleService;
    @Autowired
    private AttendanceDayStatisticsService attendanceDayStatisticsService;
    @Autowired
    private AttendanceClockInService attendanceClockInService;
    @Autowired
    private AttendanceNoticeHandler attendanceNoticeHandler;
    @Resource
    private AttendanceLeaveApproveMapper attendanceLeaveApproveMapper;
    @Autowired
    private AttendanceGroupService attendanceGroupService;
    @Resource
    private AttendanceManagerPermissionMapper attendanceManagerPermissionMapper;
    @Resource
    private AppStatisticsService appStatisticsService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private V2UserApi v2UserApi;
    @Autowired
    private AttendanceUserService attendanceUserService;
    @Autowired
    private AttendanceUserFaceService attendanceUserFaceService;
    @Autowired
    private FtbPersonnelsTurnoverManagementApi ftbPersonnelsTurnoverManagementApi;

    /**
     * 根据考勤组查询成员列表
     *
     * @param groupId 考勤组id
     * @param type    1.本组（包含离组）
     * @return List<AttendanceGroupUserVo>
     */
    @Override
    public List<AttendanceGroupUserVo> queryUsersByGroupId(String groupId, String name, Integer type, List<String> userIds, Date start, Date end) {

        List<AttendanceGroupUser> groupUserList = getAttendanceGroupUsersOfSecondment(start, end, userIds, List.of(groupId));
        if (CollectionUtil.isEmpty(groupUserList)) {
            return new ArrayList<>();
        }
        if (Objects.nonNull(type)) {
            groupUserList.removeIf(groupUser -> !groupUser.getType().equals(type));
        }
        List<AttendanceGroupUserVo> groupUserVos = JsonUtil.getJsonToList(groupUserList, AttendanceGroupUserVo.class);
        /* 查询用户信息*/
//        List<PartUserInfoVo> userVoList = userApi.getInfoByIds(groupUserVos.stream().map(AttendanceGroupUserVo::getUserId).collect(Collectors.toList()));
        ActionResult<List<UserBoundVO>> allUserInfoBatch = v2UserApi.getAllUserInfoBatch(groupUserVos.stream().map(AttendanceGroupUserVo::getUserId).distinct().collect(Collectors.toList()), null);
        if (allUserInfoBatch.getCode() != 200 && allUserInfoBatch.getData() == null) {
            return Lists.newArrayList();
        }
        List<UserBoundVO> userVoList = allUserInfoBatch.getData();
        /* 用户姓名模糊查询*/
        if (StrUtil.isNotBlank(name)) {
            userVoList = userVoList.stream().filter(userVo -> {
                return userVo.getUserName().contains(name);
            }).collect(Collectors.toList());
        }
        if (CollectionUtil.isEmpty(userVoList)) {
            return Lists.newArrayList();
        }
        Map<String, UserBoundVO> listToMap = userVoList.stream().collect(Collectors.toMap(UserBoundVO::getId, user -> user));

        List<AttendanceGroupUserVo> newGroupUserVoList = new ArrayList<>();
        groupUserVos.stream().collect(Collectors.toMap(vo -> vo.getGroupId() + vo.getUserId(), Function.identity(), (r1, r2) -> r1)).forEach((key, vo) -> {
            UserBoundVO partUserInfoVo = listToMap.get(vo.getUserId());
            if (partUserInfoVo != null) {
                vo.setRealName(partUserInfoVo.getUserName());
                String headIcon = partUserInfoVo.getHeadIcon();
                vo.setHeadIcon(UploaderUtil.uploaderImg(headIcon));
                /* 岗位信息*/
                String positionName = partUserInfoVo.getPositionName();
                vo.setPositionName(positionName);
                newGroupUserVoList.add(vo);
            }
        });
        return newGroupUserVoList;
    }

    @Override
    public List<String> getUserIds(List<String> organizeList, List<String> userIdList, Date start, Date end, Integer scopeOfAdaptation) {
        return getUserIdsAndGroupIds(organizeList, userIdList, start, end, scopeOfAdaptation).stream().map(AttendanceGroupUser::getUserId).collect(Collectors.toList());
    }
    @Override
    public List<AttendanceGroupUser> getUserIdsAndGroupIds(List<String> organizeList, List<String> userIdList, Date start, Date end, Integer scopeOfAdaptation) {
        // 自动排休逻辑
        List<AttendanceGroup> byOrgIds = CollUtil.isEmpty(organizeList) ? List.of() : attendanceGroupService.getByOrgIds(organizeList.stream().distinct().collect(Collectors.toList()));
        List<String> groupIds = byOrgIds.stream().map(AttendanceGroup::getId).distinct().collect(Collectors.toList());
        List<AttendanceGroupUser> attendanceGroupUsersOfSecondment = attendanceUserService.getAttendanceGroupUsersOfSecondment(start, end, null, 0 == scopeOfAdaptation ? null : groupIds);
        if (CollUtil.isNotEmpty(userIdList)) {
            List<AttendanceGroupUser>  attendanceGroupUsersOfSecondment1 = attendanceUserService.getAttendanceGroupUsersOfSecondment(start, end, userIdList, null);
            attendanceGroupUsersOfSecondment.addAll(attendanceGroupUsersOfSecondment1);
        }
        return attendanceGroupUsersOfSecondment;
    }

    @Override
    public void sort(UserSortModel userSortModel) {
        Map<String, Integer> collect = userSortModel.getUserSortList().stream().collect(Collectors.toMap(UserSortModel.UserSort::getUserId, UserSortModel.UserSort::getSort));
        List<AttendanceGroupUser> list = lambdaQuery().eq(AttendanceGroupUser::getGroupId, userSortModel.getGroupId())
                .in(AttendanceGroupUser::getUserId, collect.keySet())
                .list();
        list.forEach(user -> user.setSort(collect.getOrDefault(user.getUserId(), null)));
        updateBatchById(list);
    }

    @Override
    public List<AttendanceGroupUser> queryByUsersGroupIds(List<String> groupIds) {
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AttendanceGroupUser::getDeleteMark, 0)
                .in(AttendanceGroupUser::getGroupId, groupIds);
        return attendanceGroupUserMapper.selectList(queryWrapper);
    }

    @Override
    public List<AttendanceGroupUser> queryByAllUsersGroupIds(List<String> groupIds) {
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(AttendanceGroupUser::getGroupId, groupIds);
        return attendanceGroupUserMapper.selectList(queryWrapper);
    }

    @Override
    public List<AttendanceGroupUser> queryAll() {
        return lambdaQuery()
                .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
                .list();
    }

    @Override
    public List<AttendanceGroupUser> queryAllForNotSecondment() {
        return lambdaQuery()
                .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
                .eq(AttendanceGroupUser::getType, 1)
                .list();
    }


    @Override
    public List<AttendanceGroupUser> queryByUsersIds(List<String> userIds) {
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
                .in(AttendanceGroupUser::getUserId, userIds);
        return attendanceGroupUserMapper.selectList(queryWrapper);
    }

    @Override
    public List<AttendanceGroupUser> queryAllByUsersIds(List<String> userIds) {
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .in(AttendanceGroupUser::getUserId, userIds);
        return attendanceGroupUserMapper.selectList(queryWrapper);
    }

    @Override
    public List<AttendanceGroupUser> queryByUsersAndGroup(Date start, Date end, List<String> userIds, List<String> groupIds) {
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .le(Objects.nonNull(end), AttendanceGroupUser::getCreatorTime, end)
                .and(Objects.nonNull(start), x -> x.ge(AttendanceGroupUser::getRemoveTime, start)
                        .or()
                        .isNull(AttendanceGroupUser::getRemoveTime))
                .in(AttendanceGroupUser::getGroupId, groupIds)
                .in(CollUtil.isNotEmpty(userIds), AttendanceGroupUser::getUserId, userIds);
        return attendanceGroupUserMapper.selectList(queryWrapper);
    }

    @Override
    public List<AttendanceGroupUser> queryByUsersAndGroupFilterSecondment(Date start, Date end, List<String> userIds, List<String> groupIds) {
        return queryByUsersAndGroupFilterSecondment(start, end, userIds, groupIds, false);
    }

    @Override
    public List<AttendanceGroupUser> queryByUsersAndGroupFilterSecondment(Date start, Date end, List<String> userIds, List<String> groupIds, Boolean isContainsDeleteGroup) {
        List<AttendanceGroupUser> attendanceGroupUsersOfSecondmentAll = getAttendanceGroupUsersOfSecondmentAll(start, end, userIds, groupIds, isContainsDeleteGroup);
        return attendanceGroupUsersOfSecondmentAll.stream().filter(vo -> Objects.equals(vo.getType(), 1)).collect(Collectors.toList());
    }

    @Override
    public List<AttendanceGroupUser> getAttendanceGroupUsersOfSecondment(Date start, Date end, List<String> userIds, List<String> groupIds, boolean isContainsDeleteGroup) {
        List<AttendanceGroupUser> attendanceGroupUsers = getAttendanceGroupUsersOfSecondmentAll(start, end, userIds, groupIds, isContainsDeleteGroup);
        if (Objects.isNull(start) || Objects.isNull(end)) {
            return attendanceGroupUsers;
        }
        return getAttendanceGroupUsers(start, end, attendanceGroupUsers);
    }

    @Override
    public List<AttendanceGroupUser> getAttendanceGroupUsers(Date start, Date end, List<AttendanceGroupUser> attendanceGroupUsers) {
        return attendanceGroupUsers.stream().filter(user -> {
            List<SecondmentDateVo> secondmentDateVos = StringUtil.isEmpty(user.getTimeJson()) ? CollUtil.newArrayList() : JsonUtil.getJsonToList(user.getTimeJson(), SecondmentDateVo.class);
            if (!Objects.equals(user.getUserGroupType(), 2)) {
                if (CollUtil.isEmpty(secondmentDateVos)) {
                    return Boolean.TRUE;
                }
                SecondmentDateVo secondmentType = getSecondmentType(secondmentDateVos, start, end);
                if (Objects.isNull(secondmentType)) {
                    return Boolean.TRUE;
                }
                if (secondmentType.getStartTime().after(start) || secondmentType.getEndTime().before(end)) {
                    return Boolean.TRUE;
                }

                return Boolean.FALSE;
            }
            //0.无借调为true
            if (CollUtil.isEmpty(secondmentDateVos)) {
                return Boolean.FALSE;
            }
            SecondmentDateVo secondmentType = getSecondmentType(secondmentDateVos, start, end);
            if (Objects.nonNull(secondmentType)) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList());
    }

    @Override
    public List<AttendanceGroupUser> getAttendanceGroupUsersOfSecondment(Date start, Date end, List<String> userIds, List<String> groupIds) {
        return getAttendanceGroupUsersOfSecondment(start, end, userIds, groupIds, false);
    }


    private List<AttendanceGroupUser> getAttendanceGroupUsersOfSecondmentAll(Date start, Date end, List<String> userIds, List<String> groupIds, boolean isContainsDeleteGroup) {
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .and(Objects.nonNull(end), x -> x.le(AttendanceGroupUser::getCreatorTime, end)
                        .or()
                        .eq(AttendanceGroupUser::getUserGroupType, 2))
                .and(Objects.nonNull(start), x -> x.ge(AttendanceGroupUser::getRemoveTime, start)
                        .or()
                        .isNull(AttendanceGroupUser::getRemoveTime))
                .in(CollUtil.isNotEmpty(groupIds), AttendanceGroupUser::getGroupId, groupIds)
                .in(CollUtil.isNotEmpty(userIds), AttendanceGroupUser::getUserId, userIds)
                .orderByAsc(AttendanceGroupUser::getSort);
        List<AttendanceGroupUser> attendanceGroupUsers = attendanceGroupUserMapper.selectList(queryWrapper);
        List<String> collect = attendanceGroupUsers.stream().map(AttendanceGroupUser::getGroupId).filter(Objects::nonNull).collect(Collectors.toList());
        if (CollUtil.isEmpty(collect)) {
            return CollUtil.newArrayList();
        }
        List<AttendanceGroup> attendanceGroups = attendanceGroupService.listByIds(collect);
        if (CollUtil.isEmpty(attendanceGroups)) {
            return CollUtil.newArrayList();
        }
        List<String> groupIdList = attendanceGroups.stream().filter(group -> isContainsDeleteGroup || Objects.equals(group.getDeleteMark(), 0)).map(AttendanceGroup::getId).collect(Collectors.toList());
        Map<String,List<Date>> collect1 = attendanceGroupUsers.stream().filter(vo -> Objects.equals(vo.getDeleteMark(), 0)).collect(Collectors.groupingBy(AttendanceGroupUser::getUserId, Collectors.mapping(AttendanceGroupUser::getCreatorTime, Collectors.toList())));
        return attendanceGroupUsers.stream()
                .filter(group -> groupIdList.contains(group.getGroupId()))
                .filter(vo->Objects.equals(vo.getDeleteMark(), 0) || !collect1.containsKey(vo.getUserId()) || collect1.get(vo.getUserId()).stream().allMatch(date -> date.after(vo.getCreatorTime())))
                .collect(Collectors.toList());
    }

    @Override
    public List<AttendanceGroupUser> getSelfUsersBeLongToGroup(String groupId) {
        Date now = new Date();
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AttendanceGroupUser::getUserGroupType, 2)
                .isNull(AttendanceGroupUser::getRemoveTime)
                .eq(StringUtil.isNotEmpty(groupId), AttendanceGroupUser::getGroupId, groupId)
        ;
        List<AttendanceGroupUser> attendanceGroupUsers = attendanceGroupUserMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(attendanceGroupUsers)) {
            return CollUtil.newArrayList();
        }
        // 借调人员集合
        List<String> userIdList = new ArrayList<>();
        Map<String, List<AttendanceGroupUser>> userMap = attendanceGroupUsers.stream().collect(Collectors.groupingBy(AttendanceGroupUser::getUserId));
        userMap.forEach((k, v) -> {
            if (!userMap.get(k).isEmpty()) {
                for (AttendanceGroupUser attendanceGroupUserVo : userMap.get(k)) {
                    List<SecondmentDateVo> secondmentDateVos = StringUtil.isEmpty(attendanceGroupUserVo.getTimeJson()) ? new ArrayList<>() : JsonUtil.getJsonToList(attendanceGroupUserVo.getTimeJson(), SecondmentDateVo.class);
                    if (secondmentDateVos.isEmpty()) {
                        userIdList.add(k);
                        break;
                    } else {
                        for (SecondmentDateVo dateVo : secondmentDateVos) {
                            if (DateDetail.checkTimeBetween(now, dateVo.getStartTime(), dateVo.getEndTime())
                            ) {
                                userIdList.add(k);
                                break;
                            }
                        }
                    }
                }
            }
        });
        if (!userIdList.isEmpty()) {
            // 查询这些用户所属的考勤组
            QueryWrapper<AttendanceGroupUser> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.lambda()
                    .eq(AttendanceGroupUser::getUserGroupType, 1)
                    .isNull(AttendanceGroupUser::getRemoveTime)
                    .in(CollUtil.isNotEmpty(userIdList), AttendanceGroupUser::getUserId, userIdList)
            ;
            return attendanceGroupUserMapper.selectList(queryWrapper1);
        }

        return CollUtil.newArrayList();
    }

    public List<String> getUserIdList(List<String> userIds, List<String> reqUserIds) {
        if (CollUtil.isEmpty(userIds)) {
            return new ArrayList<>();
        }
        if (CollUtil.isNotEmpty(reqUserIds)) {
            if (CollUtil.isEmpty(org.apache.commons.collections4.CollectionUtils.intersection(userIds, reqUserIds))) {
                return new ArrayList<>();
            } else {
                return (List<String>) CollectionUtils.intersection(userIds, reqUserIds);
            }
        } else {
            return userIds;
        }
    }

    @Override
    public List<ClockInExportVo> getUsersDayByGroupIds(GroupUserQueryDto groupUserQueryDto, DayStatisticsPageListDto req) {
        int pageSize = (int) req.getPageSize();
        int pageNum = (int) req.getCurrentPage();
        int start = (pageNum - 1) * pageSize;
        int end = start + pageSize;

        String value = stringRedisTemplate.opsForValue().get(req.getKey());
        if (StringUtil.isNotEmpty(value)) {
            // 有缓存，拿去缓存数据
            List<ClockInExportVo> jsonToList = JsonUtil.getJsonToList(value, ClockInExportVo.class);
            if (end > jsonToList.size()) {
                end = jsonToList.size();
            }
            req.setTotal(jsonToList.size());
            if (start > jsonToList.size()) {
                return null;
            }
            return jsonToList.subList(start, end);
        }
        List<ClockInExportVo> usersByGroupVos = new ArrayList<>();
        if (StringUtil.isEmpty(groupUserQueryDto.getTenantId())) {
            groupUserQueryDto.setTenantId(UserProvider.getUser().getTenantId());
        }
        List<String> dayList = FtbUtil.getDateStr(groupUserQueryDto.getStartTime(), groupUserQueryDto.getEndTime()).getUseList();
        // 批量处理用户在时间段内考勤组数据
        SimpleDateFormat sdfhm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<AttendanceGroupUser> users = queryByAllUsersGroupIds(groupUserQueryDto.getFilterList()
                .stream().map(GroupFilterDto::getGroupId).collect(Collectors.toList()));
        if (CollUtil.isEmpty(users)) {
            return CollUtil.newArrayList();
        }
        List<String> finalAllUser = groupUserQueryDto.getUserIds();
        Map<String, List<AttendanceGroupUser>> groupUserMap = CollUtil.isNotEmpty(users) ? users.stream().filter(user ->
                finalAllUser.contains(user.getUserId())).collect(Collectors.groupingBy(AttendanceGroupUser::getUserId)) : new HashMap<>();

        for (String s : dayList) {
            Date startTime;
            Date endTime;
            try {
                startTime = sdfhm.parse(s + " 00:00:00");
                endTime = sdfhm.parse(s + " 23:59:59");
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
            // 满足条件的用户
            for (Map.Entry<String, List<AttendanceGroupUser>> listEntry : groupUserMap.entrySet()) {
                for (AttendanceGroupUser groupUser : listEntry.getValue()) {
                    List<AttendanceGroupUser> list = new ArrayList<>();
                    list.add(groupUser);
                    List<String> keyList = usersByGroupVos.stream().map(ClockInExportVo::getKeyString).distinct().collect(Collectors.toList());
                    if (attendanceDailyRuleService.findUserIsExistsByDay(list, startTime, endTime) > 0 && !keyList.contains(groupUser.getUserId() + "-" + s + "-" + groupUser.getGroupId())) {
                        ClockInExportVo groupUserDayVo = new ClockInExportVo();
                        groupUserDayVo.setUserId(groupUser.getUserId());
                        groupUserDayVo.setGroupId(groupUser.getGroupId());
                        groupUserDayVo.setDate(s);
                        groupUserDayVo.setKeyString(groupUser.getUserId() + "-" + s + "-" + groupUser.getGroupId());
                        usersByGroupVos.add(groupUserDayVo);
                    }
                }
            }
        }
        if (usersByGroupVos.isEmpty()) {
            return usersByGroupVos;
        }
        usersByGroupVos = usersByGroupVos.stream().filter(v -> groupUserQueryDto.getUserIds().contains(v.getUserId())).collect(Collectors.toList());
        if (usersByGroupVos.isEmpty()) {
            return usersByGroupVos;
        }
        usersByGroupVos.forEach(v -> {
            UserBoundVO staffRosterDto = groupUserQueryDto.getStaffRosterMap().get(v.getUserId());
            v.setDeptName(StringUtil.isNotEmpty(staffRosterDto.getOrganizeName()) ? staffRosterDto.getOrganizeName() : "");
            v.setPostName(StringUtil.isNotEmpty(staffRosterDto.getPositionName()) ? staffRosterDto.getPositionName() : "");
            v.setUserName(staffRosterDto.getName());
        });
        // 排序
        List<ClockInExportVo> collect = usersByGroupVos.stream().sorted(Comparator.comparing(ClockInExportVo::getUserId).thenComparing(ClockInExportVo::getDate)).collect(Collectors.toList());
        // 存入缓存
        stringRedisTemplate.opsForValue().set(req.getKey(), JsonUtil.getObjectToString(collect), 10 * 60 * 1000);
        // 对usersByGroupVos 手动分页
        if (end > collect.size()) {
            end = collect.size();
        }
        req.setTotal(collect.size());
        if (start > collect.size()) {
            return null;
        }
        return collect.subList(start, end);
    }

    @Override
    public List<ClockInExportVo> getClockInExportVoList(GroupUserQueryDto groupUserQueryDto, List<ClockInExportVo> usersByGroupVos) {
        DateRangeDto dateRangeDto = new DateRangeDto(groupUserQueryDto.getStartTime(), groupUserQueryDto.getEndTime());
        // 处理时段及打卡信息
        usersByGroupVos = appStatisticsService.getDayClockInPageListExport(usersByGroupVos, dateRangeDto, groupUserQueryDto.getTenantId());
        return usersByGroupVos;
    }

    @Override
    public List<String> getGroupUserIds(List<String> groupIds) {
        List<String> groupUserIds = attendanceGroupUserMapper.getGroupUserIds(groupIds);
        if (CollUtil.isEmpty(groupUserIds)) {
            groupUserIds = CollUtil.newArrayList();
        }
        // 查看对应考勤组今日有无借调
        List<String> todaySecondUser = getTodaySecondUser(groupIds);
        if (CollUtil.isEmpty(todaySecondUser)) {
            return groupUserIds.stream().distinct().collect(Collectors.toList());
        }
        groupUserIds.addAll(todaySecondUser);
        return groupUserIds.stream().distinct().collect(Collectors.toList());
    }

    @Override
    public List<AttendanceGroupUserVo> getGroupNowAllUserList(String groupId) {
        List<AttendanceGroupUserVo> list = new ArrayList<>();
        // 获取考勤组下的所有用户包含借调
        List<AttendanceGroupUser> users = getAttendanceGroupUsersOfSecondment(new Date(), new Date(), null, List.of(groupId));
        List<UserBoundVO> userBoundVO = v2UserApi.getUserPrimaryBoundBatch(users.stream().map(AttendanceGroupUser::getUserId).distinct().collect(Collectors.toList()), null).getData();
        if (CollectionUtil.isNotEmpty(userBoundVO)) {
            for (UserBoundVO boundVo : userBoundVO) {
                AttendanceGroupUserVo groupUserVo = new AttendanceGroupUserVo();
                groupUserVo.setUserId(boundVo.getId());
                groupUserVo.setRealName(boundVo.getUserName());
                groupUserVo.setNickname(boundVo.getNickname());
                groupUserVo.setHeadIcon(UploaderUtil.uploaderImg(boundVo.getHeadIcon()));
                groupUserVo.setPositionName(boundVo.getPositionName());
                list.add(groupUserVo);
            }
        }
        return list;
    }


    public List<String> getTodaySecondUser(List<String> groupIds) {
        List<String> userIds = new ArrayList<>();
        Date now = new Date();
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AttendanceGroupUser::getUserGroupType, 2)
                .isNull(AttendanceGroupUser::getRemoveTime)
                .in(AttendanceGroupUser::getGroupId, groupIds)
        ;
        List<AttendanceGroupUser> attendanceGroupUsers = attendanceGroupUserMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(attendanceGroupUsers)) {
            // 没有发生借调
            return null;
        }

        for (AttendanceGroupUser attendanceGroupUser : attendanceGroupUsers) {
            List<SecondmentDateVo> secondmentDateVos = StringUtil.isEmpty(attendanceGroupUser.getTimeJson()) ? new ArrayList<>() : JsonUtil.getJsonToList(attendanceGroupUser.getTimeJson(), SecondmentDateVo.class);
            if (secondmentDateVos.isEmpty()) {
                userIds.add(attendanceGroupUser.getUserId());
            } else {
                for (SecondmentDateVo dateVo : secondmentDateVos) {
                    if (DateDetail.checkTimeBetween(now, dateVo.getStartTime(), dateVo.getEndTime())) {
                        userIds.add(attendanceGroupUser.getUserId());
                    }
                }
            }
        }
        return userIds.stream().distinct().collect(Collectors.toList());
    }

    @Override
    public List<JoinGroupVo> checkUserJoinGroupToObject(String userIds) {
        log.error("checkUserJoinGroupToObject 入参-- userIds: {}", userIds);
        Gson gson = new Gson();
        Type mapType = new TypeToken<Map<String, String>>() {
        }.getType();
        Map<String, String> map = gson.fromJson(userIds, mapType);
        // 3. 提取值
        userIds = map.get("userIds");
        if (StrUtil.isBlank(userIds)) {
            return new ArrayList<>();
        }
        String[] userIdArr = userIds.split(",");
        ArrayList<String> userIdList = CollectionUtil.newArrayList(userIdArr);
        List<AttendanceGroupUserVo> groupUserVoList = attendanceGroupUserMapper.getGroupInUserIds(userIdList, null);
        if (CollectionUtil.isEmpty(groupUserVoList)) {
            return new ArrayList<>();
        }
        List<AttendanceGroupUserVo> groupUserVos = groupUserVoList.stream().filter(groupUser -> {
            return GroupUserTypeEnum.CUR.getCode().equals(groupUser.getType());
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(groupUserVos)) {
            List<JoinGroupVo> userNameList = new ArrayList<>();
            Map<String, AttendanceGroupUserVo> userVoMap = groupUserVos.stream().collect(Collectors.toMap(AttendanceGroupUserVo::getUserId, Function.identity()));
            log.error("checkUserJoinGroupToObject -- groupUserVos: {}", groupUserVos);
            ActionResult<List<UserBoundVO>> allUserInfoBatch = v2UserApi.getAllUserInfoBatch(groupUserVos.stream().map(AttendanceGroupUserVo::getUserId).collect(Collectors.toList()), null);
            if (allUserInfoBatch.getCode() != 200 && allUserInfoBatch.getData() == null) {
                return Lists.newArrayList();
            }
            List<UserBoundVO> userList = allUserInfoBatch.getData();
            // 拼接考勤组名称
            userList.forEach(user -> {
                JoinGroupVo joinGroupVo = new JoinGroupVo();
                String append = user.getUserName() + "已加入" + userVoMap.get(user.getId()).getGroupName() + "考勤组";
                joinGroupVo.setContent(append);
                joinGroupVo.setUserId(user.getId());
                userNameList.add(joinGroupVo);
            });
            return userNameList;
        }
        return new ArrayList<>();
    }

    @Override
    public List<UserBoundVO> getUserBoundVO(String groupId, String workGroupId) {
        AttendanceGroup byId = attendanceGroupService.getById(groupId);
        if (Objects.isNull(byId)) {
            return List.of();
        }
        return userAntifreeze.getInfoByIds(byId.getOrgId(), workGroupId);
    }

    @Override
    public List<UserBoundVO> batchGetUserBoundVO(List<String> groupIds, List<UserWorkStatusEnums> workStatusEnums) {
        List<AttendanceGroup> groupList = attendanceGroupService.listByIds(groupIds);
        if (CollUtil.isEmpty(groupList)) {
            return List.of();
        }
        List<String> orgIdList = groupList.stream().map(AttendanceGroup::getOrgId).collect(Collectors.toList());
        List<UserBoundVO> userBoundVos = userAntifreeze.batchGetInfoByIds(orgIdList, workStatusEnums);
        userBoundVos.forEach(user -> {
            // 入职失败需要你归属预入值
            if (UserWorkStatusEnums.ONBOARDING_FAILED.equals(user.getWorkStatusEnums())) {
                user.setWorkStatusEnums(UserWorkStatusEnums.PRE_ONBOARDING);
            }
        });
        return userBoundVos;
    }

    /**
     * 获取用户当前所属的借调考勤组Id
     *
     * @return String
     */
    @Override
    public String getUserNowGroup() {
        Date now = new Date();
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AttendanceGroupUser::getUserGroupType, 2)
                .isNull(AttendanceGroupUser::getRemoveTime)
                .eq(AttendanceGroupUser::getUserId, UserProvider.getUser().getUserId())
        ;
        List<AttendanceGroupUser> attendanceGroupUsers = attendanceGroupUserMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(attendanceGroupUsers)) {
            // 没有发生借调
            return null;
        }
        for (AttendanceGroupUser attendanceGroupUser : attendanceGroupUsers) {
            List<SecondmentDateVo> secondmentDateVos = StringUtil.isEmpty(attendanceGroupUser.getTimeJson()) ? new ArrayList<>() : JsonUtil.getJsonToList(attendanceGroupUser.getTimeJson(), SecondmentDateVo.class);
            if (secondmentDateVos.isEmpty()) {
                return attendanceGroupUser.getGroupId();
            } else {
                for (SecondmentDateVo dateVo : secondmentDateVos) {
                    if (DateDetail.checkTimeBetween(now, dateVo.getStartTime(), dateVo.getEndTime())) {
                        return attendanceGroupUser.getGroupId();
                    }
                }
            }
        }
        return null;
    }

    /**
     * 获取目标日期涉及的借调时段
     *
     * @param secondmentDateVos 借调时段
     * @param inPoint 入点
     * @param outPoint 出点
     * @return 借调时段
     */
    private SecondmentDateVo getSecondmentType(List<SecondmentDateVo> secondmentDateVos, Date inPoint, Date outPoint) {
        if (CollUtil.isEmpty(secondmentDateVos)) {
            return null;
        }
        for (SecondmentDateVo dateVo : secondmentDateVos) {
            if (dateVo.getStartTime().before(outPoint) && dateVo.getEndTime().after(inPoint)) {
                return dateVo;
            }
        }
        return null;
    }

    /**
     * 查询系统用户
     *
     * @param orgId 组织机构id
     * @param name  用户姓名 用于字段模糊搜索
     * @return List<AttendanceGroupUserVo>
     */
    @Override
    public List<AttendanceGroupUserVo> querySysUserList(String orgId, String name) {
        return attendanceGroupUserMapper.querySysUsers(orgId, name);
    }

    @Override
    @Transactional
    public void orgUpdateHandle(List<PermissionRelationOrganizeUserListDTO> userList) {
        if (CollUtil.isEmpty(userList)) {
            return;
        }
        try {
            TenantDataSourceUtil.switchTenant(userList.get(0).getTenantId());
        } catch (LoginException e) {
            throw new RuntimeException(e);
        }
        List<PermissionRelationOrganizeUserListDTO> reduceUser = userList.stream().filter(vo -> Objects.equals(vo.getType(), PermissionOrgReUserEnum.USER_REDUCE)).collect(Collectors.toList());
        List<PermissionRelationOrganizeUserListDTO> addUser = userList.stream().filter(vo -> Objects.equals(vo.getType(), PermissionOrgReUserEnum.USER_ADD)).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(reduceUser) && CollUtil.isNotEmpty(addUser)) {
            return;
        }
        RLock lock = null;
        try {
            String key = String.format(GROUP_JOIN_USERS_KEY, userList.get(0).getUserId());
            lock = redissonClient.getLock(key);
            if (lock.isLocked()) {
                log.error("接收组织人员添加通知，目前用户【{}】正在被变更！", userList.get(0).getUserId());
                return;
            }
            if (!lock.tryLock(2, 10, TimeUnit.SECONDS)) {
                return;
            }
            removeUsers(reduceUser, true);
            addUsers(addUser, true);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            if (Objects.nonNull(lock) && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    @Transactional
    public void addUsers(List<PermissionRelationOrganizeUserListDTO> userList, Boolean isOnboarding) {
        if (CollUtil.isEmpty(userList)) {
            return;
        }
        List<String> collect = userList.stream().map(PermissionRelationOrganizeUserListDTO::getOrganizeId).collect(Collectors.toList());
        Map<String, String> org2GroupMap = attendanceGroupService.batchSaveGroupByOrgIds(userList.get(0).getTenantId(), collect);
        List<String> userIds = userList.stream().map(PermissionRelationOrganizeUserListDTO::getUserId).collect(Collectors.toList());
        List<String> groupIds = userList.stream().map(dto -> org2GroupMap.get(dto.getOrganizeId())).collect(Collectors.toList());
        Date date = new Date();
        List<AttendanceGroupUser> attendanceGroupUsers = attendanceUserService.queryByUsersAndGroupFilterSecondment(date, date, userIds, null);
        List<String> userIds1 = attendanceGroupUsers.stream().filter(vo -> groupIds.contains(vo.getGroupId())).map(AttendanceGroupUser::getUserId).collect(Collectors.toList());
        List<AttendanceGroupUser> collect1 = userList.stream().filter(user -> !CollUtil.contains(userIds1, user.getUserId())).map(user -> {
            AttendanceGroupUser groupUser = new AttendanceGroupUser();
            groupUser.setId(RandomUtil.uuId());
            groupUser.setUserId(user.getUserId());
            groupUser.setSort(1000);
            groupUser.setGroupId(org2GroupMap.get(user.getOrganizeId()));
            groupUser.setType(GroupUserTypeEnum.CUR.getCode());
            groupUser.setUserGroupType(GroupUserTypeEnum.CUR.getCode());
            groupUser.setCreatorUserId("admin");
            groupUser.setCreatorTime(isOnboarding && Objects.nonNull(user.getEntryDate()) ? user.getEntryDate() : DateUtil.getDayBegin());
            groupUser.setDeleteMark(0);
            return groupUser;
        }).collect(Collectors.toList());
        saveBatch(collect1);
        List<AttendanceGroupUser> collect2 = attendanceGroupUsers.stream().filter(user -> userList.stream().anyMatch(vo -> StringUtil.equals(vo.getUserId(), user.getUserId()))).collect(Collectors.toList());
        Map<String, Date> collect3 = userList.stream().filter(vo -> Objects.nonNull(vo.getEntryDate())).collect(Collectors.toMap(PermissionRelationOrganizeUserListDTO::getUserId, PermissionRelationOrganizeUserListDTO::getEntryDate));
        if (CollUtil.isNotEmpty(collect2)) {
            collect2.forEach(user -> {
                if (CollUtil.contains(groupIds, user.getGroupId())) {
                    user.setCreatorTime(collect3.getOrDefault(user.getUserId(), user.getCreatorTime()));
                    return;
                }
                user.setRemoveTime(Objects.requireNonNullElse(user.getRemoveTime(), collect3.getOrDefault(user.getUserId(), new Date())));
                user.setDeleteMark(1);
            });
            updateBatchById(collect2);
        }
        String tenantId = userList.get(0).getTenantId();
        collect1.stream().collect(Collectors.groupingBy(AttendanceGroupUser::getGroupId, Collectors.mapping(AttendanceGroupUser::getUserId, Collectors.toList()))).forEach((groupId, groupUserIds) -> {
            groupUserIds.forEach(userId -> {
                MachineDealDto machineDealDto = new MachineDealDto(userId, ConstantUtil.CAL_ADD);
                attendanceUserFaceService.machineDeal(machineDealDto);
            });
            attendanceDailyRuleService.addUserFixedHandle(groupId, tenantId, groupUserIds);
            groupUserIds.forEach(userId -> attendanceClockInService.generateRepairNumForUser(groupId, userId, 1));
            sendNotice(groupId, groupUserIds, UserProvider.getUser().getTenantId(), AttendanceNoticeEnum.JOIN_GROUP);
        });
    }

    /**
     * 移除用户
     *
     * @param userList 用户列表
     */
    @Override
    @Transactional
    public void removeUsers(List<PermissionRelationOrganizeUserListDTO> userList, Boolean isTurnover) {
        if (CollUtil.isEmpty(userList)) {
            return;
        }
        //查询涉及考勤组
        List<String> collect = userList.stream().map(PermissionRelationOrganizeUserListDTO::getOldOrganizeId).collect(Collectors.toList());
        List<AttendanceGroup> groupListByOrgIds = attendanceGroupService.getGroupListByOrgIds(collect);
        Map<String, String> collect1 = groupListByOrgIds.stream().collect(Collectors.toMap(AttendanceGroup::getOrgId, AttendanceGroup::getId, (r1, r2) -> r2));
        Map<String, Date> departMap = Maps.newHashMap();
        String tenantId = userList.get(0).getTenantId();
        if (isTurnover) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            FtbDepUserDTO ftbDepUserDTO = new FtbDepUserDTO();
            ftbDepUserDTO.setUserIds(userList.stream().map(PermissionRelationOrganizeUserListDTO::getUserId).collect(Collectors.toList()));
            ftbDepUserDTO.setTenantId(tenantId);
            List<UserBoundVO> informationAboutTheDepartingPerson = ftbPersonnelsTurnoverManagementApi.getInformationAboutTheDepartingPersonNotToken(ftbDepUserDTO);
            log.error("查询离职数据为{}", JSONObject.toJSONString(informationAboutTheDepartingPerson));
            if (CollUtil.isEmpty(informationAboutTheDepartingPerson)) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                informationAboutTheDepartingPerson = ftbPersonnelsTurnoverManagementApi.getInformationAboutTheDepartingPersonNotToken(ftbDepUserDTO);
                log.error("2次查询离职数据为{}", JSONObject.toJSONString(informationAboutTheDepartingPerson));
            }
            if (CollUtil.isNotEmpty(informationAboutTheDepartingPerson)) {
                departMap.putAll(informationAboutTheDepartingPerson.stream().collect(Collectors.toMap(UserBoundVO::getId, UserBoundVO::getDepartDate)));
            }
        } else {
            Map<String, Date> collect2 = userList.stream().filter(user -> Objects.nonNull(user.getEntryDate())).collect(Collectors.toMap(PermissionRelationOrganizeUserListDTO::getUserId, PermissionRelationOrganizeUserListDTO::getEntryDate));
            departMap.putAll(collect2);
        }
        userList.stream().collect(Collectors.groupingBy(PermissionRelationOrganizeUserListDTO::getOldOrganizeId, Collectors.mapping(PermissionRelationOrganizeUserListDTO::getUserId, Collectors.toList()))).forEach((orgId, groupUserIds) -> {
            if (!collect1.containsKey(orgId)) {
                return;
            }
            String groupId = collect1.get(orgId);
            removeUsersHandle(groupId, groupUserIds, departMap, tenantId, orgId);
        });
    }

    private void updateUsers(List<PermissionRelationOrganizeUserListDTO> userList) {
        if (CollUtil.isEmpty(userList)) {
            return;
        }
        //查询涉及考勤组
        List<String> orgIds = userList.stream().map(PermissionRelationOrganizeUserListDTO::getOrganizeId).collect(Collectors.toList());
        List<AttendanceGroup> groupListByOrgIds = attendanceGroupService.getGroupListByOrgIds(orgIds);
        Map<String, String> collect1 = groupListByOrgIds.stream().collect(Collectors.toMap(AttendanceGroup::getId, AttendanceGroup::getOrgId, (r1, r2) -> r2));
        List<String> collect = new ArrayList<>(collect1.values());
        if (CollUtil.isNotEmpty(collect)) {
            JoinUserDto joinUserDto = new JoinUserDto();
            joinUserDto.setTenantId(userList.get(0).getTenantId());
            userList.stream().collect(Collectors.groupingBy(PermissionRelationOrganizeUserListDTO::getOldOrganizeId, Collectors.mapping(PermissionRelationOrganizeUserListDTO::getUserId, Collectors.toList()))).forEach((orgId, groupUserIds) -> {
                if (!collect1.containsKey(orgId)) {
                    return;
                }
                String groupId = collect1.get(orgId);
                joinUserDto.setUserIds(groupUserIds);
                joinUserDto.setGroupId(groupId);
                batchDeleteUsers(joinUserDto);
            });
            userList.stream().collect(Collectors.groupingBy(PermissionRelationOrganizeUserListDTO::getOrganizeId, Collectors.mapping(PermissionRelationOrganizeUserListDTO::getUserId, Collectors.toList()))).forEach((orgId, groupUserIds) -> {
                if (!collect1.containsKey(orgId)) {
                    return;
                }
                String groupId = collect1.get(orgId);
                joinUserDto.setGroupId(groupId);
                joinUserDto.setUserIds(groupUserIds);
                batchAddUsers(joinUserDto);
                sendNotice(groupId, joinUserDto.getUserIds(), joinUserDto.getTenantId(), AttendanceNoticeEnum.GROUP_CHANGE);
            });
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void joinUsers(JoinUserDto joinUserDto) throws HandleException {
        RLock lock = redissonClient.getLock(String.format(GROUP_JOIN_USERS_KEY, joinUserDto.getGroupId()));
        Assert.isFalse(lock.isLocked(), "当前考勤组添加用户操作正在执行中，请稍后再试");
        try {
            lock.tryLock(2, TimeUnit.MINUTES);
            QueryWrapper<AttendanceGroupUser> groupUserQueryWrapper = new QueryWrapper<>();
            groupUserQueryWrapper.lambda()
                    .eq(AttendanceGroupUser::getGroupId, joinUserDto.getGroupId())
                    .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
                    .eq(AttendanceGroupUser::getType, GroupUserTypeEnum.CUR.getCode());
            List<AttendanceGroupUser> attendanceGroupUsers = attendanceGroupUserMapper.selectList(groupUserQueryWrapper);
            List<String> userIds = attendanceGroupUsers.stream().map(AttendanceGroupUser::getUserId).collect(Collectors.toList());
            // 查询考勤组使用的考勤机
            if (CollectionUtil.isEmpty(joinUserDto.getUserIds())) {
                /* 删除考勤规则*/
                if (CollectionUtil.isNotEmpty(userIds)) {

                    attendanceDailyRuleService.clearGroupRule(joinUserDto.getGroupId(), userIds);
                    /* 移除考勤机*/
                    removeAttendanceMachine(userIds);
                    sendNotice(null, userIds, UserProvider.getUser().getTenantId(), AttendanceNoticeEnum.REMOVE_USER);
                    /* 如果用户选择为空则删除该考勤组下所有成员*/
                    lambdaUpdate()
                            .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
                            .in(AttendanceGroupUser::getUserId, userIds)
                            .set(AttendanceGroupUser::getRemoveTime, new Date())
                            .set(AttendanceGroupUser::getDeleteMark, 1)
                            .update();
                    return;
                }
            }
            if (CollectionUtil.isNotEmpty(attendanceGroupUsers)) {
                List<AttendanceGroupUser> collect = attendanceGroupUsers.stream().filter(groupUser -> !joinUserDto.getUserIds().contains(groupUser.getUserId())).collect(Collectors.toList());
                /* 要移除考勤组的成员*/
                if (CollectionUtil.isNotEmpty(collect)) {
                    List<String> removeUserIds = collect.stream().map(AttendanceGroupUser::getUserId).collect(Collectors.toList());
                    removeUsersHandle(joinUserDto.getGroupId(), removeUserIds, Maps.newHashMap(), UserProvider.getUser().getTenantId());
                }
            }

            /* 新增的成员*/
            if (CollectionUtil.isEmpty(joinUserDto.getUserIds())) {
                return;
            }

            /* 查询新增用户是否已经在考勤组*/
            List<String> addMachineUserIds = joinUserDto.getUserIds().stream().filter(userId -> !userIds.contains(userId)).collect(Collectors.toList());
            List<AttendanceGroupUser> addUserList = new ArrayList<>();
            for (String userId : addMachineUserIds) {
                AttendanceGroupUser groupUser = new AttendanceGroupUser();
                groupUser.setId(RandomUtil.uuId());
                groupUser.setUserId(userId);
                groupUser.setGroupId(joinUserDto.getGroupId());
                groupUser.setType(GroupUserTypeEnum.CUR.getCode());
                groupUser.setUserGroupType(GroupUserTypeEnum.CUR.getCode());
                groupUser.setCreatorUserId(UserProvider.getLoginUserId());
                groupUser.setCreatorTime(new Date());
                groupUser.setDeleteMark(0);
                addUserList.add(groupUser);
            }

            if (CollectionUtil.isNotEmpty(addMachineUserIds)) {
                // 用户加入考勤机
                log.info("用户加入考勤机, 考勤组id: {}, 用户id: {}", joinUserDto.getGroupId(), addMachineUserIds);
                addMachineUserIds.forEach(userId -> {
                    MachineDealDto machineDealDto = new MachineDealDto(userId, ConstantUtil.CAL_ADD);
                    attendanceUserFaceService.machineDeal(machineDealDto);
                });
            }
            if (CollectionUtil.isNotEmpty(addUserList)) {
                saveBatch(addUserList);
                attendanceDailyRuleService.addUserFixedHandle(joinUserDto.getGroupId(), addMachineUserIds);
                addMachineUserIds.forEach(userId -> attendanceClockInService.generateRepairNumForUser(joinUserDto.getGroupId(), userId, 1));
                sendNotice(joinUserDto.getGroupId(), addMachineUserIds, UserProvider.getUser().getTenantId(), AttendanceNoticeEnum.JOIN_GROUP);
                attendanceDayStatisticsService.userJoinHandleData(UserProvider.getUser().getTenantId(), joinUserDto.getGroupId(), addMachineUserIds);
            }
        } catch (Exception e) {
            log.error("考勤组添加成员失败{}", e.getMessage());
            throw new HandleException(e.getMessage());
        } finally {
            lock.unlock();
        }
    }

    private void removeUsersHandle(String groupId, List<String> removeUserIds, Map<String, Date> departMap, String tenantId) {
        removeUsersHandle(groupId, removeUserIds, departMap, UserProvider.getUser().getTenantId(), null);
    }

    private void removeUsersHandle(String groupId, List<String> removeUserIds, Map<String, Date> departMap, String tenantId, String orgId) {
        List<AttendanceGroupUser> list = lambdaQuery().in(AttendanceGroupUser::getUserId, removeUserIds)
                .eq(AttendanceGroupUser::getGroupId, groupId)
                .list();
        /* 用户移除考勤机*/
        removeUserIds.forEach(userId -> {
            MachineDealDto machineDealDto = new MachineDealDto(userId, ConstantUtil.CAL_DELETE, orgId);
            attendanceUserFaceService.machineDeal(machineDealDto);
        });
        list.forEach(remove -> {
            String nullJson = JsonUtil.getJsonToBean(new ArrayList<>(), String.class);
            remove.setTimeJson(nullJson);
            remove.setRemoveTime(Objects.isNull(remove.getRemoveTime()) ? departMap.getOrDefault(remove.getUserId(), new Date()) : remove.getRemoveTime());
            remove.setSort(2000);
            remove.setDeleteMark(1);
        });
        updateBatchById(list);
        /* 移除考勤规则*/
        Map.Entry<String, Date> stringDateEntry = departMap.entrySet().stream().findFirst().orElse(null);
        attendanceDailyRuleService.clearGroupRule(groupId, removeUserIds, Objects.isNull(stringDateEntry) ? new Date() : stringDateEntry.getValue(), tenantId);
        // 移除考勤组负责人
        attendanceGroupService.removeManager(groupId, removeUserIds);
        sendNotice(null, removeUserIds, tenantId, AttendanceNoticeEnum.REMOVE_USER);
    }

    private void sendNotice(String joinGroupId, List<String> userIds, String tenantId, AttendanceNoticeEnum attendanceNoticeEnum) {
        sendNotice(null, joinGroupId, userIds, tenantId, attendanceNoticeEnum);
    }

    private void sendNotice(Date date, String joinGroupId, List<String> userIds, String tenantId, AttendanceNoticeEnum attendanceNoticeEnum) {
        UserChangeNoticeModel userChangeNoticeModel = new UserChangeNoticeModel();
        userChangeNoticeModel.setJoinGroupId(joinGroupId);
        userChangeNoticeModel.setUserIds(userIds);
        userChangeNoticeModel.setTenantId(tenantId);
        userChangeNoticeModel.setStart(date);
        userChangeNoticeModel.setAttendanceNoticeEnum(attendanceNoticeEnum);
        attendanceNoticeHandler.send(userChangeNoticeModel);
    }

    @Override
    public void batchDeleteUsersForSendNotice(JoinUserDto joinUserDto) {
        batchDeleteUsers(joinUserDto);
        sendNotice(joinUserDto.getGroupId(), joinUserDto.getUserIds(), UserProvider.getUser().getTenantId(), AttendanceNoticeEnum.REMOVE_USER);
    }

    @Override
    public void batchDeleteUsers(JoinUserDto joinUserDto) {
        /* 移除考勤规则*/
        attendanceDailyRuleService.clearGroupRule(joinUserDto.getGroupId(), joinUserDto.getUserIds());
        /* 移除考勤机*/
        this.removeAttendanceMachine(joinUserDto.getUserIds());
        UpdateWrapper<AttendanceGroupUser> groupUserUpdateWrapper = new UpdateWrapper<>();
        groupUserUpdateWrapper.lambda()
                .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
                .in(AttendanceGroupUser::getUserId, joinUserDto.getUserIds());
        AttendanceGroupUser update = new AttendanceGroupUser();
        update.setRemoveTime(new Date());
        update.setDeleteMark(1);
        attendanceGroupUserMapper.update(update, groupUserUpdateWrapper);
    }

    @Override
    public void groupUpdateByPersonnel(GroupUpdateByUserDTO groupUpdateByUserDTO) {
//        JoinUserDto joinUserDto = new JoinUserDto();
//        joinUserDto.setUserIds(groupUpdateByUserDTO.getUserIds());
//        joinUserDto.setTenantId(groupUpdateByUserDTO.getTenantId());
//        if (Objects.equals(groupUpdateByUserDTO.getType(), 1)) {
//            if (StringUtils.isBlank(groupUpdateByUserDTO.getToGroupId())) {
//                return;
//            }
//            joinUserDto.setGroupId(groupUpdateByUserDTO.getToGroupId());
//            joinUserDto.setJoinTime(groupUpdateByUserDTO.getJoiningDate());
//            batchAddUsers(joinUserDto);
//            sendNotice(groupUpdateByUserDTO.getToGroupId(), joinUserDto.getUserIds(), groupUpdateByUserDTO.getTenantId(), AttendanceNoticeEnum.JOIN_GROUP);
//            return;
//        }
//        List<AttendanceGroupUser> list = lambdaQuery()
//                .in(AttendanceGroupUser::getUserId, joinUserDto.getUserIds())
//                .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
//                .list();
//        if (Objects.equals(groupUpdateByUserDTO.getType(), 3) || Objects.equals(groupUpdateByUserDTO.getType(), 4)) {
//            List<String> collect = list.stream().map(AttendanceGroupUser::getGroupId).collect(Collectors.toList());
//            if (CollUtil.isNotEmpty(collect)) {
//                collect.forEach(groupId -> {
//                    if (StringUtil.equals(groupId, groupUpdateByUserDTO.getToGroupId())) {
//                        return;
//                    }
//                    joinUserDto.setGroupId(groupId);
//                    batchDeleteUsers(joinUserDto);
//                });
//            }
//            if (StringUtil.isNotEmpty(groupUpdateByUserDTO.getToGroupId())) {
//                joinUserDto.setGroupId(groupUpdateByUserDTO.getToGroupId());
//                // v1.8.1
//                joinUserDto.setOldUserList(list);
//                batchAddUsers(joinUserDto);
//                sendNotice(groupUpdateByUserDTO.getToGroupId(), joinUserDto.getUserIds(), groupUpdateByUserDTO.getTenantId(), Objects.equals(groupUpdateByUserDTO.getType(), 3) ? AttendanceNoticeEnum.GROUP_CHANGE_TRANSFER_POSITION : AttendanceNoticeEnum.GROUP_CHANGE_PROMOTION);
//            }
//            return;
//        }
//        if (Objects.equals(groupUpdateByUserDTO.getType(), 2) || Objects.equals(groupUpdateByUserDTO.getType(), 5)) {
//            List<String> collect = list.stream().map(AttendanceGroupUser::getGroupId).collect(Collectors.toList());
//            collect.forEach(groupId -> {
//                if (StringUtil.equals(groupId, groupUpdateByUserDTO.getToGroupId())) {
//                    return;
//                }
//                joinUserDto.setGroupId(groupId);
//                batchDeleteUsers(joinUserDto);
//            });
//            sendNotice(null, joinUserDto.getUserIds(), groupUpdateByUserDTO.getTenantId(), AttendanceNoticeEnum.REMOVE_USER);
//        }
    }

    @Override
    public List<AttendanceGroupUserVo> getGroupUserList(String id, Boolean isPermissions) {
        List<AttendanceGroupUserVo> list = new ArrayList<>();
        List<UserBoundVO> userBoundVO;
        List<String> userIds = attendanceGroupUserMapper.getGroupUserListByIds(List.of(id));
        if (Objects.isNull(isPermissions) || isPermissions.equals(Boolean.FALSE)) {
            userBoundVO = v2UserApi.getUserPrimaryBoundBatch(userIds, null).getData();
        } else {
            // 获取有权限的人员列表
            userBoundVO = getUserBoundVO(id, null);
        }
        if (CollectionUtil.isNotEmpty(userBoundVO)) {
            for (UserBoundVO boundVo : userBoundVO) {
                AttendanceGroupUserVo groupUserVo = new AttendanceGroupUserVo();
                groupUserVo.setUserId(boundVo.getId());
                groupUserVo.setRealName(boundVo.getUserName());
                groupUserVo.setNickname(boundVo.getNickname());
                groupUserVo.setHeadIcon(UploaderUtil.uploaderImg(boundVo.getHeadIcon()));
                groupUserVo.setPositionName(boundVo.getPositionName());
                list.add(groupUserVo);
            }
        }
        return list;
    }

    private void batchAddUsers(JoinUserDto joinUserDto) {
        /* 查询新增用户是否已经在考勤组*/
        QueryWrapper<AttendanceGroupUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AttendanceGroupUser::getGroupId, joinUserDto.getGroupId())
                .in(AttendanceGroupUser::getUserId, joinUserDto.getUserIds())
                .eq(AttendanceGroupUser::getDeleteMark, Boolean.FALSE)
                .ne(AttendanceGroupUser::getType, GroupUserTypeEnum.BORROW.getCode());
        List<AttendanceGroupUser> dbGroupUserList = attendanceGroupUserMapper.selectList(queryWrapper);

        Map<String, AttendanceGroupUser> groupUserMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(dbGroupUserList)) {
            for (AttendanceGroupUser groupUser : dbGroupUserList) {
                groupUserMap.put(groupUser.getUserId(), groupUser);
            }
        }
        List<AttendanceGroupUser> addUserList = new ArrayList<>();
        List<String> addMachineUserIds = new ArrayList<>();
        for (String userId : joinUserDto.getUserIds()) {
            AttendanceGroupUser groupUser = groupUserMap.get(userId);
            if (groupUser != null && !GroupUserTypeEnum.REMOVE.getCode().equals(groupUser.getType())) {
                continue;
            }
            groupUser = new AttendanceGroupUser();
            groupUser.setId(RandomUtil.uuId());
            groupUser.setUserId(userId);
            groupUser.setGroupId(joinUserDto.getGroupId());
            groupUser.setType(GroupUserTypeEnum.CUR.getCode());
            groupUser.setUserGroupType(GroupUserTypeEnum.CUR.getCode());
            groupUser.setCreatorUserId(UserProvider.getLoginUserId());
            groupUser.setCreatorTime(Objects.nonNull(joinUserDto.getJoinTime()) ? joinUserDto.getJoinTime() : new Date());
            addUserList.add(groupUser);
            addMachineUserIds.add(userId);
        }

        if (CollectionUtil.isNotEmpty(addMachineUserIds)) {
            for (String userId : addMachineUserIds) {
                /* 加入考勤机*/
                MachineDealDto machineDealDto = new MachineDealDto(userId, ConstantUtil.CAL_ADD);
                attendanceUserFaceService.machineDeal(machineDealDto);
            }
        }
        if (CollectionUtil.isNotEmpty(addUserList)) {
            saveBatch(addUserList);
            attendanceDayStatisticsService.userJoinHandleData(joinUserDto.getTenantId(), Objects.nonNull(joinUserDto.getJoinTime()) ? joinUserDto.getJoinTime() : new Date(), joinUserDto.getGroupId(), addMachineUserIds);
            attendanceDailyRuleService.addUserFixedHandle(joinUserDto.getGroupId(), joinUserDto.getTenantId(), addMachineUserIds);
            addMachineUserIds.forEach(userId -> attendanceClockInService.generateRepairNumForUser(joinUserDto.getGroupId(), userId, 1));

            // v1.8.1 批量移动考勤组用户在原组的管理员权限
            if (!joinUserDto.getOldUserList().isEmpty()) {
                List<String> oldUserIds = joinUserDto.getOldUserList().stream().map(AttendanceGroupUser::getUserId).collect(Collectors.toList());
                // 获取用户在新组的管理员权限
                List<AttendanceManagerPermission> newGroupPermission = attendanceManagerPermissionMapper.getGroupManagerPermissionByUserIds(oldUserIds, joinUserDto.getGroupId());
                joinUserDto.getOldUserList().forEach(v -> {
                    //
                    if (null != newGroupPermission && !newGroupPermission.isEmpty()) {
                        List<AttendanceManagerPermission> myList = newGroupPermission.stream().filter(item ->
                                item.getUserId().equals(v.getUserId())
                        ).collect(Collectors.toList());
                        if (!myList.isEmpty()) {
                            // 获取用户在原组的管理员权限
                            List<AttendanceManagerPermission> yuanGroup = attendanceManagerPermissionMapper.getGroupManagerPermission(v.getUserId(), v.getGroupId());
                            attendanceManagerPermissionMapper.deleteByGroupUserId(v.getUserId(), v.getGroupId());
                            if (CollectionUtil.isNotEmpty(yuanGroup)) {
                                List<String> newGroupPermissionIds = myList.stream().map(AttendanceManagerPermission::getPermissionId).collect(Collectors.toList());
                                List<AttendanceManagerPermission> list = new ArrayList<>();
                                // 用户在新组也有权限 移除用户在旧组中重复的权限
                                yuanGroup.forEach(yuan -> {
                                    if (!newGroupPermissionIds.contains(yuan.getPermissionId())) {
                                        list.add(yuan);
                                    }
                                });
                                // 新增非重复的权限
                                if (CollectionUtil.isNotEmpty(list)) {
                                    attendanceManagerPermissionMapper.addBatch(list);
                                }
                            }
                        } else {
                            // 循环修改转移用户权限
                            attendanceManagerPermissionMapper.updateUserGroupManagerPermission(v.getUserId(), v.getGroupId(), joinUserDto.getGroupId());
                        }
                    } else {
                        // 循环修改转移用户权限
                        attendanceManagerPermissionMapper.updateUserGroupManagerPermission(v.getUserId(), v.getGroupId(), joinUserDto.getGroupId());
                    }
                });
            }
        }
    }

    /**
     * 检查用户是否已经加入考勤组
     *
     * @param userIds 用户id
     */
    @Override
    public List<String> checkUserGroup(String userIds) {
        if (StrUtil.isBlank(userIds)) {
            return new ArrayList<>();
        }
        String[] userIdArr = userIds.split(",");
        ArrayList<String> userIdList = CollectionUtil.newArrayList(userIdArr);
        List<AttendanceGroupUserVo> groupUserVoList = attendanceGroupUserMapper.getGroupInUserIds(userIdList, null);
        if (CollectionUtil.isEmpty(groupUserVoList)) {
            return new ArrayList<>();
        }
        List<AttendanceGroupUserVo> groupUserVos = groupUserVoList.stream().filter(groupUser -> {
            return GroupUserTypeEnum.CUR.getCode().equals(groupUser.getType());
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(groupUserVos)) {
            List<String> userNameList = new ArrayList<>();
            Map<String, AttendanceGroupUserVo> userVoMap = groupUserVos.stream().collect(Collectors.toMap(AttendanceGroupUserVo::getUserId, Function.identity()));
            List<UserBoundVO> userBoundVos = v2UserApi.userListAndCopy(groupUserVos.stream().map(AttendanceGroupUserVo::getUserId).collect(Collectors.toList()), false, null);
            // 拼接考勤组名称
            userBoundVos.forEach(user -> {
                String append = user.getUserName() + "已加入" + userVoMap.get(user.getId()).getGroupName() + "考勤组";
                userNameList.add(append);
            });
            return userNameList;
        }
        return new ArrayList<>();
    }

    @Override
    public Map<String, Map<String, List<String>>> batchGetUserCycleList(DateRangeDto dateRangeDto, List<String> groupIds, List<String> userIds) throws Exception {
        DateTime startDate = cn.hutool.core.date.DateUtil.beginOfDay(cn.hutool.core.date.DateUtil.parse(dateRangeDto.getStartDate()));
        DateTime endDate = cn.hutool.core.date.DateUtil.endOfDay(cn.hutool.core.date.DateUtil.parse(dateRangeDto.getEndDate()));
        DateRangeDto validDateRange = new DateRangeDto(startDate.toDateStr(), endDate.toDateStr());
        List<AttendanceGroupUser> groupUserList = this.lambdaQuery().in(CollUtil.isNotEmpty(groupIds), AttendanceGroupUser::getGroupId, groupIds).in(CollUtil.isNotEmpty(userIds), AttendanceGroupUser::getUserId, userIds).list();
        Map<String, Map<String, List<String>>> userMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(groupUserList)) {
            Map<String, List<AttendanceGroupUser>> groupUsers = groupUserList.stream().collect(Collectors.groupingBy(AttendanceGroupUser::getGroupId));
            // 【关键修复】收集每个用户的最早借调开始时间，用于计算本组结束时间
            Map<String, DateTime> userFirstSecondmentTimeMap = new HashMap<>();
            for (List<AttendanceGroupUser> users : groupUsers.values()) {
                for (AttendanceGroupUser item : users) {
                    if (Integer.valueOf(2).equals(item.getUserGroupType())) {
                        // 借调记录，解析借调时间
                        List<SecondmentDateVo> dateVoList = JsonUtil.getJsonToList(item.getTimeJson(), SecondmentDateVo.class);
                        if (CollUtil.isNotEmpty(dateVoList)) {
                            for (SecondmentDateVo vo : dateVoList) {
                                if (vo.getStartTime() != null) {
                                    DateTime secondmentStart = new DateTime(vo.getStartTime());
                                    String userId = item.getUserId();
                                    // 保留最早的借调开始时间
                                    if (!userFirstSecondmentTimeMap.containsKey(userId) || secondmentStart.isBefore(userFirstSecondmentTimeMap.get(userId))) {
                                        userFirstSecondmentTimeMap.put(userId, secondmentStart);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            for (Map.Entry<String, List<AttendanceGroupUser>> groupEntry : groupUsers.entrySet()) {
                Map<String, List<String>> userCycleMap = new HashMap<>();
                // 获取本考勤组数据 (userGroupType = 1)
                Map<String, List<AttendanceGroupUser>> thisGroupUserMap = groupEntry.getValue().stream().filter(item -> Integer.valueOf(1).equals(item.getUserGroupType())).collect(Collectors.groupingBy(AttendanceGroupUser::getUserId));
                // 获取借调考勤组数据 (userGroupType = 2)
                Map<String, List<AttendanceGroupUser>> otherGroupUserMap = groupEntry.getValue().stream().filter(item -> Integer.valueOf(2).equals(item.getUserGroupType())).collect(Collectors.groupingBy(AttendanceGroupUser::getUserId));
                // 传递借调时间映射
                dataAssemble(validDateRange, userCycleMap, thisGroupUserMap, true, userFirstSecondmentTimeMap);
                dataAssemble(validDateRange, userCycleMap, otherGroupUserMap, false, userFirstSecondmentTimeMap);
                userMap.put(groupEntry.getKey(), userCycleMap);
            }
        }
        return userMap;
    }

    private void dataAssemble(DateRangeDto dateRangeDto, Map<String, List<String>> userCycleMap, Map<String, List<AttendanceGroupUser>> groupUserMap, boolean isThisGroup, Map<String, DateTime> userFirstSecondmentTimeMap) throws Exception {
        if (CollUtil.isNotEmpty(groupUserMap)) {
            // 解析查询日期范围（保持时间精度）
            DateTime queryStart = cn.hutool.core.date.DateUtil.beginOfDay(cn.hutool.core.date.DateUtil.parse(dateRangeDto.getStartDate()));
            DateTime queryEnd = cn.hutool.core.date.DateUtil.endOfDay(cn.hutool.core.date.DateUtil.parse(dateRangeDto.getEndDate()));
            for (Map.Entry<String, List<AttendanceGroupUser>> entry : groupUserMap.entrySet()) {
                String userId = entry.getKey();
                List<String> cycleList = new ArrayList<>();
                List<AttendanceGroupUser> userList = entry.getValue();
                // 按创建时间排序
                userList = userList.stream().sorted(Comparator.comparing(AttendanceGroupUser::getCreatorTime)).collect(Collectors.toList());
                for (AttendanceGroupUser item : userList) {
                    if (isThisGroup) {
                        // ===== 本组周期计算 =====
                        Date creatorTime = item.getCreatorTime();
                        Date removeTime = item.getRemoveTime();
                        // 如果创建时间在查询结束时间之后，跳过
                        if (creatorTime != null && creatorTime.after(queryEnd.toJdkDate())) {
                            continue;
                        }
                        // 【关键修复】计算有效结束时间
                        DateTime effectiveEnd;
                        if (removeTime != null) {
                            // 优先使用 removeTime
                            DateTime removeDateTime = new DateTime(removeTime);
                            effectiveEnd = removeDateTime.isAfter(queryEnd) ? queryEnd : removeDateTime;
                        } else if (userFirstSecondmentTimeMap.containsKey(userId)) {
                            // removeTime 为 null 时，使用最早借调开始时间作为本组结束时间
                            DateTime firstSecondmentTime = userFirstSecondmentTimeMap.get(userId);
                            effectiveEnd = firstSecondmentTime.isAfter(queryEnd) ? queryEnd : firstSecondmentTime;
                        } else {
                            // 没有借调记录，使用查询结束时间
                            effectiveEnd = queryEnd;
                        }
                        // 如果结束时间在查询开始时间之前，跳过
                        if (effectiveEnd.isBefore(queryStart)) {
                            continue;
                        }
                        // 计算有效开始时间：max(加入时间，查询开始时间)
                        DateTime effectiveStart = (creatorTime != null && creatorTime.after(queryStart.toJdkDate())) ? new DateTime(creatorTime) : queryStart;
                        // 只有当开始时间 <= 结束时间时才计算重叠
                        if (effectiveStart.isAfter(effectiveEnd)) {
                            continue;
                        }
                        // 计算重叠时间段
                        HashMap<String, DateTime> dateTimeHashMap = setOverlap(effectiveStart, effectiveEnd, queryStart, queryEnd, false);
                        if (CollUtil.isNotEmpty(dateTimeHashMap)) {
                            DateTime startDt = dateTimeHashMap.get("startDate");
                            DateTime endDt = dateTimeHashMap.get("endDate");
                            String startStr = formatDateTime(startDt);
                            String endStr = formatDateTime(endDt);
                            cycleList.add(startStr + "至" + endStr);
                        }
                    } else {
                        // ===== 借调组周期计算 =====
                        List<SecondmentDateVo> dateVoList = JsonUtil.getJsonToList(item.getTimeJson(), SecondmentDateVo.class);
                        if (CollUtil.isNotEmpty(dateVoList)) {
                            for (SecondmentDateVo secondmentDateVo : dateVoList) {
                                Date startTime = secondmentDateVo.getStartTime();
                                Date endTime = secondmentDateVo.getEndTime();
                                // 借调结束时间在查询开始时间之前，跳过
                                if (endTime != null && endTime.before(queryStart.toJdkDate())) {
                                    continue;
                                }
                                // 借调开始时间在查询结束时间之后，跳过
                                if (startTime != null && startTime.after(queryEnd.toJdkDate())) {
                                    continue;
                                }
                                DateTime secondmentStart = new DateTime(startTime);
                                DateTime secondmentEnd = new DateTime(endTime);
                                // 借调结束时间不能超过查询结束时间
                                DateTime effectiveEnd = secondmentEnd.isAfter(queryEnd) ? queryEnd : secondmentEnd;
                                // 只有当开始时间 <= 结束时间时才计算重叠
                                if (secondmentStart.isAfter(effectiveEnd)) {
                                    continue;
                                }
                                HashMap<String, DateTime> dateTimeHashMap = setOverlap(secondmentStart, effectiveEnd, queryStart, queryEnd, false);
                                if (CollUtil.isNotEmpty(dateTimeHashMap)) {
                                    DateTime startDt = dateTimeHashMap.get("startDate");
                                    DateTime endDt = dateTimeHashMap.get("endDate");
                                    String startStr = formatDateTime(startDt);
                                    String endStr = formatDateTime(endDt);
                                    cycleList.add(startStr + "至" + endStr);
                                }
                            }
                        }
                    }
                }
                userCycleMap.put(userId, cycleList);
            }
        }
    }

    /**
     * 格式化日期时间：只显示日期部分，不显示时分秒
     */
    private String formatDateTime(DateTime dateTime) {
        if (dateTime == null) {
            return "";
        }
        // 直接返回日期字符串，不显示时间
        return dateTime.toDateStr();
    }
    /**
     * 获取有权限的用户列表
     *
     * @param filterList 筛选条件
     * @param workStatus 用户状态
     * @return 用户列表
     */
    private List<UserBoundVO> getUserIdArr(List<GroupFilterDto> filterList, String workStatus) {
        List<String> groupIds = filterList.stream()
                .map(GroupFilterDto::getGroupId)
                .distinct()
                .collect(Collectors.toList());
        List<String> teamIds = filterList.stream()
                .map(GroupFilterDto::getTeamId)
                .distinct()
                .collect(Collectors.toList());
        StatisticsEnumUtil.WorkStatusEnum workStatusEnum = StatisticsEnumUtil.WorkStatusEnum.getWorkStatusEnum(workStatus);
        Assert.notNull(workStatusEnum, "工作状态不正确");
        List<UserWorkStatusEnums> workStatusEnums = DayStatisticsUtils.getWorkStatusEnumList(Objects.requireNonNull(workStatusEnum));
        List<UserBoundVO> userBoundVoList = batchGetUserBoundVO(groupIds, workStatusEnums);
        return CollUtil.isNotEmpty(userBoundVoList) ? userBoundVoList : CollUtil.newArrayList();
    }

    /**
     * 用户移除考勤机
     *
     * @param removeUserIds    移除用户ids
     */
    private void removeAttendanceMachine(List<String> removeUserIds) {
        System.out.println("移除考勤机用户...");
        /* 用户移除考勤机*/
        removeUserIds.forEach(userId -> {
            MachineDealDto machineDealDto = new MachineDealDto(userId, ConstantUtil.CAL_DELETE);
            attendanceUserFaceService.machineDeal(machineDealDto);
        });
    }

    @Override
    public Boolean userGroupUpdateBySecondNotice(String tenantId) {
        String startTime = DateUtil.getmmNow() + ":00";
        String endTime = DateUtil.getmmNow() + ":59";
        List<AttendanceSelfApproveVo> selfApproveVoList = attendanceLeaveApproveMapper.getSelfApproveList(startTime, endTime);
        if (CollUtil.isEmpty(selfApproveVoList)) {
            return Boolean.TRUE;
        }
        selfApproveVoList.forEach(item -> {
            sendNotice(item.getStartTime(), item.getGroupId(), item.getUserIds(), UserProvider.getUser().getTenantId(), AttendanceNoticeEnum.GROUP_CHANGE_SECONDMENT);
        });
        return Boolean.TRUE;
    }

    @Override
    public List<GroupUserLineScheduleVo> listLineScheduleUsersByGroupId(String groupId) {
        if (StringUtil.isEmpty(groupId)) {
            return CollUtil.newArrayList();
        }
        // 以当前时刻作为有效区间，过滤掉已离组与外派借出的人员，口径与排班界面 canLineSchedule 一致
        Date now = new Date();
        List<AttendanceGroupUser> groupUsers = getAttendanceGroupUsersOfSecondment(now, now, null, List.of(groupId));
        if (CollUtil.isEmpty(groupUsers)) {
            return CollUtil.newArrayList();
        }
        List<String> userIds = groupUsers.stream()
                .map(AttendanceGroupUser::getUserId)
                .filter(StrUtil::isNotEmpty)
                .distinct()
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(userIds)) {
            return CollUtil.newArrayList();
        }
        // 批量取主岗位信息（id/userName/positionId/positionName）
        ActionResult<List<UserBoundVO>> userResult = v2UserApi.getAllUserInfoBatch(userIds, null);
        List<UserBoundVO> userInfoList = (userResult != null && userResult.getCode() == 200 && userResult.getData() != null)
                ? userResult.getData() : CollUtil.newArrayList();
        Map<String, UserBoundVO> userInfoMap = userInfoList.stream()
                .collect(Collectors.toMap(UserBoundVO::getId, Function.identity(), (a, b) -> a));
        // 按考勤组划线排班配置过滤，留下的即为允许划线排班的成员
        List<String> canLineUserIds = new ArrayList<>(userIds);
        attendanceDailyRuleService.lineSchedulesConfigFilter(groupId, canLineUserIds);
        Set<String> canLineUserIdSet = new HashSet<>(canLineUserIds);
        return userIds.stream().map(uid -> {
            GroupUserLineScheduleVo vo = new GroupUserLineScheduleVo();
            vo.setUserId(uid);
            UserBoundVO info = userInfoMap.get(uid);
            if (Objects.nonNull(info)) {
                vo.setUserName(info.getUserName());
                vo.setPositionId(info.getPositionId());
                vo.setPositionName(info.getPositionName());
            }
            vo.setCanLineSchedule(canLineUserIdSet.contains(uid));
            return vo;
        }).collect(Collectors.toList());
    }
}
