package com.hzu.labbooking.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzu.labbooking.constant.LabStatusConstansts;
import com.hzu.labbooking.constant.RecordStatusConstants;
import com.hzu.labbooking.constant.WeekStatusConstants;
import com.hzu.labbooking.domain.*;
import com.hzu.labbooking.domain.component.*;
import com.hzu.labbooking.domain.vo.CollegeApplyVO;
import com.hzu.labbooking.domain.vo.LabRecordVO;
import com.hzu.labbooking.domain.vo.LabTableVO;
import com.hzu.labbooking.domain.vo.RecordVO;
import com.hzu.labbooking.mapper.LabRecordMapper;
import com.hzu.labbooking.mapper.LabWeekMapper;
import com.hzu.labbooking.service.*;
import com.hzu.labbooking.utils.SendMailUtils;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 实验室预约信息Service业务层处理
 *
 * @author yezihao
 * @date 2021-06-14
 */
@Service
public class LabRecordServiceImpl extends ServiceImpl<LabRecordMapper, LabRecord> implements ILabRecordService {

    @Autowired
    private LabRecordMapper labRecordMapper;
    @Autowired
    private ILabWeekService weekService;
    @Autowired
    private ILabService labService;
    @Autowired
    private SendMailUtils sendMailUtils;
    @Autowired
    private ThreadPoolExecutor executor;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private ICourseService courseService;
    @Autowired
    private LabServiceImpl labServiceImpl;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private IRecordWeekService recordWeekService;
    @Autowired
    private LabWeekMapper weekMapper;
    @Autowired
    private ISysDeptService deptService;

    /**
     * 查询实验室预约信息列表
     *
     * @param labRecord 实验室预约信息
     * @return 实验室预约信息
     */
    @Override
    public List<LabRecordVO> selectLabRecordList(LabRecord labRecord, long year, long term) {
        /*
        1.根据user_id查出当前用户所有预约记录
        2.模糊检索：根据course_id检索courseName和className，通过查出的预约记录来筛选
        3.根据预约记录id获取对应的weekIdList，并封装聚合课程
        4.遍历预约记录map，封装week
         */
        // 1.根据user_id查出当前用户所有预约记录
        SysUser user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();

        QueryWrapper<LabRecord> recordQueryWrapper = new QueryWrapper<>();
        recordQueryWrapper.eq("user_id", user.getUserId());
        recordQueryWrapper.eq("year", year);
        recordQueryWrapper.eq("term", term);

        if (labRecord.getStatus() != null) {
            recordQueryWrapper.eq("status", labRecord.getStatus());
        }

        recordQueryWrapper.orderByDesc("gmt_create");
        List<LabRecord> tmpRecordList = this.list(recordQueryWrapper);

        if (tmpRecordList.isEmpty()) {
            return new ArrayList<>();
        }

        // 2.模糊检索：根据course_id检索courseName和className，通过查出的预约记录来筛选
        String name = labRecord.getName();

        Set<Long> courseIdList = new HashSet<>();
        for (LabRecord record : tmpRecordList) {
            courseIdList.add(record.getCourseId());
        }

        QueryWrapper<Course> courseQueryWrapper = new QueryWrapper<>();
        courseQueryWrapper.select("id");
        courseQueryWrapper.in("id", courseIdList);
        if (StringUtils.isNotEmpty(name)) {
            courseQueryWrapper.like("name", name);
            courseQueryWrapper.or();
            courseQueryWrapper.like("class_name", name);
        }
        List<Course> courseList = courseService.list(courseQueryWrapper);

        Set<Long> courseIdSet = new HashSet<>();
        for (Course course : courseList) {
            courseIdSet.add(course.getId());
        }

        List<LabRecord> recordList = new ArrayList<>(); // 存放模糊检索后的结果
        for (LabRecord record : tmpRecordList) {
            if (courseIdSet.contains(record.getCourseId())) {
                recordList.add(record);
            }
        }

        // 3.根据预约记录id获取对应的weekIdList，并封装聚合课程
        Map<RecordTableKey, RecordTableValue> recordWeekMap = new HashMap<>();
        for (LabRecord record : recordList) {
            List<Long> weekIdList = recordWeekService.selectWeekList(record.getId());

            QueryWrapper<LabWeek> weekQueryWrapper = new QueryWrapper<>();
            weekQueryWrapper.in("id", weekIdList);
            //weekQueryWrapper.ne("status", WeekStatusConstants.OPEN);

            // 获取当前预约所有周次
            List<LabWeek> labWeekList = weekMapper.selectList(weekQueryWrapper);

            if (labWeekList.isEmpty()) {
                continue;
            }

            RecordTableKey recordTableKey = new RecordTableKey();
            RecordTableValue recordTableValue = new RecordTableValue();
            BeanUtils.copyProperties(record, recordTableKey);
            BeanUtils.copyProperties(record, recordTableValue);

            List<Long> recordIdList = new ArrayList<>();
            recordIdList.add(record.getId());
            recordTableValue.setRecordIdList(recordIdList);

            // 若遇到相同预约，不同周次，将其添加到周次中
            if (!recordWeekMap.containsKey(recordTableKey)) {
                recordTableValue.setWeekList(labWeekList);
                recordWeekMap.put(recordTableKey, recordTableValue);
            } else {
                RecordTableValue value = recordWeekMap.get(recordTableKey);
                value.getRecordIdList().add(record.getId());
                value.getWeekList().addAll(labWeekList);
            }
        }

        // 4.遍历预约记录map，封装week
        List<LabRecordVO> voList = new ArrayList<>();
        for (RecordTableKey key : recordWeekMap.keySet()) {
            RecordTableValue value = recordWeekMap.get(key);

            List<Long> weekList = new ArrayList<>();
            for (LabWeek week : value.getWeekList()) {
                weekList.add(week.getWeek());
            }

            Course course = courseService.getById(key.getCourseId());
            Lab lab = labService.getById(key.getLabId());

            LabRecordVO vo = new LabRecordVO();
            vo.setYear(year);
            vo.setTerm(term);
            vo.setUserId(user.getUserId());
            vo.setNickName(user.getNickName());
            vo.setClassName(course.getClassName());
            vo.setCourseName(course.getName());
            vo.setNumber(course.getNumber());
            vo.setDay(key.getDay());
            vo.setTime(key.getTime());
            vo.setRejectContent(value.getRejectContent());
            vo.setLocation(lab.getLocation());
            vo.setLabName(lab.getName());
            vo.setWeekList(weekList);
            vo.setWeek(labServiceImpl.convert(weekList));
            vo.setStatus(value.getStatus());
            vo.setGmtModified(value.getGmtModified());
            vo.setRecordIdList(value.getRecordIdList());
            voList.add(vo);
        }

        // 结果更新时间降序显示(最新在前)
        //voList.sort(new Comparator<LabRecordVO>() {
        //    @Override
        //    public int compare(LabRecordVO o1, LabRecordVO o2) {
        //        return (int) -(o1.getGmtModified().getTime() - o2.getGmtModified().getTime());
        //    }
        //});

        return voList;
    }

