package jnpf.attendance.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jnpf.attendance.mapper.AttendanceBalanceUseRecordMapper;
import jnpf.attendance.mapper.AttendanceLeaveRulesMapper;
import jnpf.attendance.mapper.AttendanceUserBalanceMapper;
import jnpf.attendance.mapper.AttendanceUserBalanceRecordMapper;
import jnpf.attendance.service.AttendanceUserBalanceRecordService;
import jnpf.attendance.service.AttendanceUserBalanceService;
import jnpf.base.UserInfo;
import jnpf.entity.attendance.AttendanceBalanceRecordEntity;
import jnpf.entity.attendance.AttendanceBalanceUseRecord;
import jnpf.entity.attendance.AttendanceLeaveRules;
import jnpf.enums.attendance.BalanceEnum;
import jnpf.model.attendance.vo.attendance.AttendanceLeaveRulesVo;
import jnpf.model.attendance.vo.attendance.AttendancePublicHolidayBalance;
import jnpf.model.attendance.vo.attendance.AttendanceUserBalanceDetailVo;
import jnpf.model.attendance.vo.attendance.LeaveConsumptionDetailVo;
import jnpf.util.FtbUtil;
import jnpf.util.StringUtil;
import jnpf.util.UserProvider;
import org.jetbrains.annotations.NotNull;
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.function.Function;
import java.util.stream.Collectors;

/**
 * @author
 * @version V2.0
 * @description 用户余额获取记录表 Service实现类
 */
@Service
public class AttendanceUserBalanceRecordServiceImpl extends ServiceImpl<AttendanceUserBalanceRecordMapper, AttendanceBalanceRecordEntity> implements AttendanceUserBalanceRecordService {
    @Resource
    @Lazy
    private AttendanceUserBalanceService attendanceUserBalanceService;

    @Resource
    private AttendanceUserBalanceMapper attendanceUserBalanceMapper;

    @Resource
    @Lazy
    private UserProvider userProvider;

