package com.keao.edu.im.service.Impl;

import com.keao.edu.auth.api.client.SysUserFeignService;
import com.keao.edu.auth.api.entity.SysUser;
import com.keao.edu.common.enums.YesOrNoEnum;
import com.keao.edu.common.exception.BizException;
import com.keao.edu.im.api.entity.MemberChangedMessage;
import com.keao.edu.im.api.entity.PublishMessageDto;
import com.keao.edu.im.api.entity.ReqUserData;
import com.keao.edu.im.common.ApiException;
import com.keao.edu.im.common.BaseResponse;
import com.keao.edu.im.common.ErrorEnum;
import com.keao.edu.im.config.IMProperties;
import com.keao.edu.im.dao.*;
import com.keao.edu.im.job.ScheduleManager;
import com.keao.edu.im.mec.im.IMHelper;
import com.keao.edu.im.mec.im.message.*;
import com.keao.edu.im.pojo.*;
import com.keao.edu.im.service.RoomService;
import com.keao.edu.im.utils.CheckUtils;
import com.keao.edu.im.utils.CodeUtil;
import com.keao.edu.im.utils.DateTimeUtils;
import com.keao.edu.im.utils.IdentifierUtils;
import com.keao.edu.im.whiteboard.WhiteBoardHelper;
import com.keao.edu.user.api.client.EduUserFeignService;
import com.keao.edu.user.api.entity.ExamRoom;
import com.keao.edu.user.api.entity.ExamRoomStudentRelation;
import com.keao.edu.user.api.enums.StudentExamResultApiDto;
import com.keao.edu.util.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.keao.edu.im.pojo.RoleEnum.Student;

/**
 * Created by super_zou on 2019/11/28.
 */
@Slf4j
@Service
public class RoomServiceImpl implements RoomService {
    @Autowired
    private IMHelper imHelper;

    @Autowired
    private RoomDao roomDao;

    @Autowired
    private RoomMemberDao roomMemberDao;

    @Autowired
    private WhiteBoardHelper whiteBoardHelper;

    @Autowired
    private WhiteboardDao whiteboardDao;

    @Autowired
    private ScheduleManager scheduleManager;

    @Autowired
    private UserDao userDao;

    @Autowired
    private IMProperties imProperties;

    @Autowired
    private SysUserFeignService sysUserFeignService;

    @Autowired
    private EduUserFeignService eduUserFeignService;

    @Autowired
    private TeacherAttendanceDao teacherAttendanceDao;

    @Autowired
    private StudentAttendanceDao studentAttendanceDao;

    @Override
    public void roomQuery(String roomId) throws Exception {
        imHelper.roomQuery(roomId);
    }

    @Override
    public void startRecord(String roomId) throws Exception {
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        imHelper.startRecord(sysUser.getId().toString(),roomId);
    }

    @Override
    public RoomResult joinRecorded(Long examRegistrationId) throws Exception {
        StudentExamResultApiDto examResult = eduUserFeignService.getExamResult(examRegistrationId);
//        String roomId = examResult.getRoomId();
        String recordedRoomId = "recorded" + examResult.getRoomId();
        CheckUtils.checkArgument(recordedRoomId != null, "joinRecorded roomId must't be null");

        //是否超时
        Integer isFinishedExam = examResult.getIsFinishedExam();
        if(isFinishedExam == null || isFinishedExam == 1){
            throw new BizException("加入失败，考试已结束");
        }
        Integer recordFlag = examResult.getRecordFlag();
        if(recordFlag == null || recordFlag == 0){
            throw new BizException("加入失败，当前考试不允许录制");
        }
        String recordStartTime = examResult.getRecordStartTime();
        Date date = new Date();
        RoomResult roomResult = new RoomResult();
        if(recordStartTime != null){
            Date startTime = DateUtil.stringToDate(recordStartTime, DateUtil.EXPANDED_DATE_TIME_FORMAT);
            int minutesBetween = DateUtil.secondsBetween(startTime, date);
            int record_minutes = Integer.parseInt(eduUserFeignService.getSysConfig("record_minutes")) * 60;
            if(minutesBetween >= record_minutes){
                throw new BizException("加入失败，录制超时");
            }
            roomResult.setSurplusTime(record_minutes - minutesBetween);
        }

        SysUser sysUser = sysUserFeignService.queryUserInfo();
        String userId = sysUser.getId().toString();
        String realName = sysUser.getRealName();
//        ExamRoom examRoom = eduUserFeignService.getExamRoom(Integer.parseInt(roomId));

        RoomResult.MemberResult userResult = new RoomResult.MemberResult();
        List<RoomMember> memberList = roomMemberDao.findByRidAndUid(recordedRoomId, userId);
        ExamRoomStudentRelation examRoomStudentRelation = eduUserFeignService.getExamRoomStudentRelation(examRegistrationId);
        if(examRoomStudentRelation == null){
            throw new BizException("学员考试数据异常");
        }
        if(examRoomStudentRelation.getClassroomSwitch() == 0){
            throw new BizException("考级入口已关闭");
        }
        if (memberList.isEmpty()) {
            saveRoomMember(userId,sysUser.getAvatar(), realName, recordedRoomId, Student.getValue(), true,true, date);
            /*IMApiResultInfo resultInfo = imHelper.joinGroup(new String[]{userId}, recordedRoomId, recordedRoomId);
            if (!resultInfo.isSuccess()) {
                throw new ApiException(ErrorEnum.ERR_CREATE_ROOM_ERROR, resultInfo.getErrorMessage());
            }*/
            userResult.setMicrophone(true);
            userResult.setCamera(true);
            userResult.setHandUp(false);
            userResult.setJoinTime(date);
            log.info("joinRecorded user join the room: roomId={} , userId={}, roleEnum={}", recordedRoomId, userId, Student);
        } else {
            RoomMember roomMember = memberList.get(0);
//            roomMemberDao.updateCameraByRidAndUid(roomId, userId, !isDisableCamera);
            userResult.setCamera(roomMember.isCamera());
            userResult.setHandUp(roomMember.isHand());
            userResult.setJoinTime(roomMember.getJoinDt());
            log.info("joinRecorded user exist in the room: roomId={} , userId={}, use the last role={}", recordedRoomId, userId, Student);
        }

        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Join, userId, Student.getValue());
        msg.setTimestamp(date);
        msg.setUserName(realName);
        msg.setCamera(true);
        imHelper.publishMessage(userId, recordedRoomId, msg);

