소스 검색

Merge branch 'feature/0414-trtc' into master_saas

# Conflicts:
#	mec-biz/src/main/java/com/ym/mec/biz/dal/dao/CourseScheduleDao.java
#	mec-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml
Eric 2 년 전
부모
커밋
d76f50beee

+ 7 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/CourseScheduleDao.java

@@ -1993,4 +1993,11 @@ public interface CourseScheduleDao extends BaseDAO<Long, CourseSchedule> {
         "param") Map<String, Object> param, @Param("courseIds") List<Long> courseIds);
 
     int countTeacherServeHomeworkDetailV2(@Param("param") Map<String, Object> params, @Param("courseIds") List<Long> courseIds);
+
+    /**
+     * 网络课服务提供方
+     * @param courseScheduleId 课程ID
+     * @param serviceProvider 服务提供方
+     */
+    void updateServiceProvider(@Param("courseScheduleId") Long courseScheduleId, @Param("serviceProvider") String serviceProvider);
 }

+ 39 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dto/TencentData.java

@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.annotation.JSONField;
 import com.ym.mec.biz.dal.enums.ETencentGroupType;
 import com.ym.mec.biz.dal.enums.ETencentImCallbackCommand;
+import com.ym.mec.biz.dal.enums.ETencentTRTCCallbackCommand;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
@@ -16,6 +17,7 @@ import lombok.NoArgsConstructor;
 import java.io.Serializable;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 
@@ -25,6 +27,8 @@ import java.util.Locale;
  * @author liujunchi
  * @date 2023-03-06
  */
+@NoArgsConstructor
+@Data
 public class TencentData {
 
     // 群成员离开之后回调对象
@@ -451,4 +455,39 @@ public class TencentData {
         private Integer code;
     }
 
+
+    @NoArgsConstructor
+    @Data
+    public static class TRTCEventInfo {
+
+        @ApiModelProperty("事件组")
+        private String eventGroupId;
+        @ApiModelProperty("事件类型")
+        private ETencentTRTCCallbackCommand eventType;
+        @ApiModelProperty("事件时间")
+        private Date callbackTs;
+        @ApiModelProperty("事件消息")
+        private EventInfo eventInfo;
+
+    }
+
+    @Data
+    @ApiModel("事件消息")
+    public static class EventInfo {
+
+        @ApiModelProperty("房间号")
+        private String roomId;
+        @ApiModelProperty("事件发生时间")
+        private Date eventTs;
+        @ApiModelProperty("事件发生用户")
+        private String userId;
+        @ApiModelProperty("用户角色")
+        private String role;
+        @ApiModelProperty("终端类型")
+        private String terminalType;
+        @ApiModelProperty("用户类型")
+        private String userType;
+        @ApiModelProperty("原因")
+        private String reason;
+    }
 }

+ 10 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/CourseSchedule.java

