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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.microsvc.toolkit.middleware.rtc.RTCRoomPluginService;
import com.microsvc.toolkit.middleware.rtc.message.TencentRequest;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.biz.dal.dao.ImNetworkRoomDao;
import com.yonge.cooleshow.biz.dal.dao.SysConfigDao;
import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
import com.yonge.cooleshow.biz.dal.dto.*;
import com.yonge.cooleshow.biz.dal.entity.*;
import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
import com.yonge.cooleshow.biz.dal.enums.ImNetworkDeviceTypeEnum;
import com.yonge.cooleshow.biz.dal.enums.RoleEnum;
import com.yonge.cooleshow.biz.dal.service.*;
import com.yonge.cooleshow.biz.dal.support.IMHelper;
import com.yonge.cooleshow.common.constant.SysConfigConstant;
import com.yonge.cooleshow.common.entity.BaseResponse;
import com.yonge.cooleshow.common.enums.ErrorEnum;
import com.yonge.toolset.base.exception.BizException;
import com.yonge.toolset.utils.date.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 网络教室房间(ImNetworkRoom)表服务实现类
 *
 * @author zx
 * @since 2022-03-30 16:36:36
 */
@Service("imNetworkRoomService")
public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNetworkRoom> implements ImNetworkRoomService {

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

    @Resource
    private SysUserService sysUserService;
    @Resource
    private TeacherDao teacherDao;
    @Resource
    private ImNetworkRoomMemberService imNetworkRoomMemberService;
    @Resource
    private CourseScheduleStudentPaymentService courseScheduleStudentPaymentService;
    @Resource
    private CourseScheduleStudentMusicSheetService courseScheduleStudentMusicSheetService;
    @Resource
    private CourseScheduleService courseScheduleService;
    @Resource
    private SysConfigDao sysConfigDao;
    @Resource
    private IMHelper imHelper;
    @Resource
    private MusicSheetAccompanimentService musicSheetAccompanimentService;
    @Resource
    private MusicSheetService musicSheetService;
    @Resource
    private StudentAttendanceService studentAttendanceService;
    @Resource
    private TeacherAttendanceService teacherAttendanceService;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Resource
    private ImGroupService imGroupService;
    @Resource
    private RoomService roomService;
    @Resource
    private ImNetworkRoomMemberStatusService imNetworkRoomMemberStatusService;
    @Resource
    private ImService imService;
    @Resource
    private TransactionTemplate transactionTemplate;

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

    @Override
    public BaseResponse<ImNetworkRoomResult> joinRoom(String roomId, RoleEnum userRole) {
        // 当前登录用户ID
        Long userId = sysUserService.getUserId();
        CourseSchedule courseSchedule = Optional.ofNullable(courseScheduleService.getById(roomId)).
                orElseThrow(()->new BizException("房间信息不存在"));

        log.info("joinRoom params：roomId:{},userRole:{},userId:{}",roomId,userRole,userId);
        BasicUserInfo sysUser = Optional.ofNullable(teacherDao.getBasicUserInfo(userId)).
                orElseThrow(()-> new BizException("用户信息不存在"));
        Date now = new Date();
        courseSchedule.setUpdatedTime(now);
        //是否提前进入教室
        if (roomNotStart(courseSchedule)) {
            return BaseResponse.failed(ErrorEnum.ROOM_NOT_START);
        }
        //获取老师基本信息
        BasicUserInfo basicUserInfo = teacherDao.getBasicUserInfo(courseSchedule.getTeacherId());
        //XX分钟关闭房间
        String configValue = sysConfigDao.findConfigValue(SysConfigConstant.DESTROY_EXPIRED_PRACTICE_ROOM_MINUTE);

        ImNetworkRoomResult joinRoomResult = new ImNetworkRoomResult();
        String imUserId = imGroupService.getImUserId(sysUser.getUserId(), userRole);
        List<CourseScheduleStudentMusicSheetResult> studentMusicSheetResults =
                courseScheduleStudentMusicSheetService.getDao().queryBySheetIdAndCourseId(null,
                        Long.parseLong(roomId), null, null, null);
        //获取学员列表
        List<CourseScheduleStudentPayment> studentPayments =
                courseScheduleStudentPaymentService.lambdaQuery().eq(CourseScheduleStudentPayment::getCourseId,roomId).list();
        List<Long> studentIds = studentPayments.stream().map(CourseScheduleStudentPayment::getUserId).collect(Collectors.toList());
        //事务处理
        this.transaction(() -> {
            //初始化房间信息
            ImNetworkRoom room = null;
            try {
                room = roomService.initRoom(roomId,userRole,imUserId);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            BeanUtils.copyProperties(room,joinRoomResult);
            //定时销毁房间
            //初始化房间用户信息
            ImNetworkRoomMember roomMember = imNetworkRoomMemberService.initRoomMember(roomId,sysUser,userRole,!courseSchedule.getMuteAll());
            if(!CollectionUtils.isEmpty(studentMusicSheetResults)){
                List<CourseScheduleStudentMusicSheetResult> musicScores = studentMusicSheetResults.stream().
                        filter(e -> e.getUserId().equals(userId)).collect(Collectors.toList());
                String toJSONString = JSON.toJSONString(musicScores, SerializerFeature.DisableCircularReferenceDetect);
                roomMember.setScheduleStudentMusicScores(JSON.parseArray(toJSONString, CourseScheduleStudentMusicSheetResult.class));
            }
            ImNetworkRoomMemberResult memberResult = JSONObject.parseObject(JSONObject.toJSONString(roomMember), ImNetworkRoomMemberResult.class);
            memberResult.setJoinTime(roomMember.getJoinTime().getTime());
            memberResult.setCamera(roomMember.isCameraFlag());
            memberResult.setMicrophone(roomMember.isMicFlag());
            memberResult.setMusicModeOn(roomMember.isMusicModeFlag());
            memberResult.setHandUpOn(roomMember.isHandFlag());
            joinRoomResult.setUserInfo(memberResult);
            //获取教室剩余时长
            joinRoomResult.setSurplusTime(DateUtil.secondsBetween(now, courseSchedule.getEndTime()));
            //获取房间所有成员
            List<ImNetworkRoomMemberResult> members = imNetworkRoomMemberService.getDao().queryByRoomId(roomId);
            //如果是老师，
            if(userRole == RoleEnum.RoleTeacher){
                //重置节拍器数据
                imNetworkRoomMemberStatusService.lambdaUpdate().set(ImNetworkRoomMemberStatus::getPlayMidi,null)
                        .eq(ImNetworkRoomMemberStatus::getRoomId,roomId).update();
                //获取所有学员的伴奏下载详情
                setMusicSheetList(members,Long.parseLong(roomId));
            }else {
                //获取节拍器信息
                joinRoomResult.setPlayMidiJson(imNetworkRoomMemberStatusService.getMemberMidi(roomId));
            }
            joinRoomResult.setMembers(members);
            //课程结束后关闭教室的时间
            joinRoomResult.setAutoCloseNetworkRoomTime(configValue);
            // 创建IM群聊
            //创建并加入群组
            basicUserInfo.setImUserId(imGroupService.getImUserId(basicUserInfo.getUserId(),RoleEnum.RoleTeacher));
            try {
                imGroupService.joinImGroup(roomId, basicUserInfo,
                        courseSchedule.getServiceProvider(), studentIds);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            //获取腾讯所需基本信息配置
            joinRoomResult.setRtcRoomConfig(roomService.getRtcRoomConfig(courseSchedule.getServiceProvider(),roomMember));
        });

        joinRoomResult.setGroupId(roomId);
        joinRoomResult.setStudentNums(studentIds.size());
        redisTemplate.opsForValue().set("joinRoomSuccess:"+roomId+imUserId,imUserId,1L,TimeUnit.DAYS);
        return BaseResponse.success(joinRoomResult);
    }

    @Override
    public BaseResponse<ImNetworkRoomResult> roomInfo(ImNetworkBaseDto imNetworkBaseDto) {
        // 当前登录用户ID
        String roomId = imNetworkBaseDto.getRoomId();
        Long userId = sysUserService.getUserId();
        CourseSchedule courseSchedule = Optional.ofNullable(courseScheduleService.getById(imNetworkBaseDto.getRoomId())).
                orElseThrow(()->new BizException("房间信息不存在"));

        RoleEnum userRole = RoleEnum.RoleStudent;
        if(imNetworkBaseDto.getClientType() == ClientEnum.TEACHER){
            userRole = RoleEnum.RoleTeacher;
        }
        log.info("joinRoom params：roomId:{},userRole:{},userId:{}",roomId,userRole,userId);

        ImNetworkRoomResult joinRoomResult = new ImNetworkRoomResult();
        //初始化房间信息
        ImNetworkRoom room = roomService.lambdaQuery().eq(ImNetworkRoom::getRoomId,roomId).last("LIMIT 1").one();
        if(Objects.isNull(room)){
            return BaseResponse.failed(ErrorEnum.ROOM_NOT_START);
        }
        BeanUtils.copyProperties(room,joinRoomResult);
        //定时销毁房间
        //初始化房间用户信息
        String imUserId = imGroupService.getImUserId(userId, userRole);
        ImNetworkRoomMember roomMember = imNetworkRoomMemberService.lambdaQuery().eq(ImNetworkRoomMember::getRoomId,roomId)
                .eq(ImNetworkRoomMember::getUserId,imUserId).last("LIMIT 1").one();
        List<CourseScheduleStudentMusicSheetResult> studentMusicSheetResults =
                courseScheduleStudentMusicSheetService.getDao().queryBySheetIdAndCourseId(null, Long.parseLong(roomId), null, null, null);
        if(!CollectionUtils.isEmpty(studentMusicSheetResults)){
            List<CourseScheduleStudentMusicSheetResult> musicScores = studentMusicSheetResults.stream().
                    filter(e -> e.getUserId().equals(userId)).collect(Collectors.toList());
            String toJSONString = JSON.toJSONString(musicScores, SerializerFeature.DisableCircularReferenceDetect);
            List<CourseScheduleStudentMusicSheetResult> lists = JSON.parseArray(toJSONString, CourseScheduleStudentMusicSheetResult.class);
            roomMember.setScheduleStudentMusicScores(lists);
        }
        ImNetworkRoomMemberResult memberResult = JSONObject.parseObject(JSONObject.toJSONString(roomMember), ImNetworkRoomMemberResult.class);
        memberResult.setJoinTime(roomMember.getJoinTime().getTime());
        memberResult.setCamera(roomMember.isCameraFlag());
        memberResult.setMicrophone(roomMember.isMicFlag());
        memberResult.setMusicModeOn(roomMember.isMusicModeFlag());
        memberResult.setHandUpOn(roomMember.isHandFlag());
        joinRoomResult.setUserInfo(memberResult);
        //获取教室剩余时长
        joinRoomResult.setSurplusTime(DateUtil.secondsBetween(new Date(), courseSchedule.getEndTime()));
        //获取房间所有成员
        List<ImNetworkRoomMemberResult> members = imNetworkRoomMemberService.getDao().queryByRoomId(roomId);
        //如果是老师，
        if(userRole == RoleEnum.RoleTeacher){
            //获取所有学员的伴奏下载详情
            setMusicSheetList(members,Long.parseLong(roomId));
        }else {
            //获取节拍器信息
            joinRoomResult.setPlayMidiJson(imNetworkRoomMemberStatusService.getMemberMidi(roomId));
        }
        joinRoomResult.setMembers(members);
        //课程结束后关闭教室的时间
        joinRoomResult.setAutoCloseNetworkRoomTime(sysConfigDao.findConfigValue(SysConfigConstant.DESTROY_EXPIRED_PRACTICE_ROOM_MINUTE));
        //获取腾讯所需基本信息配置
        joinRoomResult.setRtcRoomConfig(roomService.getRtcRoomConfig(courseSchedule.getServiceProvider(),roomMember));
        joinRoomResult.setGroupId(roomId);
        joinRoomResult.setStudentNums(courseScheduleStudentPaymentService.lambdaQuery().eq(CourseScheduleStudentPayment::getCourseId,roomId).count());
        return BaseResponse.success(joinRoomResult);
    }

    private void setMusicSheetList(List<ImNetworkRoomMemberResult> roomMemberList,Long courseScheduleId){
        List<CourseScheduleStudentMusicSheetResult> musicSheetResults = courseScheduleStudentMusicSheetService.getDao().
                queryBySheetIdAndCourseId(null,courseScheduleId,null,null,null);
        if (!CollectionUtils.isEmpty(musicSheetResults)) {
            //分组塞到学员列表中
            Map<String, List<CourseScheduleStudentMusicSheetResult>> musicSheetResultMap = musicSheetResults.stream().
                    collect(Collectors.groupingBy(e-> imGroupService.getImUserId(e.getUserId(),e.getUserType()==1?RoleEnum.RoleTeacher:RoleEnum.RoleStudent)));
            roomMemberList.forEach(e -> {
                if (musicSheetResultMap.containsKey(e.getUserId())) {
                    e.setScheduleStudentMusicScores(musicSheetResultMap.get(e.getUserId()));
                }
            });
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void joinRoomSuccess(String roomId, String imUserId) throws Exception {
        log.info("joinRoomSuccess: roomId={}, imUserId={}", roomId, imUserId);
        // 用户ID
        Long userId = Long.parseLong(imGroupService.analysisImUserId(imUserId));
        // 客户端类型
        ClientEnum clientType = ClientEnum.valueOf(imGroupService.analysisImUserClient(imUserId));

        CourseSchedule courseSchedule = Optional.ofNullable(courseScheduleService.getById(roomId)).
                orElseThrow(()->new BizException("房间信息不存在"));
        BasicUserInfo sysUser = Optional.ofNullable(teacherDao.getBasicUserInfo(userId)).
                orElseThrow(()-> new BizException("用户信息不存在"));

        RoleEnum userRole = RoleEnum.RoleStudent;
        if(Objects.equals(courseSchedule.getTeacherId(),userId)
                && ClientEnum.TEACHER == clientType){

            userRole = RoleEnum.RoleTeacher;
            teacherAttendanceService.signIn(userId,courseSchedule);
        }else {
            studentAttendanceService.signIn(userId,courseSchedule);
        }
        //获取房间信息
        ImNetworkRoom room = roomService.initRoom(roomId, userRole, imUserId);
        //获取房间用户信息
        ImNetworkRoomMember roomMember = imNetworkRoomMemberService.initRoomMember(roomId,sysUser,userRole,!courseSchedule.getMuteAll());
        //发送人员变动消息
        // 获取RTC服务提供方
        imService.publishMemberChangedMessage(roomMember,courseSchedule.getServiceProvider());
        //sendDisplay
        imService.sendDisplay(roomMember, room,courseSchedule.getServiceProvider());
    }

    @Override
    public void joinRoomFailure(String roomId, ClientEnum clientType) {
        // 当前登录用户ID
        Long userId = sysUserService.getUserId();
        log.info("joinRoomFailure: roomId={}, userId={}", roomId, userId);

        // 网络教室用户身份
        RoleEnum userRole = RoleEnum.RoleTeacher;
        if (ClientEnum.STUDENT == clientType) {
            userRole = RoleEnum.RoleStudent;
        }
        // 删除网络教室学员
        String imUserId = imGroupService.getImUserId(userId, userRole);
        redisTemplate.delete("joinRoomSuccess:"+roomId+imUserId);
        imNetworkRoomMemberService.lambdaUpdate().eq(ImNetworkRoomMember::getRoomId,roomId)
                .eq(ImNetworkRoomMember::getUserId,imUserId)
                        .eq(ImNetworkRoomMember::getRole,userRole.getValue()).remove();
    }

    @Override
    public void quitRoomSuccess(String roomId, String imUserId) {
        if(redisTemplate.opsForValue().get("joinRoomSuccess:"+roomId+imUserId) == null){
            log.info("quitRoomSuccess: roomId={}, imUserId={} 未进入房间", roomId, imUserId);
            return;
        }
        log.info("quitRoomSuccess: roomId={}, imUserId={}", roomId, imUserId);
        try {
            // 客户端类型
            ClientEnum clientType = ClientEnum.valueOf(imGroupService.analysisImUserClient(imUserId));
            // 网络教室用户身份
            RoleEnum userRole = clientType == ClientEnum.TEACHER ? RoleEnum.RoleTeacher : RoleEnum.RoleStudent;

            CourseSchedule courseSchedule = Optional.ofNullable(courseScheduleService.getById(roomId))
                    .orElseThrow(()->new BizException("房间信息不存在"));
            Long courseScheduleId = courseSchedule.getId();
            Long userId = Long.parseLong(imGroupService.analysisImUserId(imUserId));

            this.transaction(()->{
                if(RoleEnum.RoleTeacher == userRole){
                    teacherAttendanceService.signOut(userId,courseSchedule);
                    //如果老师退出房间，初始化节拍器和伴奏播放配置
                    courseScheduleStudentMusicSheetService.getDao().closePlayStatus(courseScheduleId,null);
                }else {
                    //记录考勤签退
                    studentAttendanceService.signOut(userId,courseSchedule);
                }
                //退出房间
                try {
                    roomService.quitRoomSuccess(userRole,imUserId,roomId,courseSchedule.getServiceProvider());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            redisTemplate.delete("joinRoomSuccess:"+roomId+imUserId);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendImPlayMidiMessage(ImNetworkCustomMessage customMessage) throws Exception {
        RoleEnum roleEnum = RoleEnum.RoleTeacher;
        if(ClientEnum.STUDENT == customMessage.getClientType()) {
            roleEnum = RoleEnum.RoleStudent;
        }
        customMessage.setCurrentUserId(imGroupService.getImUserId(sysUserService.getUserId(),roleEnum));
        CourseSchedule courseSchedule = courseScheduleService.getById(customMessage.getRoomId());
        customMessage.setServiceProvider(courseSchedule.getServiceProvider());
        //发送节拍器消息
        imService.sendImPlayMidiMessage(customMessage);
        //记录节拍器信息
        imNetworkRoomMemberStatusService.lambdaUpdate()
                        .eq(ImNetworkRoomMemberStatus::getRoomId,customMessage.getRoomId())
                                        .set(ImNetworkRoomMemberStatus::getPlayMidi,customMessage.getContent());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pushDownloadMusicSheetMsg(ImNetworkMusicSheetDto musicSheetDto) throws Exception {
        String roomId = Optional.ofNullable(musicSheetDto).map(ImNetworkBaseDto::getRoomId).orElseThrow(() -> new BizException("房间编号不能为空"));
        Long accompanimentId = Optional.of(musicSheetDto).map(ImNetworkMusicSheetDto::getMusicScoreAccompanimentId).orElseThrow(() -> new BizException("伴奏编号不能为空"));
        log.info("pushDownloadMusicSheetMsg: roomId:{} ,accompanimentId:{}", roomId,accompanimentId);
        Long userId = sysUserService.getUserId();
        MusicSheetAccompaniment accompaniment = musicSheetAccompanimentService.getById(accompanimentId);
        List<CourseScheduleStudentMusicSheetResult> scheduleStudentMusicSheetResults = courseScheduleStudentMusicSheetService.getDao().
                queryBySheetIdAndCourseId(accompanimentId, Long.parseLong(roomId), null, null, 0);

        if (scheduleStudentMusicSheetResults.isEmpty()) {
            //第一次下载,生成数据
            List<CourseScheduleStudentPayment> studentPayments =
                    courseScheduleStudentPaymentService.lambdaQuery().eq(CourseScheduleStudentPayment::getCourseId,roomId).list();
            Set<Long> studentIds = studentPayments.stream().map(CourseScheduleStudentPayment::getUserId).collect(Collectors.toSet());
            studentIds.forEach(e -> {
                CourseScheduleStudentMusicSheetResult musicSheet = new CourseScheduleStudentMusicSheetResult();
                musicSheet.setMusicScoreAccompanimentId(accompanimentId);
                musicSheet.setSpeed(accompaniment.getSpeed());
                musicSheet.setCourseScheduleId(Long.parseLong(roomId));
                musicSheet.setUserId(e);
                musicSheet.setUserType(0);
                scheduleStudentMusicSheetResults.add(musicSheet);
            });
            CourseScheduleStudentMusicSheetResult musicSheet = new CourseScheduleStudentMusicSheetResult();
            musicSheet.setMusicScoreAccompanimentId(accompanimentId);
            musicSheet.setSpeed(accompaniment.getSpeed());
            musicSheet.setCourseScheduleId(Long.parseLong(roomId));
            musicSheet.setUserId(userId);
            musicSheet.setUserType(1);
            scheduleStudentMusicSheetResults.add(musicSheet);
            courseScheduleStudentMusicSheetService.getDao().batchInsert(scheduleStudentMusicSheetResults);
        }
        //通知学员下载伴奏
        // IM用户ID
        String imUserId = imGroupService.getImUserId(userId,musicSheetDto.getClientType());
        ImNetworkMusicSheetDownloadMessageContent content = JSON.parseObject(JSON.toJSONString(accompaniment), ImNetworkMusicSheetDownloadMessageContent.class);
        if(StringUtils.isNotEmpty(accompaniment.getMusicSubjectId())){
            content.setSubjectId(Integer.parseInt(accompaniment.getMusicSubjectId()));
        }
        content.setMp3Url(accompaniment.getAudioFileUrl());
        if(accompaniment.getMusicSheetId() != null){
            content.setExamSongId(accompaniment.getMusicSheetId().intValue());
            MusicSheet musicSheet = musicSheetService.getById(accompaniment.getMusicSheetId());
            if(musicSheet != null){
                content.setUrl(StringUtils.isNotEmpty(musicSheet.getAudioFileUrl())?musicSheet.getAudioFileUrl()
                        :StringUtils.isNotEmpty(musicSheet.getMetronomeUrl())?musicSheet.getMetronomeUrl()
                        :musicSheet.getUrl());
            }
        }
        BasicUserInfo basicUserInfo = teacherDao.getBasicUserInfo(userId);
        basicUserInfo.setImUserId(imUserId);
        CourseSchedule courseSchedule = courseScheduleService.getById(roomId);
        content.setServiceProvider(courseSchedule.getServiceProvider());
        content.setRoomId(roomId);
        imService.pushDownloadMusicSheetMsg(content,basicUserInfo);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void display(ImNetworkDisplayDataDto displayData) throws Exception {
        log.info("display in data = {}", displayData);
        CourseSchedule courseSchedule = courseScheduleService.getById(displayData.getRoomId());
        displayData.setTeacherId(imGroupService.getImUserId(courseSchedule.getTeacherId(),RoleEnum.RoleTeacher));
        roomService.display(displayData,courseSchedule.getServiceProvider());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchControlDevice(ImNetworkDeviceControlDto deviceControl) throws Exception {
        ImNetworkDeviceTypeEnum deviceType = deviceControl.getDeviceType();
        if (StringUtils.isNotEmpty(deviceControl.getUserId())) {
            controlDevice(deviceControl);
        } else {
            if (deviceType == ImNetworkDeviceTypeEnum.EXAM_SONG ||
                    deviceType == ImNetworkDeviceTypeEnum.MUSIC_SHEET ||
                    deviceType == ImNetworkDeviceTypeEnum.ACCOMPANIMENT) {
                List<CourseScheduleStudentPayment> studentPayments = courseScheduleStudentPaymentService.getDao().queryByCourseId(Long.parseLong(deviceControl.getRoomId()));

                for (CourseScheduleStudentPayment studentPayment : studentPayments) {
                    deviceControl.setUserId(imGroupService.getImUserId(String.valueOf(studentPayment.getUserId()), ClientEnum.STUDENT.name()));
                    controlDevice(deviceControl);
                }

            }else {
                // 同步全员静音状态
                if (deviceControl.getDeviceType() == ImNetworkDeviceTypeEnum.MICROPHONE || deviceControl.getCancelMuteFlag() != null) {
                    CourseSchedule courseSchedule = courseScheduleService.getById(deviceControl.getRoomId());
                    if(deviceControl.getEnable() != null){
                        courseSchedule.setMuteAll(!deviceControl.getEnable());
                    }
                    if(deviceControl.getCancelMuteFlag() != null){
                        courseSchedule.setCancelMuteFlag(deviceControl.getCancelMuteFlag());
                    }
                    courseScheduleService.updateById(courseSchedule);
                    // 修改网管课群自定义属性
                    RTCRoomPluginService pluginService = imGroupService.getRTCRoomPluginService(courseSchedule.getServiceProvider());
                    updateChatRoomGroupData(deviceControl.getRoomId(), courseSchedule, pluginService);
                }

                if(deviceControl.getEnable() != null){
                    List<ImNetworkRoomMember> roomMembers = imNetworkRoomMemberService.lambdaQuery()
                            .eq(ImNetworkRoomMember::getRoomId,deviceControl.getRoomId())
                            .eq(ImNetworkRoomMember::getRole,RoleEnum.RoleStudent.getValue()).list();
                    if (!CollectionUtils.isEmpty(roomMembers)) {
                        for (ImNetworkRoomMember roomMember : roomMembers) {
                            deviceControl.setUserId(roomMember.getUserId());
                            controlDevice(deviceControl);
                        }
                    }
                }
            }
        }
    }

    private void updateChatRoomGroupData(String roomId, CourseSchedule courseSchedule, RTCRoomPluginService pluginService) {
        // 生成群组自定义字段
        List<TencentRequest.ChatRoomGroupDefinedData> definedDataList = Lists.newArrayList();

        // 全员静音
        definedDataList.add(TencentRequest.ChatRoomGroupDefinedData.builder()
                .key("isMuteAll").value(courseSchedule.getMuteAll().toString())
                .build());
        // 取消静音
        definedDataList.add(TencentRequest.ChatRoomGroupDefinedData.builder()
                .key("cancelMuteFlag").value(courseSchedule.getCancelMuteFlag().toString())
                .build());

        if (org.apache.commons.collections.CollectionUtils.isNotEmpty(definedDataList)) {

            try {
                pluginService.chatRoomGroupDefinedData(TencentRequest.ChatRoomGroup.builder()
                        .groupId(roomId)
                        .appDefinedData(definedDataList)
                        .build());
            } catch (Exception e) {
                log.error("直播网管课自定义字段设置失败: roomId={}", roomId, e);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void controlDevice(ImNetworkDeviceControlDto deviceControl) throws Exception {
        // 老师信息
        SysUser sysUser = sysUserService.getUser();
        String roomId = deviceControl.getRoomId();

        String imUserId = deviceControl.getUserId();
        // 学生信息
        Boolean enable = deviceControl.getEnable();
        log.info("controlDevice: roomId:{} ,deviceType:{} ,enable:{} ,imUserId:{}", roomId,deviceControl.getDeviceType(),enable,imUserId);

        // RTC服务对象
        CourseSchedule courseSchedule = courseScheduleService.getById(roomId);
        BasicUserInfo teacher = teacherDao.getBasicUserInfo(sysUser.getId());
        teacher.setImUserId(imGroupService.getImUserId(teacher.getUserId(),RoleEnum.RoleTeacher));

        deviceControl.setServiceProvider(courseSchedule.getServiceProvider());

        long userId = Long.parseLong(imGroupService.analysisImUserId(imUserId));
        if(enable){
            long scheduleId = Long.parseLong(roomId);
            switch (deviceControl.getDeviceType()) {
                case MUSIC_SHEET:
                    Integer musicSheetId = Optional.ofNullable(deviceControl.getMusicScoreAccompanimentId()).
                            orElseThrow(()-> new BizException("请选择曲目"));
                    //关闭所有曲目播放
                    courseScheduleStudentMusicSheetService.getDao().closePlayStatus(scheduleId,userId);
                    //打开原音
                    courseScheduleStudentMusicSheetService.getDao().openPlayStatus(scheduleId,musicSheetId,userId);
                    break;
                case ACCOMPANIMENT:
                  Integer musicSheetAccompanimentId = Optional.ofNullable(deviceControl.getMusicScoreAccompanimentId()).
                            orElseThrow(()-> new BizException("请选择曲目"));
                    //关闭所有曲目播放
                    courseScheduleStudentMusicSheetService.getDao().closePlayStatus(scheduleId,userId);
                    //打开伴奏
                    courseScheduleStudentMusicSheetService.getDao().openAccompanimentPlayStatus(scheduleId,musicSheetAccompanimentId,userId);
                    break;
            }
        }else {
            long scheduleId = Long.parseLong(roomId);
            switch (deviceControl.getDeviceType()) {
                case MUSIC_SHEET:
                case ACCOMPANIMENT:
                    //关闭所有曲目播放
                    courseScheduleStudentMusicSheetService.getDao().closePlayStatus(scheduleId,userId);
                    break;
            }
        }
        imService.pushDeviceResourceMessage(deviceControl,teacher);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveControlDevice(ImNetworkDeviceControlDto deviceControl) throws Exception {
        log.info("approveControlDevice: roomId:{} ,deviceType:{} ,enable:{}", deviceControl.getRoomId(),deviceControl.getDeviceType(),deviceControl.getEnable());
        RoleEnum userRole = RoleEnum.RoleTeacher;
        if (ClientEnum.STUDENT == deviceControl.getClientType()) {
            userRole = RoleEnum.RoleStudent;
        }
        Long userId = sysUserService.getUserId();
        String imUserId = imGroupService.getImUserId(userId, userRole);
        ImNetworkRoomMember roomMember = Optional.ofNullable(imNetworkRoomMemberService.lambdaQuery().eq(ImNetworkRoomMember::getRoomId,deviceControl.getRoomId())
                        .eq(ImNetworkRoomMember::getUserId,imUserId)
                        .eq(ImNetworkRoomMember::getRole,userRole.getValue()).last("LIMIT 1").one()).
                orElseThrow(()-> new BizException("用户不在房间内"));
        CourseSchedule courseSchedule = courseScheduleService.getById(deviceControl.getRoomId());
        deviceControl.setServiceProvider(courseSchedule.getServiceProvider());

        imService.pushApproveControlDevice(deviceControl,roomMember,
                teacherDao.getBasicUserInfo(userId),
                teacherDao.getBasicUserInfo(courseSchedule.getTeacherId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deviceStatusSync(ImNetworkDeviceControlDto deviceStatusSync) throws Exception {
        log.info("deviceStatusSync: enable:{} ,roomId:{} ,deviceType:{}", deviceStatusSync.getEnable(),deviceStatusSync.getRoomId(),deviceStatusSync.getDeviceType());
        SysUser user = sysUserService.getUser();
        Long userId = user.getId();
        RoleEnum userRole = RoleEnum.RoleTeacher;
        if (ClientEnum.STUDENT == deviceStatusSync.getClientType()) {
            userRole = RoleEnum.RoleStudent;
        }

        CourseSchedule courseSchedule = courseScheduleService.getById(deviceStatusSync.getRoomId());
        deviceStatusSync.setServiceProvider(courseSchedule.getServiceProvider());
        String imUserId = imGroupService.getImUserId(userId, userRole);
        ImNetworkRoomMember roomMember = Optional.ofNullable(imNetworkRoomMemberService.lambdaQuery().eq(ImNetworkRoomMember::getRoomId,deviceStatusSync.getRoomId())
                        .eq(ImNetworkRoomMember::getUserId,imUserId)
                        .eq(ImNetworkRoomMember::getRole,userRole.getValue()).last("LIMIT 1").one()).
                orElseThrow(()-> new BizException("用户不在房间内"));
        imService.sendDeviceStatusSync(deviceStatusSync,roomMember);
    }

    @Override
    public List<BasicUserInfoDto> queryNoJoinStu(String roomId) {
        //获取在教室的学员列表
        List<String> studentIds = imNetworkRoomMemberService.lambdaQuery().eq(ImNetworkRoomMember::getRoomId, roomId)
                .eq(ImNetworkRoomMember::getRole, RoleEnum.RoleStudent.getValue()).list()
                .stream().map(e -> imGroupService.analysisImUserId(e.getUserId())).collect(Collectors.toList());
        //获取不在教室的学员列表
        List<BasicUserInfoDto> userInfos = courseScheduleStudentPaymentService.getDao().queryNoJoinStu(roomId,studentIds);
        for (BasicUserInfoDto item : userInfos) {
            item.setUserId(imGroupService.getImUserId(item.getUserId(),ClientEnum.STUDENT.name()));
        }
        return userInfos;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rejectControlDevice(ImNetworkDeviceControlDto deviceControl) throws Exception {
        log.info("rejectControlDevice: roomId:{} ,deviceType:{}", deviceControl.getRoomId(),deviceControl.getDeviceType());
        SysUser sysUser = sysUserService.getUser();

        CourseSchedule courseSchedule = courseScheduleService.getById(deviceControl.getRoomId());
        deviceControl.setServiceProvider(courseSchedule.getServiceProvider());
        ImNetworkRoomMember roomMember = imNetworkRoomMemberService.lambdaQuery()
                .eq(ImNetworkRoomMember::getUserId, sysUser.getId())
                .eq(ImNetworkRoomMember::getRoomId, deviceControl.getRoomId())
                .last("LIMIT 1").one();
        imService.sendRejectControlDevice(deviceControl,roomMember,
                teacherDao.getBasicUserInfo(courseSchedule.getTeacherId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void musicSheetDownNotify(ImNetworkMusicSheetDto musicSheetDto) throws Exception {
        String roomId = Optional.ofNullable(musicSheetDto).map(ImNetworkBaseDto::getRoomId).orElseThrow(() -> new BizException("房间编号不能为空"));
        Long accompanimentId = Optional.of(musicSheetDto).map(ImNetworkMusicSheetDto::getMusicScoreAccompanimentId).orElseThrow(() -> new BizException("伴奏编号不能为空"));
        Integer status = Optional.of(musicSheetDto).map(ImNetworkMusicSheetDto::getStatus).orElseThrow(() -> new BizException("伴奏下载状态不能为空"));
        log.info("musicSheetDownNotify: roomId:{} ,accompanimentId:{} ,status:{}", roomId,accompanimentId,status);
        Long userId = sysUserService.getUserId();
        List<CourseScheduleStudentMusicSheetResult> studentMusicSheetResults = courseScheduleStudentMusicSheetService.getDao().
                queryBySheetIdAndCourseId(accompanimentId, Long.parseLong(roomId),userId, null, null);
        if(CollectionUtils.isEmpty(studentMusicSheetResults)){
            return;
        }
        Optional.ofNullable(musicSheetAccompanimentService.getById(accompanimentId)).
                orElseThrow(()-> new BizException("曲目信息不存在"));
        //修改下载状态
        CourseScheduleStudentMusicSheetResult musicSheetResult = studentMusicSheetResults.get(0);
        musicSheetResult.setDownStatus(status);
        courseScheduleStudentMusicSheetService.updateById(musicSheetResult);
        //给老师发送学员曲目下载状态
        CourseSchedule courseSchedule = courseScheduleService.getById(roomId);
        ImNetworkMusicSheetDownloadStatusMessage statusMessage = new ImNetworkMusicSheetDownloadStatusMessage(status,studentMusicSheetResults);

        // IM用户ID
        String imUserId = imGroupService.getImUserId(userId,musicSheetDto.getClientType());

        imHelper.publishMessage(imUserId, courseSchedule.getTeacherId().toString(), roomId, statusMessage);
    }

    //校验用户是否提前进入教室
    private boolean roomNotStart(CourseSchedule courseSchedule){
        //是否提前进入教室
        String courseBeforeBufferTime = sysConfigDao.findConfigValue(SysConfigConstant.PRE_CREATE_PRACTICE_ROOM_MINUTE);
        if (StringUtils.isEmpty(courseBeforeBufferTime)) {
            courseBeforeBufferTime = "5";
        }
        Date addMinutes = DateUtil.addMinutes(courseSchedule.getStartTime(), Integer.parseInt(courseBeforeBufferTime));
        return courseSchedule.getStartTime().compareTo(addMinutes) > 0;
    }

    public void transaction(Runnable runnable) {
        transactionTemplate.execute(transactionStatus -> {
            runnable.run();
            return Boolean.TRUE;
        });
    }
}