package com.yonge.cooleshow.biz.dal.service.impl;

import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.COOLESHOW;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_HEART_BEAT;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_INFO;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_LIKE;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_ONLINE_USER_LIST;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_TOTAL_USER_LIST;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_USER_LAST_CLIENT_IP;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_USER_LAST_TIME;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_USER_ROOM;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.ROOM_UID;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.TEACHER_TEMP_LIVE_ROOM;
import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.USER_ID;
import static com.yonge.cooleshow.common.constant.SysConfigConstant.DESTROY_EXPIRED_LIVE_ROOM_MINUTE;
import static com.yonge.cooleshow.common.constant.SysConfigConstant.PRE_CREATE_LIVE_ROOM_MINUTE;

import java.time.LocalDate;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.biz.dal.dao.LiveRoomDao;
import com.yonge.cooleshow.biz.dal.entity.CourseSchedule;
import com.yonge.cooleshow.biz.dal.entity.CourseScheduleStudentPayment;
import com.yonge.cooleshow.biz.dal.entity.CourseScheduleTeacherSalary;
import com.yonge.cooleshow.biz.dal.entity.IMApiResultInfo;
import com.yonge.cooleshow.biz.dal.entity.ImRoomMessage;
import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
import com.yonge.cooleshow.biz.dal.entity.LiveRoom;
import com.yonge.cooleshow.biz.dal.entity.RoomInfoCache;
import com.yonge.cooleshow.biz.dal.entity.RoomUserInfoCache;
import com.yonge.cooleshow.biz.dal.entity.StudentAttendance;
import com.yonge.cooleshow.biz.dal.entity.TeacherAttendance;
import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
import com.yonge.cooleshow.biz.dal.enums.CourseScheduleEnum;
import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
import com.yonge.cooleshow.biz.dal.enums.OrderStatusEnum;
import com.yonge.cooleshow.biz.dal.enums.RoomTypeEnum;
import com.yonge.cooleshow.biz.dal.enums.TeacherSalaryEnum;
import com.yonge.cooleshow.biz.dal.service.CourseGroupService;
import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
import com.yonge.cooleshow.biz.dal.service.CourseScheduleStudentPaymentService;
import com.yonge.cooleshow.biz.dal.service.CourseScheduleTeacherSalaryService;
import com.yonge.cooleshow.biz.dal.service.LiveBroadcastRoomMemberService;
import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
import com.yonge.cooleshow.biz.dal.service.StudentAttendanceService;
import com.yonge.cooleshow.biz.dal.service.SysConfigService;
import com.yonge.cooleshow.biz.dal.service.SysMessageService;
import com.yonge.cooleshow.biz.dal.service.TeacherAttendanceService;
import com.yonge.cooleshow.biz.dal.service.UserOrderService;
import com.yonge.cooleshow.biz.dal.support.IMHelper;
import com.yonge.cooleshow.biz.dal.support.WrapperUtil;
import com.yonge.cooleshow.biz.dal.vo.TeacherLivingInfoVo;
import com.yonge.cooleshow.biz.dal.vo.UserOrderVo;
import com.yonge.cooleshow.common.enums.YesOrNoEnum;
import com.yonge.toolset.base.exception.BizException;
import com.yonge.toolset.base.page.PageInfo;
import com.yonge.toolset.mybatis.support.PageUtil;
import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
import com.yonge.toolset.utils.date.DateUtil;

/**
 * 直播房间与课程的关系表表(LiveRoom)表服务实现类
 *
 * @author hgw
 * @since 2022-03-18 15:41:17
 */