    @Transactional
    @Override
    public Boolean cancelBooking(List<Long> recordIdList) {
        /*
         1.修改预约记录状态status=3，表示已取消
         2.获取该预约所有weekId
         3.修改week状态status=1，表示可预约
         */

        if (recordIdList.isEmpty()) {
            return false;
        }

        // 1.修改预约记录状态status=3，表示已取消
        UpdateWrapper<LabRecord> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in("id", recordIdList);
        updateWrapper.set("status", RecordStatusConstants.CANCELLED);
        updateWrapper.ne("status", RecordStatusConstants.REJECTED);
        updateWrapper.set("gmt_modified", LocalDateTime.now());

        boolean update = this.update(updateWrapper);
        if (!update) {
            throw new BaseException("取消失败");
        }

        // 2.通过recordIdList获取该预约所有weekId
        List<Long> weekIdList = recordWeekService.selectAllWeekList(recordIdList);

        // 3.修改week状态status=1，表示可预约
        UpdateWrapper<LabWeek> weekUpdateWrapper = new UpdateWrapper<>();
        weekUpdateWrapper.set("status", WeekStatusConstants.OPEN);
        weekUpdateWrapper.in("id", weekIdList);
        weekUpdateWrapper.set("gmt_modified", LocalDateTime.now());

        boolean update1 = weekService.update(weekUpdateWrapper);
        if (!update1) {
            throw new BaseException("取消失败");
        }

        return true;
    }

