package jnpf.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jnpf.attendance.mapper.*;
import jnpf.attendance.service.*;
import jnpf.base.ActionResult;
import jnpf.base.UserInfo;
import jnpf.base.vo.PaginationVO;
import jnpf.entity.AttendanceGroup;
import jnpf.entity.AttendanceGroupUser;
import jnpf.entity.attendance.*;
import jnpf.enums.attendance.ClockInStatusEnum;
import jnpf.enums.attendance.ScopeBizType;
import jnpf.exception.HandleException;
import jnpf.file.FileUploadApi;
import jnpf.model.attendance.dto.AttendanceUserBalanceDto;
import jnpf.model.attendance.dto.AttendanceUserBalanceListQueryDto;
import jnpf.model.attendance.dto.OrgTeamDto;
import jnpf.model.attendance.vo.ClockInVo;
import jnpf.model.attendance.vo.attendance.*;
import jnpf.permission.V2UserApi;
import jnpf.permission.eum.v2.UserWorkStatusEnums;
import jnpf.permission.vo.v2.user.UserBoundInfoVO;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.util.*;
import jnpf.util.attendance.ExpiresTimeUtil;
import jnpf.util.attendance.RuleScopeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author panpan
 */
@Service
@Slf4j
public class AttendanceUserBalanceServiceImpl implements AttendanceUserBalanceService {

    @Resource
    private AttendanceUserBalanceMapper attendanceUserBalanceMapper;

    @Resource
    @Lazy
    private RuleScopeUtil ruleScopeUtil;

    @Autowired
    private AttendanceUserService attendanceUserService;

    @Resource
    private AttendanceLeaveRulesMapper attendanceLeaveRulesMapper;

    @Resource
    private AttendanceLeaveGrantSettingMapper attendanceLeaveGrantSettingMapper;

    @Resource
    private AttendanceClockInMapper attendanceClockInMapper;

    @Resource
    private EnableBalanceService enableBalanceService;

    @Resource
    @Lazy
    private V2UserApi v2UserApi;

    @Resource
    @Lazy
    private UserProvider userProvider;

    @Resource
    @Lazy
    private FileUploadApi fileUploadApi;

    @Resource
    @Lazy
    private AttendanceUserBalanceRecordService attendanceUserBalanceRecordService;

    @Resource
    @Lazy
    private ExpiresTimeUtil expiresTimeUtil;

    @Autowired
    private AttendanceBaseSettingService attendanceBaseSettingService;

    @Resource
    @Lazy
    private AttendanceLeaveTypeService attendanceLeaveTypeService;

    @Autowired
    @Lazy
    private AttendanceGroupService attendanceGroupService;

    @Autowired
    private CustomTenantUtil tenantUtil;