        List<UserInfo> userInfoList = userDao.findByUid(userId);
        if (userInfoList.isEmpty()) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUid(userId);
            userInfo.setName(realName);
            userInfo.setCreateDt(date);
            userInfo.setUpdateDt(date);
            userDao.save(userInfo);
        } else {
            UserInfo user = userInfoList.get(0);
            user.setUpdateDt(date);
            userDao.save(user);
        }

        userResult.setUserName(realName);
        userResult.setUserId(userId);
        userResult.setRole(Student.getValue());
        roomResult.setUserInfo(userResult);
        roomResult.setRoomId(recordedRoomId);
        roomResult.setRegistrationId(examRegistrationId);
        roomResult.setMembers(roomMemberDao.findByRid(recordedRoomId));
        log.info("joinRecorded join success: roomId = {}, userId = {}, userName={}, role = {}", recordedRoomId, userId, Student);
        if(examRegistrationId != null){
            this.publishMessage(eduUserFeignService.getPublishMessage(examRegistrationId));
        }
        return roomResult;
    }

    private BaseResponse getBaseResponse(BaseResponse baseResponse){
        baseResponse.setErrCode(500);
        return baseResponse;
    }

    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    @Override
    public BaseResponse joinRoom(Long registrationId, boolean isAudience, boolean isDisableCamera, boolean isMusicMode, String roomId) throws ApiException, Exception {
        if(registrationId != null){
            StudentExamResultApiDto examResult = eduUserFeignService.getExamResult(registrationId);
            Integer recordFlag = examResult.getRecordFlag();
            if(recordFlag != null && recordFlag == 1){
                joinRecorded(registrationId);
            }
            roomId = examResult.getRoomId();
        }

        CheckUtils.checkArgument(roomId != null, "roomId must't be null");

        log.info("joinRoom: roomId={}, isAudience={}, isDisableCamera={},isMusicMode={}", roomId, isAudience, isDisableCamera,isMusicMode);

        SysUser sysUser = sysUserFeignService.queryUserInfo();
        String userId = sysUser.getId().toString();
        String realName = sysUser.getRealName();
        ExamRoom examRoom = eduUserFeignService.getExamRoom(Integer.parseInt(roomId));
        boolean isAssistant = false;
        if (StringUtils.isNotEmpty(examRoom.getAssistantTeacherUserIdList())){
            List<String> list = Arrays.asList(examRoom.getAssistantTeacherUserIdList().split(","));
            if(list.contains(userId)){
                isAssistant = true;
            }
        }
//        String display = "";
        Date curTime = DateTimeUtils.currentUTC();
        List<Room> roomList = roomDao.findByRid(roomId);
        if (roomList.isEmpty()) {
            saveRoom(roomId, roomId, curTime, null);
            /*IMApiResultInfo resultInfo = imHelper.createGroup(new String[]{userId}, roomId, roomId);
            if (!resultInfo.isSuccess()) {
                log.error("joinRoom IM error: roomId={}, {}", roomId, resultInfo.getErrorMessage());
                throw new ApiException(ErrorEnum.ERR_CREATE_ROOM_ERROR, resultInfo.getErrorMessage());
            }
            else {
                scheduleManager.addExpiredTask(this, roomId);
            }*/
        }/* else {
            display = roomList.get(0).getDisplay();
        }*/
        RoleEnum roleEnum;
        RoomResult roomResult = new RoomResult();
        BaseResponse baseResponse = new BaseResponse(roomResult);
        RoomResult.MemberResult userResult = new RoomResult.MemberResult();
        List<RoomMember> memberList = roomMemberDao.findByRidAndUid(roomId, userId);
        if (memberList.isEmpty()) {
            if(sysUser.getId().equals(examRoom.getMainTeacherUserId())){
                roleEnum = RoleEnum.MainTeacher;
            }else if(isAssistant){
                roleEnum = RoleEnum.AssistantTeacher;
            }else {
                ExamRoomStudentRelation examRoomStudentRelation = eduUserFeignService.getExamRoomStudentRelation(registrationId);
                if(examRoomStudentRelation == null){
                    log.info("user join the room: roomId={} , userId={}", roomId, userId);
                    baseResponse.setErrMsg("学员考试数据异常");
                    return getBaseResponse(baseResponse);
                }
                if(examRoomStudentRelation.getClassroomSwitch() == 0){
                    log.info("user join the room: roomId={} , userId={}", roomId, userId);
                    baseResponse.setErrMsg("考级入口已关闭");
                    return getBaseResponse(baseResponse);
                }
                roleEnum = Student;
            }
            saveRoomMember(userId,sysUser.getAvatar(), realName, roomId, roleEnum.getValue(), !isDisableCamera,!isMusicMode, curTime);
            /*IMApiResultInfo resultInfo = imHelper.joinGroup(new String[]{userId}, roomId, roomId);
            if (!resultInfo.isSuccess()) {
                throw new ApiException(ErrorEnum.ERR_CREATE_ROOM_ERROR, resultInfo.getErrorMessage());
            }*/
            userResult.setMicrophone(true);
            userResult.setCamera(!isDisableCamera);
            userResult.setHandUp(false);
            userResult.setJoinTime(curTime);
            log.info("user join the room: roomId={} , userId={}, roleEnum={}", roomId, userId, roleEnum);
        } else {
            RoomMember roomMember = memberList.get(0);
            roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());
            if(roleEnum == Student){
                ExamRoomStudentRelation examRoomStudentRelation = eduUserFeignService.getExamRoomStudentRelation(registrationId);
                if(examRoomStudentRelation == null){
                    log.info("user join the room: roomId={} , userId={}", roomId, userId);
                    baseResponse.setErrMsg("学员考试数据异常");
                    return getBaseResponse(baseResponse);
                }
                if(examRoomStudentRelation.getClassroomSwitch() == 0){
                    log.info("user join the room: roomId={} , userId={}", roomId, userId);
                    baseResponse.setErrMsg("考级入口已关闭");
                    return getBaseResponse(baseResponse);
                }
            }
//            roomMemberDao.updateCameraByRidAndUid(roomId, userId, !isDisableCamera);
            userResult.setCamera(roomMember.isCamera());
            userResult.setHandUp(roomMember.isHand());
            userResult.setJoinTime(roomMember.getJoinDt());
            log.info("user exist in the room: roomId={} , userId={}, use the last role={}", roomId, userId, roleEnum);
        }

        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Join, userId, roleEnum.getValue());
        msg.setTimestamp(curTime);
        msg.setUserName(realName);
        msg.setCamera(!isDisableCamera);
        imHelper.publishMessage(userId, roomId, msg);
        /*if (roleEnum == RoleEnum.MainTeacher) {
            display = "display://type=0?userId=" + userId + "?uri=";
            updateDisplay(roomId, userId, display, 0);
            log.info("joinRoom, display changed: roomId={}, {}, userId={}", roomId, display, userId);
        } else if (roleEnum == RoleEnum.AssistantTeacher && display.isEmpty()) {
            display = "display://type=1?userId=" + userId + "?uri=";
            updateDisplay(roomId, userId, display, 0);
            log.info("joinRoom, display changed: roomId={}, {}, userId={}", roomId, display, userId);
        }*/

        List<UserInfo> userInfoList = userDao.findByUid(userId);
        if (userInfoList.isEmpty()) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUid(userId);
            userInfo.setName(realName);
            userInfo.setCreateDt(curTime);
            userInfo.setUpdateDt(curTime);
            userDao.save(userInfo);
        } else {
            UserInfo user = userInfoList.get(0);
            user.setUpdateDt(curTime);
            userDao.save(user);
        }

        userResult.setUserName(realName);
        userResult.setUserId(userId);
        userResult.setRole(roleEnum.getValue());
        roomResult.setUserInfo(userResult);