@@ -207,6 +207,9 @@ public class CourseSchedule  extends BaseEntity{
 	//当前课程是否是转换课
 	private Boolean convertFlag = false;
 
+	@ApiModelProperty("服务提供方")
+	private String serviceProvider;
+
 	public Boolean getSettlementReportFlag() {
 		return settlementReportFlag;
 	}
@@ -661,6 +664,13 @@ public class CourseSchedule  extends BaseEntity{
 		this.teacher = teacher;
 	}
 
+	public String getServiceProvider() {
+		return serviceProvider;
+	}
+
+	public void setServiceProvider(String serviceProvider) {
+		this.serviceProvider = serviceProvider;
+	}
 
 	public static CourseScheduleType getCourseScheduleTypeByCode(String code) {
 		CourseScheduleType type = null;

+ 31 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/enums/ETencentTRTCCallbackCommand.java

@@ -0,0 +1,31 @@
+package com.ym.mec.biz.dal.enums;
+
+import lombok.Getter;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-02
+ */
+@Getter
+public enum ETencentTRTCCallbackCommand {
+
+    EVENT_TYPE_CREATE_ROOM("101", "创建房间"),
+    EVENT_TYPE_DISMISS_ROOM("102", "解散房间"),
+    EVENT_TYPE_ENTER_ROOM("103", "进入房间"),
+    EVENT_TYPE_EXIT_ROOM("104", "退出房间"),
+    ;
+
+    private final String command;
+    private final String desc;
+
+    private final String code;
+
+    ETencentTRTCCallbackCommand(String command, String desc) {
+        this.command = command;
+        this.desc = desc;
+
+        this.code = this.name();
+    }
+}

+ 7 - 0
mec-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml

@@ -4207,4 +4207,11 @@
             <include refid="selectPage"/>
         </where>
     </select>
+
+    <update id="updateServiceProvider">
+        UPDATE course_schedule
+        SET service_provider_ = #{serviceProvider},
+            update_time_ = NOW()
+        WHERE id_ = #{courseScheduleId}
+    </update>
 </mapper>

+ 24 - 0
mec-im/src/main/java/com/ym/controller/RoomController.java

@@ -1,7 +1,9 @@
 package com.ym.controller;
 
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.ym.common.BaseResponse;
+import com.ym.mec.biz.dal.dto.TencentData;
 import com.ym.pojo.*;
 import com.ym.service.MessageService;
 import com.ym.service.RoomService;
@@ -104,6 +106,28 @@ public class RoomController{
         }
     }
 
+
+    @PostMapping(value = "/statusSyncTencent")
+    public void statusSyncTencent(@RequestBody TencentData.TRTCEventInfo eventInfo) {
+        try {
+            log.info("statusSyncTencent: {}", JSON.toJSON(eventInfo));
+            String roomId = eventInfo.getEventInfo().getRoomId();
+            String userId = eventInfo.getEventInfo().getUserId();
+            switch (eventInfo.getEventType()){
+                case EVENT_TYPE_ENTER_ROOM:
+                    //成员加入
+                    roomService.joinRoomSuccess(roomId, userId,null);
+                    break;
+                case EVENT_TYPE_EXIT_ROOM:
+                    //成员退出
+                    roomService.leaveRoomSuccess(roomId, userId,null);
+                    break;
+            }
+        }catch (Exception e){
+            log.error(e.getLocalizedMessage());
+        }
+    }
+
     @RequestMapping(value = "/downgrade", method = RequestMethod.POST)
     public Object downRole(@RequestBody ReqChangeUserRoleData data)
             throws Exception {

+ 37 - 0
mec-im/src/main/java/com/ym/pojo/ReqDeviceControlData.java

@@ -22,6 +22,12 @@ public class ReqDeviceControlData {
 	private Integer musicScoreAccompanimentId;
 	//伴奏音量
 	private Integer soundVolume = 100;
+	// 服务提供方
+	private String serviceProvider;
+	// 发送用户信息
+	private String sendUserId;
+	private String sendUserName;
+	private String avatar;
 
 	public Integer getMusicScoreAccompanimentId() {
 		return musicScoreAccompanimentId;
@@ -102,4 +108,35 @@ public class ReqDeviceControlData {
 		this.ticket = ticket;
 	}
 
+	public String getServiceProvider() {
+		return serviceProvider;
+	}
+
+	public void setServiceProvider(String serviceProvider) {
+		this.serviceProvider = serviceProvider;
+	}
+
+	public String getSendUserId() {
+		return sendUserId;
+	}
+
+	public void setSendUserId(String sendUserId) {
+		this.sendUserId = sendUserId;
+	}
+
+	public String getSendUserName() {
+		return sendUserName;
+	}
+
+	public void setSendUserName(String sendUserName) {
+		this.sendUserName = sendUserName;
+	}
+
+	public String getAvatar() {
+		return avatar;
+	}
+
+	public void setAvatar(String avatar) {
+		this.avatar = avatar;
+	}
 }

+ 2 - 0
mec-im/src/main/java/com/ym/pojo/RoomResult.java

@@ -43,6 +43,8 @@ public class RoomResult {
     private @Getter @Setter String userSig;
     @ApiModelProperty("群组id")
     private @Getter @Setter String groupId;
+    @ApiModelProperty("服务提供商")
+    private @Getter @Setter String serviceProvider;
 
     public RoomResult rtcRoomConfig(RTCRoomConfig rtcRoomConfig) {
         this.rtcRoomConfig = rtcRoomConfig;

+ 188 - 28
mec-im/src/main/java/com/ym/service/Impl/RoomServiceImpl.java

@@ -3,9 +3,12 @@ package com.ym.service.Impl;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.google.common.collect.Lists;
 import com.microsvc.toolkit.middleware.rtc.RTCRoomPluginContext;
 import com.microsvc.toolkit.middleware.rtc.RTCRoomPluginService;
 import com.microsvc.toolkit.middleware.rtc.impl.TencentCloudRTCPlugin;
+import com.microsvc.toolkit.middleware.rtc.message.ImGroupMemberWrapper;
+import com.microsvc.toolkit.middleware.rtc.message.RTCRoomMessage;
 import com.ym.common.ApiException;
 import com.ym.common.BaseResponse;
 import com.ym.common.DisplayEnum;
@@ -43,6 +46,7 @@ import com.ym.utils.DateTimeUtils;
 import com.ym.utils.IdentifierUtils;
 import com.ym.whiteboard.WhiteBoardHelper;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -253,6 +257,12 @@ public class RoomServiceImpl implements RoomService {
         }
 //        imHelper.joinGroup(new String[]{userId}, roomId, roomId);
 
+        // 主讲老师信息
+        SysUser teacherInfo = sysUserFeignService.queryUserById(courseSchedule.getActualTeacherId());
+        if (Objects.isNull(teacherInfo)) {
+            throw new BizException("主讲老师不存在");
+        }
+
         // 获取RTC服务提供方
         String rtcServiceProvider = Optional.ofNullable(sysConfigDao.findConfigValue("rtc_service_provider")).orElse("rongCloud");
 
@@ -267,42 +277,53 @@ public class RoomServiceImpl implements RoomService {
                 log.error("直播房间用户注册失败: userId={}", sysUser.getId(), e);
             }
 
-            // 主讲老师信息
-            SysUser teacherInfo = sysUserFeignService.queryUserById(courseSchedule.getActualTeacherId());
-            if (Objects.isNull(teacherInfo)) {
-                throw new BizException("主讲老师不存在");
-            }
+            // 返回配置参数
+            roomResult.userSig(userSig)
+                    .rtcRoomConfig(rtcRoomPluginContext.getPluginService().getRTCRoomConfig(String.valueOf(sysUser.getId())))
+                    .setGroupId(roomId);
+        } else {
+            // 融云RTC
+            // TODO:创建IM群聊,后续注释掉,统一使用后续流程
+            this.joinImGroup(roomId, courseSchedule.getActualTeacherId());
+        }
 
+        // 创建网络课聊天室
+        {
             String joinImGroupKey = "joinImGroup:" + roomId;
             // VIP课或网络课,IM群聊已创建标识
             Boolean exists = redisTemplate.opsForValue().setIfAbsent(joinImGroupKey, roomId, 1L, TimeUnit.DAYS);
 
-            // 缓存RTC服务提供方
-            String rtcRoomPluginKey = "rtcRoomPlugin:" + roomId;
-            redisTemplate.opsForValue().setIfAbsent(rtcRoomPluginKey, pluginService.pluginName(), 1L, TimeUnit.DAYS);
-
             if (Optional.ofNullable(exists).orElse(false)) {
                 // 创建群组
                 log.info("createImGroup: roomId = {}, userId = {}", roomId, teacherInfo.getId());
                 pluginService.chatRoomCreate(roomId, courseSchedule.getName(), teacherInfo.getAvatar());
 
+                // 群组老师信息
+                List<ImGroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList(ImGroupMemberWrapper.ImGroupMember
+                        .builder()
+                        .userId(Long.valueOf(teacherInfo.getId()))
+                        .imUserIdFormat(false)
+                        .build());
+
                 List<CourseScheduleStudentPayment> payments = courseScheduleStudentPaymentDao.findByCourseSchedule(Long.parseLong(roomId.substring(1)));
-                // 学生信息
-                List<String> collect = payments.stream().map(e -> e.getUserId().toString()).collect(Collectors.toList());
-                // 老师信息
-                collect.add(teacherInfo.getId().toString());
+                // 群组学生信息
+                if (CollectionUtils.isNotEmpty(payments)) {
+                    for (CourseScheduleStudentPayment item : payments) {
+                        groupMembers.add(ImGroupMemberWrapper.ImGroupMember
+                                .builder()
+                                .userId(Long.valueOf(item.getUserId()))
+                                .imUserIdFormat(false)
+                                .build());
+                    }
+                }
+
                 // 加入群组成员
-                log.info("joinImGroup: roomId = {}, userIds = {}", roomId, collect);
-            }
+                log.info("joinImGroup: roomId = {}, userIds = {}", roomId, groupMembers);
+                pluginService.chatRoomGroupJoin(roomId, courseSchedule.getName(), groupMembers);
 
-            // 返回配置参数
-            roomResult.userSig(userSig)
-                    .rtcRoomConfig(rtcRoomPluginContext.getPluginService().getRTCRoomConfig(String.valueOf(sysUser.getId())))
-                    .setGroupId(roomId);
-        } else {
-            // 融云RTC
-            // 创建IM群聊
-            this.joinImGroup(roomId, courseSchedule.getActualTeacherId());
+                // 更新网络课服务提供方
+                courseScheduleDao.updateServiceProvider(courseSchedule.getId(), rtcServiceProvider);
+            }
         }
 
         List<CourseScheduleStudentMusicScore> scheduleStudentMusicScores = courseScheduleStudentMusicScoreDao.queryByScoreIdAndCourseId(null, courseId, null, null, null);
@@ -933,6 +954,26 @@ public class RoomServiceImpl implements RoomService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean controlDevice(ReqDeviceControlData data) throws Exception {
+
+        // RTC服务方信息
+        if (StringUtils.isBlank(data.getServiceProvider())) {
+
+            // 课程信息
+            CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(data.getRoomId().substring(1)));
+            if (Objects.isNull(courseSchedule)) {
+                throw new BizException("无效的课程编号");
+            }
+            data.setServiceProvider(courseSchedule.getServiceProvider());
+
+            // 课程老师信息
+            SysUser sysUser = sysUserFeignService.queryUserById(courseSchedule.getTeacherId());
+            if (Objects.nonNull(sysUser)) {
+                data.setSendUserId(sysUser.getId().toString());
+                data.setSendUserName(sysUser.getRealName());
+                data.setAvatar(sysUser.getAvatar());
+            }
+        }
+
         String roomId = data.getRoomId();
         String userId = data.getUserId();
         DeviceTypeEnum typeEnum;
@@ -972,6 +1013,45 @@ public class RoomServiceImpl implements RoomService {
         SysUser authUser = sysUserFeignService.queryUserInfo();
         log.info("controlDevice: userId = {}, typeEnum = {}, enable = {} ,roomId = {}", userId, typeEnum, enable, roomId);
 
+        // RTC服务对象
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(data.getServiceProvider());
+
+        // 通知消息
+        RTCRoomMessage.MessageContent notifyContent = RTCRoomMessage.MessageContent
+                .builder()
+                .type(typeEnum.ordinal())
+                .enable(enable)
+                .targetId(userId)
+                .targetName(authUser.getUsername())
+                .sendUserInfo(RTCRoomMessage.MessageUser.builder()
+                        .sendUserId(data.getSendUserId())
+                        .sendUserName(data.getSendUserName())
+                        .avatarUrl(data.getAvatar())
+                        .build())
+                .build();
+
+        // 消息内容
+        RTCRoomMessage.MessageContent messageContent = RTCRoomMessage.MessageContent
+                .builder()
+                .type(typeEnum.ordinal())
+                .enable(enable)
+                .sendUserInfo(RTCRoomMessage.MessageUser.builder()
+                        .sendUserId(userId)
+                        .sendUserName(authUser.getUsername())
+                        .avatarUrl(authUser.getAvatar())
+                        .build())
+                .build();
+
+        // 腾讯云消息推送
+        RTCRoomMessage message = RTCRoomMessage.builder()
+                .objectName(RTCRoomMessage.DEVICE_MESSAGE)
+                .fromUserId(userId)
+                .toChatRoomId(roomId)
+                .content(messageContent)
+                .isPersisted(1)
+                .isIncludeSender(0)
+                .build();
+
         if (enable) {
             if (typeEnum.equals(DeviceTypeEnum.ExamSong)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
@@ -986,7 +1066,15 @@ public class RoomServiceImpl implements RoomService {
                 courseScheduleStudentPaymentDao.adjustExamSong(scheduleId, Integer.parseInt(userId), JSON.toJSONString(msg));
                 DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), enable);
                 deviceResourceMessage.setUserId(userId);
-                imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
+
+                // 消息发送
+                if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                    // 腾讯云推送
+                    pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.DEVICE_MESSAGE).content(messageContent));
+                } else {
+                    // 融云推送
+                    imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
+                }
             } else if (typeEnum.equals(DeviceTypeEnum.MusicScore)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
                 //关闭所有曲目播放
@@ -1023,7 +1111,15 @@ public class RoomServiceImpl implements RoomService {
                 msg.setType(taskInfo.getTypeEnum().ordinal());
                 msg.setOpUserId(authUser.getId().toString());
                 msg.setOpUserName(authUser.getUsername());
-                imHelper.publishMessage(authUser.getId().toString(), userId, roomId, msg);
+
+                // 发送消息
+                if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                    // 腾讯消息
+                    pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE).content(notifyContent));
+                } else {
+                    // 融云消息
+                    imHelper.publishMessage(authUser.getId().toString(), userId, roomId, msg);
+                }
             }
         } else {
             if (typeEnum.equals(DeviceTypeEnum.Camera)) {
@@ -1060,7 +1156,15 @@ public class RoomServiceImpl implements RoomService {
             if (userInfo != null) {
                 deviceResourceMessage.setUserName(userInfo.getName());
             }
-            imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
+
+            // 发送消息
+            if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                // 腾讯消息
+                pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.DEVICE_MESSAGE).content(messageContent));
+            } else {
+                // 融云消息
+                imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
+            }
         }
         return true;
     }