    /**
     * 每学期/每个学院实验室申请率（扇形展示eCharts）
     * 统计每个lab_id个数，通过lab_id查出实验室所属学院名称，封装返回
     * 1.查出指定学年学期所有预约记录
     * 2.通过lab_id查出实验室所属学院名称(多个重复，记录个数)
     * 3.通过lab_id检索出实验室所属学院，然后再检索出学院名称
     */
    @Override
    public List<CollegeApplyVO> selectCollegeApply(Long year, Long term) {

        // 1.查出指定学年学期所有预约记录
        QueryWrapper<LabRecord> recordQueryWrapper = new QueryWrapper<>();
        recordQueryWrapper.eq("year", year).eq("term", term);
        List<LabRecord> tmpRecordList = this.list(recordQueryWrapper);

        if (tmpRecordList.isEmpty()) {
            return new ArrayList<>();
        }

        // 2.通过lab_id查出实验室所属学院名称(多个重复，记录个数)
        Map<Long, Integer> labIdCountMap = new HashMap<>();
        for (LabRecord record : tmpRecordList) {
            labIdCountMap.put(record.getLabId(), labIdCountMap.getOrDefault(record.getLabId(), 0) + 1);
        }

        // 3.通过lab_id检索出实验室所属学院，然后再检索出学院名称
        List<CollegeApplyVO> voList = new ArrayList<>();
        for (Long labId : labIdCountMap.keySet()) {
            QueryWrapper<Lab> wrapper = new QueryWrapper<>();
            wrapper.select("dept_id").eq("id", labId);
            Long deptId = labService.getOne(wrapper).getDeptId();

            String deptName = deptService.selectDeptById(deptId).getDeptName();
            CollegeApplyVO vo = new CollegeApplyVO();
            vo.setName(deptName);
            vo.setValue(labIdCountMap.get(labId));
            voList.add(vo);
        }

        return voList;
    }

