package com.ym.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ym.common.ApiException;
import com.ym.common.BaseResponse;
import com.ym.common.DisplayEnum;
import com.ym.common.ErrorEnum;
import com.ym.config.IMProperties;
import com.ym.config.RoomProperties;
import com.ym.dao.RoomDao;
import com.ym.dao.RoomMemberDao;
import com.ym.dao.UserDao;
import com.ym.dao.WhiteboardDao;
import com.ym.job.ScheduleManager;
import com.ym.mec.auth.api.client.SysUserFeignService;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.*;
import com.ym.mec.biz.dal.dto.BasicUserDto;
import com.ym.mec.biz.dal.dto.RongyunBasicUserDto;
import com.ym.mec.biz.dal.entity.CourseSchedule;
import com.ym.mec.biz.dal.entity.SysExamSong;
import com.ym.mec.biz.dal.entity.Teacher;
import com.ym.mec.biz.dal.enums.GroupType;
import com.ym.mec.biz.dal.enums.TeachModeEnum;
import com.ym.mec.biz.service.StudentAttendanceService;
import com.ym.mec.biz.service.SysConfigService;
import com.ym.mec.biz.service.TeacherAttendanceService;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.im.IMHelper;
import com.ym.mec.im.message.*;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;
import com.ym.pojo.*;
import com.ym.service.RoomService;
import com.ym.utils.CheckUtils;
import com.ym.utils.CodeUtil;
import com.ym.utils.DateTimeUtils;
import com.ym.utils.IdentifierUtils;
import com.ym.whiteboard.WhiteBoardHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.ym.pojo.RoleEnum.RoleStudent;
import static com.ym.pojo.RoleEnum.RoleTeacher;

/**
 * Created by super_zou on 2019/11/28.
 */