@@ -1068,8 +1172,25 @@ public class RoomServiceImpl implements RoomService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean batchControlDevice(ReqDeviceControlData data) throws Exception {
+
+        // 课程ID
+        long courseScheduleId = Long.parseLong(data.getRoomId().substring(1));
+        CourseSchedule courseSchedule = courseScheduleDao.get(courseScheduleId);
+        if (Objects.isNull(courseSchedule)) {
+            throw new BizException("无效的课程编号");
+        }
+        data.setServiceProvider(courseSchedule.getServiceProvider());
+
+        // 课程老师信息
+        SysUser sysUser = sysUserFeignService.queryUserById(courseSchedule.getTeacherId());
+        if (Objects.nonNull(sysUser)) {
+            data.setSendUserId(sysUser.getId().toString());
+            data.setSendUserName(sysUser.getRealName());
+            data.setAvatar(sysUser.getAvatar());
+        }
+
         if (data.getExamSongOn() != null || data.getMusicScoreOn() != null || data.getAccompanimentOn() != null) {
-            List<BasicUserDto> students = courseScheduleStudentPaymentDao.findStudents(Long.parseLong(data.getRoomId().substring(1)));
+            List<BasicUserDto> students = courseScheduleStudentPaymentDao.findStudents(courseScheduleId);
             for (BasicUserDto e : students) {
                 data.setUserId(e.getUserId().toString());
                 controlDevice(data);
@@ -1205,7 +1326,46 @@ public class RoomServiceImpl implements RoomService {
             deviceResourceMessage.setSoundVolume(room.getSoundVolume());
         }
         deviceResourceMessage.setUserId(userId);
-        imHelper.publishMessage(userId, roomId, deviceResourceMessage, 1);
+
+        // 查询课程信息
+        CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(roomId.substring(1)));
+        if (Objects.isNull(courseSchedule)) {
+            throw new BizException("无效的课程ID");
+        }
+        data.setServiceProvider(courseSchedule.getServiceProvider());
+
+        // RTC服务对象
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(data.getServiceProvider());
+        // 消息内容
+        RTCRoomMessage.MessageContent messageContent = RTCRoomMessage.MessageContent
+                .builder()
+                .type(deviceResourceMessage.getType())
+                .enable(deviceResourceMessage.isEnable())
+                .sendUserInfo(RTCRoomMessage.MessageUser.builder()
+                        .sendUserId(authUser.getId().toString())
+                        .sendUserName(authUser.getRealName())
+                        .avatarUrl(authUser.getAvatar())
+                        .build())
+                .build();
+
+        // 腾讯云消息推送
+        RTCRoomMessage message = RTCRoomMessage.builder()
+                .objectName(RTCRoomMessage.DEVICE_MESSAGE)
+                .fromUserId(userId)
+                .toChatRoomId(roomId)
+                .content(messageContent)
+                .isPersisted(1)
+                .isIncludeSender(0)
+                .build();
+
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.matches(data.getServiceProvider())) {
+            // 发送消息
+            pluginService.sendChatRoomMessage(message);
+        } else {
+            // 融云消息推送
+            imHelper.publishMessage(userId, roomId, deviceResourceMessage, 1);
+        }
+
         log.info("syncDeviceState : {}, {}", roomId, enable);
         return true;
     }