    @Override
    public AttendanceUserBalanceListVo list(AttendanceUserBalanceListQueryDto queryDto) throws  HandleException {
        AttendanceUserBalanceListVo attendanceUserBalanceListVo = new AttendanceUserBalanceListVo();
        List<String> userIds = null;
        List<String> gIds = Optional.ofNullable(queryDto.getOrgTeamDtoList())
                .orElse(Collections.emptyList())
                .stream()
                .filter(Objects::nonNull)
                .map(OrgTeamDto::getOrganizeId)
                .filter(Objects::nonNull)
                .filter(orgId -> !orgId.isEmpty())
                .collect(Collectors.toList());
        List<AttendanceGroup> list1 = attendanceGroupService.list(new LambdaQueryWrapper<AttendanceGroup>()
                .in(AttendanceGroup::getId, gIds));
        if (CollUtil.isEmpty(list1)) {
            new HandleException("未找到对应考勤组，请检查考勤组是否存在");
        }
        try {
            Map<String, AttendanceGroup> groupMap = list1.stream().collect(Collectors.toMap(AttendanceGroup::getId, Function.identity()));
            queryDto.getOrgTeamDtoList().forEach(orgTeamDto -> {
                orgTeamDto.setOrgId(groupMap.get(orgTeamDto.getOrganizeId()).getOrgId());
            });
            List<String> orgIds = queryDto.getOrgTeamDtoList().stream().map(OrgTeamDto::getOrgId).collect(Collectors.toList());
            if (CollUtil.isEmpty(orgIds)) {
                new HandleException("未找到对应组织，请检查组织是否存在");
            }
            // 用户及工作状态筛选
            userIds = getUserIds(queryDto, userIds,orgIds);
        } catch (Exception e) {
            return attendanceUserBalanceListVo;
        }
        // 设置动态表头
        List<AttendanceLeaveType> allType = attendanceLeaveTypeService.list(new LambdaQueryWrapper<AttendanceLeaveType>()
                .eq(AttendanceLeaveType::getDeleteMark, 0)
                .eq(AttendanceLeaveType::getBuiltIn, 0)
                .orderByAsc(AttendanceLeaveType::getCreatorTime));
        List<String> typeIds = allType.stream().map(AttendanceLeaveType::getId).collect(Collectors.toList());
        // 获取动态请假类型名称
        attendanceUserBalanceListVo.setLeaveNames(allType);
        if (CollUtil.isEmpty(userIds) && (StringUtil.isNotEmpty(queryDto.getWorkStatus()) || StringUtil.isNotEmpty(queryDto.getIText()))) {
            setPagination(queryDto, List.of(), attendanceUserBalanceListVo);
            return attendanceUserBalanceListVo;
        }
        //通过组织ID查询考勤组ids
        if (CollUtil.isEmpty(list1) || list1.isEmpty()) {
            setPagination(queryDto, List.of(), attendanceUserBalanceListVo);
            return attendanceUserBalanceListVo;
        }
        // 获取在考勤组中的用户，不包含借调用户
        Date date = new Date();
        List<AttendanceGroupUser> attendanceGroupUsers = attendanceUserService.queryByUsersAndGroupFilterSecondment(date, date, userIds, gIds);
        if (CollUtil.isEmpty(attendanceGroupUsers) || attendanceGroupUsers.isEmpty()) {
            return attendanceUserBalanceListVo;
        }
        // 组装数据
        List<AttendanceUserBalanceListDataVo> list = new ArrayList<>();
        // 计算分页参数
        int startIndex = (queryDto.getCurrentPage() - 1) * queryDto.getPageSize();
        int endIndex = Math.min(startIndex + queryDto.getPageSize(), attendanceGroupUsers.size());
        // 设置分页参数
        setPagination(queryDto, attendanceGroupUsers, attendanceUserBalanceListVo);
        // 根据查出来的attendanceGroupUsers中的userID顺序手动分页
        attendanceGroupUsers = attendanceGroupUsers.subList(startIndex, endIndex);
        // 转为有序的 List
        attendanceGroupUsers.forEach(attendanceGroupUser -> {
            AttendanceUserBalanceListDataVo vo = new AttendanceUserBalanceListDataVo();
            vo.setUserId(attendanceGroupUser.getUserId());
            list.add(vo);
        });
        // 当前页的用户ids集合
        List<String> listUserIds = attendanceGroupUsers.stream().map(AttendanceGroupUser::getUserId).distinct().collect(Collectors.toList());
        ActionResult<List<UserBoundVO>> userList = v2UserApi.getAllUserInfoBatch(listUserIds, null);
        Map<String, UserBoundVO> userMap = new HashMap<>();
        if (200 == userList.getCode() && CollectionUtil.isNotEmpty(userList.getData())) {
            userMap = userList.getData().stream().collect(Collectors.toMap(UserBoundVO::getId, Function.identity()));
        }
        Map<String, UserBoundVO> finalUserMap = userMap;
        list.forEach(v -> {
            UserBoundVO userBoundVO = finalUserMap.get(v.getUserId());
            v.setUserName(null == userBoundVO ? "--" : userBoundVO.getUserName());
            v.setGroupName(null == userBoundVO ? "--" : userBoundVO.getOrganizeTreeName());
            v.setPostName(null == userBoundVO ? "--" : userBoundVO.getPositionName());
            v.setWorkStatus(null == userBoundVO ? "--" : userBoundVO.getWorkStatusName());
            v.setOrgId(null == userBoundVO ? "--" : userBoundVO.getOrganizeId());
        });
        // 获取员工现在拥有的假期规则列表(可能比余额表记录中拥有的多)  不需要优先级查询（里面包含没有启用、内置、不发放余额的规则）
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(listUserIds, ScopeBizType.LEAVE_RULES, ConstantUtil.NUM_TRUE, typeIds);
        // 当前列表用户对应的启动中的假期规则（除内置调休）包含不发放余额的规则
        List<AttendanceLeaveRules> rules = attendanceLeaveRulesMapper.selectList(new QueryWrapper<AttendanceLeaveRules>()
                .lambda()
                .eq(AttendanceLeaveRules::getDeleteMark, 0)
                .eq(AttendanceLeaveRules::getState, 1)
                .eq(AttendanceLeaveRules::getBuiltIn, 0)
                .ne(AttendanceLeaveRules::getScopeOfAdaptation, -1)
                .in(CollUtil.isNotEmpty(attendanceRuleScopes), AttendanceLeaveRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList()))
        );
        Map<String, AttendanceLeaveRules> ruleMap = rules.stream().collect(Collectors.toMap(AttendanceLeaveRules::getId, Function.identity()));
        // 启动中的假期规则Id集合（除内置调休）包含不发放余额的规则
        List<String> startRuleIds = rules.stream().map(AttendanceLeaveRules::getId).distinct().collect(Collectors.toList());
        // 过滤出启动中的（除内置调休）包含不发放余额的规则
        List<AttendanceRuleScope> scopeList = attendanceRuleScopes.stream().filter(v -> startRuleIds.contains(v.getRuleId())).collect(Collectors.toList());
        // 根据用户Id进行分组 value 包含不发放余额的规则
        Map<String, List<AttendanceRuleScope>> userRulesMap = scopeList.stream().collect(Collectors.groupingBy(AttendanceRuleScope::getUserId));
        // 查询所有类型且有余额对应的劵记录
        List<AttendanceBalanceRecordEntity> records = attendanceUserBalanceRecordService.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                .lambda()
                .and(x1 ->
                        x1.and(x ->
                                        x.eq(AttendanceBalanceRecordEntity::getType, 2)
                                                .in(AttendanceBalanceRecordEntity::getObjectId, typeIds))
                                .or()
                                .eq(AttendanceBalanceRecordEntity::getType, 3))
                .in(AttendanceBalanceRecordEntity::getUserId, listUserIds)
                .eq(AttendanceBalanceRecordEntity::getDeleteMark, 0)
                .eq(AttendanceBalanceRecordEntity::getState, 0)
                .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
        );
        // 先根据userId再根据objectId过滤出objectId假类型数据并分组计算余额
        Map<String, List<AttendanceBalanceRecordEntity>> userBalanceMap = records.stream().collect(Collectors.groupingBy(AttendanceBalanceRecordEntity::getUserId));
        list.forEach(value -> {
            // 用户相关的余额列表
            List<AttendanceBalanceRecordEntity> balanceRecordList = userBalanceMap.get(value.getUserId());
            value.setBalance(BigDecimal.ZERO);
            // 用户生效中的规则列表  对应的值一个扩展字段类型只能最多一条数据
            List<AttendanceRuleScope> scopes = userRulesMap.get(value.getUserId());
            // 获取该用户有的类型且发放了余额的的假期余额列表
            List<String> typeHitIds = new ArrayList<>();
            if (CollUtil.isNotEmpty(balanceRecordList) && CollUtil.isNotEmpty(scopes)) {
                // 保留两位小数
                BigDecimal balance = balanceRecordList.stream().filter(x -> x.getType() == 3).map(AttendanceBalanceRecordEntity::getBalance).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                value.setBalance(balance.setScale(2, RoundingMode.HALF_UP));
                balanceRecordList.removeIf(x -> x.getType() == 3);
                // 该用户有的类型
                List<String> startTypeIds = scopes.stream().map(AttendanceRuleScope::getExpand).distinct().collect(Collectors.toList());
                // 有该类型且发过劵
                Map<String, BigDecimal> map = balanceRecordList.stream().filter(x -> startTypeIds.contains(x.getObjectId())).collect(Collectors.groupingBy(AttendanceBalanceRecordEntity::getObjectId, Collectors.reducing(BigDecimal.ZERO, AttendanceBalanceRecordEntity::getBalance, BigDecimal::add)));
                typeHitIds = balanceRecordList.stream().map(AttendanceBalanceRecordEntity::getObjectId).distinct().collect(Collectors.toList());
                value.getBalanceNum().putAll(map);
            }
            List<String> finalTypeHitIds = typeHitIds;
            if (CollUtil.isNotEmpty(scopes)) {
                scopes.stream().filter(v -> !finalTypeHitIds.contains(v.getExpand())).forEach(v -> {
                    AttendanceLeaveRules attendanceLeaveRules = ruleMap.get(v.getRuleId());
                    if (null != attendanceLeaveRules) {
                        if (attendanceLeaveRules.getDistributeBalance() == 1) {
                            value.getBalanceNum().put(v.getExpand(), BigDecimal.ZERO);
                        } else {
                            value.getBalanceNum().put(v.getExpand(), new BigDecimal(-2));
                        }
                    }
                });
            }
        });
        attendanceUserBalanceListVo.setAttendanceUserBalanceListDataVo(list);
        return attendanceUserBalanceListVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserBalance(AttendanceUserBalanceDto userBalanceDto) {
        // 查询类型
        AttendanceLeaveType byId = attendanceLeaveTypeService.getOne(new QueryWrapper<AttendanceLeaveType>()
                .lambda()
                .eq(AttendanceLeaveType::getId, userBalanceDto.getObjectId())
                .eq(AttendanceLeaveType::getDeleteMark, 0)
        );
        //  入参为假期类型
        UserInfo info = userProvider.get();
        // 需要发劵的用户余额记录
        List<AttendanceBalanceRecordEntity> balanceRecordList = new ArrayList<>();
        // 需要消费劵的用户余额记录
        List<AttendancePublicHolidayBalance> needRetirementLeave = new ArrayList<>();
        // 消费记录
        List<AttendanceUserBalanceDetailVo> list = new ArrayList<>();
        // 存休另算
        if (StringUtil.isEmpty(userBalanceDto.getObjectId()) || 1 == byId.getBuiltIn()) {
            if (userBalanceDto.getBalance().compareTo(BigDecimal.ZERO) > 0) {

                userBalanceDto.getUserIds().forEach(userId -> {
                    // 发放劵
                    AttendanceBalanceRecordEntity record = new AttendanceBalanceRecordEntity();
                    record.setUserId(userId);
                    record.setType(3);
                    record.setBalance(userBalanceDto.getBalance());
                    record.setTotal(userBalanceDto.getBalance());
                    record.setUnit(2);
                    record.setExpireTime(null);
                    balanceRecordList.add(record);
                    // 记录用户的存休变动信息
                    //【XX】手动发放X天/小时余额 保留2位小数
                    AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                            .id(FtbUtil.getId())
                            .userId(userId)
                            .objectId(null)
                            .content("【" + info.getUserName() + "】手动发放" + userBalanceDto.getBalance().setScale(2, RoundingMode.HALF_UP) + "天余额")
                            .quota(userBalanceDto.getBalance())
                            .build();
                    list.add(detailVo);
                });
            } else {
                // 消费劵  劵记录需要在扣除的时候计算生成
                userBalanceDto.getUserIds().forEach(userId -> {
                    AttendancePublicHolidayBalance record = new AttendancePublicHolidayBalance();
                    record.setUserId(userId);
                    record.setRetirementLeave(1);
                    record.setBalance(userBalanceDto.getBalance().negate());
                    needRetirementLeave.add(record);
                });
            }
        } else {
            // 其他类型的假
            // 如果批量修改需校验用户有没有该类型的假期规则，没有跳过发放
            // 当前列表用户对应的启动中的假期规则（除内置调休）且不包含不发放余额的规则
            List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(userBalanceDto.getUserIds(), ScopeBizType.LEAVE_RULES, ConstantUtil.NUM_TRUE, Collections.singletonList(userBalanceDto.getObjectId()));
            if (CollUtil.isEmpty(attendanceRuleScopes)) {
                return;
            }
            Map<String, List<AttendanceRuleScope>> userRuleMap = attendanceRuleScopes.stream().collect(Collectors.groupingBy(AttendanceRuleScope::getRuleId));
            // 过滤出没有删除的且启动中且发劵的规则
            List<AttendanceLeaveRules> rules = attendanceLeaveRulesMapper.selectList(new QueryWrapper<AttendanceLeaveRules>()
                    .lambda()
                    .eq(AttendanceLeaveRules::getDeleteMark, 0)
                    .in(AttendanceLeaveRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList()))
                    .eq(AttendanceLeaveRules::getState, 1)
                    .eq(AttendanceLeaveRules::getBuiltIn, 0)
                    .ne(AttendanceLeaveRules::getScopeOfAdaptation, -1)
                    .eq(AttendanceLeaveRules::getDistributeBalance, 1)
            );
            if (CollUtil.isEmpty(rules)) {
                return;
            }
            List<AttendanceLeaveGrantSetting> grantSettings = attendanceLeaveGrantSettingMapper.selectList(new QueryWrapper<AttendanceLeaveGrantSetting>()
                    .lambda()
                    .eq(AttendanceLeaveGrantSetting::getDeleteMark, 0)
                    .in(AttendanceLeaveGrantSetting::getLeaveRulesId, rules.stream().map(AttendanceLeaveRules::getId).collect(Collectors.toList()))
            );
            Map<String, AttendanceLeaveGrantSetting> ruleSettingMap = grantSettings.stream().collect(Collectors.toMap(AttendanceLeaveGrantSetting::getLeaveRulesId, Function.identity()));
            for (AttendanceLeaveRules rule : rules) {
                // 规则对应的人员信息
                List<AttendanceRuleScope> scopes = userRuleMap.get(rule.getId());
                if (CollUtil.isEmpty(scopes)) {
                    continue;
                }
                AttendanceLeaveGrantSetting attendanceLeaveGrantSetting = ruleSettingMap.get(rule.getId());
                if (null == attendanceLeaveGrantSetting) {
                    continue;
                }
                // 计算过期时间 假期只有 不过期或者指定日期
                Date expiresTime = expiresTimeUtil.getExpiresTime(0 == attendanceLeaveGrantSetting.getExpiredType() ? 0 : 1, attendanceLeaveGrantSetting.getExpiredDayNum(), null);
                List<String> userList = scopes.stream().map(AttendanceRuleScope::getUserId).collect(Collectors.toList());
                if (userBalanceDto.getBalance().compareTo(BigDecimal.ZERO) > 0) {
                    //  发放劵
                    userList.forEach(v -> {
                        // 添加劵
                        AttendanceBalanceRecordEntity record = new AttendanceBalanceRecordEntity();
                        record.setUserId(v);
                        record.setType(2);
                        record.setObjectId(userBalanceDto.getObjectId());
                        record.setBalance(userBalanceDto.getBalance());
                        record.setTotal(userBalanceDto.getBalance());
                        record.setUnit(2);
                        record.setExpireTime(expiresTime);
                        balanceRecordList.add(record);
                        // 添加使用记录 ftb_attendance_user_balance_detail
                        //【XX】手动发放X天/小时余额 保留1位小数
                        AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                                .id(FtbUtil.getId())
                                .objectId(userBalanceDto.getObjectId())
                                .userId(v)
                                .content("【" + info.getUserName() + "】手动发放" + userBalanceDto.getBalance().setScale(2, RoundingMode.HALF_UP) + "天余额")
                                .quota(userBalanceDto.getBalance())
                                .build();
                        list.add(detailVo);
                    });
                } else {
                    // 消费劵
                    userList.forEach(v -> {
                        // 消费劵
                        AttendancePublicHolidayBalance record = new AttendancePublicHolidayBalance();
                        record.setUserId(v);
                        record.setBalance(userBalanceDto.getBalance().negate());
                        record.setRetirementLeave(rule.getRetirementLeave());
                        needRetirementLeave.add(record);
                    });
                }
            }
        }
        if (!balanceRecordList.isEmpty()) {
            // 发放劵
            attendanceUserBalanceRecordService.saveBatch(balanceRecordList);
        }
        if (!needRetirementLeave.isEmpty()) {
            // 消费劵  劵记录需要在扣除的时候计算生成
            attendanceUserBalanceRecordService.rollbackUserBalanceRecord(needRetirementLeave, StringUtil.isEmpty(userBalanceDto.getObjectId()) ? null : 1 == byId.getBuiltIn() ? null : userBalanceDto.getObjectId(), StringUtil.isEmpty(userBalanceDto.getObjectId()) ? "存休" : byId.getName(), false);
        }
        if (!list.isEmpty()) {
            // 消费记录 仅记录生成劵的使用记录
            attendanceUserBalanceMapper.batchAddBalanceDetail(list);
        }

    }

    @Override
    public AttendanceUserBalanceVo getDetail(AttendanceUserBalanceDto userBalanceDto) {
        // 假期类型
        if (null == userBalanceDto.getUserId()) {
            userBalanceDto.setUserId(userProvider.get().getId());
        }
        AttendanceUserBalanceVo bean = new AttendanceUserBalanceVo();
        List<AttendanceBalanceRecordEntity> records = attendanceUserBalanceRecordService.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                .lambda()
                .and(x -> x.eq(StringUtil.isNotEmpty(userBalanceDto.getObjectId()), AttendanceBalanceRecordEntity::getObjectId, userBalanceDto.getObjectId())
                        .or()
                        .eq(StringUtil.isEmpty(userBalanceDto.getObjectId()), AttendanceBalanceRecordEntity::getType, 3))
                .eq(AttendanceBalanceRecordEntity::getUserId, userBalanceDto.getUserId())
                .eq(AttendanceBalanceRecordEntity::getDeleteMark, 0)
                .eq(AttendanceBalanceRecordEntity::getState, 0)
                .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
        );
        // 计算余额 保留两位小数
        bean.setBalance(records.stream().map(AttendanceBalanceRecordEntity::getBalance).reduce(BigDecimal.ZERO, BigDecimal::add).max(new BigDecimal("0")).setScale(2, RoundingMode.HALF_UP));
        // 查询假期日志详情
        List<AttendanceUserBalanceDetailVo> detail = attendanceUserBalanceMapper.getDetail(userBalanceDto.getObjectId(), userBalanceDto.getUserId());
        bean.setDetail(detail);
        return bean;
    }

    @Override
    public AttendanceUserTitleVo getTitle(AttendanceUserBalanceDto userBalanceDto) {
        // 假期类型
        AttendanceUserTitleVo bean = new AttendanceUserTitleVo();
        ActionResult<UserBoundInfoVO> usersBound = v2UserApi.getUsersBound(userBalanceDto.getUserId(), userProvider.get().getTenantId());
        Assert.isTrue(usersBound.getCode().equals(200), "查询登录用户岗位信息异常");
        UserBoundInfoVO userBoundInfo = usersBound.getData();
        bean.setUserId(userBoundInfo.getUserId());
        bean.setUserName(userBoundInfo.getUserName());
        bean.setDeptName(userBoundInfo.getOrganizeName());
        bean.setPostName(userBoundInfo.getPositionName());
        bean.setSystemWorkerId(userBoundInfo.getSystemWorkerId());
        bean.setHeadIcon(fileUploadApi.getHeadIcon(UploaderUtil.uploaderImg(userBoundInfo.getHeadIcon())));
        bean.setWorkStatus(userBoundInfo.getWorkStatusName());
        // 查询动态请假类型表头
        List<BalanceTitelVo> leaveNames = new ArrayList<>();
        // 理论上返回一个扩展字段，只对应一个规则Id，不会重复
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(Collections.singletonList(userBalanceDto.getUserId()), ScopeBizType.LEAVE_RULES, ConstantUtil.NUM_TRUE, null);
        // 先固定存休
        leaveNames.add(BalanceTitelVo.builder().name("存休").type(1).build());
        // 当前列表用户对应的启动中的假期规则（除内置调休）包含不发放余额的规则
        List<AttendanceLeaveRules> rules = attendanceLeaveRulesMapper.selectList(new QueryWrapper<AttendanceLeaveRules>()
                .lambda()
                .eq(AttendanceLeaveRules::getDeleteMark, 0)
                .eq(AttendanceLeaveRules::getState, 1)
                .eq(AttendanceLeaveRules::getBuiltIn, 0)
                .ne(AttendanceLeaveRules::getScopeOfAdaptation, -1)
                .in(CollUtil.isNotEmpty(attendanceRuleScopes), AttendanceLeaveRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList()))
                .orderByAsc(AttendanceLeaveRules::getCreatorTime)
        );
        // 转换为动态表头格式
        if (CollUtil.isNotEmpty(rules)) {
            List<String> typeIds = rules.stream().map(AttendanceLeaveRules::getLeaveTypeId).collect(Collectors.toList());
            // 移除规则没有命中的假期类型
            Map<String, AttendanceLeaveRules> ruleMap = rules.stream().collect(Collectors.toMap(AttendanceLeaveRules::getLeaveTypeId, Function.identity()));
            // 命中且规则启用的类型
            List<AttendanceLeaveType> allType = attendanceLeaveTypeService.list(new LambdaQueryWrapper<AttendanceLeaveType>()
                    .in(AttendanceLeaveType::getId, typeIds)
                    .eq(AttendanceLeaveType::getDeleteMark, 0)
                    .orderByAsc(AttendanceLeaveType::getCreatorTime));
            allType.forEach(v -> {
                AttendanceLeaveRules attendanceLeaveRules = ruleMap.get(v.getId());
                leaveNames.add(BalanceTitelVo.builder().id(v.getId()).type(2).name(v.getName()).distributeBalance(attendanceLeaveRules.getDistributeBalance()).build());
            });
        }
        bean.setLeaveNames(leaveNames);
        return bean;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUserBalances(List<AttendancePublicHolidayBalance> needRetirementLeave) {
        List<AttendanceUserBalanceDetailVo> list = new ArrayList<>();
        // 记录用户的存休变动信息
        // 系统已将yyyy-mm公休未排班的X.X天/小时，自动转换为存休 保留1位小数
        needRetirementLeave.forEach(v -> {
            AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                    .id(FtbUtil.getId())
                    .objectId(null)
                    .userId(v.getUserId())
                    .content("系统已将" + v.getYearMonth() + "公休未排班的" + v.getBalance().setScale(1, RoundingMode.HALF_UP) + "天，自动转换为存休")
                    .quota(v.getBalance())
                    .build();
            list.add(detailVo);
        });
        attendanceUserBalanceMapper.batchAddBalanceDetail(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackUserBalance(List<AttendancePublicHolidayBalance> needRetirementLeave) {
        // 记录用户的存休变动信息
        List<AttendanceUserBalanceDetailVo> list = new ArrayList<>();
        // 因解封yyyy-mm的封账数据，已将公休转存的XX天/小时，自动清除 保留1位小数
        needRetirementLeave.forEach(v -> {
            AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                    .id(FtbUtil.getId())
                    .userId(v.getUserId())
                    .objectId("1")
                    .content("因解封" + v.getYearMonth() + "的封账数据，已将公休转存的" + v.getBalance().setScale(1, RoundingMode.HALF_UP) + "天，自动清除")
                    .quota(v.getBalance().negate())
                    .build();
            list.add(detailVo);
        });
        attendanceUserBalanceMapper.batchAddBalanceDetail(list);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void overTime(List<OvertimeInfoVo> overtimeInfoList, String tenantId) {

        tenantUtil.checkOutTenant(tenantId);
        log.error("2.0加班触发 overTime :{}", JSONUtil.toJsonStr(overtimeInfoList));
        if (null == overtimeInfoList || overtimeInfoList.isEmpty()) {
            return;
        }
        List<String> groupList = overtimeInfoList.stream().map(OvertimeInfoVo::getGroupId).collect(Collectors.toList());
        Map<String, AttendanceBaseSetting> settingMap = attendanceBaseSettingService.getEnableBaseSettingAll(groupList);
        log.error("2.0加班触发 settingMap :{}", JSONUtil.toJsonStr(settingMap));
        // 根据出勤规则查询上下班
        Map<String, OvertimeInfoVo> map = overtimeInfoList.stream().collect(Collectors.toMap(OvertimeInfoVo::getRuleId, Function.identity()));
        List<ClockInVo> clockInResultList = attendanceClockInMapper.getClockInResultByRuleList(new ArrayList<>(map.keySet()));
        log.error("2.0加班触发 clockInResultList :{}", JSONUtil.toJsonStr(clockInResultList));
        ConcurrentMap<String, List<ClockInVo>> clockMap = clockInResultList.stream().collect(Collectors.groupingByConcurrent(ClockInVo::getRuleId));
        //系统已将yyyy-mm-dd工作日加班的X.X天/小时，自动转换为存休 保留1位小数
        List<AttendanceUserBalanceDetailVo> balanceList = new ArrayList<>();
        List<AttendanceBalanceRecordEntity> recordList = new ArrayList<>();
        List<AttendanceEnableBalance> enableBalanceList = new ArrayList<>();
        clockMap.forEach((k, list) -> {
            if (list.size() == 2) {
                ClockInVo clockInVo = list.stream().filter(v -> v.getClockInStatus().equals(ClockInStatusEnum.NO_CLOCK.getValue())).findFirst().orElse(null);
                // 无缺卡
                if (null == clockInVo) {
                    OvertimeInfoVo overtimeInfo = map.get(k);
                    ClockInVo c1 = list.get(0);
                    ClockInVo c2 = list.get(1);
                    // 秒
                    int seconds = DateDetail.calculateSecondDiff(c1.getEffectiveTime(), c2.getEffectiveTime());
                    // 小时
                    BigDecimal balance = new BigDecimal(Math.abs(seconds)).divide(new BigDecimal(3600), 4, RoundingMode.HALF_UP);
                    // 获取对应的出勤换算比 获取对应的时长换算存休倍数，综合计算
                    AttendanceBaseSetting baseSetting = settingMap.get(overtimeInfo.getGroupId());
                    // 加班天数
                    BigDecimal compensateRatio = overtimeInfo.getOvertimeRuleDetail().getCompensateRatio();
                    balance = balance.divide(baseSetting.getAttendanceRatio(), 2, RoundingMode.HALF_UP)
                            .multiply(null == compensateRatio ? new BigDecimal(1) : compensateRatio);
                    // 判断compensateType 是否发劵还是记录
                    if (overtimeInfo.getOvertimeRuleDetail().getCompensateType().equals(ConstantUtil.BALANCE_ENABLE)) {
                        AttendanceEnableBalance enableBalance = AttendanceEnableBalance.builder()
                                .id(FtbUtil.getId())
                                .ruleId(overtimeInfo.getRuleId())
                                .overtimeType(overtimeInfo.getOvertimeRuleDetail().getOvertimeType())
                                .userId(c1.getUserId())
                                .day(overtimeInfo.getDay())
                                .balance(balance)
                                .festivalStr(overtimeInfo.getOvertimeRuleDetail().getFestivalStr())
                                .build();
                        enableBalanceList.add(enableBalance);
                    } else {
                        AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                                .id(FtbUtil.getId())
                                .userId(c1.getUserId())
                                .content("系统已将" + DateDetail.getDate2Str(overtimeInfo.getDay(), DateDetail.DF) + getType(overtimeInfo.getOvertimeRuleDetail().getOvertimeType()) + "加班的" + balance + "天，自动转换为存休")
                                .quota(balance)
                                .build();
                        balanceList.add(detailVo);
                        // 添加劵
                        AttendanceBalanceRecordEntity record = new AttendanceBalanceRecordEntity();
                        record.setUserId(c1.getUserId());
                        record.setType(3);
                        record.setObjectId(overtimeInfo.getRuleId());
                        record.setBalance(balance);
                        record.setTotal(balance);
                        record.setOverTime(overtimeInfo.getOvertimeRuleDetail().getOvertimeType());
                        record.setOverTimeDay(overtimeInfo.getDay());
                        record.setUnit(2);
                        record.setFestivalStr(overtimeInfo.getOvertimeRuleDetail().getFestivalStr());
                        recordList.add(record);
                    }
                }
            }
        });
        List<String> ruleIdList = overtimeInfoList.stream().map(OvertimeInfoVo::getRuleId).collect(Collectors.toList());
        if (!recordList.isEmpty()) {
            List<AttendanceBalanceRecordEntity> dbRecordList = attendanceUserBalanceRecordService.list(new LambdaQueryWrapper<AttendanceBalanceRecordEntity>()
                    .in(AttendanceBalanceRecordEntity::getObjectId, ruleIdList)
                    .eq(AttendanceBalanceRecordEntity::getType, 3)
                    .eq(AttendanceBalanceRecordEntity::getDeleteMark, ConstantUtil.NUM_FALSE));
            List<String> objIdList = dbRecordList.stream().map(AttendanceBalanceRecordEntity::getObjectId).distinct().collect(Collectors.toList());
            if (!dbRecordList.isEmpty()) {
                recordList.removeIf(v -> objIdList.contains(v.getObjectId()));
                balanceList.removeIf(v -> objIdList.contains(v.getObjectId()));
            }
            if (!recordList.isEmpty()) {
                attendanceUserBalanceRecordService.saveBatch(recordList);
                attendanceUserBalanceMapper.batchAddBalanceDetail(balanceList);
            }
        }
        if (!enableBalanceList.isEmpty()) {
            List<AttendanceEnableBalance> dbRecordList = enableBalanceService.list(new LambdaQueryWrapper<AttendanceEnableBalance>()
                    .in(AttendanceEnableBalance::getRuleId, ruleIdList)
                    .eq(AttendanceEnableBalance::getDeleteMark, ConstantUtil.NUM_FALSE));
            List<String> objIdList = dbRecordList.stream().map(AttendanceEnableBalance::getRuleId).distinct().collect(Collectors.toList());
            if (!dbRecordList.isEmpty()) {
                enableBalanceList.removeIf(v -> objIdList.contains(v.getRuleId()));
            }
            if (!enableBalanceList.isEmpty()) {
                enableBalanceService.saveBatch(enableBalanceList);
            }
        }
    }

    private String getType(Integer type) {

        String str;
        switch (type) {
            case 1:
                str = "工作日";
                break;
            case 2:
                str = "公休日";
                break;
            case 3:
                str = "节假日";
                break;
            default:
                str = "--";
                break;
        }
        return str;
    }

    @Override
    public BigDecimal getTotalBalance(String balanceId, String userId, boolean isRetirementLeave) {
        return attendanceUserBalanceMapper.getTotalBalance(balanceId, userId, isRetirementLeave);
    }


    private static void setPagination(AttendanceUserBalanceListQueryDto queryDto, List<AttendanceGroupUser> attendanceGroupUsers, AttendanceUserBalanceListVo attendanceUserBalanceListVo) {
        PaginationVO paginationVO = new PaginationVO();
        paginationVO.setTotal(attendanceGroupUsers.size());
        paginationVO.setPageSize((long) queryDto.getPageSize());
        paginationVO.setCurrentPage((long) queryDto.getCurrentPage());
        attendanceUserBalanceListVo.setPagination(paginationVO);
    }


    /**
     * 获取符合标准的用户ids集合
     *
     * @param queryDto 查询参数
     * @return List<String>
     */
    private List<String> getUserIds(AttendanceUserBalanceListQueryDto queryDto, List<String> userIds,List<String> orgIds) throws Exception {
        ActionResult<List<UserBoundVO>> orgUserList = v2UserApi.listTargetOrganizesOrHaveChild(orgIds, false,
                StringUtil.isNotEmpty(queryDto.getWorkStatus()) ? UserWorkStatusEnums.getEnumExceptValue(queryDto.getWorkStatus())
                        : List.of(UserWorkStatusEnums.RESIGNED), userProvider.get().getTenantId());
        if (200 != orgUserList.getCode() || ObjectUtil.isEmpty(orgUserList)) {
            throw new Exception("获取组织用户列表失败");
        }
        List<UserBoundVO> data = orgUserList.getData();
        // 备份data
        List<UserBoundVO> copyData = new ArrayList<>();
        Map<String, List<UserBoundVO>> orgMap = data.stream().collect(Collectors.groupingBy(UserBoundVO::getOrganizeId));
        // 是否筛选班组
        List<UserBoundVO> finalCopyData = copyData;
        queryDto.getOrgTeamDtoList().forEach(orgTeamDto -> {
            List<UserBoundVO> list = orgMap.get(orgTeamDto.getOrgId());
            if (!orgTeamDto.isSelected() && null != list && !list.isEmpty() && null != orgTeamDto.getWorkGroupId() && !orgTeamDto.getWorkGroupId().isEmpty()) {
                list = list.stream().filter(x -> orgTeamDto.getWorkGroupId().contains(x.getStoreTeamId())).collect(Collectors.toList());
            }
            if (null != list && !list.isEmpty()) {
                finalCopyData.addAll(list);
            }
        });
        // 如果有名字筛选
        if (StringUtils.isNotBlank(queryDto.getIText())) {
            // 收到过滤用户名称模糊匹配
            copyData = copyData.stream().filter(x -> StringUtils.isNotBlank(x.getUserName()) && x.getUserName().contains(queryDto.getIText())).collect(Collectors.toList());
        }
        if (!copyData.isEmpty()) {
            userIds = copyData.stream().map(UserBoundVO::getId).collect(Collectors.toList());
        }
        return userIds;
    }


}