//        roomResult.setDisplay(display);
        roomResult.setRoomId(roomId);
        if (registrationId != null){
            roomResult.setRegistrationId(registrationId);
        }

        roomResult.setMembers(roomMemberDao.findByRid(roomId));
//        List<Whiteboard> whiteboardList = whiteboardDao.findByRid(roomId);
//        roomResult.setWhiteboards(whiteboardList);
        log.info("join success: roomId = {}, userId = {}, userName={}, role = {}", roomId, userId, roleEnum);
        if(registrationId != null){
            this.publishMessage(eduUserFeignService.getPublishMessage(registrationId));
        }
        return baseResponse;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public void signIn(Long roomId) {
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        Integer userId = sysUser.getId();
        log.info("roomSignIn: roomId={}, userId={}", roomId, userId);
        ExamRoom examRoom = eduUserFeignService.getExamRoom(roomId.intValue());
        Date date = new Date();
        boolean isAssistant = false;
        if (StringUtils.isNotEmpty(examRoom.getAssistantTeacherUserIdList())){
            List<String> list = Arrays.asList(examRoom.getAssistantTeacherUserIdList().split(","));
            if(list.contains(userId)){
                isAssistant = true;
            }
        }
        if(userId.equals(examRoom.getMainTeacherUserId()) || isAssistant){
            TeacherAttendance attendance = teacherAttendanceDao.findByTeacherIdAndExaminationBasicIdAndExamRoomId(userId, examRoom.getExaminationBasicId().intValue(), examRoom.getId());
            if(attendance != null){
                return;
            }
            TeacherAttendance teacherAttendance = new TeacherAttendance();
            teacherAttendance.setExamRoomId(examRoom.getId());
            teacherAttendance.setSignInTime(date);
            teacherAttendance.setTeacherId(userId);
            teacherAttendance.setTenantId(examRoom.getTenantId());
            teacherAttendance.setCreateTime(date);
            teacherAttendance.setExaminationBasicId(examRoom.getExaminationBasicId().intValue());
            teacherAttendanceDao.save(teacherAttendance);
        }else {
            StudentAttendance attendance = studentAttendanceDao.findByUserIdAndExaminationBasicIdAndExamRoomId(userId, examRoom.getExaminationBasicId().intValue(), examRoom.getId());
            if(attendance != null){
                return;
            }
            StudentAttendance studentAttendance = new StudentAttendance();
            studentAttendance.setExamRoomId(examRoom.getId());
            studentAttendance.setSignInTime(date);
            studentAttendance.setUserId(userId);
            studentAttendance.setCreateTime(date);
            studentAttendance.setExaminationBasicId(examRoom.getExaminationBasicId().intValue());
            studentAttendanceDao.save(studentAttendance);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public void signOut(Long roomId) {
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        Integer userId = sysUser.getId();
        log.info("roomSignIn: roomId={}, userId={}", roomId, userId);
        ExamRoom examRoom = eduUserFeignService.getExamRoom(roomId.intValue());
        Date date = new Date();
        boolean isAssistant = false;
        if (StringUtils.isNotEmpty(examRoom.getAssistantTeacherUserIdList())){
            List<String> list = Arrays.asList(examRoom.getAssistantTeacherUserIdList().split(","));
            if(list.contains(userId)){
                isAssistant = true;
            }
        }
        if(userId.equals(examRoom.getMainTeacherUserId()) || isAssistant){
            TeacherAttendance attendance = teacherAttendanceDao.findByTeacherIdAndExaminationBasicIdAndExamRoomId(userId, examRoom.getExaminationBasicId().intValue(), examRoom.getId());
            if(attendance == null){
                attendance = new TeacherAttendance();
                attendance.setExamRoomId(examRoom.getId());
                attendance.setSignInTime(date);
                attendance.setSignOutTime(date);
                attendance.setTeacherId(userId);
                attendance.setTenantId(examRoom.getTenantId());
                attendance.setCreateTime(date);
                attendance.setExaminationBasicId(examRoom.getExaminationBasicId().intValue());
                teacherAttendanceDao.save(attendance);
            }else {
                attendance.setSignOutTime(date);
                teacherAttendanceDao.saveAndFlush(attendance);
            }
        }else {
            StudentAttendance attendance = studentAttendanceDao.findByUserIdAndExaminationBasicIdAndExamRoomId(userId, examRoom.getExaminationBasicId().intValue(), examRoom.getId());
            if(attendance == null){
                attendance = new StudentAttendance();
                attendance.setExamRoomId(examRoom.getId());
                attendance.setSignInTime(date);
                attendance.setSignOutTime(date);
                attendance.setUserId(userId);
                attendance.setCreateTime(date);
                attendance.setExaminationBasicId(examRoom.getExaminationBasicId().intValue());
                studentAttendanceDao.save(attendance);
            }else {
                attendance.setSignOutTime(date);
                studentAttendanceDao.saveAndFlush(attendance);
            }
        }
    }

    private 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);
        try {
            roomDao.save(room);
        }catch (Exception e){
            throw new BizException("当前线上课人数较多,请稍后重试");
        }
    }

    private void saveRoomMember(String userId, String headUrl, String userName, String roomId, int role, boolean cameraOn,boolean musicModeOn, Date joinTime) {
        RoomMember roomMember = new RoomMember();
        roomMember.setUid(userId);
        roomMember.setName(userName);
        roomMember.setRid(roomId);
        roomMember.setRole(role);
        roomMember.setCamera(cameraOn);
        roomMember.setJoinDt(joinTime);
        roomMember.setMusicMode(musicModeOn);
        roomMember.setHeadUrl(headUrl);
        roomMemberDao.save(roomMember);
    }

    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    @Override
    public Boolean leaveRoom(Long registrationId,String userId,String roomId) throws Exception {
        if(registrationId != null){
            StudentExamResultApiDto examResult = eduUserFeignService.getExamResult(registrationId);
            Integer recordFlag = examResult.getRecordFlag();
            if(recordFlag != null && recordFlag == 1){
                levelRecorded(registrationId);
            }
            roomId = examResult.getRoomId();
        }

        SysUser user = null;
        if(StringUtils.isEmpty(userId)){
            user = sysUserFeignService.queryUserInfo();
            userId = user.getId().toString();
        }else {
            user = sysUserFeignService.queryUserById(Integer.parseInt(userId));
        }
        log.info("leaveRoom: roomId={}, userId={}", roomId,userId);

        CheckUtils.checkArgument(userId != null, "userId must't be null");
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        List<Room> roomList = roomDao.findByRid(roomId);
        if (roomList.size() == 0) {
            log.error("room : {} not exist ", roomId);
            return false;
//            throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
        }
        List<RoomMember> roomMemberList = roomMemberDao.findByRidAndUid(roomId, userId);
        if (roomMemberList.size() == 0) {
            log.error("{} not exist in room: {}", userId, roomId);
            return false;
//            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        }

        int userRole = roomMemberList.get(0).getRole();
        log.info("leaveRoom: roomId={}, role={}", roomId, RoleEnum.getEnumByValue(userRole));

        /*if (userRole == RoleEnum.MainTeacher.getValue() || userRole == RoleEnum.AssistantTeacher.getValue()) {
            if (isUserDisplay(roomList.get(0), userId)) {
                updateDisplay(roomId, userId, "", 0);
                log.info("clear display cause speaker leave: roomId={}", roomId);
            } else {
                log.info("don't update current display: room={}, role={}", roomList.get(0), RoleEnum.getEnumByValue(userRole));
            }
        } else {
            log.info("don't update current display: room={}, userRole={}", roomList.get(0), RoleEnum.getEnumByValue(userRole));
        }*/

        if (roomMemberDao.countByRid(roomId) == 1) {
            roomMemberDao.deleteUserByRidAndUid(roomId, userId);
            roomDao.deleteByRid(roomId);
            /*IMApiResultInfo apiResultInfo = null;
            try {
                roomMemberDao.deleteUserByRidAndUid(roomId, userId);
                roomDao.deleteByRid(roomId);
                apiResultInfo = imHelper.dismiss(userId, roomId);
                if (apiResultInfo.getCode() == 200) {
                    roomMemberDao.deleteUserByRidAndUid(roomId, userId);
                    roomDao.deleteByRid(roomId);
//                    deleteWhiteboardByUser(roomId, userId);
                    log.info("dismiss the room: {},userId: {}", roomId,userId);
                } else {
                    log.error("{} exit {} room error: {}", userId, roomId, apiResultInfo.getErrorMessage());
                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
                }
            } catch (Exception e) {
                log.error("{} exit {} room error: {}", userId, roomId, e.getMessage());
                throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, e.getMessage());
            }*/
        } else {
            roomMemberDao.deleteUserByRidAndUid(roomId, userId);

            /*IMApiResultInfo apiResultInfo = null;
            try {
                apiResultInfo = imHelper.quit(new String[]{userId}, roomId);
                if (apiResultInfo.isSuccess()) {
                    roomMemberDao.deleteUserByRidAndUid(roomId, userId);
                    MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, userId, userRole);
                    msg.setUserName(user.getUsername());
                    imHelper.publishMessage(userId, roomId, msg);
                    imHelper.quit(new String[]{userId}, roomId);
                    log.info("quit group: roomId={},userId: {}", roomId,userId);
                } else {
                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
                }
            } catch (Exception e) {
                log.error("leave room error: roomId={}, {}", roomId, e.getMessage());
                throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR);
            }*/
        }
        userDao.deleteByUid(userId);
//        this.signOut(Long.parseLong(roomId));
        if(registrationId != null){
            this.publishMessage(eduUserFeignService.getPublishMessage(registrationId));
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    @Override
    public Boolean levelRecorded(Long registrationId) throws Exception {
        StudentExamResultApiDto examResult = eduUserFeignService.getExamResult(registrationId);
        String roomId = "recorded" + examResult.getRoomId();
        SysUser user = sysUserFeignService.queryUserInfo();
        String userId = user.getId().toString();
        log.info("levelRecorded: roomId={}, userId={}", roomId,userId);

        CheckUtils.checkArgument(userId != null, "userId must't be null");
        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
        List<Room> roomList = roomDao.findByRid(roomId);
        if (roomList.size() == 0) {
            log.error("levelRecorded room : {} not exist ", roomId);
            return false;
        }
        List<RoomMember> roomMemberList = roomMemberDao.findByRidAndUid(roomId, userId);
        if (roomMemberList.size() == 0) {
            log.error("levelRecorded {} not exist in room: {}", userId, roomId);
            return false;
        }

        int userRole = roomMemberList.get(0).getRole();
        log.info("levelRecorded: roomId={}, role={}", roomId, RoleEnum.getEnumByValue(userRole));

        if (roomMemberDao.countByRid(roomId) == 1) {
            roomMemberDao.deleteUserByRidAndUid(roomId, userId);
            roomDao.deleteByRid(roomId);

            /*IMApiResultInfo apiResultInfo = null;
            try {
                apiResultInfo = imHelper.dismiss(userId, roomId);
                if (apiResultInfo.getCode() == 200) {
                    roomMemberDao.deleteUserByRidAndUid(roomId, userId);
                    roomDao.deleteByRid(roomId);
                    log.info("levelRecorded dismiss the room: {},userId: {}", roomId,userId);
                } else {
                    log.error("levelRecorded {} exit {} room error: {}", userId, roomId, apiResultInfo.getErrorMessage());
                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
                }
            } catch (Exception e) {
                log.error("levelRecorded {} exit {} room error: {}", userId, roomId, e.getMessage());
                throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, e.getMessage());
            }*/
        } else {
            roomMemberDao.deleteUserByRidAndUid(roomId, userId);

            /*IMApiResultInfo apiResultInfo = null;
            try {
                apiResultInfo = imHelper.quit(new String[]{userId}, roomId);
                if (apiResultInfo.isSuccess()) {
                    roomMemberDao.deleteUserByRidAndUid(roomId, userId);
                    MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, userId, userRole);
                    msg.setUserName(user.getUsername());
                    imHelper.publishMessage(userId, roomId, msg);
                    imHelper.quit(new String[]{userId}, roomId);
                    log.info("levelRecorded quit group: roomId={},userId: {}", roomId,userId);
                } else {
                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
                }
            } catch (Exception e) {
                log.error("levelRecorded leave room error: roomId={}, {}", roomId, e.getMessage());
                throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR);
            }*/
        }
        userDao.deleteByUid(userId);
        if(registrationId != null){
            this.publishMessage(eduUserFeignService.getPublishMessage(registrationId));
        }
        return true;
    }

    @Override
    public void joinGroup(String userIds, String groupId, String groupName) throws Exception {
        imHelper.joinGroup(userIds.split(","),groupId,groupName);
    }

    @Override
    public void quitGroup(String userIds, String groupId) throws Exception {
        imHelper.quit(userIds.split(","),groupId);
    }

    @Override
    public void dismissGroup(String userId, String groupId) throws Exception {
        imHelper.dismiss(userId,groupId);
    }

    private 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(rollbackFor = Exception.class)
    @Override
    public void destroyRoom(String roomId) {
        roomDao.deleteByRid(roomId);
        whiteboardDao.deleteByRid(roomId);
        List<Room> roomList = roomDao.findByRid(roomId);
        if (roomList.isEmpty()) {
            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
    @Override
    public Boolean downgrade(String roomId, List<ReqChangeUserRoleData.ChangedUser> users) throws ApiException, 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();
        List<Room> roomList = roomDao.findByRid(roomId);
        if (roomList.isEmpty()) {
            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 {
                List<RoomMember> oldUsers = roomMemberDao.findByRidAndUid(roomId, changedUserId);
                if (oldUsers.size() > 0) {
                    /*if (changedRole.equals(RoleEnum.RoleAudience)) {
                        int r = roomMemberDao.updateRoleByRidAndUid(roomId, changedUserId, changedRole.getValue());
                        RoleChangedMessage.ChangedUser u = new RoleChangedMessage.ChangedUser(changedUserId, changedRole.getValue());
                        List<UserInfo> userInfoList = userDao.findByUid(changedUserId);
                        if (!userInfoList.isEmpty()) {
                            u.setUserName(userInfoList.get(0).getName());
                        }
                        changedUsers.add(u);
                        log.info("change the role: {}, {}, {}, result: {}", roomId, userId, changedRole, r);
                        result = true;
                    }*/
                    if (oldUsers.get(0).getRole() != Student.getValue() && isUserDisplay(roomList.get(0), oldUsers.get(0).getUid())) {
                        updateDisplay(roomId, userId, "", 1);
                    } else {
                        log.info("don't update display: room={}, userRole={}", roomList.get(0), RoleEnum.getEnumByValue(oldUsers.get(0).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
    public Boolean kickMember(ReqUserData data) throws ApiException, Exception {
        StudentExamResultApiDto examResult = eduUserFeignService.getExamResult(data.getRegistrationId());
        String roomId = examResult.getRoomId();

        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");

        List<RoomMember> kickedUsers = roomMemberDao.findByRidAndUid(roomId, userId);
        int result = roomMemberDao.deleteUserByRidAndUid(roomId, userId);
        log.info("kickMember: roomId={}, userId={}, result = {}", roomId, userId, result);
        if (result == 0) {
            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        } else {
            MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Kick, userId, kickedUsers.get(0).getRole());
            List<UserInfo> userInfoList = userDao.findByUid(userId);
            if (!userInfoList.isEmpty()) {
                msg.setUserName(userInfoList.get(0).getName());
            }
            if("recorded".equals(data.getType())){
                msg.setRoomId("recorded" + roomId);
            }
            IMApiResultInfo apiResultInfo = imHelper.publishMessage(userId, roomId, msg, 1);
            if (!apiResultInfo.isSuccess()) {
                throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR);
            }
            Thread.sleep(50);
            log.info("published msg: msg={}", msg);
            List<Room> roomList = roomDao.findByRid(roomId);
            if (kickedUsers.get(0).getRole() != Student.getValue() && isUserDisplay(roomList.get(0), userId)) {
                updateDisplay(roomId, userId, "", 1);
            } else {
                log.info("don't update display: room={}, userRole={}", roomId, RoleEnum.getEnumByValue(kickedUsers.get(0).getRole()));
            }
        }
        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
    public Boolean display(String roomId, int type, String uri) throws ApiException, 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, RoleEnum.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 {
            display += "?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
    public String createWhiteBoard(String roomId) throws ApiException, 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();
            List<Room> roomList = roomDao.findByRid(roomId);
            int whiteboardNameIndex = roomList.get(0).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
    public Boolean deleteWhiteboard(String roomId, String whiteBoardId) throws ApiException, 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");

        List<Room> roomList = roomDao.findByRid(roomId);
        CheckUtils.checkArgument(!roomList.isEmpty(), "room not exist");

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

        String display = roomList.get(0).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={}", roomList.get(0));
        }

        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
    public List<RoomResult.WhiteboardResult> getWhiteboard(String roomId) throws ApiException, 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
    public Boolean turnWhiteBoardPage(String roomId, String whiteBoardId, int page) throws ApiException, 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<Room> roomList = roomDao.findByRid(roomId);
        CheckUtils.checkArgument(!roomList.isEmpty(), "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
    public Boolean controlDevice(String roomId, String userId, DeviceTypeEnum typeEnum, boolean enable) throws ApiException, 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");
        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, userId), "room member not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        log.info("controlDevice: userId={}, typeEnum={}, onOff={}", userId, typeEnum, enable);

        if (enable) {
            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 {
                roomMemberDao.updateMusicByRidAndUid(roomId, userId, false);
            }
            DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), false);
            deviceResourceMessage.setUserId(userId);
            List<UserInfo> userInfoList = userDao.findByUid(userId);
            if (!userInfoList.isEmpty()) {
                deviceResourceMessage.setUserName(userInfoList.get(0).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(), Student.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 {
            throw new ApiException(ErrorEnum.ERR_REQUEST_PARA_ERR);
        }
        return result;
    }

    @Override
    public Boolean approveControlDevice(String roomId, String ticket) throws ApiException, 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());
        } 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
    public Boolean rejectControlDevice(String roomId, String ticket) throws ApiException, 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 ApiException, 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;
        if (type.equals(DeviceTypeEnum.Camera)) {
            result = roomMemberDao.updateCameraByRidAndUid(roomId, userId, enable);
            deviceResourceMessage = new DeviceStateChangedMessage(type.ordinal(), enable);
        } else if (type.equals(DeviceTypeEnum.Microphone)){
            result = roomMemberDao.updateMicByRidAndUid(roomId, userId, enable);
            deviceResourceMessage = new DeviceStateChangedMessage(type.ordinal(), enable);
        }else {
            result = roomMemberDao.updateMusicByRidAndUid(roomId, userId, enable);
            deviceResourceMessage = new DeviceStateChangedMessage(type.ordinal(), enable);
        }
        deviceResourceMessage.setUserId(userId);
        imHelper.publishMessage(userId, roomId, deviceResourceMessage, 1);
        log.info("syncDeviceState : {}, {}, result = {}", roomId, enable, result);
        return true;
    }

    @Override
    public List<RoomResult.MemberResult> getMembers(String roomId) throws ApiException, 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());

            return roomResult.getMembers();
        }
        return null;
    }

    /*@Override
    public Boolean applySpeech(String roomId) throws ApiException, 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
    public Boolean approveSpeech(String roomId, String ticket) throws ApiException, 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(), Student.getValue());

        SpeechResultMessage msg = new SpeechResultMessage(SpeechResultMessage.Action_Approve);
        List<UserInfo> userInfoList = userDao.findByUid(taskInfo.getApplyUserId());
        msg.setOpUserId(userId);
        msg.setOpUserName(authUser.getUsername());
        msg.setReqUserId(taskInfo.getApplyUserId());
        if (!userInfoList.isEmpty()) {
            msg.setReqUserName(userInfoList.get(0).getName());
        }
        msg.setRole(Student.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(), Student.getValue());
        if (!userInfoList.isEmpty()) {
            user.setUserName(userInfoList.get(0).getName());
        }
        changedUserList.add(user);
        rcMsg.setUsers(changedUserList);
        imHelper.publishMessage(userId, roomId, rcMsg, 1);

        return true;
    }

    @Override
    public Boolean rejectSpeech(String roomId, String ticket) throws ApiException, 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(Student.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());
        }
    }

    /*private 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
    public Boolean transfer(String roomId, String userId) throws ApiException, 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);
        List<RoomMember> roomMemberList = roomMemberDao.findByRidAndUid(roomId, userId);
        if (roomMemberList.size() == 0) {
            log.error("assistant transfer error: {} toUser = {}, opUser={}", roomId, userId, userId);
            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        }

        List<Room> roomList = roomDao.findByRid(roomId);
        if (roomList.size() == 0) {
            log.error("assistant transfer error: {} toUser = {}, opUser={}", roomId, userId, userId);
            throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
        }

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

        roomMemberDao.updateRoleByRidAndUid(roomId, userId, Student.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
    public Boolean inviteUpgradeRole(String roomId, String targetUserId, int targetRole) throws ApiException, 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);

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

//        checkOverMax(roomId, targetUser.get(0), 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
    public Boolean approveUpgradeRole(String roomId, String ticket) throws ApiException, 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);

        List<RoomMember> targetUser = roomMemberDao.findByRidAndUid(roomId, userId);
        if (targetUser.isEmpty()) {
            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.get(0), 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
    public Boolean rejectUpgradeRole(String roomId, String ticket) throws ApiException, 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
    public Boolean changeRole(String roomId, String targetUserId, int targetRole) throws ApiException, 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(RoleEnum.RoleTeacher), "only set to teacher");
        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, targetUserId), "room member not exist");
        SysUser authUser = sysUserFeignService.queryUserInfo();
        String userId = authUser.getId().toString();

        List<RoomMember> targetUser = roomMemberDao.findByRidAndUid(roomId, targetUserId);
        if (targetUser.isEmpty()) {
            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
        } else {
            if (!RoleEnum.getEnumByValue(targetUser.get(0).getRole()).equals(Student)) {
                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, RoleEnum.MainTeacher.getValue());
        if (!teachers.isEmpty()) {
            roomMemberDao.updateRoleByRidAndUid(roomId, teachers.get(0).getUid(), Student.getValue());
            RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(teachers.get(0).getUid(), Student.getValue());
            List<UserInfo> userInfoList = userDao.findByUid(teachers.get(0).getUid());
            if (!userInfoList.isEmpty()) {
                user.setUserName(userInfoList.get(0).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);
        List<UserInfo> userInfoList = userDao.findByUid(targetUserId);
        if (!userInfoList.isEmpty()) {
            user.setUserName(userInfoList.get(0).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
    public Boolean memberOnlineStatus(List<ReqMemberOnlineStatus> statusList, String nonce, String timestamp, String signature) throws ApiException, 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
    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 == RoleEnum.MainTeacher.getValue() || userRole == RoleEnum.AssistantTeacher.getValue()) {
                    List<Room> rooms = roomDao.findByRid(member.getRid());
                    if (rooms.isEmpty()) {
                        break;
                    }
                    if (isUserDisplay(rooms.get(0), 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, isolation = Isolation.READ_COMMITTED)
    public boolean statusSync(String roomId,String userId) throws Exception {
        log.info("statusSync: roomId={}, userId={}", roomId, userId);
        List<RoomMember> byRidAndUid = roomMemberDao.findByRidAndUid(roomId, userId);
        if(byRidAndUid.size() > 0){
//            SysUser sysUser = sysUserFeignService.queryUserById(Integer.parseInt(userId));

        	List<Room> roomList = roomDao.findByRid(roomId);
            if (roomList.size() == 0) {
                log.error("room : {} not exist ", roomId);
                throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
            }

            List<RoomMember> roomMemberList = roomMemberDao.findByRidAndUid(roomId, userId);
            if (roomMemberList.size() == 0) {
                log.error("{} not exist in room: {}", userId, roomId);
                throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
            }

            int userRole = roomMemberList.get(0).getRole();
            log.info("statusSync: roomId={}, role={}", roomId, RoleEnum.getEnumByValue(userRole));

            if (userRole == RoleEnum.MainTeacher.getValue() || userRole == RoleEnum.AssistantTeacher.getValue()) {
                if (isUserDisplay(roomList.get(0), userId)) {
                    try {
                        updateDisplay(roomId, userId, "", 0);
                    }catch (Exception e){
                        log.error(e.getMessage());
                    }
                    log.info("clear display cause speaker leave: roomId={}", roomId);
                } else {
                    log.info("don't update current display: room={}, role={}", roomList.get(0), RoleEnum.getEnumByValue(userRole));
                }
            } else {
                log.info("don't update current display: room={}, userRole={}", roomList.get(0), RoleEnum.getEnumByValue(userRole));
            }

            if (roomMemberDao.countByRid(roomId) == 1) {
                IMApiResultInfo apiResultInfo = null;
                try {
                    apiResultInfo = imHelper.dismiss(userId, roomId);
                    if (apiResultInfo.getCode() == 200) {
//                        roomMemberDao.deleteUserByRidAndUid(roomId, userId);
                        roomDao.deleteByRid(roomId);
                        deleteWhiteboardByUser(roomId, userId);
                        log.info("dismiss the room: {},userId: {}", roomId,userId);
                    } else {
                        log.error("{} exit {} room error: {}", userId, roomId, apiResultInfo.getErrorMessage());
                        throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
                    }
                } catch (Exception e) {
                    log.error("{} exit {} room error: {}", userId, roomId, e.getMessage());
                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, e.getMessage());
                }
            } else {
                IMApiResultInfo apiResultInfo = null;
                try {
                    apiResultInfo = imHelper.quit(new String[]{userId}, roomId);
                    if (apiResultInfo.isSuccess()) {
//                        roomMemberDao.deleteUserByRidAndUid(roomId, userId);
                        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, userId, userRole);
                        imHelper.publishMessage(userId, roomId, msg);
                        imHelper.quit(new String[]{userId}, roomId);
                        log.info("quit group: roomId={},userId: {}", roomId,userId);
                    } else {
                        throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
                    }
                } catch (Exception e) {
                    log.error("leave room error: roomId={}, {}", roomId, e.getMessage());
                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR);
                }
            }
            userDao.deleteByUid(userId);
            return true;
        }
        return true;
    }

    @Override
    public void onChannelNotify(ChannelStateNotify notify) throws IOException {

    }

    @Override
    public void publishMessage(PublishMessageDto publishMessageDto) throws Exception {
        IMApiResultInfo apiResultInfo = imHelper.publishMessage(publishMessageDto.getUserId(),
                publishMessageDto.getRoomId(),
                publishMessageDto.getMemberChangedMessage(),
                publishMessageDto.getIncludeSender());
    }

    /*@Override
    public void sendImPlayMidiMessage(PlayMidiMessageData playMidiMessageData) throws Exception {
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        if(sysUser == null){
            throw new BizException("用户信息获取失败");
        }
//        roomService.sendImPlayMidiMessage(playMidiMessageData);
        GroupMessage groupMessage = new GroupMessage();
        String content = playMidiMessageData.getContent();
        String roomId = playMidiMessageData.getRoomId();
        CustomMessage customMessage = JSONObject.parseObject(content,CustomMessage.class);
        groupMessage.setContent(customMessage);
        groupMessage.setTargetId(roomId.split(","));
        groupMessage.setSenderId(sysUser.getId().toString());
        groupMessage.setObjectName("DY:PlayMidiMessage");
        messageService.groupSend(groupMessage);
        //记录节拍器消息
        String userId = playMidiMessageData.getUserId();
//        long courseId = Long.parseLong(roomId.substring(1));
        *//*if(StringUtils.isEmpty(userId)){
            //批量操作节拍器状态
            courseScheduleStudentPaymentDao.openPlayMidi(courseId,userId,content);
            roomMemberDao.updatePlayMidiJsonByRidAndRole(roomId,RoleEnum.RoleStudent.getValue(), content);
        }else {
            //关闭某个学员
            if(!customMessage.getEnable()){
                roomMemberDao.updatePlayMidiJsonByRidAndRole(playMidiMessageData.getRoomId(),RoleEnum.RoleTeacher.getValue(), null);
            }
            roomMemberDao.updatePlayMidiJsonByRidAndUid(playMidiMessageData.getRoomId(),userId,content);
        }*//*
    }*/

    /*@Override
    public void sendImPlayMidiMessage(PlayMidiMessageData playMidiMessageData) {
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        if(sysUser == null){
            throw new BizException("用户信息获取失败");
        }
        sysMessageService.batchSendImPlayMidiMessage(sysUser.getId().toString(),playMidiMessageData.getRoomId(),playMidiMessageData.getContent());
    }*/

    private void updateDisplay(String roomId, String senderId, String display, Integer isIncludeSender) throws ApiException, Exception {
        roomDao.updateDisplayByRid(roomId, display);
        DisplayMessage displayMessage = new DisplayMessage(display);
        imHelper.publishMessage(senderId, roomId, displayMessage, isIncludeSender);
    }
    private boolean isTeacherDisplay(Room room, String userId) {
        return !room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId);
    }

    private boolean isTeacherDisplayWhiteboard(Room room, String userId) {
        return !room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId) && room.getDisplay().contains("type=2");
    }

    private boolean isAssistantDisplay(Room room, String userId) {
        return !room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId);
    }

    private 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;
    }
}