    /**
     * 添加预约记录和时间
     */
    @Transactional
    @Override
    public int addRecordAndWeek(LabTableVO vo, Long userId) {
        List<Long> weekList = vo.getCheckedValue(); // 选择的周次
        if (weekList.isEmpty()) {
            throw new BaseException("没有插入任何预约记录");
        }

        // 1.校验容量，capacity和number
        Lab lab = labService.getById(vo.getLabId());
        Course course = courseService.getById(vo.getCourseId());
        if (lab.getCapacity() < course.getNumber()) {
            throw new BaseException("该实验室无法容纳！");
        }

        // 2.首先校验时间段是否已存在预约；存在是否审批，week是否被占用；
        QueryWrapper<LabRecord> recordQueryWrapper = new QueryWrapper<>();
        recordQueryWrapper
                .eq("lab_id", vo.getLabId())
                .eq("year", vo.getYear())
                .eq("term", vo.getTerm())
                .eq("day", vo.getDay())
                .eq("time", vo.getTime())
                .ne("status", RecordStatusConstants.REJECTED)
                .ne("status", RecordStatusConstants.CANCELLED);
        List<LabRecord> tmpRecordList = this.list(recordQueryWrapper);

        Map<RecordKey, RecordValue> recordMap = new HashMap<>();

        if (!tmpRecordList.isEmpty()) {
            // 之前预约过，聚合之前预约过的记录，只聚合待审批状态
            Map<MapKey, List<LabWeek>> mp = new HashMap<>();
            for (LabRecord record : tmpRecordList) {
                List<Long> weekIdList = recordWeekService.selectWeekList(record.getId());
                QueryWrapper<LabWeek> weekQueryWrapper = new QueryWrapper<>();
                weekQueryWrapper.in("id", weekIdList);
                List<LabWeek> labWeekList = weekService.list(weekQueryWrapper);
                // 封装day,time对应预约周次
                MapKey key = new MapKey(record.getDay(), record.getTime());

                List<LabWeek> labWeeks = new ArrayList<>(labWeekList);
                RecordKey recordKey =
                        new RecordKey(record.getUserId(), record.getCourseId(), record.getStatus());
                RecordValue recordValue = new RecordValue(record.getId(), labWeeks);
                recordMap.put(recordKey, recordValue);

                if (!mp.containsKey(key)) {
                    mp.put(key, labWeekList);
                } else {
                    List<LabWeek> value = mp.get(key);
                    value.addAll(labWeekList);
                }
            }

            for (MapKey key : mp.keySet()) {
                List<LabWeek> value = mp.get(key);
                // 判断时间段是否被占用，关闭状态表示不可预约
                for (LabWeek week : value) {
                    if (weekList.contains(week.getWeek()) &&
                            week.getStatus() == WeekStatusConstants.CLOSED) {
                        throw new BaseException("该时间段已被占用");
                    }
                }
            }
        }

        // 3.判断当前用户是否在该时间段已有预约，若为自己先前预约，修改record，添加week与record_week
        // 否则新增record，添加week与record_week
        // 注意：存在记录，但week状态为1可预约状态，则修改，修改week时将status置为0是幂等的；
        int recordCnt = 0; // 统计record插入数据个数

        RecordKey key = new RecordKey();
        key.setUserId(userId);
        key.setCourseId(vo.getCourseId());
        key.setStatus((long) RecordStatusConstants.NOT_APPROVE);

        // 不存在相同预约记录
        if (!recordMap.containsKey(key)) {
            // 封装record
            LabRecord newRecord = new LabRecord();
            newRecord.setUserId(userId);
            newRecord.setCourseId(vo.getCourseId());
            newRecord.setLabId(vo.getLabId());
            newRecord.setYear(vo.getYear());
            newRecord.setTerm(vo.getTerm());
            newRecord.setDay(vo.getDay());
            newRecord.setTime(vo.getTime());
            newRecord.setStatus((long) RecordStatusConstants.NOT_APPROVE);

            int r = this.insertLabRecord(newRecord);
            if (r <= 0) {
                throw new BaseException("预约失败");
            }
            // 封装week
            List<LabWeek> labWeekList = new ArrayList<>();
            for (Long week : weekList) {
                LabWeek labWeek = new LabWeek();
                labWeek.setWeek(week);
                labWeek.setStatus((long) WeekStatusConstants.CLOSED);
                labWeekList.add(labWeek);
            }

            boolean w = weekService.saveBatch(labWeekList);
            if (!w) {
                throw new BaseException("预约失败");
            }

            // 封装record_week
            List<RecordWeek> recordWeekList = new ArrayList<>();
            for (LabWeek labWeek : labWeekList) {
                RecordWeek recordWeek = new RecordWeek();
                recordWeek.setRecordId(newRecord.getId());
                recordWeek.setWeekId(labWeek.getId());
                recordWeekList.add(recordWeek);
            }

            boolean rw = recordWeekService.saveBatch(recordWeekList);
            if (!rw) {
                throw new BaseException("预约失败");
            }
            ++recordCnt;
        } else {
            // 先前预约过，且状态为待审批0，修改record状态/时间，直接插入week、record_week
            // 若checkedValue对应week状态为1打开状态，则修改，否则新增记录
            RecordValue value = recordMap.get(key);

            // 1.修改预约记录状态
            UpdateWrapper<LabRecord> wrapper = new UpdateWrapper<>();
            wrapper.set("status", RecordStatusConstants.NOT_APPROVE);
            wrapper.eq("id", value.getRecordId());
            wrapper.set("gmt_modified", LocalDateTime.now());

            boolean r = this.update(wrapper);
            if (!r) {
                throw new BaseException("预约失败");
            }
            // 2.找出需要修改的week的id，修改其状态为0关闭状态
            List<LabWeek> existsWeek = value.getLabWeekList();
            List<Long> updateIds = new ArrayList<>();
            for (LabWeek week : existsWeek) {
                if (weekList.contains(week.getWeek())) {
                    updateIds.add(week.getId());
                }
            }
            if (!updateIds.isEmpty()) {
                UpdateWrapper<LabWeek> updateWrapper = new UpdateWrapper<>();
                updateWrapper.set("status", WeekStatusConstants.CLOSED);
                updateWrapper.in("id", updateIds);
                updateWrapper.set("gmt_modified", LocalDateTime.now());

                boolean uw = weekService.update(updateWrapper);
                if (!uw) {
                    throw new BaseException("预约失败");
                }

                // 3.求剩下week的差集添加到week/record_week
                weekList.removeAll(updateIds);
            }

            // 封装week
            List<LabWeek> labWeekList = new ArrayList<>();
            for (Long week : weekList) {
                LabWeek labWeek = new LabWeek();
                labWeek.setWeek(week);
                labWeek.setStatus((long) WeekStatusConstants.CLOSED);
                labWeekList.add(labWeek);
            }

            boolean w = weekService.saveBatch(labWeekList);
            if (!w) {
                throw new BaseException("预约失败");
            }
            // 封装record_week
            List<RecordWeek> recordWeekList = new ArrayList<>();
            for (LabWeek labWeek : labWeekList) {
                RecordWeek recordWeek = new RecordWeek();
                recordWeek.setRecordId(value.getRecordId());
                recordWeek.setWeekId(labWeek.getId());
                recordWeekList.add(recordWeek);
            }

            boolean rw = recordWeekService.saveBatch(recordWeekList);
            if (!rw) {
                throw new BaseException("预约失败");
            }
            ++recordCnt;
        }

        // 4.邮件提醒
        CompletableFuture.runAsync(() -> {
            sendEmail("实验室预约审核提醒", vo.getEmail(),
                    "实验室预约系统中有用户进行了实验室预约，请前往审核");
        }, executor);

        return recordCnt;
    }

    @Transactional
    @Override
    public Boolean approveBooking(List<RecordVO> vos) {
        if (vos.isEmpty()) {
            return false;
        }
        for (RecordVO vo : vos) {
            Boolean approve = approve(vo);
            if (!approve) {
                throw new BaseException("审批失败");
            }
        }
        return true;
    }

