package com.qmth.exam.reserve.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.qmth.boot.core.concurrent.service.ConcurrentService;
import com.qmth.boot.core.exception.StatusException;
import com.qmth.exam.reserve.bean.Constants;
import com.qmth.exam.reserve.bean.RichTextBean;
import com.qmth.exam.reserve.bean.apply.*;
import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
import com.qmth.exam.reserve.bean.examsite.ExamSiteCacheBean;
import com.qmth.exam.reserve.bean.login.LoginUser;
import com.qmth.exam.reserve.cache.CacheConstants;
import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService;
import com.qmth.exam.reserve.cache.impl.ExamSiteCacheService;
import com.qmth.exam.reserve.dao.StudentApplyDao;
import com.qmth.exam.reserve.entity.ApplyTaskEntity;
import com.qmth.exam.reserve.entity.ExamSiteEntity;
import com.qmth.exam.reserve.entity.StudentApplyEntity;
import com.qmth.exam.reserve.entity.TimePeriodEntity;
import com.qmth.exam.reserve.enums.ApplyStatus;
import com.qmth.exam.reserve.service.*;
import com.qmth.exam.reserve.util.DateUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ExamReserveServiceImpl implements ExamReserveService {

    private static final Logger log = LoggerFactory.getLogger(ExamReserveServiceImpl.class);

    @Autowired
    private ApplyTaskCacheService applyTaskCacheService;

    @Autowired
    private ApplyTaskService applyTaskService;

    @Autowired
    private ExamSiteService examSiteService;

    @Autowired
    private ExamSiteCacheService examSiteCacheService;

    @Autowired
    private TimePeriodService timePeriodService;

    @Autowired
    private StudentApplyService studentApplyService;

    @Autowired
    private ConcurrentService concurrentService;

    @Override
    public void saveStudentApply(LoginUser student, Long examSiteId, Long timePeriodId) {
        if (examSiteId == null) {
            throw new StatusException("考点ID不能为空");
        }
        if (timePeriodId == null) {
            throw new StatusException("预约时段ID不能为空");
        }

        CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(student.getOrgId());
        if (curApplyTask == null) {
            log.warn("预约失败，尚未开启预约任务！stuApplyTaskId:{}", student.getApplyTaskId());
            throw new StatusException("尚未开启预约任务");
        }

        if (!curApplyTask.getTaskId().equals(student.getApplyTaskId())) {
            log.warn("预约失败，当前预约任务与学生的不匹配！curApplyTaskId:{} stuApplyTaskId:{}", curApplyTask.getTaskId(), student.getApplyTaskId());
            throw new StatusException("当前预约任务与学生的不匹配");
        }

        TimePeriodEntity timePeriod = timePeriodService.getById(timePeriodId);
        if (timePeriod == null || !curApplyTask.getTaskId().equals(timePeriod.getApplyTaskId())) {
            log.warn("预约失败，当前预约时段信息有误！timePeriodId:{}", timePeriodId);
            throw new StatusException("当前预约时段信息有误");
        }

        // 考前N天，禁止考生自主预约
        Date allowDate = DateUtil.changeDateAndTimeEnd(new Date(), curApplyTask.getAllowApplyDays());
        Date curDate = new Date(timePeriod.getStartTime());
        if (curDate.before(allowDate)) {
            // 跳过过期时段，“当前时段开始时间”在“允许预约时间”之前，则禁止预约
            log.warn("预约失败，当前预约时段已禁止预约！allowApplyDays:{} timePeriodStart:{}",
                    curApplyTask.getAllowApplyDays(), timePeriod.getStartTime());
            throw new StatusException("当前预约时段已禁止预约");
        }

        // 考生预约操作锁
        String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, student.getId());
        RLock studentApplyLock = (RLock) concurrentService.getLock(studentApplyLockKey);
        try {
            if (!studentApplyLock.tryLock()) {
                log.warn("获取锁失败，同一个考生不允许同时操作预约！lockKey:{}", studentApplyLockKey);
                throw new StatusException(Constants.SYSTEM_BUSY);
            }

            log.info("获取锁成功！lockKey:{}", studentApplyLockKey);
            if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
                // 系统自动预约“任务执行期间”不允许预约
                log.warn("系统自动预约中，不允许考生操作预约！lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
                throw new StatusException(Constants.SYSTEM_BUSY);
            }

            // 考生剩余预约次数 = 允许预约次数 - 已完成预约次数
            int studentApplyNumber = applyTaskCacheService.getStudentApplyNumber(student.getId());
            int studentApplyFinishCount = applyTaskCacheService.getStudentApplyFinishCount(student.getId());
            int unApplyNumber = studentApplyNumber - studentApplyFinishCount;
            if (unApplyNumber < 1) {
                String msg = "当前学生无剩余可约时段次数，已完成预约" + studentApplyNumber + "次";
                log.warn(msg);
                throw new StatusException(msg);
            }

            // 当前预约时段剩余可约数量
            int availableCount = applyTaskCacheService.getApplyAvailableCount(examSiteId, timePeriodId);
            if (availableCount < 1) {
                log.warn("预约失败，当前预约时段已约满！examSiteId:{} timePeriodId:{}", examSiteId, timePeriodId);
                throw new StatusException("当前预约时段已约满");
            }

            // 某考点某时段的“已预约数量” 累加1（优先执行抢占1个数量）
            applyTaskCacheService.increaseApplyFinishCount(examSiteId, timePeriodId);

            try {
                StudentApplyEntity existApply = this.getStudentApply(student.getId(), examSiteId, timePeriodId);
                // ApplyRecordCacheBean existApply = applyTaskCacheService.getStudentApplyRecord(student.getId(), examSiteId, timePeriodId);
                if (existApply != null) {
                    if (!existApply.getCancel()) {
                        // 某考点某时段的“已预约数量” 累减1（释放1个被占数量）
                        applyTaskCacheService.decreaseApplyFinishCount(examSiteId, timePeriodId);

                        log.warn("当前时段已预约，请勿重复预约！examSiteId:{} timePeriodId:{}", examSiteId, timePeriodId);
                        throw new StatusException("当前时段已预约，请勿重复预约");
                    }

                    // 存在“已取消”预约记录，则恢复预约
                    // existApply.setCancel(false);
                    // existApply.setOperateId(student.getId());
                    // existApply.setOperateTime(System.currentTimeMillis());
                    // // 推送至预约队列
                    // applyTaskCacheService.pushStudentApplyRecordQueue(existApply);
                    // // 保存至预约缓存
                    // applyTaskCacheService.saveStudentApplyRecord(existApply);

                    this.updateStudentApplyForCancel(existApply.getId(), false, student.getId());
                } else {
                    // 不存在则新增预约记录
                    // ApplyRecordCacheBean newApply = new ApplyRecordCacheBean();
                    // newApply.setStudentId(student.getId());
                    // newApply.setExamSiteId(examSiteId);
                    // newApply.setTimePeriodId(timePeriodId);
                    // newApply.setCancel(false);
                    // newApply.setOperateId(student.getId());
                    // newApply.setOperateTime(System.currentTimeMillis());
                    // // 推送至预约队列
                    // applyTaskCacheService.pushStudentApplyRecordQueue(newApply);
                    // // 保存至预约缓存
                    // applyTaskCacheService.saveStudentApplyRecord(newApply);

                    StudentApplyEntity newApply = new StudentApplyEntity();
                    newApply.setStudentId(student.getId());
                    newApply.setExamSiteId(examSiteId);
                    newApply.setTimePeriodId(timePeriodId);
                    newApply.setOperateId(student.getId());
                    newApply.setCancel(false);
                    studentApplyService.save(newApply);
                }

                log.warn("预约成功！studentId:{} examSiteId:{} timePeriodId:{}", student.getId(), examSiteId, timePeriodId);
            } catch (Exception e) {
                if (e instanceof StatusException) {
                    throw e;
                }

                // 异常时，释放1个被占数量
                applyTaskCacheService.decreaseApplyFinishCount(examSiteId, timePeriodId);
                log.error("预约异常！examSiteId:{} timePeriodId:{} err:{}", examSiteId, timePeriodId, e.getMessage());
                throw new StatusException("预约失败，请稍后再试！", e);
            }
        } finally {
            try {
                // 解锁前检查当前线程是否持有该锁
                if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) {
                    studentApplyLock.unlock();
                    log.info("解锁成功！lockKey:{}", studentApplyLockKey);
                }
            } catch (Exception e) {
                log.warn("解锁失败！lockKey:{} err:{}", studentApplyLockKey, e.getMessage());
            }
        }
    }

    @Override
    public void cancelStudentApply(LoginUser student, Long examSiteId, Long timePeriodId) {
        if (examSiteId == null) {
            throw new StatusException("考点ID不能为空");
        }
        if (timePeriodId == null) {
            throw new StatusException("预约时段ID不能为空");
        }

        CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(student.getOrgId());
        if (curApplyTask == null) {
            log.warn("取消预约失败，尚未开启预约任务！stuApplyTaskId:{}", student.getApplyTaskId());
            return;
        }
        if (!curApplyTask.getTaskId().equals(student.getApplyTaskId())) {
            log.warn("取消预约失败，当前预约任务与学生的不匹配！curApplyTaskId:{} stuApplyTaskId:{}", curApplyTask.getTaskId(), student.getApplyTaskId());
            return;
        }

        StudentApplyEntity existApply = this.getStudentApply(student.getId(), examSiteId, timePeriodId);
        // ApplyRecordCacheBean existApply = applyTaskCacheService.getStudentApplyRecord(student.getId(), examSiteId, timePeriodId);
        if (existApply == null) {
            log.warn("取消预约失败，预约记录不存在！examSiteId:{} timePeriodId:{}", examSiteId, timePeriodId);
            return;
        }

        if (existApply.getCancel()) {
            log.warn("当前时段已取消，忽略重复取消！examSiteId:{} timePeriodId:{}", examSiteId, timePeriodId);
            return;
        }

        TimePeriodEntity timePeriod = timePeriodService.getById(timePeriodId);
        if (timePeriod == null) {
            log.warn("取消预约失败，预约时段不存在！examSiteId:{} timePeriodId:{}", examSiteId, timePeriodId);
            return;
        }

        // 考前N天，禁止考生自主取消预约
        Date allowDate = DateUtil.changeDateAndTimeEnd(new Date(), curApplyTask.getAllowApplyCancelDays());
        Date curDate = new Date(timePeriod.getStartTime());
        if (curDate.before(allowDate)) {
            // “当前时段开始时间”在“允许取消时间”之前，禁止取消预约
            log.warn("取消预约失败，当前预约记录已禁止取消预约！allowApplyCancelDays:{} timePeriodStart:{}",
                    curApplyTask.getAllowApplyCancelDays(), timePeriod.getStartTime());
            throw new StatusException("当前预约记录已禁止取消预约");
        }

        // 考生预约操作锁
        String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, student.getId());
        RLock studentApplyLock = (RLock) concurrentService.getLock(studentApplyLockKey);
        try {
            if (!studentApplyLock.tryLock()) {
                log.warn("获取锁失败，同一个考生不允许同时操作预约！lockKey:{}", studentApplyLockKey);
                throw new StatusException(Constants.SYSTEM_BUSY);
            }

            log.info("获取锁成功！lockKey:{}", studentApplyLockKey);
            if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
                // 系统自动预约“任务执行期间”不允许取消预约
                log.warn("系统自动预约中，不允许考生操作预约！lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
                throw new StatusException(Constants.SYSTEM_BUSY);
            }

            // existApply.setCancel(true);
            // existApply.setOperateId(student.getId());
            // existApply.setOperateTime(System.currentTimeMillis());
            // // 推送至预约队列
            // applyTaskCacheService.pushStudentApplyRecordQueue(existApply);
            // // 保存至预约缓存
            // applyTaskCacheService.saveStudentApplyRecord(existApply);

            this.updateStudentApplyForCancel(existApply.getId(), true, student.getId());

            // 某考点某时段的“已预约数量” 累减1
            applyTaskCacheService.decreaseApplyFinishCount(examSiteId, timePeriodId);

            log.warn("取消预约成功！studentId:{} examSiteId:{} timePeriodId:{}", student.getId(), examSiteId, timePeriodId);
        } finally {
            try {
                // 解锁前检查当前线程是否持有该锁
                if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) {
                    studentApplyLock.unlock();
                    log.info("解锁成功！lockKey:{}", studentApplyLockKey);
                }
            } catch (Exception e) {
                log.warn("解锁失败！lockKey:{} err:{}", studentApplyLockKey, e.getMessage());
            }
        }
    }

    private StudentApplyEntity getStudentApply(Long studentId, Long examSiteId, Long timePeriodId) {
        LambdaQueryWrapper<StudentApplyEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StudentApplyEntity::getExamSiteId, examSiteId);
        wrapper.eq(StudentApplyEntity::getTimePeriodId, timePeriodId);
        wrapper.eq(StudentApplyEntity::getStudentId, studentId);
        return studentApplyService.getOne(wrapper);
    }

    // @Transactional
    public void updateStudentApplyForCancel(Long applyId, boolean cancel, Long operateId) {
        LambdaUpdateWrapper<StudentApplyEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(StudentApplyEntity::getCancel, cancel);
        updateWrapper.set(StudentApplyEntity::getOperateId, operateId);
        updateWrapper.set(StudentApplyEntity::getUpdateTime, System.currentTimeMillis());
        updateWrapper.eq(StudentApplyEntity::getId, applyId);
        studentApplyService.update(updateWrapper);
    }

    private boolean hasTicketNumber(Long studentId, Long examSiteId, Long timePeriodId) {
        LambdaQueryWrapper<StudentApplyEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(StudentApplyEntity::getTicketNumber);//只查询ticketNumber等字段
        wrapper.eq(StudentApplyEntity::getExamSiteId, examSiteId);
        wrapper.eq(StudentApplyEntity::getTimePeriodId, timePeriodId);
        wrapper.eq(StudentApplyEntity::getStudentId, studentId);
        StudentApplyEntity e = studentApplyService.getOne(wrapper);
        return e != null && StringUtils.isNotEmpty(e.getTicketNumber());
    }

    private Map<Long, TimePeriodEntity> getTimePeriods(Set<Long> timePeriodIds) {
        LambdaQueryWrapper<TimePeriodEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(TimePeriodEntity::getId, TimePeriodEntity::getStartTime, TimePeriodEntity::getEndTime);
        wrapper.in(TimePeriodEntity::getId, timePeriodIds);
        List<TimePeriodEntity> timePeriods = timePeriodService.list(wrapper);
        Map<Long, TimePeriodEntity> maps = new HashMap<>();
        for (TimePeriodEntity timePeriod : timePeriods) {
            maps.put(timePeriod.getId(), timePeriod);
        }
        return maps;
    }

    @Override
    public List<ApplyVO> getStudentApplyList(LoginUser student, Boolean cancel) {
        CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(student.getOrgId());
        if (curApplyTask == null) {
            return new ArrayList<>();
        }

        StudentApplyDao baseMapper = (StudentApplyDao) studentApplyService.getBaseMapper();
        List<ApplyVO> list = baseMapper.getStudentApplyList(student.getId(), cancel);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }

        // 考前N天，禁止考生自主取消预约
        Date allowDate = DateUtil.changeDateAndTimeEnd(new Date(), curApplyTask.getAllowApplyCancelDays());

        for (ApplyVO vo : list) {
            vo.setShowTicket(false);
            vo.setAllowCancel(false);

            if (!vo.getCancel()) {
                if (StringUtils.isNotEmpty(vo.getTicketNumber())) {
                    // 准考证号已生成，则可查看
                    vo.setShowTicket(true);
                }
            }

            Date curDate = new Date(vo.getTimePeriodStart());
            if (curDate.after(allowDate)) {
                // “当前时段开始时间”在“允许取消时间”之后，可以取消预约
                vo.setAllowCancel(true);
            }
        }

        Collections.sort(list);
        return list;
    }

    @Override
    public List<ApplyVO> getStudentApplyListFromCache(LoginUser student, Boolean cancel) {
        CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(student.getOrgId());
        if (curApplyTask == null) {
            return new ArrayList<>();
        }

        Map<String, ApplyRecordCacheBean> maps = applyTaskCacheService.getStudentApplyRecords(student.getId());
        if (MapUtils.isEmpty(maps)) {
            return new ArrayList<>();
        }

        Set<Long> timePeriodIds = maps.values().stream().map(ApplyRecordCacheBean::getTimePeriodId).collect(Collectors.toSet());
        Map<Long, TimePeriodEntity> timePeriods = this.getTimePeriods(timePeriodIds);

        // 考前N天，禁止考生自主取消预约
        Date allowDate = DateUtil.changeDateAndTimeEnd(new Date(), curApplyTask.getAllowApplyCancelDays());

        List<ApplyVO> list = new ArrayList<>();
        for (ApplyRecordCacheBean bean : maps.values()) {
            if (cancel != null && cancel != bean.getCancel()) {
                // 按“取消预约”状态过滤
                continue;
            }

            ApplyVO vo = new ApplyVO();
            vo.setShowTicket(false);
            vo.setAllowCancel(false);
            vo.setCancel(bean.getCancel());

            TimePeriodEntity timePeriod = timePeriods.get(bean.getTimePeriodId());
            vo.setTimePeriodId(bean.getTimePeriodId());
            if (timePeriod != null) {
                vo.setTimePeriodStart(timePeriod.getStartTime());
                vo.setTimePeriodEnd(timePeriod.getEndTime());

                Date curDate = new Date(vo.getTimePeriodStart());
                if (curDate.after(allowDate)) {
                    // “当前时段开始时间”在“允许取消时间”之后，可以取消预约
                    vo.setAllowCancel(true);
                }
            }

            ExamSiteCacheBean examSite = examSiteCacheService.getExamSiteById(bean.getExamSiteId());
            vo.setExamSiteId(bean.getExamSiteId());
            if (examSite != null) {
                vo.setExamSiteName(examSite.getExamSiteName());
                vo.setExamSiteAddress(examSite.getExamSiteAddress());
                vo.setCategoryId(examSite.getCategoryId());
                vo.setCategoryName(examSite.getCategoryName());
            }

            if (!vo.getCancel()) {
                // 准考证号已生成，则可查看
                vo.setShowTicket(this.hasTicketNumber(bean.getStudentId(), bean.getExamSiteId(), bean.getTimePeriodId()));
            }

            list.add(vo);
        }

        Collections.sort(list);
        return list;
    }

    @Override
    public List<ApplyVO> getStudentApplyListForCurrent(LoginUser student) {
        // 默认过滤掉“已取消预约”的记录
        List<ApplyVO> list = this.getStudentApplyList(student, false);

        Date now = new Date();
        List<ApplyVO> newList = new ArrayList<>();
        for (ApplyVO vo : list) {
            Date curDate = new Date(vo.getTimePeriodEnd());
            if (curDate.before(now)) {
                // “当前时段结束时间”在“当前时间”之前，则过滤掉
                continue;
            }
            newList.add(vo);
        }

        return newList;
    }

    @Override
    public TicketInfo getApplyTicket(Long studentId, Long examSiteId, Long timePeriodId) {
        if (examSiteId == null) {
            throw new StatusException("考点ID不能为空");
        }
        if (timePeriodId == null) {
            throw new StatusException("预约时段ID不能为空");
        }

        StudentApplyDao baseMapper = (StudentApplyDao) studentApplyService.getBaseMapper();
        TicketInfo info = baseMapper.getStudentApplyTicket(studentId, examSiteId, timePeriodId);
        if (info == null || info.getCancel()) {
            throw new StatusException("准考证信息不存在");
        }
        if (StringUtils.isEmpty(info.getTicketNumber())) {
            throw new StatusException("准考证信息尚未生成");
        }
        return info;
    }

    @Override
    public List<String> getApplyDateList(LoginUser student) {
        // 日期格式：yyyyMMdd
        List<String> dateList = new ArrayList<>();

        CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(student.getOrgId());
        if (curApplyTask == null) {
            return dateList;
        }

        LambdaQueryWrapper<TimePeriodEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(TimePeriodEntity::getStartTime);// 只查询startTime等字段
        wrapper.eq(TimePeriodEntity::getApplyTaskId, curApplyTask.getTaskId());
        List<TimePeriodEntity> timePeriods = timePeriodService.list(wrapper);
        if (CollectionUtils.isEmpty(timePeriods)) {
            return dateList;
        }

        // 考前N天，禁止考生自主预约
        Date allowDate = DateUtil.changeDateAndTimeEnd(new Date(), curApplyTask.getAllowApplyDays());
        Set<String> dates = new HashSet<>();
        FastDateFormat fdf = FastDateFormat.getInstance("yyyyMMdd");
        for (TimePeriodEntity timePeriod : timePeriods) {
            Date curDate = new Date(timePeriod.getStartTime());
            if (curDate.before(allowDate)) {
                // 跳过过期时段，“当前时段开始时间”在“允许预约时间”之前，则禁止预约
                continue;
            }

            dates.add(fdf.format(curDate));
        }

        dateList.addAll(dates);
        Collections.sort(dateList);
        return dateList;
    }

    @Override
    public ApplyTimePeriodResult getApplyTimePeriodList(LoginUser student, Long examSiteId, String date) {
        if (examSiteId == null) {
            throw new StatusException("考点ID不能为空");
        }
        if (StringUtils.isEmpty(date)) {
            throw new StatusException("预约日期不能为空");
        }

        Date startTime, endTime;
        try {
            FastDateFormat fdf = FastDateFormat.getInstance("yyyyMMddHHmmss");
            startTime = fdf.parse(date + "000000");
            endTime = fdf.parse(date + "235959");
        } catch (ParseException e) {
            log.error(e.getMessage(), e);
            throw new StatusException("预约日期值的格式不正确");
        }

        CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(student.getOrgId());
        if (curApplyTask == null) {
            throw new StatusException("尚未开启预约任务");
        }

        LambdaQueryWrapper<TimePeriodEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(TimePeriodEntity::getId, TimePeriodEntity::getStartTime, TimePeriodEntity::getEndTime);
        wrapper.eq(TimePeriodEntity::getApplyTaskId, curApplyTask.getTaskId());
        wrapper.between(TimePeriodEntity::getStartTime, startTime.getTime(), endTime.getTime());
        List<TimePeriodEntity> timePeriods = timePeriodService.list(wrapper);

        // 获取某考生的 所有的“预约记录”
        Map<String, ApplyRecordCacheBean> maps = applyTaskCacheService.getStudentApplyRecordsFromDB(student.getId());
        int studentApplyFinishCount = 0;
        for (ApplyRecordCacheBean bean : maps.values()) {
            if (!bean.getCancel()) {
                studentApplyFinishCount++;
            }
        }

        // 考生剩余预约次数 = 允许预约次数 - 已完成预约次数
        int studentApplyNumber = applyTaskCacheService.getStudentApplyNumber(student.getId());
        int unApplyNumber = studentApplyNumber - studentApplyFinishCount;

        List<TimePeriodInfo> periodList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(timePeriods)) {
            for (TimePeriodEntity timePeriod : timePeriods) {
                TimePeriodInfo info = new TimePeriodInfo();
                info.setTimePeriodId(timePeriod.getId());
                info.setTimePeriodStart(timePeriod.getStartTime());
                info.setTimePeriodEnd(timePeriod.getEndTime());

                int availableCount = applyTaskCacheService.getApplyAvailableCount(examSiteId, timePeriod.getId());
                info.setAvailableCount(availableCount);
                if (availableCount < 1) {
                    info.setStatus(ApplyStatus.FULL);
                } else {
                    info.setStatus(ApplyStatus.AVAILABLE);
                }

                String hashKey = String.format("%s_%s", examSiteId, timePeriod.getId());
                ApplyRecordCacheBean existApply = maps.get(hashKey);
                if (existApply != null && !existApply.getCancel()) {
                    info.setStatus(ApplyStatus.FINISHED);
                }

                periodList.add(info);
            }
        }

        ApplyTimePeriodResult result = new ApplyTimePeriodResult();
        result.setUnApplyNumber(Math.max(unApplyNumber, 0));
        result.setPeriodList(periodList);

        return result;
    }

    @Override
    public RichTextBean getExamNotice(Long applyTaskId) {
        if (applyTaskId == null) {
            throw new StatusException("预约任务ID不能为空");
        }

        ApplyTaskEntity applyTask = applyTaskService.getById(applyTaskId);
        if (applyTask == null) {
            throw new StatusException("考试须知信息不存在");
        }

        RichTextBean bean = new RichTextBean();
        bean.setContent(applyTask.getNotice());
        return bean;
    }

    @Override
    public RichTextBean getExamSiteGuide(Long examSiteId) {
        if (examSiteId == null) {
            throw new StatusException("考点ID不能为空");
        }

        ExamSiteEntity examSite = examSiteService.getById(examSiteId);
        if (examSite == null) {
            throw new StatusException("考场引导信息不存在");
        }

        RichTextBean bean = new RichTextBean();
        bean.setContent(examSite.getGuide());
        return bean;
    }

}