@Service("liveRoomService")
public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> implements LiveRoomService {
    private final static Logger log = LoggerFactory.getLogger(LiveRoomServiceImpl.class);

    @Autowired
    private SysUserFeignService sysUserFeignService;
    @Autowired
    private IMHelper imHelper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private CourseGroupService courseGroupService;
    @Autowired
    private CourseScheduleService courseScheduleService;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private UserOrderService userOrderService;
    @Autowired
    private CourseScheduleTeacherSalaryService courseScheduleTeacherSalaryService;
    @Autowired
    private CourseScheduleStudentPaymentService courseScheduleStudentPaymentService;
    @Autowired
    private StudentAttendanceService studentAttendanceService;
    @Autowired
    private TeacherAttendanceService teacherAttendanceService;
    @Autowired
    private SysMessageService sysMessageService;

    @Autowired
    private LiveBroadcastRoomMemberService liveBroadcastRoomMemberService;
    @Autowired
    private SysUserMapper sysUserMapper;
    /**
     * 生成房间UID
     *
     * <p> userId：主讲人id
     * <p> en ：房间类型
     */
    private final BiFunction<Long, RoomTypeEnum, String> GenRoomUid = (userId, en) -> String
            .join("-", COOLESHOW, en.getCode(), userId.toString(), new Date().getTime() + "");

    @Override
    public LiveRoomDao getDao() {
        return this.baseMapper;
    }

    /**
     * 分页查询当前用户关联的房间信息
     *
     * @param param 参数
     *              <p>roomType 房间类型  LIVE-直播课  TEMP-临时直播间
     *              <p>liveState 直播状态 0未开始 1已开始 2已结束
     *              <p>page 页数
     *              <p>rows 每页数量
     */
    @Override
    public PageInfo<LiveRoom> queryPageRoom(Map<String, Object> param) {
        String roomType = WrapperUtil.toStr(param, "roomType");
        Integer liveState = WrapperUtil.toInt(param, "liveState");
        Long userId = this.getSysUser().getId();
        Page<LiveRoom> pageInfo = PageUtil.getPageInfo(param);
        pageInfo.setDesc("id_");
        IPage<LiveRoom> page = this.page(pageInfo, Wrappers.<LiveRoom>lambdaQuery()
                .eq(WrapperUtil.StrPredicate.test(roomType), LiveRoom::getType, roomType)
                .eq(WrapperUtil.ObjPredicate.test(liveState), LiveRoom::getLiveState, liveState)
                .eq(LiveRoom::getRoomState, 0)
                .eq(LiveRoom::getSpeakerId, userId)
        );
        return PageUtil.pageInfo(page);
    }

    /**
     * 校验房间信息，及个人信息
     *
     * @param roomUid 房间uid
     */
    @Override
    public RoomInfoCache studentCheckRoomInfo(String roomUid) {
        return this.checkStudentRoom(roomUid, this.getSysUser());
    }

    /**
     * 主讲人查询并校验房间信息
     *
     * @param roomUid 房间uid
     * @return
     */
    @Override
    public RoomInfoCache speakerCheckRoomInfo(String roomUid) {
        Long userId = this.getSysUser().getId();
        LiveRoom liveRoom = this.getOne(Wrappers.<LiveRoom>lambdaQuery()
                .eq(LiveRoom::getRoomUid, roomUid));
        if (Objects.isNull(liveRoom)) {
            log.error("teacherCheckRoomInfo>>>live not start  roomUid: {} userId:{}", roomUid, userId);
            throw new BizException("直播还未开始!");
        }
        if (liveRoom.getLiveState().equals(2)) {
            throw new BizException("直播已结束");
        }
        //校验房间是否存在
        RBucket<RoomInfoCache> roomInfoCache = getLiveRoomInfo(roomUid);
        if (!roomInfoCache.isExists()) {
            log.error("teacherCheckRoomInfo>>>live not start  roomUid: {} userId:{}", roomUid, userId);
            throw new BizException("直播还未开始!");
        }
        RoomInfoCache roomInfo = roomInfoCache.get();
        //校验进入房间的是否是该直播间的主讲人
        if (!roomInfo.getSpeakerId().equals(userId)) {
            log.error("teacherCheckRoomInfo>>>not speaker  roomUid: {} userId:{}", roomUid, userId);
            throw new BizException("您不是该直播间的主讲人!");
        }
        roomInfo.setLikeNum(getLike(roomUid));
        roomInfo.setLookNum(getNum.apply(this::getOnlineUserCache, roomUid));
        return roomInfo;
    }

    /**
     * 定时任务创建直播间
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createCourseLiveRoom() {
        //查询房间提前创建的时间
        String preCreateRoomMinute = sysConfigService.findConfigValue(PRE_CREATE_LIVE_ROOM_MINUTE);
        if (StringUtils.isEmpty(preCreateRoomMinute)) {
            log.info("createCourseLiveRoom>>>> 未查询到配置：{}", PRE_CREATE_LIVE_ROOM_MINUTE);
            return;
        }
        //查询课时表生成直播间
        List<CourseSchedule> courseScheduleList = courseScheduleService.list(Wrappers.<CourseSchedule>lambdaQuery()
                .eq(CourseSchedule::getType, CourseScheduleEnum.LIVE.getCode())
                .in(CourseSchedule::getStatus, CourseScheduleEnum.NOT_START.getCode(), CourseScheduleEnum.ING.getCode())
                .eq(CourseSchedule::getLock, 0)
                .eq(CourseSchedule::getClassDate, LocalDate.now().toString()));
        if (CollectionUtils.isEmpty(courseScheduleList)) {
            return;
        }
        //获取课程组名称
        Map<Long, String> titleMap = new HashMap<>();
        //获取课程组介绍
        Map<Long, String> remarkMap = new HashMap<>();
        courseScheduleList.stream()
                .map(CourseSchedule::getCourseGroupId)
                .distinct()
                .map(courseGroupService::getById)
                .filter(Objects::nonNull)
                .forEach(courseGroup -> {
                    titleMap.put(courseGroup.getId(), courseGroup.getName());
                    remarkMap.put(courseGroup.getId(), courseGroup.getCourseIntroduce());
                });
        //生成课程对应的房间
        RoomTypeEnum en = RoomTypeEnum.LIVE;
        Date now = new Date();
        courseScheduleList.forEach(c -> {
            //课程开始时间-提前创建时间 = 创建房间时间
            Date createRoomTime = DateUtil.addMinutes(c.getStartTime(), -Integer.parseInt(preCreateRoomMinute));
            if (now.getTime() < createRoomTime.getTime()) {
                return;
            }
            //避免重复创建直播间
            int count = this.count(Wrappers.<LiveRoom>lambdaQuery().eq(LiveRoom::getCourseId, c.getId()));
            if (count > 0) {
                return;
            }
            LiveRoom room = new LiveRoom();
            room.setCourseGroupId(c.getCourseGroupId());
            room.setCourseId(c.getId());
            room.setRoomUid(GenRoomUid.apply(c.getTeacherId(), en));
            room.setRoomTitle(titleMap.get(c.getCourseGroupId()));
            room.setLiveRemark(remarkMap.get(c.getCourseGroupId()));
            room.setSpeakerId(c.getTeacherId());
            room.setLiveStartTime(c.getStartTime());
            room.setLiveEndTime(c.getEndTime());
            room.setLiveState(1);
            room.setRoomState(0);
            room.setType(en.getCode());
            room.setCreatedBy(-2L);
            room.setCreatedTime(now);
            this.save(room);
            //去融云创建房间及创建房间缓存信息
            this.createLiveRoomInfo(room);
            //开课提醒
            this.pushLiveCreateRoom(room);
        });
    }

    /**
     * 开课提醒
     */
    private void pushLiveCreateRoom(LiveRoom room) {
        log.info("pushLiveCreateRoom JIGUANG start param : {}", JSONObject.toJSONString(room));
        try {
            //查询老师信息
            SysUser teacherInfo = this.getSysUser(room.getSpeakerId());
            //极光-消息推送
            Map<Long, String> teacherMap = new HashMap<>();
            teacherMap.put(teacherInfo.getId(), teacherInfo.getPhone());
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.LIVE_REMINDER,
                    teacherMap, null, 0, null, ClientEnum.TEACHER.getCode(),
                    teacherInfo.getUsername(), room.getRoomTitle());
            log.info("pushLiveCreateRoom JIGUANG ok ");
        } catch (Exception e) {
            log.error("pushLiveCreateRoom JIGUANG error param : {}", JSONObject.toJSONString(room));
            log.error("pushLiveCreateRoom JIGUANG error", e.getCause());
        }
    }

    /**
     * 创建临时房间-直播间
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createTempLiveRoom(Map<String, Object> param) {
        //查询主讲人信息
        SysUser sysUser = this.getSysUser();
        Long teacherId = sysUser.getId();
        List<LiveRoom> liveRoomList = this.list(Wrappers.<LiveRoom>lambdaQuery()
                .eq(LiveRoom::getSpeakerId, teacherId)
                .eq(LiveRoom::getType, RoomTypeEnum.TEMP.getCode())
                .eq(LiveRoom::getLiveState, 1)
                .eq(LiveRoom::getRoomState, 0)
        );
        if (CollectionUtils.isNotEmpty(liveRoomList)) {
            throw new BizException("当前有直播间未结束，无需创建直播！");
        }
        String roomTitle = WrapperUtil.toStr(param, "roomTitle", "房间标题不能为空!");
        String liveRemark = WrapperUtil.toStr(param, "liveRemark", "直播间描述不能为空!");
        Integer liveTime = WrapperUtil.toInt(param, "liveTime", "请选择直播时长!");
        String coverPic = WrapperUtil.toStr(param, "coverPic", "直播封面图不能为空!");
        if (roomTitle.length() > 20) {
            throw new BizException("直播标题不能超出20个字");
        }
        Date liveStartTime = new Date();
        Date liveEndTime = DateUtil.addMinutes(liveStartTime, liveTime);
        //当前时间有课程则不能开启直播
        boolean check = courseScheduleService.checkTeacherCourseTime(teacherId, liveStartTime, liveEndTime);
        if (check) {
            //如果当前时间和课程时间有交集则不能开启临时直播
            throw new BizException("当前选择的时间段有课无法开启直播");
        }
        RoomTypeEnum en = RoomTypeEnum.TEMP;
        String roomUid = GenRoomUid.apply(teacherId, en);
        LiveRoom room = new LiveRoom();
        room.setCourseGroupId(-1L);
        room.setCourseId(-1L);
        room.setRoomUid(roomUid);
        room.setRoomTitle(roomTitle);
        room.setLiveRemark(liveRemark);
        room.setSpeakerId(teacherId);
        room.setLiveStartTime(liveStartTime);
        room.setLiveEndTime(liveEndTime);
        room.setLiveState(1);
        room.setRoomState(0);
        room.setCreatedBy(teacherId);
        room.setCreatedTime(liveStartTime);
        room.setType(en.getCode());
        room.setCoverPic(coverPic);
        this.save(room);
        log.info("createTempLiveRoom>>>>>>room:{}", room.getRoomUid());
        //去融云创建房间及创建房间缓存信息
        this.createLiveRoomInfo(room, sysUser);
        //写入临时直播间列表
        RMap<Long, String> map = redissonClient.getMap(TEACHER_TEMP_LIVE_ROOM);
        map.put(teacherId, roomUid);
        return roomUid;
    }

    /**
     * 去融云创建房间及创建房间缓存信息-直播间
     */
    private void createLiveRoomInfo(LiveRoom room) {
        //查询主讲人信息
        SysUser sysUser = this.getSysUser(room.getSpeakerId());
        this.createLiveRoomInfo(room, sysUser);
    }

    private void createLiveRoomInfo(LiveRoom room, SysUser sysUser) {
        try {
            //生成主讲人信息
            this.createRoomInfoCache(room, sysUser);
            //去融云创建房间
            this.createLiveRoom(room.getRoomUid(), room.getRoomTitle());
        } catch (Exception e) {
            throw new BizException("创建直播间失败!", e.getCause());
        }
    }

    //生成主讲人信息
    private void createRoomInfoCache(LiveRoom room, SysUser sysUser) {
        RoomInfoCache roomCache = new RoomInfoCache();
        roomCache.setSpeakerId(sysUser.getId());
        roomCache.setSpeakerName(sysUser.getUsername());
        roomCache.setSpeakerPic(sysUser.getAvatar());
        roomCache.setSpeakerState(1);
        roomCache.setRoomUid(room.getRoomUid());
        roomCache.setRoomTitle(room.getRoomTitle());
        roomCache.setLiveRemark(room.getLiveRemark());
        roomCache.setLiveStartTime(room.getLiveStartTime());
        roomCache.setLiveEndTime(room.getLiveEndTime());
        roomCache.setCreateRoomTime(new Date());
        String configValue = sysConfigService.findConfigValue(PRE_CREATE_LIVE_ROOM_MINUTE);
        String expireCloseMinute = sysConfigService.findConfigValue(DESTROY_EXPIRED_LIVE_ROOM_MINUTE);
        roomCache.setExpiredMinute(Integer.parseInt(configValue));
        roomCache.setExpiredCloseMinute(Integer.parseInt(expireCloseMinute));
        roomCache.setRoomType(room.getType());
        roomCache.setCourseId(room.getCourseId());
        roomCache.setCourseGroupId(room.getCourseGroupId());

        //写入房间信息
        getLiveRoomInfo(room.getRoomUid()).set(roomCache, 2L, TimeUnit.DAYS);
        //生成0点赞
        redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, room.getRoomUid()))
                .set(0, 2L, TimeUnit.DAYS);
        //生成房间人员列表数据
        redissonClient.getMap(LIVE_ROOM_TOTAL_USER_LIST.replace(ROOM_UID, room.getRoomUid()))
                .expire(2L, TimeUnit.DAYS);
    }

    /**
     * 创建房间-聊天室
     *
     * @param roomId   房间Uid
     * @param roomName 房间名称
     */
    private void createLiveRoom(String roomId, String roomName) {
        IMApiResultInfo resultInfo;
        try {
            resultInfo = imHelper.createChatRoom(roomId, roomName);
        } catch (Exception e) {
            throw new BizException("创建聊天室失败!" + e.getMessage());
        }
        if (!resultInfo.isSuccess()) {
            throw new BizException("创建聊天室失败!" + resultInfo.getErrorMessage());
        }
        log.info("create chatRoom success: {}", roomId);
    }

    /**
     * 定时任务-清理过期的房间
     */
    public void destroyExpiredLiveRoom() {
        //查询房间过期时间
        String expiredMinuteStr = sysConfigService.findConfigValue(DESTROY_EXPIRED_LIVE_ROOM_MINUTE);
        if (StringUtils.isEmpty(expiredMinuteStr)) {
            log.info("roomDestroy>>>> 未查询到配置：{}", DESTROY_EXPIRED_LIVE_ROOM_MINUTE);
            return;
        }
        Date now = new Date();
        //查询已经开始并且没有删除及销毁的直播间
        List<LiveRoom> list = this.list(Wrappers.<LiveRoom>lambdaQuery()
                .eq(LiveRoom::getRoomState, 0)
                .eq(LiveRoom::getLiveState, 1)
                .le(LiveRoom::getLiveEndTime, now));
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        list.forEach(room -> {
            //直播间过期时间
            Date expiredDate = room.getLiveEndTime();
            if (room.getType().equals(RoomTypeEnum.LIVE.getCode())) {
                //如果是直播课，那么结束时间 = 直播间结束时间 + 设置的过期分钟数
                expiredDate = DateUtil.addMinutes(room.getLiveEndTime(), Integer.parseInt(expiredMinuteStr));
            }
            //当前时间 大于 直播间过期时间
            if (now.getTime() >= expiredDate.getTime()) {
                this.destroyLiveRoom(room);
            }
        });
    }

    /**
     * 关闭直播间
     *
     * @param roomUId 房间uid
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void destroyLiveRoom(String roomUId) {
        LiveRoom liveRoom = this.getOne(Wrappers.<LiveRoom>lambdaQuery().eq(LiveRoom::getRoomUid, roomUId));
        this.destroyLiveRoom(liveRoom);
    }

    /**
     * 关闭直播间
     *
     * @param room 房间信息
     */
    private void destroyLiveRoom(LiveRoom room) {
        if (Objects.isNull(room)) {
            return;
        }
        //直播课
        if (room.getType().equals(RoomTypeEnum.LIVE.getCode())) {
            //查询老师分润表
            List<CourseScheduleTeacherSalary> salaryList = courseScheduleTeacherSalaryService.list(Wrappers.<CourseScheduleTeacherSalary>lambdaQuery()
                    .eq(CourseScheduleTeacherSalary::getCourseScheduleId, room.getCourseId())
            );
            if (CollectionUtils.isEmpty(salaryList)) {
                return;
            }
            salaryList.forEach(salary -> {
                //修改教师课酬状态-已结算
                salary.setStatus(TeacherSalaryEnum.WAIT.getCode());
                courseScheduleTeacherSalaryService.updateById(salary);
            });
        }
        //修改房间状态
        room.setLiveState(2);
        // room.setRoomState(1);
        this.updateById(room);

        String speakerIdStr = room.getSpeakerId().toString();
        String roomUid = room.getRoomUid();

        //关闭直播间发送消息
        ImRoomMessage message = new ImRoomMessage();
        message.setFromUserId(speakerIdStr);
        message.setToChatroomId(roomUid);
        message.setObjectName(ImRoomMessage.FORCED_OFFLINE);
        message.setContent(speakerIdStr);
        try {
            this.publishRoomMessage(message);
            log.info("destroyLiveRoom>>>> FORCED_OFFLINE : {}", speakerIdStr);
        } catch (Exception e) {
            log.error("destroyLiveRoom>>>>  FORCED_OFFLINE {}", e.getMessage());
        }

        Date now = new Date();
        //删除人员与房间关联关系缓存的方法
        Consumer<String> deleteUserRoomCache = (id) -> redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, id)).deleteAsync();
        //给老师签退
        teacherAttendanceService.update(Wrappers.<TeacherAttendance>lambdaUpdate()
                .eq(TeacherAttendance::getTeacherId, room.getSpeakerId())
                .eq(TeacherAttendance::getCourseScheduleId, room.getCourseId())
                .set(TeacherAttendance::getSignOutTime, now));
        //删除老师与房间关联关系
        deleteUserRoomCache.accept(speakerIdStr);
        //获取在线人员信息
        RMap<Long, String> onlineUserCache = this.getOnlineUserCache(roomUid);
        onlineUserCache.forEach((id, s) -> {
            //删除观看者对应直播间编号的缓存
            deleteUserRoomCache.accept(id.toString());
            //观看者签退
            studentAttendanceService.update(Wrappers.<StudentAttendance>lambdaUpdate()
                    .eq(StudentAttendance::getStudentId, id)
                    .eq(StudentAttendance::getCourseScheduleId, room.getCourseId())
                    .set(StudentAttendance::getSignOutTime, now));
        });
        //删除房间在线人员缓存
        onlineUserCache.deleteAsync();
        //删除房间信息
        this.getLiveRoomInfo(roomUid).deleteAsync();
        //删除点赞数
        redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).deleteAsync();
        //删除当前主讲人最后一次进入房间的ip
        redissonClient.getBucket(LIVE_USER_LAST_CLIENT_IP.replace(ROOM_UID, roomUid).replace(USER_ID, speakerIdStr)).deleteAsync();
        //删除当前主讲人最后一次心跳
        redissonClient.getBucket(LIVE_ROOM_HEART_BEAT.replace(ROOM_UID, roomUid)).deleteAsync();
        //删除房间全部人员缓存
        this.getTotalUserCache(roomUid).deleteAsync();
        //删除该临时直播间列表
        RMap<Long, String> map = redissonClient.getMap(TEACHER_TEMP_LIVE_ROOM);
        map.remove(room.getSpeakerId());
    }

    /**
     * 销毁房间-聊天室
     *
     * @param roomId 房间Uid
     */
    private void ImDestroyLiveRoom(String roomId) {
        try {
            //删除服务器房间
            IMApiResultInfo resultInfo = imHelper.deleteChrm(Collections.singletonList(roomId));
            if (!resultInfo.isSuccess()) {
                log.error("destroyLiveRoom error" + resultInfo.getErrorMessage());
            }
            log.info("destroyLiveRoom success: {}", roomId);
        } catch (Exception e) {
            log.error("destroyLiveRoom error" + e.getMessage());
        }
    }

    /**
     * <p>主讲人处理进入和退出房间数据
     * <p>观看者只处理退出房间数据
     *
     * @param userState 用户状态数据
     */
    public void opsRoom(List<ImUserStateSync> userState) {
        if (CollectionUtils.isEmpty(userState)) {
            return;
        }
        userState.forEach(user -> {
            log.info("opsRoom>>>> {}", JSONObject.toJSONString(user));
            if (StringUtils.isBlank(user.getStatus())) {
                return;
            }
            String userIdStr = user.getUserid();
            //获取当前用户所在房间的uid
            RBucket<String> userRoom = redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userIdStr));
            if (!userRoom.isExists()) {
                return;
            }
            String roomUid = userRoom.get();

            Date now = new Date();
            //获取当前用户状态变更的时间
            long userStateTime = Optional.ofNullable(user.getTime()).orElse(now.getTime());
            RBucket<Long> userStateTimeCache = redissonClient.getBucket(LIVE_USER_LAST_TIME.replace(USER_ID, userIdStr));
            if (userStateTimeCache.isExists()) {
                //缓存的时间比当前传入时间大则放弃这条数据
                long cacheTime = userStateTimeCache.get();
                if (cacheTime > userStateTime) {
                    return;
                }
            }
            //将最新的时间写入缓存
            userStateTimeCache.set(userStateTime, 60L, TimeUnit.MINUTES);
            //根据房间号获取房间信息
            RBucket<RoomInfoCache> roomInfoCache = this.getLiveRoomInfo(roomUid);
            if (!roomInfoCache.isExists()) {
                return;
            }
            RoomInfoCache roomInfo = roomInfoCache.get();
            // 查询userId是不是主讲人 ,如果是主讲人则返回
            if (roomInfo.getSpeakerId().toString().equals(userIdStr)) {
                this.opsSpeaker(roomInfoCache, user, now, userIdStr);
                return;
            }
            //这里开始只处理观看者的数据，观看者只接受退出消息 status=0 是进入房间
            if (user.getStatus().equals("0")) {
                return;
            }
            //用户id
            Long userId = Long.valueOf(userIdStr);
            //从房间累计用户信息中查询该用户的信息
            RMap<Long, String> roomTotalUser = this.getTotalUserCache(roomUid);
            //该房间未查询到用户数据则不处理
            if (!roomTotalUser.isExists()) {
                return;
            }
            if (!roomTotalUser.containsKey(userId)) {
                return;
            }
            //查询用户数据
            RoomUserInfoCache userInfo = JSONObject.toJavaObject(JSONObject.parseObject(roomTotalUser.get(userId)), RoomUserInfoCache.class);
            userInfo.setLastOutTime(now);
            String userJsonStr = JSONObject.toJSONString(userInfo);
            roomTotalUser.fastPut(userId, userJsonStr);
            //查询在线人员列表
            RMap<Long, String> onlineUserInfo = this.getOnlineUserCache(roomUid);
            if (!onlineUserInfo.isExists()) {
                return;
            }
            liveBroadcastRoomMemberService.saveRecord(roomUid,userId, InOrOutEnum.OUT, YesOrNoEnum.NO);
            //从在线人员列表删除该人员
            onlineUserInfo.fastRemove(userId);
            //学员退出 写学生考勤表
            studentAttendanceService.update(Wrappers.<StudentAttendance>lambdaUpdate()
                    .eq(StudentAttendance::getStudentId, userId)
                    .eq(StudentAttendance::getCourseScheduleId, roomInfo.getCourseId())
                    .set(StudentAttendance::getSignOutTime, now));
            //向直播间发送当前在线人数消息
            this.sendOnlineUserCount(roomUid, userId, onlineUserInfo.size());
            log.info("opsRoom>>>> looker userInfo: {}", userJsonStr);
            //用户离开直播间发送退出房间消息给主讲人
            ImRoomMessage message = new ImRoomMessage();
            message.setFromUserId(userId.toString());
            message.setToChatroomId(roomUid);
            message.setObjectName(ImRoomMessage.RC_CHATROOM_LEAVE);
            message.setContent(userId);
            try {
                this.publishRoomMessage(message);
                log.info("opsRoom>>>> looker RC_CHATROOM_LEAVE : {}", userJsonStr);
            } catch (Exception e) {
                log.error("opsRoom>>>>  looker error RC_CHATROOM_LEAVE {}", e.getMessage());
                log.error("opsRoom>>>>  looker error sendMessage {} : RC_CHATROOM_LEAVE : {}", message, userJsonStr);
            }
        });

    }

    /**
     * 主讲人
     */
    private void opsSpeaker(RBucket<RoomInfoCache> roomInfoCache, ImUserStateSync user, Date now, String userIdStr) {
        RoomInfoCache roomInfo = roomInfoCache.get();
        String roomUid = roomInfo.getRoomUid();
        //进退房间写老师考勤表
        TeacherAttendance teacherAttendance = teacherAttendanceService.getOne(Wrappers.<TeacherAttendance>lambdaQuery()
                .eq(TeacherAttendance::getTeacherId, userIdStr)
                .eq(TeacherAttendance::getCourseScheduleId, roomInfo.getCourseId()));

        //最后一次进入房间的clientIp
        RBucket<String> lastClientIp = redissonClient.getBucket(LIVE_USER_LAST_CLIENT_IP.replace(ROOM_UID, roomUid).replace(USER_ID, userIdStr));
        //主讲人进入房间
        if (user.getStatus().equals("0")) {
            roomInfo.setSpeakerState(0);
            roomInfo.setJoinRoomTime(now);
            //将本次进入房间的clientIp添加到主讲人最后一次clientIp缓存中
            if (StringUtils.isNotBlank(user.getClientIp())) {
                lastClientIp.set(user.getClientIp());
            }
            //查询老师是否有进入过放假，没有则写老师考勤表的进入时间
            if (Objects.isNull(teacherAttendance)) {
                this.setTeacherAttendance(Long.parseLong(userIdStr), roomInfo.getCourseGroupId(), roomInfo.getCourseId());
            }
            roomInfoCache.set(roomInfo);
            log.info("opsRoom>>>> join speakerCache {}", JSONObject.toJSONString(roomInfo));
            return;
        }
        //退出房间 - 校验本次退出直播间的clientIp 是不是上次进入房间的clientIp
        if (StringUtils.isNotBlank(user.getClientIp())) {
            if (lastClientIp.isExists()) {
                //如果是上次进入房间的clientIp和本次退出房间的clientIp不相同，则直接忽略
                if (!user.getClientIp().equals(lastClientIp.get())) {
                    return;
                }
            }
        }
        //如果退出时间大于进入时间就无需再次退出-直接返回
        if (compareDate.test(roomInfo.getExitRoomTime(), roomInfo.getJoinRoomTime())) {
            return;
        }

        roomInfo.setSpeakerState(1);
        roomInfo.setExitRoomTime(now);
        if (Objects.isNull(teacherAttendance)) {
            teacherAttendance = new TeacherAttendance();
            teacherAttendance.setTeacherId(Long.parseLong(userIdStr));
            teacherAttendance.setCourseGroupType(CourseScheduleEnum.LIVE.getCode());
            teacherAttendance.setCourseGroupId(roomInfo.getCourseGroupId());
            teacherAttendance.setCourseScheduleId(roomInfo.getCourseId());
            teacherAttendance.setSignInTime(now);
            teacherAttendance.setSignOutTime(now);
            teacherAttendance.setCreateTime(now);
            teacherAttendanceService.save(teacherAttendance);
        } else {
            //修改老师考勤表
            teacherAttendance.setSignOutTime(now);
            teacherAttendance.setUpdateTime(now);
            teacherAttendanceService.updateById(teacherAttendance);
        }
        log.info("opsRoom>>>> exit speakerCache {}", JSONObject.toJSONString(roomInfo));
        roomInfoCache.set(roomInfo);
    }

    /**
     * 对比时间,2个时间一个为空返回false,否则对比时间大小
     * <p>- date1 时间1
     * <p>- date2 时间2
     */
    private final BiPredicate<Date, Date> compareDate = (date1, date2) -> {
        if (Objects.nonNull(date1) && Objects.nonNull(date2)) {
            return date1.getTime() > date2.getTime();
        } else {
            return false;
        }
    };

    /**
     * 同步点赞数量
     *
     * @param roomUid 房间uid
     * @param likeNum 点赞数
     */
    @Override
    public void syncLike(String roomUid, Integer likeNum) {
        redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).set(likeNum);
        //增加房间心跳
        redissonClient.getBucket(LIVE_ROOM_HEART_BEAT.replace(ROOM_UID, roomUid)).set(new Date());
    }

    /**
     * 主讲人-进入房间
     *
     * @param roomUid 房间uid
     */
    public RoomInfoCache speakerJoinRoom(String roomUid) {
        RoomInfoCache roomInfo = this.speakerCheckRoomInfo(roomUid);
        Date now = new Date();
        roomInfo.setSpeakerState(0);
        roomInfo.setJoinRoomTime(now);
        liveBroadcastRoomMemberService.saveRecord(roomUid, roomInfo.getSpeakerId(), InOrOutEnum.IN, YesOrNoEnum.NO);
        //查询老师是否有进入过，没有则写老师考勤表的进入时间
        this.setTeacherAttendance(roomInfo.getSpeakerId(), roomInfo.getCourseGroupId(), roomInfo.getCourseId());
        //记录当前用户对应的房间uid
        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, roomInfo.getSpeakerId().toString())).set(roomUid, 2L, TimeUnit.DAYS);
        return roomInfo;
    }

    /**
     * 观看者-进入房间
     *
     * @param roomUid 房间uid
     * @param userId  用户id 观看者id
     */
    public RoomInfoCache joinRoom(String roomUid, Long userId) {
        Optional.ofNullable(roomUid).orElseThrow(() -> new BizException("房间编号不能为空！"));
        Optional.ofNullable(userId).orElseThrow(() -> new BizException("人员编号不能为空！"));
        //获取进入房间人员信息
        SysUser sysUser = this.getSysUser(userId);
        //校验信息
        RoomInfoCache roomInfo = this.checkStudentRoom(roomUid, sysUser);
        Date now = new Date();
        //房间累计用户信息-指只要进入到该房间的用户都要记录
        RMap<Long, String> roomTotalUser = this.getTotalUserCache(roomUid);
        //判断是否第一次进房间
        RoomUserInfoCache userInfo;
        liveBroadcastRoomMemberService.saveRecord(roomUid,userId, InOrOutEnum.IN, YesOrNoEnum.YES);
        if (roomTotalUser.containsKey(userId)) {
            //多次进入更新动态进入时间
            userInfo = JSONObject.toJavaObject(JSONObject.parseObject(roomTotalUser.get(userId)), RoomUserInfoCache.class);
        } else {
            //第一次进该房间 写入用户首次进入时间
            userInfo = new RoomUserInfoCache();
            userInfo.setUserId(sysUser.getId());
            userInfo.setUserName(sysUser.getRealName());
            userInfo.setFirstJoinTime(now);
        }
        //查询学生是否有进入过，没有则写学生考勤表的进入时间
        this.setStudentAttendance(userId, roomInfo.getCourseGroupId(), roomInfo.getCourseId());
        userInfo.setDynamicJoinTime(now);
        //用户json信息
        String userJsonStr = JSONObject.toJSONString(userInfo);
        //写入人员缓存
        roomTotalUser.fastPut(userId, userJsonStr);
        //写入在线人员缓存
        RMap<Long, String> onlineUserCache = this.getOnlineUserCache(roomUid);
        onlineUserCache.fastPut(userId, userJsonStr);
        log.info("joinRoom>>>> userInfo: {}", userJsonStr);
        //向直播间发送当前在线人数消息
        this.sendOnlineUserCount(roomUid, userId, onlineUserCache.size());
        log.info("join sendOnlineUserCount>>>> param is null   roomUid: {}  fromUserId:{}  count:{}", roomUid, userId, onlineUserCache.size());
        log.info("joinRoom>>>> userInfo: {}", userJsonStr);
        //记录当前用户对应的房间uid
        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userId.toString())).set(roomUid, 2L, TimeUnit.DAYS);
        return roomInfo;
    }

    //校验学生与房间的关系
    private RoomInfoCache checkStudentRoom(String roomUid, SysUser sysUser) {
        //校验房间是否存在
        RBucket<RoomInfoCache> roomInfoCache = this.getLiveRoomInfo(roomUid);
        if (!roomInfoCache.isExists()) {
            throw new BizException("直播还未开始或已结束!");
        }
        RoomInfoCache roomInfo = roomInfoCache.get();
        //校验观看者是否买过课,如果是非临时直播间，则校验是否已经买过课
        if (!roomInfo.getRoomType().equals(RoomTypeEnum.TEMP.getCode())) {
            // 查询该学员成功购买课程
            CourseScheduleStudentPayment studentPayment = courseScheduleStudentPaymentService.getOne(Wrappers.<CourseScheduleStudentPayment>lambdaQuery()
                    .eq(CourseScheduleStudentPayment::getUserId, sysUser.getId())
                    .eq(CourseScheduleStudentPayment::getCourseId, roomInfo.getCourseId())
                    .eq(CourseScheduleStudentPayment::getCourseGroupId, roomInfo.getCourseGroupId())
                    .eq(CourseScheduleStudentPayment::getCourseType, CourseScheduleEnum.LIVE.getCode()));
            if (Objects.nonNull(studentPayment)) {
                UserOrderVo detail = userOrderService.detail(studentPayment.getOrderNo(), studentPayment.getUserId());
                if (Objects.isNull(detail)) {
                    throw new BizException("您还未购买该课程.");
                }
                //不等于已支付的都是异常
                if (detail.getStatus().equals(OrderStatusEnum.CLOSE) || detail.getStatus().equals(OrderStatusEnum.FAIL)) {
                    throw new BizException("您还未购买该课程。");
                }
                if (detail.getStatus().equals(OrderStatusEnum.WAIT_PAY) || detail.getStatus().equals(OrderStatusEnum.PAYING)) {
                    throw new BizException("该课程的订单" + detail.getStatus().getMsg() + "，请先支付完成后再上课。");
                }
            } else {
                throw new BizException("您还未购买该课程!");
            }
        }
        //刷新当前房间的点赞及观看人数信息
        roomInfo.setLikeNum(getLike(roomUid));
        roomInfo.setLookNum(getNum.apply(this::getOnlineUserCache, roomUid));
        return roomInfo;
    }

    /**
     * 查询学生是否有进入过，没有则写学生考勤表的进入时间
     *
     * @param studentId     学生id
     * @param courseGroupId 课程组id
     * @param courseId      课程id
     */
    private void setStudentAttendance(Long studentId, Long courseGroupId, Long courseId) {
        //写学生第一次进入时间
        StudentAttendance studentAttendance = studentAttendanceService.getOne(Wrappers.<StudentAttendance>lambdaQuery()
                .eq(StudentAttendance::getStudentId, studentId)
                .eq(StudentAttendance::getCourseScheduleId, courseId));
        if (Objects.isNull(studentAttendance)) {
            Date now = new Date();
            studentAttendance = new StudentAttendance();
            studentAttendance.setCourseGroupType(CourseScheduleEnum.LIVE.getCode());
            studentAttendance.setCourseGroupId(courseGroupId);
            studentAttendance.setCourseScheduleId(courseId);
            studentAttendance.setStudentId(studentId);
            studentAttendance.setSignInTime(now);
            studentAttendance.setCreateTime(now);
            studentAttendanceService.save(studentAttendance);
            log.info("SignInTime >>>  user {} courseId {}", studentId, courseId);
        }
    }

    /**
     * 查询老师是否有进入过，没有则写老师考勤表的进入时间
     *
     * @param teacherId     老师id
     * @param courseGroupId 课程组id
     * @param courseId      课程id
     */
    private void setTeacherAttendance(Long teacherId, Long courseGroupId, Long courseId) {
        TeacherAttendance teacherAttendance = teacherAttendanceService.getOne(Wrappers.<TeacherAttendance>lambdaQuery()
                .eq(TeacherAttendance::getTeacherId, teacherId)
                .eq(TeacherAttendance::getCourseScheduleId, courseId));
        if (Objects.isNull(teacherAttendance)) {
            Date now = new Date();
            teacherAttendance = new TeacherAttendance();
            teacherAttendance.setTeacherId(teacherId);
            teacherAttendance.setCourseGroupType(CourseScheduleEnum.LIVE.getCode());
            teacherAttendance.setCourseGroupId(courseGroupId);
            teacherAttendance.setCourseScheduleId(courseId);
            teacherAttendance.setSignInTime(now);
            teacherAttendance.setCreateTime(now);
            teacherAttendanceService.save(teacherAttendance);
        }
    }

    /**
     * 向直播间发送当前在线人数消息
     *
     * @param roomUid    房间uid
     * @param fromUserId 发送人id
     * @param count      人数
     */
    private void sendOnlineUserCount(String roomUid, Long fromUserId, Integer count) {
        //校验传入参数，房间uid和发送人id不能为空
        if (!WrapperUtil.checkObj(roomUid, fromUserId, count)) {
            log.info(" sendOnlineUserCount>>>> param is null   roomUid: {}  fromUserId:{}  count:{}", roomUid, fromUserId, count);
            return;
        }
        ImRoomMessage message = new ImRoomMessage();
        message.setObjectName(ImRoomMessage.MEMBER_COUNT);
        message.setToChatroomId(roomUid);
        HashMap<String, Integer> sendMap = new HashMap<>();
        sendMap.put("count", count);
        message.setFromUserId(fromUserId.toString());
        message.setContent(sendMap);
        //发送消息
        try {
            this.publishRoomMessage(message);
            log.info("sendOnlineUserCount>>>> message: {}", JSONObject.toJSONString(message));
        } catch (Exception e) {
            log.error("sendOnlineUserCount>>>> error {}", e.getMessage());
            log.error("sendOnlineUserCount>>>> sendMessage {} :", JSONObject.toJSONString(message));
        }
    }

    /**
     * 发送消息
     *
     * @param message
     */
    @Override
    public void publishRoomMessage(ImRoomMessage message) {
        String msgStr = JSONObject.toJSONString(message);
        log.info("publishRoomMessage message : {}", msgStr);
        IMApiResultInfo resultInfo;
        try {
            resultInfo = imHelper.publishRoomMessage(message.getFromUserId(), message.getToChatroomId(), message);
        } catch (Exception e) {
            throw new BizException("消息发送失败" + e.getMessage());
        }
        if (!resultInfo.isSuccess()) {
            throw new BizException("消息发送失败!" + resultInfo.getErrorMessage());
        }
        log.info("publishRoomMessage success: {}", msgStr);
    }

    private SysUser getSysUser(Long userId) {
        return Optional.ofNullable(userId)
                .map(sysUserFeignService::queryUserById)
                .orElseThrow(() -> new BizException("用户不存在"));
    }

    private SysUser getSysUser() {
        return Optional.ofNullable(sysUserFeignService.queryUserInfo())
                .orElseThrow(() -> new BizException("用户不存在"));
    }

    /**
     * 查询在观看直播的用户信息
     *
     * @param roomUid 直播间uid
     */
    private List<RoomUserInfoCache> getTotalUserInfo(String roomUid) {
        RMap<Long, String> roomTotalUser = getTotalUserCache(roomUid);
        return roomTotalUser.values().parallelStream()
                .map(a -> JSONObject.toJavaObject(JSONObject.parseObject(a), RoomUserInfoCache.class))
                .collect(Collectors.toList());
    }

    /**
     * 获取直播间所有用户信息缓存
     *
     * @param roomUid 直播间uid
     * @return RMap key:userId value:RoomUserInfoCache -> jsonToString
     */
    private RMap<Long, String> getTotalUserCache(String roomUid) {
        return redissonClient.getMap(LIVE_ROOM_TOTAL_USER_LIST.replace(ROOM_UID, roomUid));
    }

    /**
     * 查询在观看直播的用户信息
     *
     * @param roomUid 直播间uid
     */
    private List<RoomUserInfoCache> getOnlineUserInfo(String roomUid) {
        RMap<Long, String> roomTotalUser = getOnlineUserCache(roomUid);
        return roomTotalUser.values().parallelStream()
                .map(a -> JSONObject.toJavaObject(JSONObject.parseObject(a), RoomUserInfoCache.class))
                .collect(Collectors.toList());
    }

    /**
     * 获取直播间在线用户信息缓存
     *
     * @param roomUid 直播间uid
     * @return RMap key:userId value:BaseRoomUserVo -> jsonToString
     */
    private RMap<Long, String> getOnlineUserCache(String roomUid) {
        return redissonClient.getMap(LIVE_ROOM_ONLINE_USER_LIST.replace(ROOM_UID, roomUid));
    }

    /**
     * 获取房间点赞数
     *
     * @param roomUid 房间UId
     * @return 点赞数
     */
    private int getLike(String roomUid) {
        //点赞数
        Object like = redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).get();
        if (Objects.isNull(like)) {
            return 0;
        }
        return (int) like;
    }

    /**
     * 获取房间缓存中的用户数量/观看人数
     *
     * <p> func：查询用户数量/观看人数的方法
     * <p> roomUid ：房间uid
     * <p> return ：用户数量/观看人数
     */
    private final BiFunction<Function<String, RMap<Long, ?>>, String, Integer> getNum = (func, roomUid) -> Optional.of(roomUid)
            .map(func)
            .filter(RMap::isExists)
            .map(RMap::size)
            .orElse(0);

    /**
     * 获取房间信息
     *
     * @param roomUid 房间编号
     */
    private RBucket<RoomInfoCache> getLiveRoomInfo(String roomUid) {
        return redissonClient.getBucket(LIVE_ROOM_INFO.replace(ROOM_UID, roomUid));
    }

    /**
     * 方便测试观察房间数据的方法
     */
    public Map<String, Object> test(String roomUid) {
        //result
        Map<String, Object> result = new HashMap<>();
        //获取房间信息
        RBucket<RoomInfoCache> speakerCache = this.getLiveRoomInfo(roomUid);
        if (speakerCache.isExists()) {
            result.put("房间信息信息", speakerCache.get());
        } else {
            result.put("房间信息", "房间信息不存在");
        }

        //校验房间心跳是否过期没续租
        RBucket<Date> lastRoomHeartbeatCache = redissonClient.getBucket(LIVE_ROOM_HEART_BEAT.replace(ROOM_UID, roomUid));
        if (lastRoomHeartbeatCache.isExists()) {
            result.put("房间心跳", DateUtil.dateToString(lastRoomHeartbeatCache.get(), DateUtil.EXPANDED_DATE_TIME_FORMAT));
        } else {
            result.put("房间心跳", "房间心跳不存在");
        }

        //点赞数
        Object like = redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).get();
        if (Objects.isNull(like)) {
            like = 0;
        }
        result.put("点赞数", like);

        int totalLook = 0;
        int look = 0;

        //累计总观看的用户数量
        List<RoomUserInfoCache> totalUserInfo = this.getTotalUserInfo(roomUid);
        if (CollectionUtils.isNotEmpty(totalUserInfo)) {
            totalLook = totalUserInfo.size();
            result.put("总人员数据", totalUserInfo);
        } else {
            result.put("总人员数据", "没有人员数据");
        }
        result.put("总观看人数", totalLook);
        //正在房间观看的用户数据
        List<RoomUserInfoCache> onlineUserInfo = this.getOnlineUserInfo(roomUid);
        if (CollectionUtils.isNotEmpty(onlineUserInfo)) {
            look = onlineUserInfo.size();
            result.put("正在观看的人员信息", onlineUserInfo);
        } else {
            result.put("正在观看的人员信息", "没有正在观看的人员数据");
        }
        result.put("实时观看数", look);
        return result;
    }

    @Override
    public LiveRoom getLiveInRoomBySpeakerId(Long speakerId) {
        return baseMapper.getLiveInRoomBySpeakerId(speakerId);
    }

	@Override
	public List<TeacherLivingInfoVo> queryTeacherLivingList() {
		return baseMapper.queryTeacherLivingList();
	}
    @Override
    public void whetherMic(String roomUid, Integer whetherMic) {
        //校验房间是否存在
        RBucket<RoomInfoCache> roomInfoCache = getLiveRoomInfo(roomUid);
        if (!roomInfoCache.isExists()) {
            log.error("teacherCheckRoomInfo>>>live not start  roomUid: {} ", roomUid);
            throw new BizException("直播还未开始!");
        }
        RoomInfoCache roomInfo = roomInfoCache.get();
        roomInfo.setWhether_mic(whetherMic);
        roomInfoCache.set(roomInfo);
    }

    /**
     * 直播间详情信息
     *
     * @param liveRoomId 直播间编号
     * @param userId 分享用户ID
     * @return LiveRoomWrapper
     */
    @Override
    public LiveRoomWrapper findLiveRoomDetailInfoByRoomId(String liveRoomId, Long userId) {

        // 直播间信息
        LiveRoom liveRoom = getBaseMapper().selectOne(Wrappers.<LiveRoom>lambdaQuery()
                .eq(LiveRoom::getRoomUid, liveRoomId));
        if (Objects.isNull(liveRoom)) {
            return null;
        }

        LiveRoomWrapper roomWrapper = LiveRoomWrapper.from(JSON.toJSONString(liveRoom));

        // 直播老师信息
        com.yonge.cooleshow.biz.dal.entity.SysUser teacher = sysUserMapper.selectById(roomWrapper.getSpeakerId());
        if (Objects.nonNull(teacher)) {

            roomWrapper.setTeacher(JSON.parseObject(JSON.toJSONString(teacher), LiveRoomWrapper.UserInfo.class));
        }

        // 分享同学信息
        if (Optional.ofNullable(userId).orElse(0L) > 0) {

            com.yonge.cooleshow.biz.dal.entity.SysUser student = sysUserMapper.selectById(userId);
            roomWrapper.setStudent(JSON.parseObject(JSON.toJSONString(student), LiveRoomWrapper.UserInfo.class));
        }

        return roomWrapper;
    }
}