    @Resource
    private AttendanceBalanceUseRecordMapper attendanceBalanceUseRecordMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackUserBalanceRecord(List<AttendancePublicHolidayBalance> needRetirementLeave, String balanceId, String leaveName, boolean isPublicHoliday) {
        UserInfo info = userProvider.get();
        List<String> userIds = needRetirementLeave.stream().map(AttendancePublicHolidayBalance::getUserId).distinct().collect(Collectors.toList());
        // 组织存休抵扣日志
        List<AttendanceUserBalanceDetailVo> detailVoList = new ArrayList<>();
        // 查询用户已有的记录列表快过期的排前面
        List<AttendanceBalanceRecordEntity> userBalanceRecordList = this.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                .lambda()
                .eq(AttendanceBalanceRecordEntity::getState, 0)
                .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
                .and(x -> x.eq(StringUtil.isNotEmpty(balanceId), AttendanceBalanceRecordEntity::getObjectId, balanceId)
                        .or()
                        .eq(StringUtil.isEmpty(balanceId), AttendanceBalanceRecordEntity::getType, 3))
                .in(AttendanceBalanceRecordEntity::getUserId, userIds)
                .orderByDesc(AttendanceBalanceRecordEntity::getExpireTime));
        boolean b = true;
        // 需修改的劵
        List<AttendanceBalanceRecordEntity> list = new ArrayList<>();
        if (null != userBalanceRecordList && !userBalanceRecordList.isEmpty()) {
            b = false;
            // 需退劵的map
            Map<String, AttendancePublicHolidayBalance> userNeedMap = needRetirementLeave.stream().collect(Collectors.toMap(AttendancePublicHolidayBalance::getUserId, Function.identity()));
            // 用户已有劵的map
            Map<String, List<AttendanceBalanceRecordEntity>> userMap = userBalanceRecordList.stream().collect(Collectors.groupingBy(AttendanceBalanceRecordEntity::getUserId));
            // 劵不够的用户集合
            List<AttendancePublicHolidayBalance> needList = new ArrayList<>();
            userNeedMap.forEach((userId, needBalance) -> {
                BigDecimal inputTypeBalance = consumption(userId, needBalance, userMap, list, needList);
                //【XX】手动发放X天/小时余额 保留2位小数
                AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                        .id(FtbUtil.getId())
                        .objectId(balanceId)
                        .userId(userId)
                        .content("【" + info.getUserName() + "】手动去除" + inputTypeBalance.setScale(2, RoundingMode.HALF_UP) + "天余额")
                        .quota(inputTypeBalance.negate())
                        .build();
                detailVoList.add(detailVo);
            });
            // 过滤出需要使用存休抵扣的集合
            List<AttendancePublicHolidayBalance> collect = needList.stream().filter(x -> x.getRetirementLeave() == 1).collect(Collectors.toList());
            if (null != balanceId && !collect.isEmpty()) {
                // 还需使用存休抵扣的用户
                List<String> needRetirementUserIds = collect.stream().map(AttendancePublicHolidayBalance::getUserId).distinct().collect(Collectors.toList());
                // 查询用户已有的记录列表快过期的排前面
                List<AttendanceBalanceRecordEntity> userRetirementList = this.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                        .lambda()
                        .eq(AttendanceBalanceRecordEntity::getState, 0)
                        .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
                        .eq(AttendanceBalanceRecordEntity::getType, 3)
                        .in(AttendanceBalanceRecordEntity::getUserId, needRetirementUserIds)
                        .orderByDesc(AttendanceBalanceRecordEntity::getExpireTime));
                Map<String, List<AttendanceBalanceRecordEntity>> userMap1 = userRetirementList.stream().collect(Collectors.groupingBy(AttendanceBalanceRecordEntity::getUserId));
                collect.forEach(needBalance -> {
                    // 还需要使用存休抵扣
                    BigDecimal consumption = consumption(needBalance.getUserId(), needBalance, userMap1, list, needList);
                    //【"事假"】使用X.X天/小时余额
                    AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                            .id(FtbUtil.getId())
                            .objectId(null)
                            .userId(needBalance.getUserId())
                            .content("【" + leaveName + "】使用" + consumption.setScale(2, RoundingMode.HALF_UP) + "天余额")
                            .quota(consumption.negate())
                            .build();
                    detailVoList.add(detailVo);
                });
            }
        }
        // 如果没有本类型的劵但是可以使用存休抵扣
        if (b) {
            List<AttendancePublicHolidayBalance> collect = needRetirementLeave.stream().filter(x -> x.getRetirementLeave() == 1).collect(Collectors.toList());
            if (null != balanceId && !collect.isEmpty()) {
                // 劵不够的用户集合
                List<AttendancePublicHolidayBalance> needList = new ArrayList<>();
                // 还需使用存休抵扣的用户
                List<String> needRetirementUserIds = collect.stream().map(AttendancePublicHolidayBalance::getUserId).distinct().collect(Collectors.toList());
                // 查询用户已有的记录列表快过期的排前面
                List<AttendanceBalanceRecordEntity> userRetirementList = this.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                        .lambda()
                        .eq(AttendanceBalanceRecordEntity::getState, 0)
                        .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
                        .eq(AttendanceBalanceRecordEntity::getType, 3)
                        .in(AttendanceBalanceRecordEntity::getUserId, needRetirementUserIds)
                        .orderByDesc(AttendanceBalanceRecordEntity::getExpireTime));
                Map<String, List<AttendanceBalanceRecordEntity>> userMap1 = userRetirementList.stream().collect(Collectors.groupingBy(AttendanceBalanceRecordEntity::getUserId));
                collect.forEach(needBalance -> {
                    // 还需要使用存休抵扣
                    BigDecimal consumption = consumption(needBalance.getUserId(), needBalance, userMap1, list, needList);
                    //【"事假"】使用X.X天/小时余额
                    AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                            .id(FtbUtil.getId())
                            .objectId(null)
                            .userId(needBalance.getUserId())
                            .content("【" + leaveName + "】使用" + consumption.setScale(2, RoundingMode.HALF_UP) + "天余额")
                            .quota(consumption.negate())
                            .build();
                    detailVoList.add(detailVo);
                });
            }
        }
        this.updateBatchById(list);
        if (!isPublicHoliday && !detailVoList.isEmpty()) {
            // 消费记录
            attendanceUserBalanceMapper.batchAddBalanceDetail(detailVoList);
        }

    }

    /**
     * 需要转存劵的用户余额记录 (存休不能触发)
     * 对应类型的劵 消费，同时生成新的劵（过期时间相同）
     *
     * @param ruleIds 假日类型Id（ftb_attendance_leave_rules）
     * @param userIds 用户id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void needRetirementBalance(List<String> ruleIds, List<String> userIds) {
        // 查询用户对应类型的劵记录 (余额大于0)
        List<AttendanceBalanceRecordEntity> userBalanceRecordList = this.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                .lambda()
                .eq(AttendanceBalanceRecordEntity::getState, 0)
                .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
                .eq(AttendanceBalanceRecordEntity::getType, 2)
                .in(AttendanceBalanceRecordEntity::getObjectId, ruleIds)
                .in(AttendanceBalanceRecordEntity::getUserId, userIds)
                .and(x -> x.gt(AttendanceBalanceRecordEntity::getBalance, BigDecimal.ZERO)));
        if (null != userBalanceRecordList && !userBalanceRecordList.isEmpty()) {
            // 需修改的劵
            List<AttendanceBalanceRecordEntity> addList = new ArrayList<>();
            for (AttendanceBalanceRecordEntity record : userBalanceRecordList) {
                // 生成新的劵
                AttendanceBalanceRecordEntity newRecord = new AttendanceBalanceRecordEntity();
                newRecord.setUserId(record.getUserId());
                newRecord.setBalance(record.getBalance());
                newRecord.setTotal(record.getBalance());
                newRecord.setUnit(record.getUnit());
                newRecord.setType(3);
                newRecord.setExpireTime(record.getExpireTime());
                addList.add(newRecord);
                record.setBalance(BigDecimal.ZERO);
                record.setState(1);
                record.setIsOver(1);
            }
            // 批量使用劵
            this.updateBatchById(userBalanceRecordList);
            // 批量添加劵
            this.saveBatch(addList);
        }

    }

    @Override
    public LeaveConsumptionDetailVo leaveSimulationConsumption(String userId, BigDecimal balance, AttendanceLeaveRulesVo userLeaveDetail) {
        //  类型
        LeaveConsumptionDetailVo leaveConsumptionDetailVo = new LeaveConsumptionDetailVo();
        leaveConsumptionDetailVo.setUnconsumedBalance(balance);
        // 查询用户已有的记录列表快过期的排前面
        List<AttendanceBalanceRecordEntity> userBalanceRecordList = getAttendanceUserBalanceRecords(userId, 1 == userLeaveDetail.getBuiltIn() ? null : userLeaveDetail.getLeaveTypeId());
        if (null != userBalanceRecordList && !userBalanceRecordList.isEmpty()) {
            simulationConsumption(balance, userBalanceRecordList, 1 == userLeaveDetail.getBuiltIn() ? null : userLeaveDetail.getLeaveTypeId(), leaveConsumptionDetailVo, userId, userLeaveDetail);
            return leaveConsumptionDetailVo;
        }
        // 如果入参balanceId不为空，但没有查询到假余额，需进行补偿逻辑，校验是否需要使用存休抵扣
        if (StringUtil.isNotEmpty(userLeaveDetail.getLeaveTypeId()) && 1 == userLeaveDetail.getRetirementLeave()) {
            BigDecimal balanceCopy = balance;
            // 该类型的假期开启存休抵扣
            balance = getRetirementLeave(balance, leaveConsumptionDetailVo, userId);
            leaveConsumptionDetailVo.setUnconsumedBalance(balance);
            // 查询余额表，计算剩余总余额
            BigDecimal totalBalance = getTotalBalance(userLeaveDetail.getLeaveTypeId(), userId, 1 == userLeaveDetail.getRetirementLeave());
            leaveConsumptionDetailVo.setBalance(Objects.isNull(totalBalance) ? BigDecimal.ZERO : totalBalance.subtract(balanceCopy).max(BigDecimal.ZERO));
        }
        return leaveConsumptionDetailVo;
    }

    /**
     * 请假模拟消费
     *
     * @param balance                  需消费的余额
     * @param userBalanceRecordList    假对应劵信息
     * @param balanceId                假Id  存休时为空
     * @param leaveConsumptionDetailVo 返回的组装对象数据
     * @param userId                   用户id
     * @param userLeaveDetail     请假规则 可能为空
     **/
    private void simulationConsumption(BigDecimal balance, List<AttendanceBalanceRecordEntity> userBalanceRecordList, String balanceId, LeaveConsumptionDetailVo leaveConsumptionDetailVo, String userId, AttendanceLeaveRulesVo userLeaveDetail) {
        BigDecimal balanceCopy = balance;
        boolean isRetirementLeave = 1 == userLeaveDetail.getRetirementLeave();
        // 需回退的余额
        // 根据过期时间排序，快过期的排前面
        balanceSort(userBalanceRecordList);
        // 消费的入参类型使用的余额
        BigDecimal inputTypeBalance = BigDecimal.ZERO;
        // 进行劵消费
        for (AttendanceBalanceRecordEntity record : userBalanceRecordList) {
            if (balance.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
            // 消费劵
            if (record.getBalance().compareTo(balance) >= 0) {
                inputTypeBalance = inputTypeBalance.add(balance);
                balance = BigDecimal.ZERO;
            } else {
                // 劵不足，消费劵
                inputTypeBalance = inputTypeBalance.add(record.getBalance());
                balance = balance.subtract(record.getBalance());
            }
        }
        leaveConsumptionDetailVo.setInputTypeBalance(inputTypeBalance);
        if (null == balanceId) {
            leaveConsumptionDetailVo.setInputTypeBalanceStr(inputTypeBalance.setScale(2, RoundingMode.HALF_UP) + "天存休");
        } else {
            leaveConsumptionDetailVo.setInputTypeBalanceStr(inputTypeBalance.setScale(2, RoundingMode.HALF_UP) + "天" + userLeaveDetail.getLeaveTypeName());
            if (isRetirementLeave) {
                // 该类型的假期开启存休抵扣
                balance = getRetirementLeave(balance, leaveConsumptionDetailVo, userId);
            }
        }
        leaveConsumptionDetailVo.setUnconsumedBalance(balance);
        if (balance.compareTo(BigDecimal.ZERO) > 0) {
            // 未抵扣的余额大于0，则不用再次计算总的剩余总余额
            leaveConsumptionDetailVo.setBalance(BigDecimal.ZERO);
        } else {
            // 查询余额表，计算剩余总余额
            BigDecimal totalBalance = getTotalBalance(balanceId, userId, false);
            leaveConsumptionDetailVo.setBalance(totalBalance.subtract(balanceCopy));
        }
    }

    private BigDecimal getTotalBalance(String balanceId, String userId, boolean isRetirementLeave) {
        return attendanceUserBalanceService.getTotalBalance(balanceId, userId, isRetirementLeave);
    }

    /**
     * 其他类型的假开启了存休抵扣调用
     *
     * @param userId  用户id
     * @param balance 需消费的余额
     * @return 剩余的未消费余额
     **/
    @NotNull
    private BigDecimal getRetirementLeave(BigDecimal balance, LeaveConsumptionDetailVo leaveConsumptionDetailVo, String userId) {
        if (balance.compareTo(BigDecimal.ZERO) > 0) {
            // 不是存休且余额大于0，说明有劵未使用完 ，判断该类型的假是否开启了余额抵扣
            // 查询存休并进行抵扣
            List<AttendanceBalanceRecordEntity> retirementLeaves = getAttendanceUserBalanceRecords(userId, null);
            if (null != retirementLeaves && !retirementLeaves.isEmpty()) {
                balanceSort(retirementLeaves);
                BigDecimal retirementLeave = BigDecimal.ZERO;
                // 进行劵消费
                for (AttendanceBalanceRecordEntity record : retirementLeaves) {
                    if (balance.compareTo(BigDecimal.ZERO) <= 0) {
                        break;
                    }
                    // 消费劵
                    if (record.getBalance().compareTo(balance) >= 0) {
                        retirementLeave = retirementLeave.add(balance);
                        balance = BigDecimal.ZERO;
                    } else {
                        // 劵不足，消费劵
                        balance = balance.subtract(record.getBalance());
                        retirementLeave = retirementLeave.add(record.getBalance());
                    }
                }
                leaveConsumptionDetailVo.setRetirementLeave(retirementLeave);
                leaveConsumptionDetailVo.setRetirementLeaveStr(retirementLeave.setScale(2, RoundingMode.HALF_UP) + "天存休");
            }
        }
        return balance;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LeaveConsumptionDetailVo leaveConsumption(String userId, BigDecimal balance, AttendanceLeaveRulesVo userLeaveDetail, Date startTime, Date endTime, String applyId) {
        LeaveConsumptionDetailVo leaveConsumptionDetailVo = new LeaveConsumptionDetailVo();
//        AttendanceLeaveRules attendanceLeaveRules = null;
        // 需消费的劵
        List<AttendanceBalanceRecordEntity> list = new ArrayList<>();
        // 消费的劵记录
        List<AttendanceBalanceUseRecord> useRecordList = new ArrayList<>();
        // 查询用户已有的记录列表快过期的排前面
        List<AttendanceBalanceRecordEntity> userBalanceRecordList = getAttendanceUserBalanceRecords(userId, 1 == userLeaveDetail.getBuiltIn() ? null : userLeaveDetail.getLeaveTypeId());
        // 预防重复进入
        boolean flag = true;
        if (null != userBalanceRecordList && !userBalanceRecordList.isEmpty()) {
            flag = false;
            consumptionOne(balance, userBalanceRecordList, 1 == userLeaveDetail.getBuiltIn() ? null : userLeaveDetail.getLeaveTypeId(), leaveConsumptionDetailVo, userId, userLeaveDetail, list, useRecordList, startTime, endTime, applyId);
        }
        // 如果入参balanceId不为空，但没有查询到假余额，需进行补偿逻辑，校验是否需要使用存休抵扣
        if (flag && StringUtil.isNotEmpty(userLeaveDetail.getLeaveTypeId()) && 1 == userLeaveDetail.getRetirementLeave()) {
//            BigDecimal balanceCopy = balance;
            // 该类型的假期开启存休抵扣
            balance = getRetirementLeaveOne(balance, leaveConsumptionDetailVo, userId, list, useRecordList, startTime, endTime, applyId);
            leaveConsumptionDetailVo.setUnconsumedBalance(balance);
            // 查询余额表，计算剩余总余额
            // 进入这里代表是其他请假类型且开启存休抵扣且自身没有余额
//            BigDecimal totalBalance = getTotalBalance(userLeaveDetail.getLeaveTypeId(), userId, 1 == userLeaveDetail.getRetirementLeave());
            leaveConsumptionDetailVo.setBalance(BigDecimal.ZERO);
        }
        if (flag && StringUtil.isNotEmpty(userLeaveDetail.getLeaveTypeId()) && 0 == userLeaveDetail.getRetirementLeave()) {
            // 处理对应类型没有余额且不开启存休抵扣的情况
            leaveConsumptionDetailVo.setUnconsumedBalance(balance);
        }
        if (!list.isEmpty()) {
            // 修改余额记录表
            this.updateBatchById(list);
        }
        // 修改用户余额表与余额日志表
        updateUserBalance(1 == userLeaveDetail.getBuiltIn() ? null : userLeaveDetail.getLeaveTypeId(), leaveConsumptionDetailVo, userLeaveDetail, userId);
        // 添加使用记录表
        if (!useRecordList.isEmpty()) {
            attendanceBalanceUseRecordMapper.addBatch(useRecordList);
        }
        return leaveConsumptionDetailVo;
    }

    private void updateUserBalance(String balanceId, LeaveConsumptionDetailVo leaveConsumptionDetailVo, AttendanceLeaveRulesVo userLeaveDetail, String userId) {
        List<AttendanceUserBalanceDetailVo> list = new ArrayList<>();
        // 重新统计用户的余额
        if (null != balanceId) {
            if (leaveConsumptionDetailVo.getRetirementLeave().compareTo(BigDecimal.ZERO) > 0) {
                //【"事假"】使用X.X天/小时余额
                AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                        .id(FtbUtil.getId())
                        .objectId(null)
                        .userId(userId)
                        .content("【" + userLeaveDetail.getLeaveTypeName() + "】使用" + leaveConsumptionDetailVo.getRetirementLeave().setScale(2, RoundingMode.HALF_UP) + "天余额")
                        .quota(leaveConsumptionDetailVo.getRetirementLeave().negate())
                        .build();
                list.add(detailVo);
            }
            if (leaveConsumptionDetailVo.getInputTypeBalance().compareTo(BigDecimal.ZERO) > 0) {
                //使用X.X天/小时余额
                AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                        .id(FtbUtil.getId())
                        .objectId(balanceId)
                        .userId(userId)
                        .content("使用" + leaveConsumptionDetailVo.getInputTypeBalance().setScale(2, RoundingMode.HALF_UP) + "天余额")
                        .quota(leaveConsumptionDetailVo.getInputTypeBalance().negate())
                        .build();
                list.add(detailVo);
            }
        } else {
            if (leaveConsumptionDetailVo.getInputTypeBalance().compareTo(BigDecimal.ZERO) > 0) {
                //【"事假"】使用X.X天/小时余额
                AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                        .id(FtbUtil.getId())
                        .objectId(null)
                        .userId(userId)
                        .content("使用" + leaveConsumptionDetailVo.getInputTypeBalance().setScale(2, RoundingMode.HALF_UP) + "天余额")
                        .quota(leaveConsumptionDetailVo.getInputTypeBalance().negate())
                        .build();
                list.add(detailVo);
            }
        }
        if (!list.isEmpty()) {
            // 记录余额使用日志
            attendanceUserBalanceMapper.batchAddBalanceDetail(list);
        }
    }


    /**
     * 查询用户已有的假期类型记录列表
     * 查询存休时只用关注type为3的记录（包含了加班转的存休）
     * @param userId    用户id
     * @param balanceId 假日类型Id（ftb_attendance_leave_rules）（存休时为空）
     * @return 劵列表
     */
    private List<AttendanceBalanceRecordEntity> getAttendanceUserBalanceRecords(String userId, String balanceId) {
        return this.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                .lambda()
                .eq(AttendanceBalanceRecordEntity::getState, 0)
                .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
                .and(x -> x.eq(StringUtil.isNotEmpty(balanceId), AttendanceBalanceRecordEntity::getObjectId, balanceId)
                        .or()
                        .eq(StringUtil.isEmpty(balanceId), AttendanceBalanceRecordEntity::getType, 3))
                .eq(AttendanceBalanceRecordEntity::getUserId, userId));
    }

    /**
     * 消费一个人的劵
     *
     * @param userBalanceRecordList 劵列表
     */
    private void consumptionOne(BigDecimal balance, List<AttendanceBalanceRecordEntity> userBalanceRecordList, String balanceId, LeaveConsumptionDetailVo leaveConsumptionDetailVo, String userId, AttendanceLeaveRulesVo userLeaveDetail, List<AttendanceBalanceRecordEntity> list, List<AttendanceBalanceUseRecord> useRecordList, Date startTime, Date endTime, String applyId) {
        balanceSort(userBalanceRecordList);
        // 需回退的余额
        // 根据过期时间排序，快过期的排前面
        BigDecimal balanceCopy = balance;
        boolean isRetirementLeave = null != userLeaveDetail && 1 == userLeaveDetail.getRetirementLeave();
        // 消费的入参类型使用的余额
        BigDecimal inputTypeBalance = BigDecimal.ZERO;
        // 需回退的余额
        // 根据过期时间排序，快过期的排前面
        // 进行劵消费
        for (AttendanceBalanceRecordEntity record : userBalanceRecordList) {
            if (balance.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
            // 本张劵消费余额
            BigDecimal quota = new BigDecimal("0");
            // 消费劵
            if (record.getBalance().compareTo(balance) >= 0) {
                quota = balance;
                inputTypeBalance = inputTypeBalance.add(balance);
                record.setBalance(record.getBalance().subtract(balance));
                balance = BigDecimal.ZERO;
            } else {
                // 劵不足，消费劵
                quota = record.getBalance();
                inputTypeBalance = inputTypeBalance.add(record.getBalance());
                balance = balance.subtract(record.getBalance());
                record.setBalance(BigDecimal.ZERO);
            }
            // 标记已使用完
            if (record.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
                record.setIsOver(1);
            }
            // 更新劵
            list.add(record);
            // 使用记录
            AttendanceBalanceUseRecord useRecord = AttendanceBalanceUseRecord.builder()
                    .balanceId(record.getId())
                    .quota(quota)
                    .unit(BalanceEnum.BALANCE_UNIT_T.getCode())
                    .useType(0)
                    .objectId(applyId)
                    .startTime(startTime)
                    .endTime(endTime)
                    .lock(BalanceEnum.BALANCE_LOCK_YXF.getCode())
                    .build();
            useRecord.setId(FtbUtil.getId());
            useRecordList.add(useRecord);
        }
        leaveConsumptionDetailVo.setInputTypeBalance(inputTypeBalance);
        if (null == balanceId) {
            leaveConsumptionDetailVo.setInputTypeBalanceStr(inputTypeBalance + "天存休");
        } else {
            leaveConsumptionDetailVo.setInputTypeBalanceStr(inputTypeBalance + "天" + userLeaveDetail.getLeaveTypeName());
            if (isRetirementLeave) {
                // 该类型的假期开启存休抵扣
                balance = getRetirementLeaveOne(balance, leaveConsumptionDetailVo, userId, list, useRecordList, startTime, endTime, applyId);
            }
        }
        leaveConsumptionDetailVo.setUnconsumedBalance(balance);
        if (balance.compareTo(BigDecimal.ZERO) > 0) {
            // 未抵扣的余额大于0，则不用再次计算总的剩余总余额
            leaveConsumptionDetailVo.setBalance(BigDecimal.ZERO);
        } else {
            // 查询余额表，计算剩余总余额
            BigDecimal totalBalance = getTotalBalance(balanceId, userId, false);
            leaveConsumptionDetailVo.setBalance(Objects.isNull(totalBalance) ? BigDecimal.ZERO : totalBalance.subtract(balanceCopy).max(BigDecimal.ZERO));
        }
    }

    @NotNull
    private BigDecimal getRetirementLeaveOne(BigDecimal balance, LeaveConsumptionDetailVo leaveConsumptionDetailVo, String userId, List<AttendanceBalanceRecordEntity> list, List<AttendanceBalanceUseRecord> useRecordList, Date startTime, Date endTime, String applyId) {
        if (balance.compareTo(BigDecimal.ZERO) > 0) {
            // 不是存休且余额大于0，说明有劵未使用完 ，判断该类型的假是否开启了余额抵扣
            // 查询存休并进行抵扣
            List<AttendanceBalanceRecordEntity> retirementLeaveS = getAttendanceUserBalanceRecords(userId, null);
            if (null != retirementLeaveS && !retirementLeaveS.isEmpty()) {
                balanceSort(retirementLeaveS);
                BigDecimal retirementLeave = BigDecimal.ZERO;
                // 进行劵消费
                for (AttendanceBalanceRecordEntity record : retirementLeaveS) {
                    if (balance.compareTo(BigDecimal.ZERO) <= 0) {
                        break;
                    }
                    // 本张劵消费余额
                    BigDecimal quota = new BigDecimal("0");
                    // 消费劵
                    if (record.getBalance().compareTo(balance) >= 0) {
                        quota = balance;
                        retirementLeave = retirementLeave.add(balance);
                        record.setBalance(record.getBalance().subtract(balance));
                        balance = BigDecimal.ZERO;
                    } else {
                        // 劵不足，消费劵
                        quota = record.getBalance();
                        balance = balance.subtract(record.getBalance());
                        retirementLeave = retirementLeave.add(record.getBalance());
                        record.setBalance(BigDecimal.ZERO);
                    }
                    // 标记已使用完
                    if (record.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
                        record.setIsOver(1);
                    }
                    // 更新劵
                    list.add(record);
                    // 使用记录
                    AttendanceBalanceUseRecord useRecord = AttendanceBalanceUseRecord.builder()
                            .balanceId(record.getId())
                            .quota(quota)
                            .unit(BalanceEnum.BALANCE_UNIT_T.getCode())
                            .useType(0)
                            .objectId(applyId)
                            .startTime(startTime)
                            .endTime(endTime)
                            .lock(BalanceEnum.BALANCE_LOCK_YXF.getCode())
                            .build();
                    useRecord.setId(FtbUtil.getId());
                    useRecordList.add(useRecord);
                }
                leaveConsumptionDetailVo.setRetirementLeave(retirementLeave);
                leaveConsumptionDetailVo.setRetirementLeaveStr(retirementLeave + "天存休");
            }
        }
        return balance;
    }

    /**
     * 劵排序 快过期的排前面
     *
     * @param userBalanceRecordList 劵列表
     */
    private static void balanceSort(List<AttendanceBalanceRecordEntity> userBalanceRecordList) {
        userBalanceRecordList.sort((o1, o2) -> {
            if (o1.getExpireTime() == null && o2.getExpireTime() == null) {
                return 0;
            }
            if (o1.getExpireTime() == null) {
                return 1; // null值放最后
            }
            if (o2.getExpireTime() == null) {
                return -1; // null值放最后
            }
            return o1.getExpireTime().before(o2.getExpireTime()) ? -1 : 1;
        });
    }

    /**
     * 劵消费
     *
     * @param userId  用户id
     * @param balancevo 需退的劵
     * @param userMap 劵的map
     * @param list    修改的劵
     * @param needList 劵不够的用户集合
     * @return 抵扣的劵余额
     */
    private static BigDecimal consumption(String userId, AttendancePublicHolidayBalance balancevo, Map<String, List<AttendanceBalanceRecordEntity>> userMap, List<AttendanceBalanceRecordEntity> list, List<AttendancePublicHolidayBalance> needList) {
        BigDecimal balance = balancevo.getBalance();
        BigDecimal inputTypeBalance = BigDecimal.ZERO;
        // 需回退的余额
        List<AttendanceBalanceRecordEntity> userBalanceRecord = userMap.get(userId);
        if (null != userBalanceRecord && !userBalanceRecord.isEmpty()) {
            // 根据过期时间排序，快过期的排前面
            balanceSort(userBalanceRecord);
            // 进行劵消费
            for (AttendanceBalanceRecordEntity record : userBalanceRecord) {
                if (balance.compareTo(BigDecimal.ZERO) <= 0) {
                    break;
                }
                // 消费劵
                if (record.getBalance().compareTo(balance) >= 0) {
                    inputTypeBalance = inputTypeBalance.add(balance);
                    record.setBalance(record.getBalance().subtract(balance));
                    balance = BigDecimal.ZERO;
                } else {
                    // 劵不足，消费劵
                    inputTypeBalance = inputTypeBalance.add(record.getBalance());
                    balance = balance.subtract(record.getBalance());
                    record.setBalance(BigDecimal.ZERO);
                }
                // 标记已使用完
                if (record.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
                    record.setIsOver(1);
                }
                // 更新劵
                list.add(record);
            }
        }
        if (balance.compareTo(BigDecimal.ZERO) > 0) {
            // 劵不够，记录该用户
            AttendancePublicHolidayBalance balance1 = new AttendancePublicHolidayBalance();
            balance1.setUserId(userId);
            balance1.setBalance(balance);
            balance1.setRetirementLeave(balancevo.getRetirementLeave());
            needList.add(balance1);
        }
        return inputTypeBalance;
    }
}