    private Boolean approve(RecordVO vo) {
        List<Long> recordIdList = vo.getRecordIdList();
        if (recordIdList.isEmpty()) {
            return false;
        }

        Lab lab = labService.selectLabById(vo.getLabId());
        if (lab.getStatus() == LabStatusConstansts.DELETED) {
            throw new BaseException("实验室已删除，审批失败");
        }

        Long status = vo.getStatus();
        String labName = vo.getLabName();
        String location = vo.getLocation();
        String rejectContent = vo.getRejectContent();
        // 获取预约用户信息
        SysUser user = userService.selectUserById(vo.getUserId());
        String email = user.getEmail();
        String nickName = user.getNickName();


        // 修改record状态status为指定状态，同时修改week状态
        //通过预约状态判断是否通过或驳回
        if (status == RecordStatusConstants.APPROVE) {

            //审批通过，修改预约记录状态为通过
            UpdateWrapper<LabRecord> updateWrapper = new UpdateWrapper<>();
            updateWrapper.in("id", recordIdList);
            updateWrapper.set("status", RecordStatusConstants.APPROVE);
            updateWrapper.ne("status", RecordStatusConstants.REJECTED);
            updateWrapper.ne("status", RecordStatusConstants.APPROVE);
            updateWrapper.set("gmt_modified", LocalDateTime.now());

            boolean u = this.update(updateWrapper);
            if (!u) {
                throw new BaseException("审批通过失败");
            }
            // 预约提交时已经确定week关闭状态，这里不需要修改

            // 发送邮件提示成功
            CompletableFuture.runAsync(() -> {
                sendEmail("实验室审批通过提醒", email,
                        nickName + " 老师，你预约的 " + labName + " 已审批通过，请到 " + location + " 上课");
            }, executor);

        } else if (status == RecordStatusConstants.REJECTED) {

            //审批失败，修改预约记录状态为通过
            UpdateWrapper<LabRecord> updateWrapper = new UpdateWrapper<>();
            updateWrapper.in("id", recordIdList);
            updateWrapper.set("status", RecordStatusConstants.REJECTED);
            updateWrapper.set("reject_content", rejectContent);
            updateWrapper.ne("status", RecordStatusConstants.APPROVE);
            updateWrapper.ne("status", RecordStatusConstants.REJECTED);
            updateWrapper.set("gmt_modified", LocalDateTime.now());

            boolean update = this.update(updateWrapper);
            if (!update) {
                throw new BaseException("审批驳回失败");
            }

            // 根据record_id获取week_id，然后修改week状态为可开启状态
            List<Long> weekIdList = recordWeekService.selectAllWeekList(recordIdList);

            UpdateWrapper<LabWeek> weeUpdateWrapper = new UpdateWrapper<>();
            weeUpdateWrapper.set("status", WeekStatusConstants.OPEN);
            weeUpdateWrapper.in("id", weekIdList);
            weeUpdateWrapper.set("gmt_modified", LocalDateTime.now());

            boolean u1 = weekService.update(weeUpdateWrapper);
            if (!u1) {
                throw new BaseException("审批驳回失败");
            }

            //发送邮件给老师
            CompletableFuture.runAsync(() -> {
                sendEmail("实验室审批驳回提醒", email,
                        nickName + " 老师，你预约的 " + labName + " 审批驳回，驳回理由为："
                                + rejectContent + "。如有需要，请重新预约。");
            }, executor);

        } else {
            throw new BaseException("实验室列表预约状态异常");
        }

        return true;
    }

    /**
     * 管理员取消实验室预约
     */
    @Transactional
    @Override
    public Boolean managerCancelBooking(List<RecordVO> vos) {
        if (vos.isEmpty()) {
            return false;
        }
        for (RecordVO vo : vos) {
            Boolean cancel = managerCancel(vo);
            if (!cancel) {
                throw new BaseException("取消失败");
            }
        }
        return true;
    }