@Slf4j
@Service
public class RoomServiceImpl implements RoomService {
    @Autowired
    private IMHelper imHelper;
    @Autowired
    private RoomProperties roomProperties;
    @Autowired
    private RoomDao roomDao;
    @Autowired
    private RoomMemberDao roomMemberDao;
    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
    @Autowired
    private WhiteBoardHelper whiteBoardHelper;
    @Autowired
    private WhiteboardDao whiteboardDao;
    @Autowired
    private ScheduleManager scheduleManager;
    @Autowired
    private UserDao userDao;
    @Autowired
    private TeacherDao teacherDao;
    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private TeacherAttendanceService teacherAttendanceService;
    @Autowired
    private StudentAttendanceService studentAttendanceService;
    @Autowired
    private IMProperties imProperties;
    @Autowired
    private SysUserFeignService sysUserFeignService;
    @Autowired
    private SysExamSongDao sysExamSongDao;
    @Autowired
    private SysConfigDao sysConfigDao;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Override
    public Integer getCurrentCourseId(String roomId){
        String continueCourseTime = sysConfigDao.findConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME);
        if(StringUtils.isEmpty(continueCourseTime)){
            continueCourseTime = "5";
        }
        CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(roomId));
        CourseSchedule schedule = courseSchedule;
        //如果当前课程是连堂课，那么获取第一节课的课程编号
        while (true){
            String classDate = DateUtil.format(schedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
            String startClassTime = DateUtil.format(schedule.getStartClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
            schedule = courseScheduleDao.getLastCourse(schedule.getClassGroupId(),classDate + " " + startClassTime,schedule.getActualTeacherId(),continueCourseTime);
            if(schedule != null){
                roomId = schedule.getId().toString();
            }else {
                break;
            }
        }
        return Integer.parseInt(roomId);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    @Override
    public RoomResult joinRoom(String roomId) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");

        SysUser sysUser = sysUserFeignService.queryUserInfo();
        String userId = sysUser.getId().toString();
        log.info("joinRoom: roomId={}, userId={}", roomId, userId);

        Teacher teacher = teacherDao.get(Integer.parseInt(userId));
        CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(roomId));
        if(courseSchedule.getTeachMode() == TeachModeEnum.OFFLINE){
            throw new ApiException(ErrorEnum.JOIN_ROOM_ERROR);
        }

        String continueCourseTime = sysConfigDao.findConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME);
        if(StringUtils.isEmpty(continueCourseTime)){
            continueCourseTime = "5";
        }

        String autoCloseNetworkRoomTime = sysConfigDao.findConfigValue(SysConfigService.AUTO_CLOSE_NETWORK_ROOM);
        if(StringUtils.isEmpty(autoCloseNetworkRoomTime)){
            continueCourseTime = "15";
        }
        RoomResult roomResult = new RoomResult();
        roomResult.setAutoCloseNetworkRoomTime(autoCloseNetworkRoomTime);
        CourseSchedule schedule = courseSchedule;
        //如果当前课程是连堂课，那么获取第一节课的课程编号
        while (true){
            String classDate = DateUtil.format(schedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
            String startClassTime = DateUtil.format(schedule.getStartClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
            schedule = courseScheduleDao.getLastCourse(schedule.getClassGroupId(),classDate + " " + startClassTime,schedule.getActualTeacherId(),continueCourseTime);
            if(schedule != null){
                roomId = schedule.getId().toString();
                roomResult.setAutoCloseFlag(false);
            }else {
                break;
            }
        }
        Long courseId = Long.parseLong(roomId);
        //记录用户实际选择的房间
        if(courseSchedule.getGroupType() == GroupType.COMM){
            roomId = "I" + roomId;
        }else {
            roomId = "S" + roomId;
        }
        redisTemplate.opsForValue().setIfAbsent(roomId + userId,courseSchedule.getId().toString());
        log.info("joinRoom current: roomId={}, userId={}", roomId, userId);
        Date curTime = DateTimeUtils.currentUTC();

        RoleEnum roleEnum;


        RoomResult.MemberResult userResult = new RoomResult.MemberResult();
        RoomMember member = roomMemberDao.findByRidAndUid(roomId, userId);

        String userName;
        if (member == null) {
            int count = roomMemberDao.countByRidAndExcludeRole(roomId, RoleEnum.RoleAudience.getValue());
            if (count == roomProperties.getMaxCount()) {
                log.info("join error Over max count: roomId = {}, userId = {}", roomId,userId);
                throw new ApiException(ErrorEnum.ERR_OVER_MAX_COUNT);
            }
            if(teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())){
                roleEnum = RoleTeacher;
                userName = sysUser.getRealName();
            }else {
                roleEnum = RoleStudent;
                userName = sysUser.getUsername();
            }
            userResult.setMicrophone(true);
            userResult.setCamera(true);
            userResult.setHandUpOn(false);
            userResult.setJoinTime(curTime);
            saveRoomMember(userId,sysUser.getAvatar(), userName, roomId, roleEnum.getValue(), curTime);
            imHelper.joinGroup(new String[]{userId}, roomId, roomId);
        } else {
            roleEnum = RoleEnum.getEnumByValue(member.getRole());
            if(roleEnum == RoleTeacher){
                courseScheduleStudentPaymentDao.adjustPlayMidi(courseId,null,null);
            }
            userName = member.getName();
            userResult.setCamera(member.isCamera());
            userResult.setHandUpOn(member.isHand());
            userResult.setJoinTime(member.getJoinDt());
        }

        String display = "";
        if (roleEnum == RoleTeacher) {
            display = "display://type=1?userId=" + userId + "?uri=";
        } else if (roleEnum == RoleEnum.RoleAssistant && display.isEmpty()) {
            display = "display://type=0?userId=" + userId + "?uri=";
        }else {
            ExamSongDownloadData examSongDownloadData;
            String json = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(courseId, sysUser.getId());
            if(StringUtils.isEmpty(json)){
                examSongDownloadData = new ExamSongDownloadData();
            }else {
                examSongDownloadData = JSON.parseObject(json,ExamSongDownloadData.class);
            }
            //获取学员曲目下载状态
            userResult.setExamSongDownloadJson(examSongDownloadData);
            Room room = roomDao.findByRid(roomId);
            if (room != null) {
                display = room.getDisplay();
            }
        }

        userResult.setUserName(userName);
        userResult.setUserId(userId);
        userResult.setRole(roleEnum.getValue());
        //获取节拍器信息
        String midi = courseScheduleStudentPaymentDao.getMidiByCourseIdAndUserId(courseId.toString(),userId);
        userResult.setPlayMidiJson(JSONObject.parseObject(midi,CustomMessage.class));

        //获取当前课程剩余时长
        String classDate = DateUtil.format(courseSchedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
        String endClassTime = DateUtil.format(courseSchedule.getEndClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
        roomResult.setSurplusTime(DateUtil.secondsBetween(new Date(),DateUtil.stringToDate(classDate + " " + endClassTime,DateUtil.EXPANDED_DATE_TIME_FORMAT)));

        roomResult.setUserInfo(userResult);
        roomResult.setDisplay(display);
        roomResult.setRoomId(roomId);

        List<RoomMember> roomMemberList = roomMemberDao.findByRid(roomId);
        if(roomMemberList != null && roomMemberList.size() > 0){
            Set<String> userIds = roomMemberList.stream().map(e -> e.getUid()).collect(Collectors.toSet());
            Map<Integer,String> midiMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryMidiByUserIdsAndCourseId(userIds,courseId.toString()));
            Map<Integer,String> examSongMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryExamSongByUserIdsAndCourseId(userIds,courseId.toString()));
            roomResult.setMembers(roomMemberList,midiMap,examSongMap);
        }
        roomResult.setWhiteboards(whiteboardDao.findByRid(roomId));
        log.info("join room: roomId = {}, userId = {}, userName={}, role = {}", roomId, userId, userName, roleEnum);
        return roomResult;
    }

    public RoomMember saveRoomMember(String roomId,String userId){
        SysUser sysUser = sysUserFeignService.queryUserById(Integer.parseInt(userId));

        Teacher teacher = teacherDao.get(Integer.parseInt(userId));
        CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(roomId.substring(1)));

        Date curTime = DateTimeUtils.currentUTC();

        RoleEnum roleEnum;
        RoomMember member = roomMemberDao.findByRidAndUid(roomId, userId);
        String userName;
        if (member == null) {
            if(teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())){
                roleEnum = RoleTeacher;
                userName = sysUser.getRealName();
            }else {
                roleEnum = RoleStudent;
                userName = sysUser.getUsername();
            }
            member = saveRoomMember(userId, sysUser.getAvatar(), userName, roomId, roleEnum.getValue(), curTime);
        }
        return member;
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    @Override
    public void joinRoomFailure(String roomId, String userId) {
        RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
        if(roomMember == null){
            return ;
        }
        log.info("joinRoomFailure : roomId={}, userId={}", roomId, userId);
        //如果加入失败，删除该用户数据
        roomMemberDao.deleteUserByRidAndUid(roomId,userId);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    @Override
    public void joinRoomSuccess(String roomId,String userId,String deviceNum) throws Exception {
        RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
        if(roomMember == null){
            roomMember = saveRoomMember(roomId,userId);
        }
        String joinSuccessKey = "joinRoomSuccess"+ roomId + userId;
        if(redisTemplate.hasKey(joinSuccessKey)){
            //兼容旧版本，防止重复调用
            redisTemplate.delete(joinSuccessKey);
            RoleEnum roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());
            if(roleEnum == RoleTeacher && StringUtils.isNotEmpty(deviceNum)){
                signInSuccess(roomMember,deviceNum);
            }
            return;
        }
        String leaveSuccessKey = "leaveRoomSuccess"+ roomId + userId;
        if(redisTemplate.hasKey(leaveSuccessKey)){
            redisTemplate.delete(leaveSuccessKey);
        }
        redisTemplate.opsForValue().setIfAbsent(joinSuccessKey,roomId,2l,TimeUnit.SECONDS);
        log.info("joinRoomSuccess : roomId={}, userId={}", roomId, userId);

        RoleEnum roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());
        CourseSchedule schedule = courseScheduleDao.getLock(Long.parseLong(roomId.substring(1)));

        String display = "";
        if(roleEnum == RoleTeacher){
            //如果是老师加入房间，调整节拍器状态
            courseScheduleStudentPaymentDao.adjustPlayMidi(schedule.getId(),null,null);
            display = "display://type=1?userId=" + userId + "?uri=";
        }else if (roleEnum == RoleEnum.RoleAssistant){
            display = "display://type=0?userId=" + userId + "?uri=";
        }
        Date curTime = DateTimeUtils.currentUTC();
        Room room = roomDao.findByRid(roomId);
        if (room == null) {
            saveRoom(roomId, roomId, curTime, display);
            IMApiResultInfo resultInfo = imHelper.createGroup(new String[]{userId}, roomId, roomId);
            if (!resultInfo.isSuccess()) {
                log.error("joinRoomSuccess createGroup error: roomId={}, {}", roomId, resultInfo.getErrorMessage());
                throw new ApiException(ErrorEnum.ERR_CREATE_ROOM_ERROR, resultInfo.getErrorMessage());
            }
        }else{
            if(roleEnum == RoleTeacher || roleEnum == RoleEnum.RoleAssistant){
                updateDisplay(roomId, userId, display, 0);
            }
        }

        UserInfo userInfo = userDao.findByUid(userId);
        if (userInfo == null) {
            userInfo = new UserInfo();
            userInfo.setUid(userId);
            userInfo.setName(roomMember.getName());
            userInfo.setCreateDt(curTime);
            userInfo.setUpdateDt(curTime);
            userDao.save(userInfo);
        }
        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Join, userId, roleEnum.getValue());
        msg.setTimestamp(curTime);
        msg.setUserName(roomMember.getName());
        msg.setCamera(true);
        Boolean playMidi = false;
        Boolean examSong = false;
        if(roleEnum == RoleStudent){
            String midiByCourseIdAndUserId = courseScheduleStudentPaymentDao.getMidiByCourseIdAndUserId(schedule.getId().toString(), userId);
            //获取节拍器信息
            if(StringUtils.isNotEmpty(midiByCourseIdAndUserId)){
                JSONObject jsonObject = JSONObject.parseObject(midiByCourseIdAndUserId);
                if(jsonObject.get("enable") != null){
                    playMidi = Boolean.parseBoolean(jsonObject.get("enable").toString());
                }
            }
            String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(schedule.getId(), Integer.parseInt(userId));
            if(StringUtils.isNotEmpty(examJson)){
                ExamSongDownloadData examSongDownloadData = JSON.parseObject(examJson, ExamSongDownloadData.class);
                examSong = examSongDownloadData.getEnable();
            }
        }
        msg.setHandUpOn(roomMember.isHand());
        msg.setMetronomeSwitch(playMidi);
        msg.setExamSongSwitch(examSong);
        imHelper.publishMessage(userId, roomId, msg);
        log.info("join room success: roomId = {}, userId = {}, role = {}", roomId, userId, roleEnum);
        signInSuccess(roomMember,deviceNum);
    }

    public void signInSuccess(RoomMember roomMember,String deviceNum) {
        String roomId = roomMember.getRid();
        String userId = roomMember.getUid();
        String currentRoomIdKey = roomId + userId;

        Long firstCourseId = Long.parseLong(roomId.substring(1));
        Long currentRoomId;
        if(redisTemplate.hasKey(currentRoomIdKey)){
            currentRoomId = Long.parseLong(redisTemplate.opsForValue().get(currentRoomIdKey));
        }else {
            log.error("signInFailure: roomId={}, userId={}", roomId, userId);
            currentRoomId = firstCourseId;
        }
        log.info("signInSuccess: currentRoomId={}", currentRoomId);
        log.info("signInSuccess: roomId={}, userId={}", roomId, userId);
        Integer userIdInt = Integer.parseInt(userId);

        RoleEnum roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());
        if(roleEnum == RoleTeacher){
            teacherAttendanceService.addTeacherAttendanceSignIn(firstCourseId,userIdInt,currentRoomId,deviceNum);
        }else {
            studentAttendanceService.addStudentAttendanceSignIn(firstCourseId,userIdInt,currentRoomId);
        }
        redisTemplate.delete(roomId + userId);
    }

    public void saveRoom(String roomId, String roomName, Date createTime, String display) {
        Room room = new Room();
        room.setRid(roomId);
        room.setName(roomName);
        room.setCreateDt(createTime);
        room.setDisplay(display);
        room.setWhiteboardNameIndex(0);
        roomDao.save(room);
    }

    public RoomMember saveRoomMember(String userId, String headUrl, String userName, String roomId, int role, Date joinTime) {
        RoomMember roomMember = new RoomMember();
        roomMember.setUid(userId);
        roomMember.setName(userName);
        roomMember.setRid(roomId);
        roomMember.setRole(role);
        roomMember.setCamera(true);
        roomMember.setJoinDt(joinTime);
        roomMember.setMusicMode(false);
        roomMember.setHeadUrl(headUrl);
        roomMemberDao.save(roomMember);
        return roomMember;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    @Override
    public void leaveRoomSuccess(String roomId,String userId,String deviceNum) throws Exception {
        RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
        String leaveSuccessKey = "leaveRoomSuccess"+ roomId + userId;
        Integer firstCourseId = Integer.parseInt(roomId.substring(1));
        if(redisTemplate.hasKey(leaveSuccessKey)){
            redisTemplate.delete(leaveSuccessKey);
            if (StringUtils.isNotEmpty(deviceNum)){
                //如果设备号不为空，更新设备号
                RoleEnum roleEnum;
                if(roomMember == null){
                    Teacher teacher = teacherDao.get(Integer.parseInt(userId));
                    CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(roomId));
                    if(teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())){
                        roleEnum = RoleTeacher;
                    }else {
                        roleEnum = RoleStudent;
                    }
                }else {
                    roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());
                }
                if(roleEnum == RoleTeacher){
                    teacherAttendanceService.addTeacherAttendanceSignOut(firstCourseId.longValue(),Integer.parseInt(userId),deviceNum);
                }
            }
            return;
        }
        if(roomMember == null){
            return ;
        }
        String joinSuccessKey = "joinRoomSuccess"+ roomId + userId;
        if(redisTemplate.hasKey(joinSuccessKey)){
            redisTemplate.delete(joinSuccessKey);
        }
        redisTemplate.opsForValue().setIfAbsent(leaveSuccessKey,roomId,2,TimeUnit.SECONDS);
        //记录
        log.info("leaveRoomSuccess: roomId={}, userId={}", roomId,userId);
        RoleEnum roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());

        if(roleEnum == RoleTeacher){
            courseScheduleStudentPaymentDao.adjustPlayMidi(firstCourseId,null,null);
            courseScheduleStudentPaymentDao.adjustExamSong(firstCourseId.longValue(),null,null);
            teacherAttendanceService.addTeacherAttendanceSignOut(firstCourseId.longValue(),Integer.parseInt(userId),deviceNum);
        }else {
            studentAttendanceService.addStudentAttendanceSignOut(firstCourseId.longValue(),Integer.parseInt(userId));
        }
        Room room = roomDao.findByRid(roomId);
        if (room == null) {
            roomMemberDao.deleteUserByRidAndUid(roomId, userId);
            userDao.deleteByUid(userId);
            return;
        }

        if (roleEnum != RoleStudent && isUserDisplay(room, userId)) {
            updateDisplay(roomId, userId, "", 0);
        }
        if (roomMemberDao.countByRid(roomId) == 1) {
            roomMemberDao.deleteUserByRidAndUid(roomId, userId);
            roomDao.deleteByRid(roomId);
            deleteWhiteboardByUser(roomId, userId);
            imHelper.dismiss(userId, roomId);
            log.info("leaveRoomSuccess dismiss the room: {},userId: {}", roomId,userId);
        } else {
//            imHelper.quit(new String[]{userId}, roomId);
            roomMemberDao.deleteUserByRidAndUid(roomId, userId);
            MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, userId, roleEnum.getValue());
            msg.setUserName(roomMember.getName());
            imHelper.publishMessage(userId, roomId, msg);
            imHelper.quit(new String[]{userId}, roomId);
            log.info("leaveRoomSuccess quit group: roomId={},userId: {}", roomId,userId);
        }
        userDao.deleteByUid(userId);
    }

    public void deleteWhiteboardByUser(String roomId, String userId) throws Exception {
        List<Whiteboard> whiteboardList = whiteboardDao.findByRidAndCreator(roomId, userId);
        if (!whiteboardList.isEmpty()) {
            whiteboardDao.deleteByRidAndCreator(roomId, userId);
            for (Whiteboard wb : whiteboardList) {
                whiteBoardHelper.destroy(wb.getWbRoom());
            }
        }
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    @Override
    public void destroyRoom(String roomId) {
        whiteboardDao.deleteByRid(roomId);
        Room room = roomDao.findByRid(roomId);
        if (room == null) {
            List<RoomMember> list = roomMemberDao.findByRid(roomId);
            if (!list.isEmpty()) {
                try {
                    imHelper.dismiss(list.get(0).getUid(), roomId);
                } catch (Exception e) {
                    log.error("destroyRoom: {}", e.getMessage());
                    e.printStackTrace();
                }
            }
            roomMemberDao.deleteByRid(roomId);
            log.info("destroyRoom: {}", roomId);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public Boolean downgrade(String roomId, List<ReqChangeUserRoleData.ChangedUser> users) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(users.size() > 0, "the changed user list must't be null");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        Room room = roomDao.findByRid(roomId);
        if (room == null) {
            throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
        }

        boolean result = false;
        List<RoleChangedMessage.ChangedUser> changedUsers = new ArrayList<>();
        for (ReqChangeUserRoleData.ChangedUser user : users) {
            String changedUserId = user.getUserId();
            RoleEnum changedRole = RoleEnum.getEnumByValue(user.getRole());
            if (changedUserId.equals(userId)) {
                log.error("can not change self role: {}, {}, {}", roomId, userId, changedRole);
                throw new ApiException(ErrorEnum.ERR_CHANGE_SELF_ROLE);
            } else {
                RoomMember oldUser = roomMemberDao.findByRidAndUid(roomId, changedUserId);
                if (oldUser != null) {
                    if (changedRole.equals(RoleEnum.RoleAudience)) {
                        int r = roomMemberDao.updateRoleByRidAndUid(roomId, changedUserId, changedRole.getValue());
                        RoleChangedMessage.ChangedUser u = new RoleChangedMessage.ChangedUser(changedUserId, changedRole.getValue());
                        UserInfo userInfo = userDao.findByUid(changedUserId);
                        if (userInfo != null) {
                            u.setUserName(userInfo.getName());
                        }
                        changedUsers.add(u);
                        log.info("change the role: {}, {}, {}, result: {}", roomId, userId, changedRole, r);
                        result = true;
                    }
                    if (oldUser.getRole() == RoleTeacher.getValue() && isUserDisplay(room, oldUser.getUid())) {
                        updateDisplay(roomId, userId, "", 1);
                    } else {
                        log.info("don't update display: room={}, userRole={}", room, RoleEnum.getEnumByValue(oldUser.getRole()));
                    }
                } else {
                    log.info("role changed fail, not exist: {} - {} - {}", roomId, userId, changedRole);
                }
            }
        }
        if (result) {
            RoleChangedMessage msg = new RoleChangedMessage(userId);
            msg.setUsers(changedUsers);
            imHelper.publishMessage(userId, roomId, msg, 1);
        }
        return result;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    public Boolean kickMember(String roomId) throws Exception {
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        CheckUtils.checkArgument(userId != null, "userId must't be null");
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        log.info("kickMember: roomId={}, userId={}", roomId, userId);
        RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
        if(roomMember == null){
            return true;
        }

        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Kick, userId, roomMember.getRole());
        msg.setUserName(roomMember.getName());

        imHelper.publishMessage(userId, roomId, msg, 1);

        Room room = roomDao.findByRid(roomId);
        if (roomMember.getRole() == RoleTeacher.getValue() && isUserDisplay(room, userId)) {
            updateDisplay(roomId, userId, "", 1);
        }

        roomMemberDao.deleteUserByRidAndUid(roomId, userId);
        userDao.deleteByUid(userId);

        IMApiResultInfo apiResultInfo = imHelper.quit(new String[]{userId}, roomId);
        if (!apiResultInfo.isSuccess()) {
            throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean display(String roomId, int type, String uri, String targetUserId) throws Exception {
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        log.info("display in room: {}, type = {}, uri = {}", roomId, type, uri);
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(type >= 0 && type < DisplayEnum.values().length, "type not exist");
        DisplayEnum displayEnum = DisplayEnum.values()[type];

        if (displayEnum.equals(DisplayEnum.None)) {
            roomDao.updateDisplayByRid(roomId, "");
            DisplayMessage displayMessage = new DisplayMessage("");
            IMApiResultInfo apiResultInfo = imHelper.publishMessage(userId, roomId, displayMessage);
            if (apiResultInfo.isSuccess()) {
                return true;
            } else {
                throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, apiResultInfo.getErrorMessage());
            }
        }

        String display = "display://type=" + type;
        if (displayEnum.equals(DisplayEnum.Teacher)) {
            List<RoomMember> teachers = roomMemberDao.findByRidAndRole(roomId, RoleTeacher.getValue());
            if (teachers.isEmpty()) {
                throw new ApiException(ErrorEnum.ERR_TEACHER_NOT_EXIST_IN_ROOM);
            } else {
                display += "?userId=" + teachers.get(0).getUid() + "?uri=";
                roomDao.updateDisplayByRid(roomId, display);
                DisplayMessage displayMessage = new DisplayMessage(display);
                imHelper.publishMessage(userId, roomId, displayMessage);
                log.info("change display to teacher: roomId={}, display={}", roomId, display);
            }
        } else if (displayEnum.equals(DisplayEnum.Assistant)) {
            List<RoomMember> assistants = roomMemberDao.findByRidAndRole(roomId, RoleEnum.RoleAssistant.getValue());
            if (assistants.isEmpty()) {
                throw new ApiException(ErrorEnum.ERR_ASSISTANT_NOT_EXIST_IN_ROOM);
            } else {
                display += "?userId=" + assistants.get(0).getUid() + "?uri=";
                roomDao.updateDisplayByRid(roomId, display);
                DisplayMessage displayMessage = new DisplayMessage(display);
                imHelper.publishMessage(userId, roomId, displayMessage);
                log.info("change display to assistant: roomId={}, display={}", roomId, display);
            }
        } else if (displayEnum.equals(DisplayEnum.Screen)) {
            display += "?userId=" + userId + "?uri=";
            roomDao.updateDisplayByRid(roomId, display);
            DisplayMessage displayMessage = new DisplayMessage(display);
            imHelper.publishMessage(userId, roomId, displayMessage);
            log.info("change display to screen: roomId={}, display={}", roomId, display);
        } else if (displayEnum.equals(DisplayEnum.STUDENT)) {
            display += "?userId=" + targetUserId + "?uri=" + uri;
            roomDao.updateDisplayByRid(roomId, display);
            DisplayMessage displayMessage = new DisplayMessage(display);
            imHelper.publishMessage(userId, roomId, displayMessage);
            log.info("change display to screen: roomId={}, display={}", roomId, display);
        } else {
            display += "?userId=" + userId + "?uri=" + uri;
//            CheckUtils.checkArgument(uri != null, "uri must't be null");
//            CheckUtils.checkArgument(whiteboardDao.findByRidAndWbid(roomId, uri).size() > 0, "whiteboard not exist");
            roomDao.updateDisplayByRid(roomId, display);
            DisplayMessage displayMessage = new DisplayMessage(display);
            imHelper.publishMessage(userId, roomId, displayMessage);
        }
        log.info("result display in room: {}, type = {}, uri = {}", roomId, type, uri);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createWhiteBoard(String roomId) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
//        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
//        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, userId), "room member not exist");

        log.info("createWhiteBoard: roomId = {}", roomId);

        String wbRoom = IdentifierUtils.uuid();
        WhiteBoardApiResultInfo resultInfo = whiteBoardHelper.create(wbRoom);
        if (resultInfo.isSuccess()) {
            String wbId = resultInfo.getData();
            Date date = DateTimeUtils.currentUTC();
            Room room = roomDao.findByRid(roomId);
            int whiteboardNameIndex = room.getWhiteboardNameIndex() + 1;
            String name = "白板" + whiteboardNameIndex;
            roomDao.updateWhiteboardNameIndexByRid(roomId, whiteboardNameIndex);
            Whiteboard wb = new Whiteboard();
            wb.setRid(roomId);
            wb.setWbRoom(wbRoom);
            wb.setWbid(wbId);
            wb.setName(name);
            wb.setCreator(userId);
            wb.setCreateDt(date);
            wb.setCurPg(0);
            whiteboardDao.save(wb);
            WhiteboardMessage wbmsg = new WhiteboardMessage(WhiteboardMessage.Create);
            wbmsg.setWhiteboardId(wbId);
            wbmsg.setWhiteboardName(name);
            imHelper.publishMessage(userId, roomId, wbmsg);
            String display = "display://type=2?userId=" + userId + "?uri=" + wbId;
            roomDao.updateDisplayByRid(roomId, display);
            DisplayMessage displayMessage = new DisplayMessage(display);
            imHelper.publishMessage(userId, roomId, displayMessage, 1);

            return wbId;
        } else {
            throw new ApiException(ErrorEnum.ERR_CREATE_WHITE_BOARD, resultInfo.getMsg());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWhiteboard(String roomId, String whiteBoardId) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(whiteBoardId != null, "whiteBoardId must't be null");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();

        List<Whiteboard> whiteboardList = whiteboardDao.findByRidAndWbid(roomId, whiteBoardId);
        CheckUtils.checkArgument(whiteboardList.size() > 0, "whiteboard not exist");

        Room room = roomDao.findByRid(roomId);
        CheckUtils.checkArgument(room != null, "room not exist");

        log.info("deleteWhiteboard: room={}, whiteBoardId={}", room, whiteBoardId);

        String display = room.getDisplay();
        if (display.contains("uri=" + whiteBoardId)) {
            int result = roomDao.updateDisplayByRid(roomId, "");
            log.info("clear room display, room: {}, result: {}", roomId, result);
            DisplayMessage displayMessage = new DisplayMessage("");
            imHelper.publishMessage(userId, roomId, displayMessage, 1);
        } else {
            log.info("no display to clean: room={}", room);
        }

        String wbRoom = whiteboardList.get(0).getWbRoom();
        WhiteBoardApiResultInfo resultInfo = whiteBoardHelper.destroy(wbRoom);
        if (resultInfo.isSuccess()) {
            int result = whiteboardDao.deleteByWbid(whiteBoardId);
            log.info("delete whiteboard: roomId = {}, whiteBoardId = {}, result = {}", roomId, whiteBoardId, result);
            WhiteboardMessage wbmsg = new WhiteboardMessage(WhiteboardMessage.Delete);
            wbmsg.setWhiteboardId(whiteBoardId);
            imHelper.publishMessage(userId, roomId, wbmsg, 1);
            return true;
        } else {
            throw new ApiException(ErrorEnum.ERR_DELETE_WHITE_BOARD, resultInfo.getMsg());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<RoomResult.WhiteboardResult> getWhiteboard(String roomId) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");

        List<Whiteboard> whiteboards = whiteboardDao.findByRid(roomId);
        List<RoomResult.WhiteboardResult> result = new ArrayList<>();
        for (Whiteboard wb : whiteboards) {
            RoomResult.WhiteboardResult r = new RoomResult.WhiteboardResult();
            r.setName(wb.getName());
            r.setCurPg(wb.getCurPg());
            r.setWhiteboardId(wb.getWbid());
            result.add(r);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean turnWhiteBoardPage(String roomId, String whiteBoardId, int page) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(whiteBoardId != null, "whiteBoardId must't be null");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        Room room = roomDao.findByRid(roomId);
        CheckUtils.checkArgument(room != null, "room not exist");

        int result = whiteboardDao.updatePageByRidAndWbid(roomId, whiteBoardId, page);
        log.info("turn page to: {}, room: {}, wb : {}; r: {}", page, roomId, whiteBoardId, result);

        TurnPageMessage turnPageMessage = new TurnPageMessage(whiteBoardId, userId, page);
        imHelper.publishMessage(userId, roomId, turnPageMessage);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean controlDevice(String roomId, String userId, DeviceTypeEnum typeEnum, boolean enable) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(userId != null, "userId must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        log.info("controlDevice: userId={}, typeEnum={}, onOff={}", userId, typeEnum, enable);

        if (enable) {
            if (typeEnum.equals(DeviceTypeEnum.ExamSong)){
                long scheduleId = Long.parseLong(roomId.substring(1));
                ExamSongDownloadData msg;
                String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, Integer.parseInt(userId));
                if(StringUtils.isEmpty(examJson)){
                    throw new BizException("学员伴奏信息异常");
                }else {
                    msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                    msg.setEnable(enable);
                }
                courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,Integer.parseInt(userId),JSON.toJSONString(msg));
                DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), enable);
                deviceResourceMessage.setUserId(userId);
                imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
            }else {
                String ticket = IdentifierUtils.uuid();
                ControlDeviceTaskInfo taskInfo = new ControlDeviceTaskInfo();
                taskInfo.setRoomId(roomId);
                taskInfo.setTypeEnum(typeEnum);
                taskInfo.setOnOff(true);
                taskInfo.setApplyUserId(authUser.getId().toString());
                taskInfo.setTargetUserId(userId);
                taskInfo.setTicket(ticket);
                scheduleManager.addTask(taskInfo);
                ControlDeviceNotifyMessage msg = new ControlDeviceNotifyMessage(ActionEnum.Invite.ordinal());
                msg.setTicket(ticket);
                msg.setType(taskInfo.getTypeEnum().ordinal());
                msg.setOpUserId(authUser.getId().toString());
                msg.setOpUserName(authUser.getUsername());
                imHelper.publishMessage(authUser.getId().toString(), userId, roomId, msg);
            }
        } else {
            if (typeEnum.equals(DeviceTypeEnum.Camera)) {
                roomMemberDao.updateCameraByRidAndUid(roomId, userId, false);
            } else if (typeEnum.equals(DeviceTypeEnum.Microphone)){
                roomMemberDao.updateMicByRidAndUid(roomId, userId, false);
            } else if (typeEnum.equals(DeviceTypeEnum.HandUp)){
                roomMemberDao.updateHandByRidAndUid(roomId, userId, false);
            } else if (typeEnum.equals(DeviceTypeEnum.ExamSong)){
                long scheduleId = Long.parseLong(roomId.substring(1));
                ExamSongDownloadData msg;
                String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, Integer.parseInt(userId));
                if(StringUtils.isEmpty(examJson)){
                    throw new BizException("学员伴奏信息异常");
                }else {
                    msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                    msg.setEnable(enable);
                }
                courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,Integer.parseInt(userId),JSON.toJSONString(msg));
            }else {
                roomMemberDao.updateMusicByRidAndUid(roomId, userId, false);
            }
            DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), false);
            deviceResourceMessage.setUserId(userId);
            UserInfo userInfo = userDao.findByUid(userId);
            if (userInfo != null) {
                deviceResourceMessage.setUserName(userInfo.getName());
            }
            imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchControlDevice(ReqDeviceControlData data) throws Exception {
        List<RoomMember> roomMembers;
        if(StringUtils.isNotEmpty(data.getUserId())){
            roomMembers = new ArrayList<>();
            String[] split = data.getUserId().split(",");
            for (int i = 0; i < split.length; i++) {
                roomMembers.add(new RoomMember(split[i]));
            }
        }else {
            roomMembers = roomMemberDao.findByRidAndRole(data.getRoomId(), RoleStudent.getValue());
        }
        if(roomMembers.size() == 0){
            return false;
        }
        boolean result = true;
        if (data.getCameraOn() != null) {
            for (RoomMember e:roomMembers) {
                controlDevice(data.getRoomId(), e.getUid(), DeviceTypeEnum.Camera, data.getCameraOn());
            }
        } else if (data.getMicrophoneOn() != null) {
            for (RoomMember e:roomMembers) {
                controlDevice(data.getRoomId(), e.getUid(), DeviceTypeEnum.Microphone, data.getMicrophoneOn());
            }
        } else if (data.getMusicModeOn() != null) {
            for (RoomMember e:roomMembers) {
                controlDevice(data.getRoomId(), e.getUid(), DeviceTypeEnum.MusicMode, data.getMusicModeOn());
            }
        } else if (data.getExamSongOn() != null) {
            if(StringUtils.isNotEmpty(data.getUserId())){
                for (RoomMember e:roomMembers) {
                    controlDevice(data.getRoomId(), e.getUid(), DeviceTypeEnum.ExamSong, data.getExamSongOn());
                }
            }else {
                List<BasicUserDto> students = courseScheduleStudentPaymentDao.findStudents(Long.parseLong(data.getRoomId().substring(1)));
                for (BasicUserDto e:students) {
                    controlDevice(data.getRoomId(), e.getUserId().toString(), DeviceTypeEnum.ExamSong, data.getExamSongOn());
                }
            }
        } else {
            throw new ApiException(ErrorEnum.ERR_REQUEST_PARA_ERR);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean approveControlDevice(String roomId, String ticket) throws Exception {
        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        log.info("approveControlDevice: ticket={}", ticket);
        ControlDeviceTaskInfo taskInfo = (ControlDeviceTaskInfo) scheduleManager.executeTask(ticket);
        if (taskInfo.getTypeEnum().equals(DeviceTypeEnum.Camera)) {
            roomMemberDao.updateCameraByRidAndUid(roomId, userId, taskInfo.isOnOff());
        }if (taskInfo.getTypeEnum().equals(DeviceTypeEnum.ExamSong)) {
            long scheduleId = Long.parseLong(roomId.substring(1));
            ExamSongDownloadData msg;
            String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, authUser.getId());
            if(StringUtils.isEmpty(examJson)){
                throw new BizException("学员伴奏信息异常");
            }else {
                msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                msg.setEnable(true);
            }
            courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,authUser.getId(),JSON.toJSONString(msg));
        } else {
            roomMemberDao.updateMicByRidAndUid(roomId, userId, taskInfo.isOnOff());
        }
        ControlDeviceNotifyMessage msg = new ControlDeviceNotifyMessage(ActionEnum.Approve.ordinal());
        msg.setType(taskInfo.getTypeEnum().ordinal());
        msg.setOpUserId(userId);
        msg.setOpUserName(authUser.getUsername());
        imHelper.publishMessage(userId, taskInfo.getApplyUserId(), roomId, msg);

        DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(taskInfo.getTypeEnum().ordinal(), taskInfo.isOnOff());
        deviceResourceMessage.setUserId(userId);
        imHelper.publishMessage(userId, roomId, deviceResourceMessage, 1);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean rejectControlDevice(String roomId, String ticket) throws Exception {
        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();

        log.info("rejectControlDevice: ticket={}", ticket);
        ControlDeviceTaskInfo taskInfo = (ControlDeviceTaskInfo) scheduleManager.executeTask(ticket);
        ControlDeviceNotifyMessage msg = new ControlDeviceNotifyMessage(ActionEnum.Reject.ordinal());
        msg.setType(taskInfo.getTypeEnum().ordinal());
        msg.setOpUserId(userId);
        msg.setOpUserName(authUser.getUsername());
        imHelper.publishMessage(userId, taskInfo.getApplyUserId(), roomId, msg);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean syncDeviceState(String roomId, DeviceTypeEnum type, boolean enable) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();

        int result;
        DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(type.ordinal(), enable);
        if (type.equals(DeviceTypeEnum.Camera)) {
            result = roomMemberDao.updateCameraByRidAndUid(roomId, userId, enable);
        } else if (type.equals(DeviceTypeEnum.Microphone)){
            result = roomMemberDao.updateMicByRidAndUid(roomId, userId, enable);
        } else if (type.equals(DeviceTypeEnum.HandUp)){
            result = roomMemberDao.updateHandByRidAndUid(roomId, userId, enable);
        } else if (type.equals(DeviceTypeEnum.ExamSong)){
            long scheduleId = Long.parseLong(roomId.substring(1));
            ExamSongDownloadData msg;
            String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, authUser.getId());
            if(StringUtils.isEmpty(examJson)){
                throw new BizException("学员伴奏信息异常");
            }else {
                msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                msg.setEnable(enable);
            }
            result = courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,authUser.getId(),JSON.toJSONString(msg));
        }else {
            result = roomMemberDao.updateMusicByRidAndUid(roomId, userId, enable);
        }
        deviceResourceMessage.setUserId(userId);
        imHelper.publishMessage(userId, roomId, deviceResourceMessage, 1);
        log.info("syncDeviceState : {}, {}, result = {}", roomId, enable, result);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<RoomResult.MemberResult> getMembers(String roomId) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        List<RoomMember> roomMemberList = roomMemberDao.findByRid(roomId);
        if(roomMemberList != null && roomMemberList.size() > 0){
            RoomResult roomResult = new RoomResult();
            Set<String> userIds = roomMemberList.stream().map(e -> e.getUid()).collect(Collectors.toSet());
            Map<Integer,String> midiMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryMidiByUserIdsAndCourseId(userIds,roomId.substring(1)));
            Map<Integer,String> examSongMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryExamSongByUserIdsAndCourseId(userIds,roomId.substring(1)));
            roomResult.setMembers(roomMemberList,midiMap,examSongMap);
            return roomResult.getMembers();
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean applySpeech(String roomId) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        List<RoomMember> assistants = roomMemberDao.findByRidAndRole(roomId, RoleEnum.RoleAssistant.getValue());
        if (assistants.isEmpty()) {
            throw new ApiException(ErrorEnum.ERR_ASSISTANT_NOT_EXIST_IN_ROOM);
        }

        String ticket = IdentifierUtils.uuid();
        ScheduledTaskInfo scheduledTaskInfo = new ScheduledTaskInfo();
        scheduledTaskInfo.setTicket(ticket);
        scheduledTaskInfo.setRoomId(roomId);
        scheduledTaskInfo.setApplyUserId(userId);
        scheduledTaskInfo.setTargetUserId(assistants.get(0).getUid());
        scheduleManager.addTask(scheduledTaskInfo);

        log.info("applySpeech: task = {}", scheduledTaskInfo);

        ApplyForSpeechMessage msg = new ApplyForSpeechMessage();
        msg.setTicket(ticket);
        msg.setReqUserId(userId);
        IMApiResultInfo resultInfo = imHelper.publishMessage(userId, assistants.get(0).getUid(), roomId, msg);

        log.info("apply for speech: {}, task = {}", roomId, scheduledTaskInfo);
        if (resultInfo.isSuccess()) {
            return true;
        } else {
            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean approveSpeech(String roomId, String ticket) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();

        int count = roomMemberDao.countByRidAndExcludeRole(roomId, RoleEnum.RoleAudience.getValue());
        if (count == roomProperties.getMaxCount()) {
            log.error("approveSpeech error: roomId = {}, ticket={}", roomId, ticket);
            throw new ApiException(ErrorEnum.ERR_OVER_MAX_COUNT);
        }

        ScheduledTaskInfo taskInfo = scheduleManager.executeTask(ticket);
        log.info("approveSpeech: task = {}", taskInfo);
        roomMemberDao.updateRoleByRidAndUid(roomId, taskInfo.getApplyUserId(), RoleStudent.getValue());

        SpeechResultMessage msg = new SpeechResultMessage(SpeechResultMessage.Action_Approve);
        UserInfo userInfo = userDao.findByUid(taskInfo.getApplyUserId());
        msg.setOpUserId(userId);
        msg.setOpUserName(authUser.getUsername());
        msg.setReqUserId(taskInfo.getApplyUserId());
        if (userInfo != null) {
            msg.setReqUserName(userInfo.getName());
        }
        msg.setRole(RoleStudent.getValue());
        IMApiResultInfo resultInfo = imHelper.publishMessage(userId, taskInfo.getApplyUserId(), roomId, msg);
        if (!resultInfo.isSuccess()) {
            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
        }

        RoleChangedMessage rcMsg = new RoleChangedMessage(userId);
        List<RoleChangedMessage.ChangedUser> changedUserList = new ArrayList<>();
        RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(taskInfo.getApplyUserId(), RoleStudent.getValue());
        if (userInfo != null) {
            user.setUserName(userInfo.getName());
        }
        changedUserList.add(user);
        rcMsg.setUsers(changedUserList);
        imHelper.publishMessage(userId, roomId, rcMsg, 1);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean rejectSpeech(String roomId, String ticket) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        ScheduledTaskInfo taskInfo = scheduleManager.executeTask(ticket);

        log.info("rejectSpeech: task = {}", taskInfo);
        SpeechResultMessage msg = new SpeechResultMessage(SpeechResultMessage.Action_Reject);
        msg.setOpUserId(userId);
        msg.setOpUserName(authUser.getUsername());
        msg.setRole(RoleStudent.getValue());
        IMApiResultInfo resultInfo = imHelper.publishMessage(userId, taskInfo.getApplyUserId(), roomId, msg);
        if (resultInfo.isSuccess()) {
            return true;
        } else {
            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
        }
    }

    public void checkOverMax(String roomId, RoomMember targetUser, int targetRole) {
        if (RoleEnum.getEnumByValue(targetUser.getRole()).equals(RoleEnum.RoleAudience)) {
            int count = roomMemberDao.countByRidAndExcludeRole(roomId, RoleEnum.RoleAudience.getValue());
            if (count == roomProperties.getMaxCount()) {
                log.error("assign error: roomId = {}, userId = {}, role = {}", roomId, targetUser.getRid(), targetUser.getRole());
                throw new ApiException(ErrorEnum.ERR_OVER_MAX_COUNT);
            }
        } else if (targetRole > targetUser.getRole()) {
            throw new ApiException(ErrorEnum.ERR_DOWNGRADE_ROLE);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean transfer(String roomId, String userId) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(userId != null, "userId must't be null");
        CheckUtils.checkArgument(!userId.equals(userId), "can't set self role");

        log.info("transfer: roomId = {}, userId = {}", roomId, userId);
        RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
        if (roomMember == null) {
            log.error("assistant transfer error: {} toUser = {}, opUser={}", roomId, userId, userId);
            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        }

        Room room = roomDao.findByRid(roomId);
        if (room == null) {
            log.error("assistant transfer error: {} toUser = {}, opUser={}", roomId, userId, userId);
            throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
        }

        if (isUserDisplay(room, userId) || isUserDisplay(room, userId)) {
            updateDisplay(roomId, userId, "", 1);
        } else {
            log.info("don't update display: room={}", room);
        }

        roomMemberDao.updateRoleByRidAndUid(roomId, userId, RoleStudent.getValue());
        roomMemberDao.updateRoleByRidAndUid(roomId, userId, RoleEnum.RoleAssistant.getValue());

        AssistantTransferMessage msg = new AssistantTransferMessage();
        msg.setOpUserId(userId);
        msg.setToUserId(userId);
        IMApiResultInfo resultInfo = imHelper.publishMessage(userId, roomId, msg, 1);
        if (resultInfo.isSuccess()) {
            return true;
        } else {
            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean inviteUpgradeRole(String roomId, String targetUserId, int targetRole) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(targetUserId != null, "userId must't be null");
        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, targetUserId), "room member not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        log.info("inviteUpgradeRole roomId = {}, targetUserId = {}, targetRole = {}", roomId, targetUserId, targetRole);

        RoomMember targetUser = roomMemberDao.findByRidAndUid(roomId, targetUserId);
        if (targetUser == null) {
            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        }

        checkOverMax(roomId, targetUser, targetRole);

        String ticket = IdentifierUtils.uuid();

        UpgradeRoleTaskInfo taskInfo = new UpgradeRoleTaskInfo();
        taskInfo.setTicket(ticket);
        taskInfo.setRoomId(roomId);
        taskInfo.setApplyUserId(userId);
        taskInfo.setTargetUserId(targetUserId);
        taskInfo.setRole(RoleEnum.getEnumByValue(targetRole));
        scheduleManager.addTask(taskInfo);

        UpgradeRoleMessage msg = new UpgradeRoleMessage(ActionEnum.Invite.ordinal());
        msg.setTicket(ticket);
        msg.setOpUserId(userId);
        msg.setOpUserName(authUser.getUsername());
        msg.setRole(targetRole);
        IMApiResultInfo resultInfo = imHelper.publishMessage(userId, targetUserId, roomId, msg);
        if (resultInfo.isSuccess()) {
            return true;
        } else {
            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean approveUpgradeRole(String roomId, String ticket) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        UpgradeRoleTaskInfo taskInfo = (UpgradeRoleTaskInfo) scheduleManager.executeTask(ticket);
        log.info("approveUpgradeRole roomId = {}, task={}", roomId, taskInfo);

        RoomMember targetUser = roomMemberDao.findByRidAndUid(roomId, userId);
        if (targetUser == null) {
            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        }
        if (!taskInfo.getTargetUserId().equals(userId)) {
            throw new ApiException(ErrorEnum.ERR_APPLY_TICKET_INVALID);
        }

        checkOverMax(roomId, targetUser, taskInfo.getRole().getValue());
        roomMemberDao.updateRoleByRidAndUid(roomId, userId, taskInfo.getRole().getValue());

        UpgradeRoleMessage msg = new UpgradeRoleMessage(ActionEnum.Approve.ordinal());

        msg.setOpUserName(authUser.getUsername());
        msg.setOpUserId(userId);
        msg.setRole(taskInfo.getRole().getValue());
        IMApiResultInfo resultInfo = imHelper.publishMessage(userId, taskInfo.getApplyUserId(), roomId, msg);
        if (!resultInfo.isSuccess()) {
            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
        }

        RoleChangedMessage rcMsg = new RoleChangedMessage(userId);
        List<RoleChangedMessage.ChangedUser> changedUserList = new ArrayList<>();
        RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(userId, taskInfo.getRole().getValue());
        user.setUserName(authUser.getUsername());
        changedUserList.add(user);
        rcMsg.setUsers(changedUserList);
        imHelper.publishMessage(userId, roomId, rcMsg, 1);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean rejectUpgradeRole(String roomId, String ticket) throws Exception {
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();
        UpgradeRoleTaskInfo taskInfo = (UpgradeRoleTaskInfo) scheduleManager.executeTask(ticket);
        UpgradeRoleMessage msg = new UpgradeRoleMessage(ActionEnum.Reject.ordinal());
        msg.setOpUserName(authUser.getUsername());
        msg.setOpUserId(userId);
        msg.setRole(taskInfo.getRole().getValue());
        IMApiResultInfo resultInfo = imHelper.publishMessage(userId, taskInfo.getApplyUserId(), roomId, msg);
        if (resultInfo.isSuccess()) {
            return true;
        } else {
            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean changeRole(String roomId, String targetUserId, int targetRole) throws Exception {

        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        CheckUtils.checkArgument(targetUserId != null, "userId must't be null");
        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
        CheckUtils.checkArgument(RoleEnum.getEnumByValue(targetRole).equals(RoleTeacher), "only set to teacher");
        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, targetUserId), "room member not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();

        RoomMember targetUser = roomMemberDao.findByRidAndUid(roomId, targetUserId);
        if (targetUser == null) {
            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        } else {
            if (!RoleEnum.getEnumByValue(targetUser.getRole()).equals(RoleStudent)) {
                log.error("change role error: targetUserId={}, targetRole = {}", targetUser, RoleEnum.getEnumByValue(targetRole));
                throw new ApiException(ErrorEnum.ERR_CHANGE_ROLE);
            }
        }

        log.info("changeRole: roomId={}, targetUserId={}", roomId, targetUserId);
        List<RoleChangedMessage.ChangedUser> changedUserList = new ArrayList<>();
        RoleChangedMessage msg = new RoleChangedMessage(userId);

        List<RoomMember> teachers = roomMemberDao.findByRidAndRole(roomId, RoleTeacher.getValue());
        if (!teachers.isEmpty()) {
            roomMemberDao.updateRoleByRidAndUid(roomId, teachers.get(0).getUid(), RoleStudent.getValue());
            RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(teachers.get(0).getUid(), RoleStudent.getValue());
            UserInfo userInfo = userDao.findByUid(teachers.get(0).getUid());
            if (userInfo != null) {
                user.setUserName(userInfo.getName());
            }
            changedUserList.add(user);
        } else {
            log.info("change directly cause no teacher exist in room, roomId={}", roomId);
        }

        roomMemberDao.updateRoleByRidAndUid(roomId, targetUserId, targetRole);
        RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(targetUserId, targetRole);
        UserInfo userInfo = userDao.findByUid(targetUserId);
        if (userInfo != null) {
            user.setUserName(userInfo.getName());
        }
        changedUserList.add(user);
        msg.setUsers(changedUserList);
        imHelper.publishMessage(userId, roomId, msg, 1);

        String display = "display://type=1?userId=" + targetUserId + "?uri=";
        DisplayMessage displayMessage = new DisplayMessage(display);
        roomDao.updateDisplayByRid(roomId, display);
        imHelper.publishMessage(userId, roomId, displayMessage, 1);
        log.info("changeRole, display changed: roomId={}, {}, targetUserId={}", roomId, display, targetUserId);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean memberOnlineStatus(List<ReqMemberOnlineStatus> statusList, String nonce, String timestamp, String signature) throws Exception {
        String sign = imProperties.getSecret() + nonce + timestamp;
        String signSHA1 = CodeUtil.hexSHA1(sign);
        if (!signSHA1.equals(signature)) {
            log.info("memberOnlineStatus signature error");
            return true;
        }

        for (ReqMemberOnlineStatus status : statusList) {
            int s = Integer.parseInt(status.getStatus());
            String userId = status.getUserId();

            log.info("memberOnlineStatus, userId={}, status={}", userId, status);
            //1：offline 离线； 0: online 在线
            if (s == 1) {
                List<RoomMember> members = roomMemberDao.findByUid(userId);
                if (!members.isEmpty()) {
                    scheduleManager.userIMOffline(userId);
                }
            } else if (s == 0) {
                scheduleManager.userIMOnline(userId);
            }
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userIMOfflineKick(String userId) {
        List<RoomMember> members = roomMemberDao.findByUid(userId);
        for (RoomMember member : members) {
            int userRole = member.getRole();
            log.info("userIMOfflineKick: roomId={}, {}, role={}", member.getRid(), userId, RoleEnum.getEnumByValue(userRole));
            try {
                if (userRole == RoleTeacher.getValue() || userRole == RoleEnum.RoleAssistant.getValue()) {
                    Room room = roomDao.findByRid(member.getRid());
                    if (room == null) {
                        break;
                    }
                    if (isUserDisplay(room, member.getUid())) {
                        updateDisplay(member.getRid(), member.getUid(), "", 0);
                        log.info("memberOnlineStatus offline: roomId={}, {}", member.getRid(), member.getUid());
                    }
                }
                if (roomMemberDao.countByRid(member.getRid()) == 1) {
                    IMApiResultInfo apiResultInfo = null;
                    apiResultInfo = imHelper.dismiss(member.getUid(), member.getRid());
                    if (apiResultInfo.getCode() == 200) {
                        roomMemberDao.deleteUserByRidAndUid(member.getRid(), member.getUid());
                        roomDao.deleteByRid(member.getRid());
                        deleteWhiteboardByUser(member.getRid(), member.getUid());
                        log.info("dismiss the room: {},userId: {}", member.getRid(),userId);
                    } else {
                        log.error("{} exit {} room error: {}", member.getUid(), member.getRid(), apiResultInfo.getErrorMessage());
                    }
                } else {
                    IMApiResultInfo apiResultInfo = null;
                    apiResultInfo = imHelper.quit(new String[]{member.getUid()}, member.getRid());
                    if (apiResultInfo.isSuccess()) {
                        roomMemberDao.deleteUserByRidAndUid(member.getRid(), member.getUid());
                        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, member.getUid(), userRole);
                        msg.setUserName(member.getName());
                        imHelper.publishMessage(member.getUid(), member.getRid(), msg);
                        imHelper.quit(new String[]{member.getUid()}, member.getRid());
                        log.info("quit group: roomId={},userId: {}", member.getRid(), member.getUid());
                    } else {
                        log.error("{} exit {} room error: {}", member.getUid(), member.getRid(), apiResultInfo.getErrorMessage());
                    }
                }
                userDao.deleteByUid(member.getUid());
            } catch (Exception e) {
                log.error("userIMOfflineKick error: userId={}", userId);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendImPlayMidiMessage(PlayMidiMessageData playMidiMessageData) throws Exception {
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        if(sysUser == null){
            throw new BizException("用户信息获取失败");
        }
        String content = playMidiMessageData.getContent();
        String roomId = playMidiMessageData.getRoomId();
        CustomMessage customMessage = JSONObject.parseObject(content,CustomMessage.class);
        String userId = sysUser.getId().toString();
        MetronomeMessageMessage displayMessage = new MetronomeMessageMessage(customMessage);
        imHelper.publishMessage(userId, roomId, displayMessage, 1);
        //记录节拍器消息
        courseScheduleStudentPaymentDao.adjustPlayMidi(Long.parseLong(roomId.substring(1)),playMidiMessageData.getUserId(),content);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pushDownloadExamSongMsg(String roomId, Integer examSongId) throws Exception {
        SysUser authUser = sysUserFeignService.queryUserInfo();

        SysExamSong sysExamSong = sysExamSongDao.get(examSongId);
        if(sysExamSong == null){
            throw new BizException("曲目信息不存在");
        }
        //学员曲目下载状态改为未下载
        ExamSongDownloadData json = new ExamSongDownloadData();
        json.setExamSongName(sysExamSong.getName());
        json.setUrl(sysExamSong.getUrl());
        json.setStatus(0);
        json.setExamSongId(examSongId);
        courseScheduleStudentPaymentDao.adjustExamSong(Long.parseLong(roomId.substring(1)),null, JSON.toJSONString(json));

        ExamSongMessage examSongMessage = new ExamSongMessage();
        examSongMessage.setExamSongName(sysExamSong.getName());
        examSongMessage.setUrl(sysExamSong.getUrl());
        examSongMessage.setExamSongId(examSongId);
        ExamSongDownloadMessageMessage msg = new ExamSongDownloadMessageMessage(examSongMessage);
        imHelper.publishMessage(authUser.getId().toString(), roomId, msg, 0);
    }

    @Override
    public List<RongyunBasicUserDto> queryNoJoinStu(String roomId) {
        return courseScheduleStudentPaymentDao.queryNoJoinStu(roomId,roomId.substring(1));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void adjustExamSong(String roomId, Integer status,Integer examSongId) throws Exception {
        if(roomId == null || status == null || examSongId == null){
            throw new BizException("参数校验失败");
        }
        SysUser authUser = sysUserFeignService.queryUserInfo();
        long scheduleId = Long.parseLong(roomId.substring(1));

        SysExamSong sysExamSong = sysExamSongDao.get(examSongId);
        if(sysExamSong == null){
            throw new BizException("曲目信息不存在");
        }
        ExamSongDownloadData msg;
        String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, authUser.getId());
        if(StringUtils.isEmpty(examJson)){
            msg = new ExamSongDownloadData();
            msg.setExamSongName(sysExamSong.getName());
            msg.setUrl(sysExamSong.getUrl());
            msg.setStatus(status);
            msg.setExamSongId(examSongId);
        }else {
            msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
            msg.setStatus(status);
        }
        courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,authUser.getId(),JSON.toJSONString(msg));

        //给老师发送学员曲目下载状态
        CourseSchedule courseSchedule = courseScheduleDao.get(scheduleId);
        ExamSongDownloadStatusMessage deviceResourceMessage = new ExamSongDownloadStatusMessage(status,authUser.getId(),examSongId);
        imHelper.publishMessage(authUser.getId().toString(),courseSchedule.getActualTeacherId().toString(), roomId, deviceResourceMessage);
    }

    public void updateDisplay(String roomId, String senderId, String display, Integer isIncludeSender) throws Exception {
        roomDao.updateDisplayByRid(roomId, display);
        DisplayMessage displayMessage = new DisplayMessage(display);
        imHelper.publishMessage(senderId, roomId, displayMessage, isIncludeSender);
    }

    public boolean isUserDisplay(Room room, String userId) {
        boolean result = false;
        if (!room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId)) {
            if (room.getDisplay().contains("type=0") || room.getDisplay().contains("type=1") || room.getDisplay().contains("type=3")) {
                result = true;
            }
        }
        return result;
    }
}