    private Boolean managerCancel(RecordVO vo) {
    /*
     1.修改预约记录状态status=3，表示已取消
     2.获取该预约所有weekId
     3.修改week状态status=1，表示可预约
     */
        List<Long> recordIdList = vo.getRecordIdList();
        if (recordIdList.isEmpty()) {
            return false;
        }
        Long status = vo.getStatus();
        if (status == RecordStatusConstants.NOT_APPROVE ||
                status == RecordStatusConstants.REJECTED ||
                status == RecordStatusConstants.CANCELLED) {
            return false;
        }
        // 1.修改预约记录状态status=3，表示已取消
        UpdateWrapper<LabRecord> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in("id", recordIdList);
        updateWrapper.set("status", RecordStatusConstants.CANCELLED);
        updateWrapper.set("gmt_modified", LocalDateTime.now());
        boolean update = this.update(updateWrapper);
        if (!update) {
            return false;
        }

        // 2.通过recordIdList获取该预约所有weekId
        List<Long> weekIdList = recordWeekService.selectAllWeekList(recordIdList);

        // 3.修改week状态status=1，表示可预约
        UpdateWrapper<LabWeek> weekUpdateWrapper = new UpdateWrapper<>();
        weekUpdateWrapper.set("status", WeekStatusConstants.OPEN);
        weekUpdateWrapper.in("id", weekIdList);
        weekUpdateWrapper.set("gmt_modified", LocalDateTime.now());
        boolean update1 = weekService.update(weekUpdateWrapper);
        if (!update1) {
            return false;
        }

        // 发送邮件给教师
        SysUser user = userService.selectUserById(vo.getUserId());

        CompletableFuture.runAsync(() -> {
            sendEmail("实验室审批取消提醒", user.getEmail(),
                    user.getNickName() + " 老师，你预约的 " + vo.getLabName() +
                            " 已被取消，很抱歉给你带来困扰，如有需要，请重新预约。");
        }, executor);

        return true;
    }

    /**
     * 管理员查询实验室预约记录
     *
     * @param labRecord 查询VO
     * @param year      学年
     * @param term      学期
     * @return 结果
     */
    @Override
    public List<LabRecordVO> selectManagerRecordList(LabRecord labRecord, long year, long term) {
        /*
        1.根据user_id查出当前用户所有预约记录
        2.模糊检索：根据course_id检索courseName和className，通过查出的预约记录来筛选
        3.根据预约记录id获取对应的weekIdList，并封装聚合课程
        4.遍历预约记录map，封装week
         */
        // 1.根据管理员user_id查出拥有的lab_id，根据lab_id查出关联实验室所有预约记录
        SysUser user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();

        QueryWrapper<Lab> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id").eq("user_id", user.getUserId());
        List<Lab> labList = labService.list(queryWrapper);
        if (labList.isEmpty()) {
            return new ArrayList<>();
        }
        List<Long> labIdList = new ArrayList<>();
        for (Lab lab : labList) {
            labIdList.add(lab.getId());
        }

        QueryWrapper<LabRecord> recordQueryWrapper = new QueryWrapper<>();
        recordQueryWrapper.in("lab_id", labIdList);
        recordQueryWrapper.eq("year", year);
        recordQueryWrapper.eq("term", term);

        if (labRecord.getStatus() != null) {
            recordQueryWrapper.eq("status", labRecord.getStatus());
        }

        recordQueryWrapper.orderByDesc("gmt_create");
        List<LabRecord> tmpRecordList = this.list(recordQueryWrapper);

        if (tmpRecordList.isEmpty()) {
            return new ArrayList<>();
        }

        // 2.模糊检索：根据course_id检索courseName和className，通过查出的预约记录来筛选
        String name = labRecord.getName();

        Set<Long> courseIdList = new HashSet<>();
        for (LabRecord record : tmpRecordList) {
            courseIdList.add(record.getCourseId());
        }

        QueryWrapper<Course> courseQueryWrapper = new QueryWrapper<>();
        courseQueryWrapper.select("id");
        courseQueryWrapper.in("id", courseIdList);
        if (StringUtils.isNotEmpty(name)) {
            courseQueryWrapper.like("name", name);
            courseQueryWrapper.or();
            courseQueryWrapper.like("class_name", name);
        }
        List<Course> courseList = courseService.list(courseQueryWrapper);

        Set<Long> courseIdSet = new HashSet<>();
        for (Course course : courseList) {
            courseIdSet.add(course.getId());
        }

        List<LabRecord> recordList = new ArrayList<>(); // 存放模糊检索后的结果
        for (LabRecord record : tmpRecordList) {
            if (courseIdSet.contains(record.getCourseId())) {
                recordList.add(record);
            }
        }

        // 3.根据预约记录id获取对应的weekIdList，并封装聚合课程
        Map<RecordTableKey, RecordTableValue> recordWeekMap = new HashMap<>();
        for (LabRecord record : recordList) {
            List<Long> weekIdList = recordWeekService.selectWeekList(record.getId());

            QueryWrapper<LabWeek> weekQueryWrapper = new QueryWrapper<>();
            weekQueryWrapper.in("id", weekIdList);
            //weekQueryWrapper.ne("status", WeekStatusConstants.OPEN);

            // 获取当前预约所有周次
            List<LabWeek> labWeekList = weekMapper.selectList(weekQueryWrapper);

            if (labWeekList.isEmpty()) {
                continue;
            }

            RecordTableKey recordTableKey = new RecordTableKey();
            RecordTableValue recordTableValue = new RecordTableValue();
            BeanUtils.copyProperties(record, recordTableKey);
            BeanUtils.copyProperties(record, recordTableValue);

            List<Long> recordIdList = new ArrayList<>();
            recordIdList.add(record.getId());
            recordTableValue.setRecordIdList(recordIdList);

            // 若遇到相同预约，不同周次，将其添加到周次中
            if (!recordWeekMap.containsKey(recordTableKey)) {
                recordTableValue.setWeekList(labWeekList);
                recordWeekMap.put(recordTableKey, recordTableValue);
            } else {
                RecordTableValue value = recordWeekMap.get(recordTableKey);
                value.getRecordIdList().add(record.getId());
                value.getWeekList().addAll(labWeekList);
            }
        }

        // 4.遍历预约记录map，封装week
        List<LabRecordVO> voList = new ArrayList<>();
        for (RecordTableKey key : recordWeekMap.keySet()) {
            RecordTableValue value = recordWeekMap.get(key);

            List<Long> weekList = new ArrayList<>();
            for (LabWeek week : value.getWeekList()) {
                weekList.add(week.getWeek());
            }

            Course course = courseService.getById(key.getCourseId());
            Lab lab = labService.getById(key.getLabId());

            LabRecordVO vo = new LabRecordVO();
            vo.setYear(year);
            vo.setTerm(term);
            vo.setLabId(key.getLabId());
            vo.setUserId(key.getUserId());
            vo.setNickName(user.getNickName());
            vo.setClassName(course.getClassName());
            vo.setCourseName(course.getName());
            vo.setNumber(course.getNumber());
            vo.setDay(key.getDay());
            vo.setTime(key.getTime());
            vo.setRejectContent(value.getRejectContent());
            vo.setLocation(lab.getLocation());
            vo.setLabName(lab.getName());
            vo.setWeekList(weekList);
            vo.setWeek(labServiceImpl.convert(weekList));
            vo.setStatus(value.getStatus());
            vo.setGmtModified(value.getGmtModified());
            vo.setRecordIdList(value.getRecordIdList());
            voList.add(vo);
        }

        // 结果更新时间降序显示(最新在前)
        //voList.sort(new Comparator<LabRecordVO>() {
        //    @Override
        //    public int compare(LabRecordVO o1, LabRecordVO o2) {
        //        return (int) -(o1.getGmtModified().getTime() - o2.getGmtModified().getTime());
        //    }
        //});

        return voList;
    }

    /**
     * 新增实验室预约信息
     *
     * @param labRecord 实验室预约信息
     * @return 结果
     */
    @Override
    public int insertLabRecord(LabRecord labRecord) {
        return labRecordMapper.insertLabRecord(labRecord);
    }

    /**
     * 发送邮件,异步执行
     */
    public void sendEmail(String subject, String receiver, String text) {
        String content =
                "<html>\r\n" +
                        " <body>\r\n" +
                        "  <div id=\"contentDiv\" onmouseover=\"getTop().stopPropagation(event);\" onclick=\"getTop().preSwapLink(event, 'spam', 'ZC1222-PrLAp4T0Z7Z7UUMYzqLkb8a');\" style=\"position:relative;font-size:14px;height:auto;padding:15px 15px 10px 15px;z-index:1;zoom:1;line-height:1.7;\" class=\"body\">    \r\n" +
                        "  <div id=\"qm_con_body\"><div id=\"mailContentContainer\" class=\"qmbox qm_con_body_content qqmail_webmail_only\" style=\"\">\r\n" +
                        "<style>\r\n" +
                        "  .qmbox .email-body{color:#40485B;font-size:14px;font-family:-apple-system, \"Helvetica Neue\", Helvetica, \"Nimbus Sans L\", \"Segoe UI\", Arial, \"Liberation Sans\", \"PingFang SC\", \"Microsoft YaHei\", \"Hiragino Sans GB\", \"Wenquanyi Micro Hei\", \"WenQuanYi Zen Hei\", \"ST Heiti\", SimHei, \"WenQuanYi Zen Hei Sharp\", sans-serif;background:#f8f8f8;}.qmbox .pull-right{float:right;}.qmbox a{color:#FE7300;text-decoration:underline;}.qmbox a:hover{color:#fe9d4c;}.qmbox a:active{color:#b15000;}.qmbox .logo{text-align:center;margin-bottom:20px;}.qmbox .panel{background:#fff;border:1px solid #E3E9ED;margin-bottom:10px;}.qmbox .panel-header{font-size:18px;line-height:30px;padding:10px 20px;background:#fcfcfc;border-bottom:1px solid #E3E9ED;}.qmbox .panel-body{padding:20px;}.qmbox .container{width:50%;min-width:300px;padding:20px;margin:0 auto;}.qmbox .text-center{text-align:center;}.qmbox .thumbnail{padding:4px;max-width:100%;border:1px solid #E3E9ED;}.qmbox .btn-primary{color:#fff;font-size:16px;padding:8px 14px;line-height:20px;border-radius:2px;display:inline-block;background:#FE7300;text-decoration:none;}.qmbox .btn-primary:hover,.qmbox .btn-primary:active{color:#fff;}.qmbox .footer{color:#9B9B9B;font-size:12px;margin-top:40px;}.qmbox .footer a{color:#9B9B9B;}.qmbox .footer a:hover{color:#fe9d4c;}.qmbox .footer a:active{color:#b15000;}.qmbox .email-body#mail_to_teacher{line-height:26px;color:#40485B;font-size:16px;padding:0px;}.qmbox .email-body#mail_to_teacher .container,.qmbox .email-body#mail_to_teacher .panel-body{padding:0px;}.qmbox .email-body#mail_to_teacher .container{padding-top:20px;}.qmbox .email-body#mail_to_teacher .textarea{padding:32px;}.qmbox .email-body#mail_to_teacher .say-hi{font-weight:500;}.qmbox .email-body#mail_to_teacher .paragraph{margin-top:24px;}.qmbox .email-body#mail_to_teacher .paragraph .pro-name{color:#000000;}.qmbox .email-body#mail_to_teacher .paragraph.link{margin-top:32px;text-align:center;}.qmbox .email-body#mail_to_teacher .paragraph.link .button{background:#4A90E2;border-radius:2px;color:#FFFFFF;text-decoration:none;padding:11px 17px;line-height:14px;display:inline-block;}.qmbox .email-body#mail_to_teacher ul.pro-desc{list-style-type:none;margin:0px;padding:0px;padding-left:16px;}.qmbox .email-body#mail_to_teacher ul.pro-desc li{position:relative;}.qmbox .email-body#mail_to_teacher ul.pro-desc li::before{content:'';width:3px;height:3px;border-radius:50%;background:red;position:absolute;left:-15px;top:11px;background:#40485B;}.qmbox .email-body#mail_to_teacher .blackboard-area{height:600px;padding:40px;background-image:url();color:#FFFFFF;}.qmbox .email-body#mail_to_teacher .blackboard-area .big-title{font-size:32px;line-height:45px;text-align:center;}.qmbox .email-body#mail_to_teacher .blackboard-area .desc{margin-top:8px;}.qmbox .email-body#mail_to_teacher .blackboard-area .desc p{margin:0px;text-align:center;line-height:28px;}.qmbox .email-body#mail_to_teacher .blackboard-area .card:nth-child(odd){float:left;margin-top:45px;}.qmbox .email-body#mail_to_teacher .blackboard-area .card:nth-child(even){float:right;margin-top:45px;}.qmbox .email-body#mail_to_teacher .blackboard-area .card .title{font-size:18px;text-align:center;margin-bottom:10px;}\r\n" +
                        "</style>\r\n" +
                        "<meta>\r\n" +
                        "<div class=\"email-body\" style=\"background-color: rgb(246, 244, 236);\">\r\n" +
                        "<div class=\"container\">\r\n" +
                        "<div class=\"panel\" style=\"background-color: rgb(246, 244, 236);\">\r\n" +
                        "<div class=\"panel-header\" style=\"background-color: rgb(246, 244, 236);\">\r\n" +
                        "审核提醒\r\n" +
                        "\r\n" +
                        "</div>\r\n" +
                        "<div class=\"panel-body\">\r\n" +
                        "<p>您好！</p>\r\n" +
                        "<p>" + text + "</p>\r\n" +
                        "</div>\r\n" +
                        "</div>\r\n" +
                        "</div>\r\n" +
                        "</div>\r\n" +
                        "</div>\r\n" +
                        "<style type=\"text/css\">.qmbox style, .qmbox script, .qmbox head, .qmbox link, .qmbox meta {display: none !important;}</style></div></div><!-- --><style>#mailContentContainer .txt {height:auto;}</style>  </div>\r\n" +
                        " </body>\r\n" +
                        "</html>";
        //发邮箱
        sendMailUtils.sendEmail(subject, receiver, content);
    }

}
