Browse Source

腾讯直播

liujc 1 year ago
parent
commit
caf12ae434
22 changed files with 1603 additions and 987 deletions
  1. 76 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/EGroupDefinedDataType.java
  2. 245 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImController.java
  3. 0 96
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImUserFriendController.java
  4. 17 12
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java
  5. 4 0
      cooleshow-user/user-biz/pom.xml
  6. 521 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentData.java
  7. 40 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentImCallbackResult.java
  8. 3 191
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseGroup.java
  9. 9 220
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseSchedule.java
  10. 10 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImRoomMessage.java
  11. 3 39
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImUserStateSync.java
  12. 17 142
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java
  13. 7 23
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoomVideo.java
  14. 7 164
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/RoomInfoCache.java
  15. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java
  16. 30 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentGroupType.java
  17. 31 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentImCallbackCommand.java
  18. 30 4
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java
  19. 2 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java
  20. 549 90
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java
  21. 0 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomVideoServiceImpl.java
  22. 1 1
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/task/TaskController.java

+ 76 - 0
cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/EGroupDefinedDataType.java

@@ -0,0 +1,76 @@
+package com.yonge.cooleshow.common.enums;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-20
+ */
+public enum EGroupDefinedDataType {
+
+    /**
+     * 主讲人状态
+     */
+    ANCHOR_STATUS("主讲人状态"), // "ONLINE":"OFFLINE"
+
+    /**
+     * 全局禁言
+     */
+    GLOBAL_BAN("全局禁言"), // 开启禁言"ON" 关闭禁言 "OFF"
+
+    /**
+     * 点赞数
+     */
+    LIKES("点赞数"), // 点赞数
+
+    /**
+     * 在线人数
+     */
+    MEMBER_ONLINE("在线人数"), // 在线人数
+
+
+    /**
+     * 累计观看人数
+     */
+    MEMBER_TOTAL("累计观看人数"), // 累计观看人数
+
+    /**
+     * 当前房间推流状态
+     */
+    LIVE_STATUS("当前房间推流状态"), //开播 "ON" 暂停直播 "OFF"
+
+    /**
+     * 主播摄像头状态
+     */
+    ANCHOR_CAMERA("主播摄像头状态"), // 开启摄像头 "ON" 关闭摄像头 "OFF"
+
+    /**
+     * 主播全员闭麦状态
+     */
+    ANCHOR_MIC("主播全员闭麦状态"), // 开启 "ON" 关闭 "OFF"
+    ;
+    private String code;
+
+    private String msg;
+
+    EGroupDefinedDataType(String msg) {
+        this.msg = msg;
+        this.code = this.name();
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+}

+ 245 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImController.java

@@ -0,0 +1,245 @@
+package com.yonge.cooleshow.admin.controller.open;
+
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
+import com.microsvc.toolkit.middleware.live.LivePluginContext;
+import com.microsvc.toolkit.middleware.live.LivePluginService;
+import com.microsvc.toolkit.middleware.live.impl.TencentCloudLivePlugin;
+import com.yonge.cooleshow.admin.io.request.course.CourseRelationVo;
+import com.yonge.cooleshow.admin.io.request.im.IMNotifyMessageVO;
+import com.yonge.cooleshow.admin.io.request.im.UserFriendInfoVO;
+import com.yonge.cooleshow.biz.dal.dto.TencentData;
+import com.yonge.cooleshow.biz.dal.dto.TencentImCallbackResult;
+import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.im.ETencentImCallbackCommand;
+import com.yonge.cooleshow.biz.dal.service.CourseRelationMusicAlbumService;
+import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
+import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
+import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 用户通讯录表(ImUserFriend)表控制层
+ *
+ * @author zx
+ * @since 2022-03-22 10:45:59
+ */
+@Api(tags = "用户通讯录表")
+@RestController
+@RequestMapping("/open")
+@Slf4j
+public class ImController extends BaseController {
+
+    /**
+     * 服务对象
+     */
+    @Resource
+    private ImUserFriendService imUserFriendService;
+    @Autowired
+    private CourseRelationMusicAlbumService courseRelationMusicAlbumService;
+
+    @Autowired
+    private LivePluginContext livePluginContext;
+
+    @Autowired
+    private LiveRoomService liveRoomService;
+
+    @ApiOperation("新用户添加客服")
+    @PostMapping(value = "/im/customerService")
+    public HttpResponseResult<Boolean> customerService(@RequestBody UserFriendInfoVO info) {
+
+        if (info.invalidRequestParam()) {
+            return failed("无效的请求参数");
+        }
+
+        // 新用户自动绑定系统客服
+        int ret = imUserFriendService.registerUserBindCustomerService(info.getUserId(),
+                info.getFriendIds(), ClientEnum.valueOf(info.getClientType()));
+
+        return succeed(ret > 0);
+    }
+
+    /**
+     * 发送系统客服消息
+     * @param info IMNotifyMessageVO
+     * @return HttpResponseResult<Boolean>
+     */
+    @PostMapping(value = "/im/message")
+    public HttpResponseResult<Boolean> sendSysCustomerServiceMessage(@RequestBody IMNotifyMessageVO info) {
+
+        if (info.invalidRequestParam()) {
+            return failed("无效的请求参数");
+        }
+
+        // 发送客服通知消息
+        imUserFriendService.sendCustomerServiceNotifyMessage(info.getSender(),
+                CustomerService.NotifyMessage.from(info.jsonString()));
+
+        return succeed();
+    }
+
+    /**
+     * 同步用户购买课程赠送数据
+     * @param query CourseRelationVo.CourseRelationQuery
+     * @return HttpResponseResult<Boolean>
+     */
+    @ApiOperation("已购买课程赠送同步")
+    @PostMapping(value = "/course/sync")
+    public HttpResponseResult<Boolean> syncUserPurchaseRelationMusicAlbum(@RequestBody CourseRelationVo.CourseRelationQuery query) {
+
+        if (Objects.isNull(query.getCourseGroupId()) || Objects.isNull(query.getCourseType())) {
+            return failed("无效的请求参数");
+        }
+
+        // 同步数据
+        courseRelationMusicAlbumService.asyncUpdateCourseRelationMusicAlbumInfo(query.getCourseGroupId(), query.getCourseType());
+
+        return succeed();
+    }
+
+
+    @ApiOperation("腾讯im 回调接口")
+    @PostMapping(value = "/tencentImCallback")
+    public TencentImCallbackResult tencentImCallback(@RequestBody String body, HttpServletRequest request) {
+        log.info("tencentImCallback body:{}", body);
+
+        LivePluginService pluginService = livePluginContext.getPluginService(TencentCloudLivePlugin.PLUGIN_NAME);
+        String appKey = pluginService.getLiveRoomConfig().getAppKey();
+
+        log.info("tencentImCallback request param:{}", JSON.toJSONString(request.getParameterMap()));
+
+        List<String> sdkList = Arrays.asList(request.getParameterValues("SdkAppid"));
+        if (sdkList == null || sdkList.size() == 0) {
+            log.error("tencentImCallback sdkAppid is null");
+            return new TencentImCallbackResult();
+        }
+        if (!sdkList.contains(appKey)) {
+            log.error("tencentImCallback sdkAppid is not match");
+            return new TencentImCallbackResult();
+        }
+
+        String clientIP = request.getParameter("ClientIP");
+        String optPlatform = request.getParameter("OptPlatform");
+
+
+        if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKONMEMBERSTATECHANGE.getCommand())) {
+            // 直播群成员在线状态回调
+            TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange = TencentData.CallbackOnMemberStateChange.toObject(
+                    body);
+
+            log.debug("callbackOnMemberStateChange: {}", callbackOnMemberStateChange);
+            callbackOnMemberStateChange.setClientIP(clientIP);
+            callbackOnMemberStateChange.setOptPlatform(optPlatform);
+            // 直播间成员状态变更
+            liveRoomService.callbackOnMemberStateChange(callbackOnMemberStateChange);
+        } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERMEMBEREXIT.getCommand())) {
+            // 群成员离开之后回调
+            TencentData.CallbackAfterMemberExit callbackAfterMemberExit = TencentData.CallbackAfterMemberExit.toObject(
+                    body);
+
+            log.debug("callbackAfterMemberExit: {}", callbackAfterMemberExit);
+            callbackAfterMemberExit.setClientIP(clientIP);
+            callbackAfterMemberExit.setOptPlatform(optPlatform);
+
+            // 直播间成员状态变更
+            liveRoomService.callbackAfterMemberExit(callbackAfterMemberExit);
+        } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERNEWMEMBERJOIN.getCommand())) {
+            // 新成员入群之后回调
+            TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin = TencentData.CallbackAfterNewMemberJoin.toObject(
+                    body);
+
+            log.debug("CallbackAfterNewMemberJoin: {}", callbackAfterNewMemberJoin);
+            callbackAfterNewMemberJoin.setClientIP(clientIP);
+            callbackAfterNewMemberJoin.setOptPlatform(optPlatform);
+
+            // 直播间成员状态变更
+            liveRoomService.callbackAfterNewMemberJoin(callbackAfterNewMemberJoin);
+        }
+
+
+        return new TencentImCallbackResult();
+    }
+
+
+    @ApiOperation("腾讯云直播-推流 回调接口")
+    @PostMapping(value = "/tencentStreamEventCallback")
+    public TencentData.StreamEventCallbackResult tencentStreamEventCallback(@RequestBody String body) {
+
+        log.info("tencentStreamEventCallback body:{}", body);
+
+        TencentData.CallbackStreamStateEvent event = TencentData.CallbackStreamStateEvent.from(body);
+
+
+        ImUserStateSync imUserState = new ImUserStateSync();
+        imUserState.setUserid(getSpeakerId(event.getStreamId()).toString());
+        // 断流事件通知
+        if (event.getEventType() == 0) {
+            imUserState.setStatus("3");
+        }
+
+        // 推流事件通知
+        if (event.getEventType() == 1) {
+
+            imUserState.setStatus("0");
+        }
+        liveRoomService.opsRoom(Lists.newArrayList(imUserState));
+
+        return TencentData.StreamEventCallbackResult.builder().code(0).build();
+    }
+
+    private Integer getSpeakerId(String streamId) {
+        return Integer.parseInt(streamId.split("_")[1]);
+    }
+
+    private String getRoomUid(String streamId) {
+        return streamId.split("_")[0];
+    }
+
+    @ApiOperation("腾讯云直播-录制 回调接口")
+    @PostMapping(value = "/tencentStreamRecordCallback")
+    public TencentData.StreamEventCallbackResult tencentStreamRecordCallback(@RequestBody String body) {
+
+        log.info("tencentStreamRecordCallback body:{}", body);
+
+        TencentData.CallbackSteamRecordEvent event = TencentData.CallbackSteamRecordEvent.from(body);
+
+        // 直播录制事件通知
+        if (event.getStreamId().startsWith("LIVE")) {
+            log.info("taskId={}, url={}", event.getTaskId(), event.getVideoUrl());
+
+            // 生成直播录制信息
+            liveRoomService.createLiveRoomVideoRecord(event);
+        }
+
+        return TencentData.StreamEventCallbackResult.builder().code(0).build();
+    }
+
+    @ApiOperation("腾讯云直播-推流异常 回调接口")
+    @PostMapping(value = "/tencentStreamExceptionCallback")
+    public TencentData.StreamEventCallbackResult tencentStreamExceptionCallback(@RequestBody String body) {
+
+        log.info("tencentStreamExceptionCallback body:{}", body);
+
+        return TencentData.StreamEventCallbackResult.builder().code(0).build();
+    }
+
+}
+

+ 0 - 96
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImUserFriendController.java

@@ -1,96 +0,0 @@
-package com.yonge.cooleshow.admin.controller.open;
-
-import com.yonge.cooleshow.admin.io.request.course.CourseRelationVo;
-import com.yonge.cooleshow.admin.io.request.im.IMNotifyMessageVO;
-import com.yonge.cooleshow.admin.io.request.im.UserFriendInfoVO;
-import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
-import com.yonge.cooleshow.biz.dal.service.CourseRelationMusicAlbumService;
-import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
-import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
-import com.yonge.cooleshow.common.controller.BaseController;
-import com.yonge.cooleshow.common.entity.HttpResponseResult;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.annotation.Resource;
-import java.util.Objects;
-
-/**
- * 用户通讯录表(ImUserFriend)表控制层
- *
- * @author zx
- * @since 2022-03-22 10:45:59
- */
-@Api(tags = "用户通讯录表")
-@RestController
-@RequestMapping("/open")
-public class ImUserFriendController extends BaseController {
-
-    /**
-     * 服务对象
-     */
-    @Resource
-    private ImUserFriendService imUserFriendService;
-    @Autowired
-    private CourseRelationMusicAlbumService courseRelationMusicAlbumService;
-
-    @ApiOperation("新用户添加客服")
-    @PostMapping(value = "/im/customerService")
-    public HttpResponseResult<Boolean> customerService(@RequestBody UserFriendInfoVO info) {
-
-        if (info.invalidRequestParam()) {
-            return failed("无效的请求参数");
-        }
-
-        // 新用户自动绑定系统客服
-        int ret = imUserFriendService.registerUserBindCustomerService(info.getUserId(),
-                info.getFriendIds(), ClientEnum.valueOf(info.getClientType()));
-
-        return succeed(ret > 0);
-    }
-
-    /**
-     * 发送系统客服消息
-     * @param info IMNotifyMessageVO
-     * @return HttpResponseResult<Boolean>
-     */
-    @PostMapping(value = "/im/message")
-    public HttpResponseResult<Boolean> sendSysCustomerServiceMessage(@RequestBody IMNotifyMessageVO info) {
-
-        if (info.invalidRequestParam()) {
-            return failed("无效的请求参数");
-        }
-
-        // 发送客服通知消息
-        imUserFriendService.sendCustomerServiceNotifyMessage(info.getSender(),
-                CustomerService.NotifyMessage.from(info.jsonString()));
-
-        return succeed();
-    }
-
-    /**
-     * 同步用户购买课程赠送数据
-     * @param query CourseRelationVo.CourseRelationQuery
-     * @return HttpResponseResult<Boolean>
-     */
-    @ApiOperation("已购买课程赠送同步")
-    @PostMapping(value = "/course/sync")
-    public HttpResponseResult<Boolean> syncUserPurchaseRelationMusicAlbum(@RequestBody CourseRelationVo.CourseRelationQuery query) {
-
-        if (Objects.isNull(query.getCourseGroupId()) || Objects.isNull(query.getCourseType())) {
-            return failed("无效的请求参数");
-        }
-
-        // 同步数据
-        courseRelationMusicAlbumService.asyncUpdateCourseRelationMusicAlbumInfo(query.getCourseGroupId(), query.getCourseType());
-
-        return succeed();
-    }
-}
-

+ 17 - 12
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java

@@ -1,17 +1,6 @@
 package com.yonge.cooleshow.admin.task;
 
-import com.yonge.cooleshow.biz.dal.service.ActivityPlanEvaluationService;
-import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
-import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
-import com.yonge.cooleshow.biz.dal.service.CustomerServiceBatchSendingService;
-import com.yonge.cooleshow.biz.dal.service.MusicAlbumService;
-import com.yonge.cooleshow.biz.dal.service.MusicSheetService;
-import com.yonge.cooleshow.biz.dal.service.PlatformCashAccountRecordService;
-import com.yonge.cooleshow.biz.dal.service.TeacherStyleVideoService;
-import com.yonge.cooleshow.biz.dal.service.UserAccountRecordService;
-import com.yonge.cooleshow.biz.dal.service.UserBindingTeacherService;
-import com.yonge.cooleshow.biz.dal.service.UserOrderService;
-import com.yonge.cooleshow.biz.dal.service.VideoLessonGroupService;
+import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.common.constant.SysConfigConstant;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
@@ -71,6 +60,9 @@ public class TaskController extends BaseController {
     @Autowired
     private CustomerServiceBatchSendingService customerServiceBatchSendingService;
 
+    @Autowired
+    private LiveRoomService liveRoomService;
+
     /***
      * 轮询用户订单
      * @author liweifan
@@ -183,4 +175,17 @@ public class TaskController extends BaseController {
         return HttpResponseResult.succeed();
     }
 
+
+    /**
+     * 直播间销毁
+     */
+    @GetMapping("/destroyLiveRoom")
+    public HttpResponseResult<Object> destroyLiveRoom() {
+
+        // 群发消息定时
+        liveRoomService.destroyLiveRoom();
+
+        return HttpResponseResult.succeed();
+    }
+
 }

+ 4 - 0
cooleshow-user/user-biz/pom.xml

@@ -104,6 +104,10 @@
             <artifactId>toolset-payment</artifactId>
             <version>${project.toolset.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.microsvc.toolkit.middleware</groupId>
+            <artifactId>microsvc-middleware-live</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 521 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentData.java

@@ -0,0 +1,521 @@
+package com.yonge.cooleshow.biz.dal.dto;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yonge.cooleshow.biz.dal.enums.im.ETencentGroupType;
+import com.yonge.cooleshow.biz.dal.enums.im.ETencentImCallbackCommand;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-06
+ */
+@NoArgsConstructor
+@Data
+public class TencentData {
+
+    // 群成员离开之后回调对象
+    @NoArgsConstructor
+    @Data
+    public static class CallbackAfterMemberExit {
+
+
+        private String clientIP;
+
+        private String optPlatform;
+
+        // 回调命令
+        private ETencentImCallbackCommand callbackCommand;
+        // 群组 ID
+        private String groupId;
+        // 群组类型
+        private ETencentGroupType type;
+        // 成员离开方式:Kicked-被踢;Quit-主动退群
+        private String exitType;
+        // 操作者
+        private String operatorAccount;
+        // 离开群的成员列表
+        private List<MemberListDTO> exitMemberList;
+        //毫秒级别,事件触发时间戳
+        private LocalDateTime eventTime;
+
+        public static CallbackAfterMemberExit toObject(String jsonString) {
+
+            CallbackAfterMemberExit res = new CallbackAfterMemberExit();
+
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+            res.setCallbackCommand(ETencentImCallbackCommand.valueOf(jsonObject.getString("CallbackCommand").replace(".","_").toUpperCase(
+                Locale.ROOT)));
+            res.setGroupId(jsonObject.getString("GroupId"));
+            res.setType(jsonObject.getObject("Type",ETencentGroupType.class));
+            res.setExitType(jsonObject.getString("ExitType"));
+            res.setOperatorAccount(jsonObject.getString("Operator_Account"));
+
+            JSONArray exitMemberList = jsonObject.getJSONArray("ExitMemberList");
+            if (!exitMemberList.isEmpty()) {
+                List<MemberListDTO> memberListDTOS = new ArrayList<>();
+                for (int i = 0; i < exitMemberList.size(); i++) {
+                    MemberListDTO memberListDTO = new MemberListDTO();
+                    JSONObject exitMemberListJSONObject = exitMemberList.getJSONObject(i);
+                    String member_account = exitMemberListJSONObject.getString("Member_Account");
+                    memberListDTO.setMemberAccount(member_account);
+                    memberListDTOS.add(memberListDTO);
+                }
+                res.setExitMemberList(memberListDTOS);
+            }
+
+
+
+            res.setEventTime(((Timestamp)jsonObject.getTimestamp("EventTime")).toLocalDateTime());
+
+            return res;
+        }
+    }
+
+    @NoArgsConstructor
+    @Data
+    public static class MemberListDTO {
+        private String memberAccount;
+    }
+
+    // 直播群成员在线状态回调
+    @NoArgsConstructor
+    @Data
+    public static class CallbackOnMemberStateChange {
+
+        // 客户端平台,对应不同的平台类型,可能的取值有:
+        // RESTAPI(使用 REST API 发送请求)、Web(使用 Web SDK 发送请求)、
+        // Android、iOS、Windows、Mac、iPad、Unknown(使用未知类型的设备发送请求)
+        private String clientIP;
+
+        private String optPlatform;
+        // 回调命令
+        private ETencentImCallbackCommand callbackCommand;
+        // 群组 ID
+        private String groupId;
+        // 事件类型:Offline - 掉线、Online - 重新上线
+        private String eventType;
+        // 离开群的成员列表
+        private List<MemberListDTO> memberList;
+
+        public static CallbackOnMemberStateChange toObject(String jsonString) {
+
+            CallbackOnMemberStateChange res = new CallbackOnMemberStateChange();
+
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+            res.setCallbackCommand(ETencentImCallbackCommand.valueOf(jsonObject.getString("CallbackCommand").replace(".","_").toUpperCase(
+                Locale.ROOT)));
+            res.setGroupId(jsonObject.getString("GroupId"));
+
+            JSONArray exitMemberList = jsonObject.getJSONArray("MemberList");
+            if (!exitMemberList.isEmpty()) {
+                List<MemberListDTO> memberListDTOS = new ArrayList<>();
+                for (int i = 0; i < exitMemberList.size(); i++) {
+                    MemberListDTO memberListDTO = new MemberListDTO();
+                    JSONObject exitMemberListJSONObject = exitMemberList.getJSONObject(i);
+                    String member_account = exitMemberListJSONObject.getString("Member_Account");
+                    memberListDTO.setMemberAccount(member_account);
+                    memberListDTOS.add(memberListDTO);
+                }
+                res.setMemberList(memberListDTOS);
+            }
+
+            res.setEventType(jsonObject.getString("EventType"));
+
+            return res;
+        }
+    }
+
+    @NoArgsConstructor
+    @Data
+    public static class CallbackAfterNewMemberJoin {
+
+        private String clientIP;
+
+        private String optPlatform;
+        // 回调命令
+        private ETencentImCallbackCommand callbackCommand;
+        // 群组 ID
+        private String groupId;
+        // 群组类型
+        private ETencentGroupType type;
+        // 入群方式:Apply(申请入群);Invited(邀请入群)
+        private String joinType;
+        // 操作者成员
+        private String operatorAccount;
+        // 新入群成员列表
+        private List<MemberListDTO> newMemberList;
+        //毫秒级别,事件触发时间戳
+        private LocalDateTime eventTime;
+
+        public static CallbackAfterNewMemberJoin toObject(String jsonString) {
+
+            CallbackAfterNewMemberJoin res = new CallbackAfterNewMemberJoin();
+
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+            res.setCallbackCommand(ETencentImCallbackCommand.valueOf(jsonObject.getString("CallbackCommand").replace(".","_").toUpperCase(
+                Locale.ROOT)));
+            res.setGroupId(jsonObject.getString("GroupId"));
+            res.setType(jsonObject.getObject("Type",ETencentGroupType.class));
+            res.setJoinType(jsonObject.getString("JoinType"));
+            res.setOperatorAccount(jsonObject.getString("Operator_Account"));
+
+            JSONArray exitMemberList = jsonObject.getJSONArray("NewMemberList");
+            if (!exitMemberList.isEmpty()) {
+                List<MemberListDTO> memberListDTOS = new ArrayList<>();
+                for (int i = 0; i < exitMemberList.size(); i++) {
+                    MemberListDTO memberListDTO = new MemberListDTO();
+                    JSONObject exitMemberListJSONObject = exitMemberList.getJSONObject(i);
+                    String member_account = exitMemberListJSONObject.getString("Member_Account");
+                    memberListDTO.setMemberAccount(member_account);
+                    memberListDTOS.add(memberListDTO);
+                }
+                res.setNewMemberList(memberListDTOS);
+            }
+
+
+            res.setEventTime(((Timestamp)jsonObject.getTimestamp("EventTime")).toLocalDateTime());
+
+            return res;
+        }
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("推断流事件回调通知")
+    public static class CallbackStreamStateEvent implements Serializable {
+
+
+        @ApiModelProperty("推流域名")
+        @JSONField(name = "app")
+        private String app;
+
+        @ApiModelProperty("用户 APPID")
+        @JSONField(name = "appid")
+        private Integer appid;
+
+        @ApiModelProperty("推流路径")
+        @JSONField(name = "appname")
+        private String appname;
+
+        @ApiModelProperty("同直播流名称")
+        @JSONField(name = "channel_id")
+        private String channelId;
+
+        @ApiModelProperty("断流事件通知推流时长,单位毫秒")
+        @JSONField(name = "push_duration")
+        private String pushDuration;
+
+        @ApiModelProperty("推断流错误码")
+        @JSONField(name = "errcode")
+        private Integer errcode;
+
+        @ApiModelProperty("推断流错误描述")
+        @JSONField(name = "errmsg")
+        private String errmsg;
+
+        @ApiModelProperty("事件消息产生的 UNIX 时间戳")
+        @JSONField(name = "event_time")
+        private Integer eventTime;
+
+        @ApiModelProperty("事件类型: 推流:1;断流:0")
+        @JSONField(name = "event_type")
+        private Integer eventType;
+
+        @ApiModelProperty("判断是否为国内外推流。1-6为国内,7-200为国外")
+        @JSONField(name = "set_id")
+        private Integer setId;
+
+        @ApiModelProperty("直播接入点的 IP")
+        @JSONField(name = "node")
+        private String node;
+
+        @ApiModelProperty("消息序列号,标识一次推流活动,一次推流活动会产生相同序列号的推流和断流消息")
+        @JSONField(name = "sequence")
+        private String sequence;
+
+        @ApiModelProperty("直播流名称")
+        @JSONField(name = "stream_id")
+        private String streamId;
+
+        @ApiModelProperty("用户推流 URL 所带参数")
+        @JSONField(name = "stream_param")
+        private String streamParam;
+
+        @ApiModelProperty("用户推流 IP")
+        @JSONField(name = "user_ip")
+        private String userIp;
+
+        @ApiModelProperty("视频宽度,最开始推流回调的时候若视频头部信息缺失,可能为0")
+        @JSONField(name = "width")
+        private Integer width;
+
+        @ApiModelProperty("视频高度,最开始推流回调的时候若视频头部信息缺失,可能为0")
+        @JSONField(name = "height")
+        private Integer height;
+
+        @ApiModelProperty("事件通知安全签名 sign = MD5(key + t)")
+        @JSONField(name = "sign")
+        private String sign;
+
+        @ApiModelProperty("过期时间,事件通知签名过期 UNIX 时间戳")
+        @JSONField(name = "t")
+        private Integer t;
+
+        public static CallbackStreamStateEvent from(String jsonString) {
+            return JSON.parseObject(jsonString, CallbackStreamStateEvent.class);
+        }
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("流录制事件回调通知")
+    public static class CallbackSteamRecordEvent implements Serializable {
+
+        @ApiModelProperty("事件类型:直播录制: 100")
+        @JSONField(name = "event_type")
+        private Integer eventType;
+
+        @ApiModelProperty("用户 APPID")
+        @JSONField(name = "appid")
+        private Integer appid;
+
+        @ApiModelProperty("推流域名")
+        @JSONField(name = "app")
+        private String app;
+
+        @ApiModelProperty("json对象字符串,用户自定义数据")
+        @JSONField(name = "callback_ext")
+        private String callbackExt;
+
+        @ApiModelProperty("推流路径")
+        @JSONField(name = "appname")
+        private String appname;
+
+        @ApiModelProperty("直播流名称")
+        @JSONField(name = "stream_id")
+        private String streamId;
+
+        @ApiModelProperty("同直播流名称")
+        @JSONField(name = "channel_id")
+        private String channelId;
+
+        @ApiModelProperty("点播 file ID,在 云点播平台 可以唯一定位一个点播视频文件")
+        @JSONField(name = "file_id")
+        private String fileId;
+
+        @ApiModelProperty("点播文件ID")
+        @JSONField(name = "record_file_id")
+        private String recordFileId;
+
+        @ApiModelProperty("文件格式: FLV,HLS,MP4,AAC")
+        @JSONField(name = "file_format")
+        private String fileFormat;
+
+        @ApiModelProperty("录制任务 ID")
+        @JSONField(name = "task_id")
+        private String taskId;
+
+        @ApiModelProperty("录制任务开始写文件的时间;不能以该值作为录制内容的开始时间,录制内容的开始时间 = end_time – duration")
+        @JSONField(name = "start_time")
+        private Integer startTime;
+
+        @ApiModelProperty("录制任务结束写文件的时间")
+        @JSONField(name = "end_time")
+        private Integer endTime;
+
+        @ApiModelProperty("录制任务开始写文件的时间,微秒部分")
+        @JSONField(name = "start_time_usec")
+        private Integer startTimeUsec;
+
+        @ApiModelProperty("录制任务结束写文件的时间,微秒部分")
+        @JSONField(name = "end_time_usec")
+        private Integer endTimeUsec;
+
+        @ApiModelProperty("录制文件时长,单位秒")
+        @JSONField(name = "duration")
+        private Integer duration;
+
+        @ApiModelProperty("录制文件大小,单位字节")
+        @JSONField(name = "file_size")
+        private Integer fileSize;
+
+        @ApiModelProperty("用户推流 URL 所带参数(自定义)")
+        @JSONField(name = "stream_param")
+        private String streamParam;
+
+        @ApiModelProperty("录制文件下载 URL")
+        @JSONField(name = "video_url")
+        private String videoUrl;
+
+        @ApiModelProperty("录制开始拉流收到的首帧 pts ")
+        @JSONField(name = "media_start_time")
+        private Integer mediaStartTime;
+
+        @ApiModelProperty("录制从转码拉流录制对应的码率(单位 kbps)")
+        @JSONField(name = "record_bps")
+        private Integer recordBps;
+
+        @ApiModelProperty("事件通知安全签名 sign = MD5(key + t)")
+        @JSONField(name = "sign")
+        private String sign;
+
+        @ApiModelProperty("过期时间,事件通知签名过期 UNIX 时间戳")
+        @JSONField(name = "t")
+        private Integer t;
+
+
+        public static CallbackSteamRecordEvent from(String json) {
+            return JSON.parseObject(json, CallbackSteamRecordEvent.class);
+        }
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("流异常事件回调通知")
+    public static class CallbackStreamExceptionEvent implements Serializable {
+
+        @ApiModelProperty("用户 APPID")
+        @JSONField(name = "appid")
+        private Integer appid;
+
+        @ApiModelProperty("推流事件回调时间(单位ms)")
+        @JSONField(name = "data_time")
+        private Long dataTime;
+
+        @ApiModelProperty("推流域名")
+        @JSONField(name = "domain")
+        private String domain;
+
+        @ApiModelProperty("事件类型:推流异常:321")
+        @JSONField(name = "event_type")
+        private Integer eventType;
+
+        @JSONField(name = "interface")
+        private String interfaceX;
+        @JSONField(name = "path")
+        private String path;
+
+        @ApiModelProperty("有异常事件时,上报间隔(单位ms)")
+        @JSONField(name = "report_interval")
+        private Integer reportInterval;
+
+        @JSONField(name = "sequence")
+        private String sequence;
+
+        @ApiModelProperty("直播流名称")
+        @JSONField(name = "stream_id")
+        private String streamId;
+
+        @ApiModelProperty("用户推流 URL 所带参数")
+        @JSONField(name = "stream_param")
+        private String streamParam;
+
+        @JSONField(name = "timeout")
+        private Integer timeout;
+
+        @ApiModelProperty("详细异常事件事件组")
+        @JSONField(name = "abnormal_event")
+        private String abnormalEvent;
+
+        public static CallbackStreamExceptionEvent from(String json) {
+            return JSON.parseObject(json, CallbackStreamExceptionEvent.class);
+        }
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("流事件回调结果")
+    public static class StreamEventCallbackResult implements Serializable {
+
+        @ApiModelProperty("事件响应状态")
+        private Integer code;
+    }
+
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("腾讯云回调事件")
+    public static class TRTCEventInfo implements Serializable {
+
+        @JsonProperty("EventGroupId")
+        @ApiModelProperty("事件组")
+        private Integer eventGroupId;
+
+        @JsonProperty("EventType")
+        @ApiModelProperty("事件类型")
+        private Integer eventType;
+
+        @JsonProperty("CallbackTs")
+        @ApiModelProperty("事件时间")
+        private Long callbackTs;
+
+        @JsonProperty("EventInfo")
+        @ApiModelProperty("事件消息")
+        private EventInfo eventInfo;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("事件消息")
+    public static class EventInfo implements Serializable {
+
+        @JsonProperty("RoomId")
+        @ApiModelProperty("房间号")
+        private String roomId;
+
+        @JsonProperty("EventTs")
+        @ApiModelProperty("事件发生时间")
+        private Long eventTs;
+
+        @JsonProperty("UserId")
+        @ApiModelProperty("事件发生用户")
+        private String userId;
+
+        @JsonProperty("Role")
+        @ApiModelProperty("用户角色")
+        private String role;
+
+        @JsonProperty("TerminalType")
+        @ApiModelProperty("终端类型")
+        private String terminalType;
+
+        @JsonProperty("UserType")
+        @ApiModelProperty("用户类型")
+        private String userType;
+
+        @JsonProperty("Reason")
+        @ApiModelProperty("原因")
+        private String reason;
+    }
+}

+ 40 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentImCallbackResult.java

@@ -0,0 +1,40 @@
+package com.yonge.cooleshow.biz.dal.dto;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-02
+ */
+public class TencentImCallbackResult {
+
+    private String ActionStatus = "OK";
+
+    private String ErrorInfo;
+
+    private int ErrorCode = 0;
+
+    public String getActionStatus() {
+        return ActionStatus;
+    }
+
+    public void setActionStatus(String actionStatus) {
+        ActionStatus = actionStatus;
+    }
+
+    public String getErrorInfo() {
+        return ErrorInfo;
+    }
+
+    public void setErrorInfo(String errorInfo) {
+        ErrorInfo = errorInfo;
+    }
+
+    public int getErrorCode() {
+        return ErrorCode;
+    }
+
+    public void setErrorCode(int errorCode) {
+        ErrorCode = errorCode;
+    }
+}

+ 3 - 191
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseGroup.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
@@ -18,6 +19,8 @@ import java.util.Date;
  * @author hgw
  * @since 2022-03-23 14:35:19
  */
+
+@Data
 @ApiModel(value = "course_group-课程组表")
 public class CourseGroup implements Serializable {
     @TableId(value = "id_", type = IdType.AUTO)
@@ -116,196 +119,5 @@ public class CourseGroup implements Serializable {
     @ApiModelProperty(value = "更新时间")
     private Date updatedTime;
 
-    public String getReason() {
-        return reason;
-    }
-
-    public void setReason(String reason) {
-        this.reason = reason;
-    }
-
-    public Long getId() {
-        return id;
-    }
-
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public Long getTeacherId() {
-        return teacherId;
-    }
-
-    public void setTeacherId(Long teacherId) {
-        this.teacherId = teacherId;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public Long getSubjectId() {
-        return subjectId;
-    }
-
-    public void setSubjectId(Long subjectId) {
-        this.subjectId = subjectId;
-    }
-
-    public Integer getSingleCourseMinutes() {
-        return singleCourseMinutes;
-    }
-
-    public void setSingleCourseMinutes(Integer singleCourseMinutes) {
-        this.singleCourseMinutes = singleCourseMinutes;
-    }
-
-    public Integer getCourseNum() {
-        return courseNum;
-    }
-
-    public void setCourseNum(Integer courseNum) {
-        this.courseNum = courseNum;
-    }
-
-    public String getCourseIntroduce() {
-        return courseIntroduce;
-    }
-
-    public void setCourseIntroduce(String courseIntroduce) {
-        this.courseIntroduce = courseIntroduce;
-    }
-
-    public BigDecimal getCoursePrice() {
-        return coursePrice;
-    }
-
-    public void setCoursePrice(BigDecimal coursePrice) {
-        this.coursePrice = coursePrice;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public Date getSalesStartDate() {
-        return salesStartDate;
-    }
-
-    public void setSalesStartDate(Date salesStartDate) {
-        this.salesStartDate = salesStartDate;
-    }
-
-    public Date getSalesEndDate() {
-        return salesEndDate;
-    }
-
-    public void setSalesEndDate(Date salesEndDate) {
-        this.salesEndDate = salesEndDate;
-    }
-
-    public String getBackgroundPic() {
-        return backgroundPic;
-    }
-
-    public void setBackgroundPic(String backgroundPic) {
-        this.backgroundPic = backgroundPic;
-    }
-
-    public Integer getMixStudentNum() {
-        return mixStudentNum;
-    }
-
-    public void setMixStudentNum(Integer mixStudentNum) {
-        this.mixStudentNum = mixStudentNum;
-    }
-
-    public Integer getPreStudentNum() {
-        return preStudentNum;
-    }
-
-    public void setPreStudentNum(Integer preStudentNum) {
-        this.preStudentNum = preStudentNum;
-    }
-
-    public String getImGroupId() {
-        return imGroupId;
-    }
-
-    public void setImGroupId(String imGroupId) {
-        this.imGroupId = imGroupId;
-    }
-
-    public Date getCourseStartTime() {
-        return courseStartTime;
-    }
-
-    public void setCourseStartTime(Date courseStartTime) {
-        this.courseStartTime = courseStartTime;
-    }
-
-    public Long getCreatedBy() {
-        return createdBy;
-    }
-
-    public void setCreatedBy(Long createdBy) {
-        this.createdBy = createdBy;
-    }
-
-    public Date getCreatedTime() {
-        return createdTime;
-    }
-
-    public void setCreatedTime(Date createdTime) {
-        this.createdTime = createdTime;
-    }
-
-    public Long getUpdatedBy() {
-        return updatedBy;
-    }
-
-    public void setUpdatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-    }
-
-    public Date getUpdatedTime() {
-        return updatedTime;
-    }
-
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
-
-    public Integer getCompleteCourseNum() {
-        return completeCourseNum;
-    }
-
-    public void setCompleteCourseNum(Integer completeCourseNum) {
-        this.completeCourseNum = completeCourseNum;
-    }
-
-    public YesOrNoEnum getAuditVersion() {
-        return auditVersion;
-    }
-
-    public void setAuditVersion(YesOrNoEnum auditVersion) {
-        this.auditVersion = auditVersion;
-    }
 }
 

+ 9 - 220
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseSchedule.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -15,6 +16,7 @@ import java.util.Date;
  * @author hgw
  * @since 2022-03-23 14:35:20
  */
+@Data
 @ApiModel(value = "course_schedule-老师课程表")
 public class CourseSchedule implements Serializable {
     @TableId(value = "id_", type = IdType.AUTO)
@@ -25,6 +27,13 @@ public class CourseSchedule implements Serializable {
     @ApiModelProperty(value = "课程组id_")
     private Long courseGroupId;
 
+
+
+    @TableField("room_uid_")
+    @ApiModelProperty(value = "房间编号 直播课直播房间号")
+    private String roomUid;
+
+
     @TableField("type_")
     @ApiModelProperty(value = "类型 practice陪练课 live直播课  CourseScheduleEnum")
     private String type;
@@ -85,225 +94,5 @@ public class CourseSchedule implements Serializable {
     @ApiModelProperty(value = "更新时间")
     private Date updatedTime;
 
-    public Integer getSingleCourseTime() {
-        return singleCourseTime;
-    }
-
-    public void setSingleCourseTime(Integer singleCourseTime) {
-        this.singleCourseTime = singleCourseTime;
-    }
-
-    public static CourseSchedule build() {
-        return new CourseSchedule();
-    }
-
-    public Long getId() {
-        return id;
-    }
-
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    public CourseSchedule id(Long id) {
-        this.id = id;
-        return this;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public CourseSchedule type(String type) {
-        this.type = type;
-        return this;
-    }
-
-    public Long getTeacherId() {
-        return teacherId;
-    }
-
-    public void setTeacherId(Long teacherId) {
-        this.teacherId = teacherId;
-    }
-
-    public CourseSchedule teacherId(Long teacherId) {
-        this.teacherId = teacherId;
-        return this;
-    }
-
-    public Date getClassDate() {
-        return classDate;
-    }
-
-    public void setClassDate(Date classDate) {
-        this.classDate = classDate;
-    }
-
-    public CourseSchedule classDate(Date classDate) {
-        this.classDate = classDate;
-        return this;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public CourseSchedule status(String status) {
-        this.status = status;
-        return this;
-    }
-
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public CourseSchedule courseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-        return this;
-    }
-
-    public Integer getClassNum() {
-        return classNum;
-    }
-
-    public void setClassNum(Integer classNum) {
-        this.classNum = classNum;
-    }
-
-    public CourseSchedule classNum(Integer classNum) {
-        this.classNum = classNum;
-        return this;
-    }
-
-    public Date getStartTime() {
-        return startTime;
-    }
-
-    public void setStartTime(Date startTime) {
-        this.startTime = startTime;
-    }
-
-    public CourseSchedule startTime(Date startTime) {
-        this.startTime = startTime;
-        return this;
-    }
-
-    public Date getEndTime() {
-        return endTime;
-    }
-
-    public void setEndTime(Date endTime) {
-        this.endTime = endTime;
-    }
-
-    public CourseSchedule endTime(Date endTime) {
-        this.endTime = endTime;
-        return this;
-    }
-
-    public Integer getLock() {
-        return lock;
-    }
-
-    public void setLock(Integer lock) {
-        this.lock = lock;
-    }
-
-    public CourseSchedule lock(Integer lock) {
-        this.lock = lock;
-        return this;
-    }
-
-    public Date getLockTime() {
-        return lockTime;
-    }
-
-    public void setLockTime(Date lockTime) {
-        this.lockTime = lockTime;
-    }
-
-    public CourseSchedule lockTime(Date lockTime) {
-        this.lockTime = lockTime;
-        return this;
-    }
-
-    public Integer getExStudentNum() {
-        return exStudentNum;
-    }
-
-    public void setExStudentNum(Integer exStudentNum) {
-        this.exStudentNum = exStudentNum;
-    }
-
-    public CourseSchedule exStudentNum(Integer exStudentNum) {
-        this.exStudentNum = exStudentNum;
-        return this;
-    }
-
-    public Long getCreatedBy() {
-        return createdBy;
-    }
-
-    public void setCreatedBy(Long createdBy) {
-        this.createdBy = createdBy;
-    }
-
-    public CourseSchedule createdBy(Long createdBy) {
-        this.createdBy = createdBy;
-        return this;
-    }
-
-    public Date getCreatedTime() {
-        return createdTime;
-    }
-
-    public void setCreatedTime(Date createdTime) {
-        this.createdTime = createdTime;
-    }
-
-    public CourseSchedule createdTime(Date createdTime) {
-        this.createdTime = createdTime;
-        return this;
-    }
-
-    public Long getUpdatedBy() {
-        return updatedBy;
-    }
-
-    public void setUpdatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-    }
-
-    public CourseSchedule updatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-        return this;
-    }
-
-    public Date getUpdatedTime() {
-        return updatedTime;
-    }
-
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
-
-    public CourseSchedule updatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-        return this;
-    }
-
 }
 

+ 10 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImRoomMessage.java

@@ -13,6 +13,7 @@ public class ImRoomMessage extends BaseMessage {
     //objectName 类型-将所有人强制踢出房间
     public static final String FORCED_OFFLINE = "RC:ForcedOffline";
 
+    private String serviceProvider;
     /**
      * 消息类型
      */
@@ -33,6 +34,15 @@ public class ImRoomMessage extends BaseMessage {
      */
     private String toChatroomId;
 
+
+    public String getServiceProvider() {
+        return serviceProvider;
+    }
+
+    public void setServiceProvider(String serviceProvider) {
+        this.serviceProvider = serviceProvider;
+    }
+
     @Override
     public String getObjectName() {
         return objectName;

+ 3 - 39
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImUserStateSync.java

@@ -1,6 +1,7 @@
 package com.yonge.cooleshow.biz.dal.entity;
 
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 
@@ -10,6 +11,7 @@ import java.io.Serializable;
  * @author hgw
  * Created by 2022-02-18
  */
+@Data
 public class ImUserStateSync implements Serializable {
 
     @ApiModelProperty(value = "用户 Id")
@@ -27,43 +29,5 @@ public class ImUserStateSync implements Serializable {
     @ApiModelProperty(value = "用户当前的 IP 地址及端口")
     private String clientIp;
 
-    public String getUserid() {
-        return userid;
-    }
-
-    public void setUserid(String userid) {
-        this.userid = userid;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public String getOs() {
-        return os;
-    }
-
-    public void setOs(String os) {
-        this.os = os;
-    }
-
-    public Long getTime() {
-        return time;
-    }
-
-    public void setTime(Long time) {
-        this.time = time;
-    }
-
-    public String getClientIp() {
-        return clientIp;
-    }
-
-    public void setClientIp(String clientIp) {
-        this.clientIp = clientIp;
-    }
+    private String roomUid;
 }

+ 17 - 142
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -16,19 +17,20 @@ import java.util.Date;
  * @author hgw
  * @since 2022-03-23 14:35:19
  */
+@Data
 @ApiModel(value = "live_room-直播房间与课程的关系表表")
 public class LiveRoom implements Serializable {
     @TableId(value = "id_", type = IdType.AUTO)
     @ApiModelProperty(value = "主键")
     private Long id;
 
-    @TableField("course_group_id_")
-    @ApiModelProperty(value = "课程组id")
-    private Long courseGroupId;
-
-    @TableField("course_id_")
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
+//    @TableField("course_group_id_")
+//    @ApiModelProperty(value = "课程组id")
+//    private Long courseGroupId;
+//
+//    @TableField("course_id_")
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
 
     @TableField("speaker_id_")
     @ApiModelProperty(value = "主讲人id/老师id")
@@ -42,6 +44,14 @@ public class LiveRoom implements Serializable {
     @ApiModelProperty(value = "房间标题/最多12个字")
     private String roomTitle;
 
+    @TableField("service_provider_")
+    @ApiModelProperty(value = "服务提供方")
+    private String serviceProvider;
+
+    @TableField("video_record_")
+    @ApiModelProperty(value = "录制记录")
+    private String videoRecord;
+
     @TableField("live_start_time_")
     @ApiModelProperty(value = "直播开始时间")
     private Date liveStartTime;
@@ -86,140 +96,5 @@ public class LiveRoom implements Serializable {
     @ApiModelProperty(value = "更新时间")
     private Date updatedTime;
 
-    public Long getId() {
-        return id;
-    }
-
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public Long getCourseId() {
-        return courseId;
-    }
-
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
-
-    public Long getSpeakerId() {
-        return speakerId;
-    }
-
-    public void setSpeakerId(Long speakerId) {
-        this.speakerId = speakerId;
-    }
-
-    public String getRoomUid() {
-        return roomUid;
-    }
-
-    public void setRoomUid(String roomUid) {
-        this.roomUid = roomUid;
-    }
-
-    public String getRoomTitle() {
-        return roomTitle;
-    }
-
-    public void setRoomTitle(String roomTitle) {
-        this.roomTitle = roomTitle;
-    }
-
-    public Date getLiveStartTime() {
-        return liveStartTime;
-    }
-
-    public void setLiveStartTime(Date liveStartTime) {
-        this.liveStartTime = liveStartTime;
-    }
-
-    public Date getLiveEndTime() {
-        return liveEndTime;
-    }
-
-    public void setLiveEndTime(Date liveEndTime) {
-        this.liveEndTime = liveEndTime;
-    }
-
-    public String getLiveRemark() {
-        return liveRemark;
-    }
-
-    public void setLiveRemark(String liveRemark) {
-        this.liveRemark = liveRemark;
-    }
-
-    public Integer getLiveState() {
-        return liveState;
-    }
-
-    public void setLiveState(Integer liveState) {
-        this.liveState = liveState;
-    }
-
-    public Integer getRoomState() {
-        return roomState;
-    }
-
-    public void setRoomState(Integer roomState) {
-        this.roomState = roomState;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public Long getCreatedBy() {
-        return createdBy;
-    }
-
-    public void setCreatedBy(Long createdBy) {
-        this.createdBy = createdBy;
-    }
-
-    public Date getCreatedTime() {
-        return createdTime;
-    }
-
-    public void setCreatedTime(Date createdTime) {
-        this.createdTime = createdTime;
-    }
-
-    public Long getUpdatedBy() {
-        return updatedBy;
-    }
-
-    public void setUpdatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-    }
-
-    public Date getUpdatedTime() {
-        return updatedTime;
-    }
-
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
-
-    public String getCoverPic() {
-        return coverPic;
-    }
-
-    public void setCoverPic(String coverPic) {
-        this.coverPic = coverPic;
-    }
 }
 

+ 7 - 23
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoomVideo.java

@@ -22,13 +22,13 @@ public class LiveRoomVideo implements Serializable {
     @ApiModelProperty(value = "主键")
     private Long id;
 
-    @TableField("course_group_id_")
-    @ApiModelProperty(value = "课程组id")
-    private Long courseGroupId;
-
-    @TableField("course_id_")
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
+//    @TableField("course_group_id_")
+//    @ApiModelProperty(value = "课程组id")
+//    private Long courseGroupId;
+//
+//    @TableField("course_id_")
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
 
     @TableField("room_uid_")
     @ApiModelProperty(value = "房间编号")
@@ -67,22 +67,6 @@ public class LiveRoomVideo implements Serializable {
         this.id = id;
     }
 
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public Long getCourseId() {
-        return courseId;
-    }
-
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
-
     public String getRoomUid() {
         return roomUid;
     }

+ 7 - 164
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/RoomInfoCache.java

@@ -2,6 +2,7 @@ package com.yonge.cooleshow.biz.dal.entity;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -10,13 +11,14 @@ import java.util.Date;
  * @author hgw
  * Created by 2022年3月24日
  */
+@Data
 public class RoomInfoCache implements Serializable {
 
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
-
-    @ApiModelProperty(value = "课程组id")
-    private Long courseGroupId;
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
+//
+//    @ApiModelProperty(value = "课程组id")
+//    private Long courseGroupId;
 
     @ApiModelProperty(value = "主讲人id")
     private Long speakerId;
@@ -75,163 +77,4 @@ public class RoomInfoCache implements Serializable {
     @ApiModelProperty(value = "是否允许连麦 0允许 1不允许")
     private Integer whether_mic = 0;
 
-    public Integer getWhether_mic() {
-        return whether_mic;
-    }
-
-    public void setWhether_mic(Integer whether_mic) {
-        this.whether_mic = whether_mic;
-    }
-
-    public Integer getExpiredCloseMinute() {
-        return expiredCloseMinute;
-    }
-
-    public void setExpiredCloseMinute(Integer expiredCloseMinute) {
-        this.expiredCloseMinute = expiredCloseMinute;
-    }
-
-    public Long getCourseId() {
-        return courseId;
-    }
-
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
-
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public Long getSpeakerId() {
-        return speakerId;
-    }
-
-    public void setSpeakerId(Long speakerId) {
-        this.speakerId = speakerId;
-    }
-
-    public String getSpeakerName() {
-        return speakerName;
-    }
-
-    public void setSpeakerName(String speakerName) {
-        this.speakerName = speakerName;
-    }
-
-    public Integer getSpeakerState() {
-        return speakerState;
-    }
-
-    public void setSpeakerState(Integer speakerState) {
-        this.speakerState = speakerState;
-    }
-
-    public String getRoomUid() {
-        return roomUid;
-    }
-
-    public void setRoomUid(String roomUid) {
-        this.roomUid = roomUid;
-    }
-
-    public Date getLiveStartTime() {
-        return liveStartTime;
-    }
-
-    public void setLiveStartTime(Date liveStartTime) {
-        this.liveStartTime = liveStartTime;
-    }
-
-    public Date getLiveEndTime() {
-        return liveEndTime;
-    }
-
-    public void setLiveEndTime(Date liveEndTime) {
-        this.liveEndTime = liveEndTime;
-    }
-
-    public Date getCreateRoomTime() {
-        return createRoomTime;
-    }
-
-    public void setCreateRoomTime(Date createRoomTime) {
-        this.createRoomTime = createRoomTime;
-    }
-
-    public Integer getExpiredMinute() {
-        return expiredMinute;
-    }
-
-    public void setExpiredMinute(Integer expiredMinute) {
-        this.expiredMinute = expiredMinute;
-    }
-
-    public Date getJoinRoomTime() {
-        return joinRoomTime;
-    }
-
-    public void setJoinRoomTime(Date joinRoomTime) {
-        this.joinRoomTime = joinRoomTime;
-    }
-
-    public Date getExitRoomTime() {
-        return exitRoomTime;
-    }
-
-    public void setExitRoomTime(Date exitRoomTime) {
-        this.exitRoomTime = exitRoomTime;
-    }
-
-    public String getRoomType() {
-        return roomType;
-    }
-
-    public void setRoomType(String roomType) {
-        this.roomType = roomType;
-    }
-
-    public Integer getLikeNum() {
-        return likeNum;
-    }
-
-    public void setLikeNum(Integer likeNum) {
-        this.likeNum = likeNum;
-    }
-
-    public Integer getLookNum() {
-        return lookNum;
-    }
-
-    public void setLookNum(Integer lookNum) {
-        this.lookNum = lookNum;
-    }
-
-    public String getSpeakerPic() {
-        return speakerPic;
-    }
-
-    public void setSpeakerPic(String speakerPic) {
-        this.speakerPic = speakerPic;
-    }
-
-    public String getRoomTitle() {
-        return roomTitle;
-    }
-
-    public void setRoomTitle(String roomTitle) {
-        this.roomTitle = roomTitle;
-    }
-
-    public String getLiveRemark() {
-        return liveRemark;
-    }
-
-    public void setLiveRemark(String liveRemark) {
-        this.liveRemark = liveRemark;
-    }
 }

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java

@@ -7,7 +7,7 @@ import java.util.stream.Collectors;
 public enum RoomTypeEnum {
 
     LIVE("直播课"),
-    PRACTICE("陪练课"),
+//    PRACTICE("陪练课"),
     TEMP("临时直播间");
 
     private final String code;

+ 30 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentGroupType.java

@@ -0,0 +1,30 @@
+package com.yonge.cooleshow.biz.dal.enums.im;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-06
+ */
+public enum ETencentGroupType {
+
+    Public("陌生人社交群"),
+    Work("好友工作群"),
+    AVChatRoom("直播群"),
+    Meeting("临时会议群"),
+    Community("社群"),
+
+    ;
+
+    private String code;
+
+    private String msg;
+
+
+    ETencentGroupType(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+
+}

+ 31 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentImCallbackCommand.java

@@ -0,0 +1,31 @@
+package com.yonge.cooleshow.biz.dal.enums.im;
+
+import lombok.Getter;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-02
+ */
+@Getter
+public enum ETencentImCallbackCommand {
+
+    // STATE_STATECHANGE("State.StateChange", "用户状态变更"),
+    GROUP_CALLBACKAFTERMEMBEREXIT("Group.CallbackAfterMemberExit", "群成员离开之后回调"),
+    GROUP_CALLBACKAFTERNEWMEMBERJOIN("Group.CallbackAfterNewMemberJoin", "新成员入群之后回调"),
+    GROUP_CALLBACKONMEMBERSTATECHANGE("Group.CallbackOnMemberStateChange", "直播群成员在线状态回调"),
+    ;
+
+    private final String command;
+    private final String desc;
+
+    private final String code;
+
+    ETencentImCallbackCommand(String command, String desc) {
+        this.command = command;
+        this.desc = desc;
+
+        this.code = this.name();
+    }
+}

+ 30 - 4
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java

@@ -1,17 +1,18 @@
 package com.yonge.cooleshow.biz.dal.service;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.yonge.cooleshow.biz.dal.dao.LiveRoomDao;
-import com.yonge.cooleshow.biz.dal.entity.ImRoomMessage;
-import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
-import com.yonge.cooleshow.biz.dal.entity.LiveRoom;
-import com.yonge.cooleshow.biz.dal.entity.RoomInfoCache;
+import com.yonge.cooleshow.biz.dal.dto.TencentData;
+import com.yonge.cooleshow.biz.dal.entity.*;
+import com.yonge.cooleshow.biz.dal.enums.RoomTypeEnum;
 import com.yonge.cooleshow.biz.dal.vo.TeacherLivingInfoVo;
 import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
 import com.yonge.toolset.base.page.PageInfo;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * 直播房间与课程的关系表表(LiveRoom)表服务接口
@@ -67,6 +68,8 @@ public interface LiveRoomService extends IService<LiveRoom> {
      */
     void destroyLiveRoom(String roomUId);
 
+    void updateLiveCourseSchedule(String preCreateRoomMinute, Map<Long, String> titleMap, Map<Long, String> remarkMap, RoomTypeEnum en, Date now, CourseSchedule c);
+
     /**
      * 创建临时房间-直播间
      */
@@ -145,5 +148,28 @@ public interface LiveRoomService extends IService<LiveRoom> {
      * @return LiveRoomWrapper
      */
     LiveRoomWrapper findLiveRoomDetailInfoByRoomId(String liveRoomId, Long userId);
+
+    void createLiveRoomVideoRecord(TencentData.CallbackSteamRecordEvent event);
+
+
+    /**
+     * 直播群成员在线状态回调处理
+     *
+     */
+    void callbackOnMemberStateChange(TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange);
+    /**
+     * 群成员离开之后回调
+     */
+    void callbackAfterMemberExit(TencentData.CallbackAfterMemberExit callbackAfterMemberExit);
+
+    /**
+     * 新成员入群之后回调
+     */
+    void callbackAfterNewMemberJoin(TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin);
+
+    // 定时任务凌晨2点,关闭腾讯直播间, 融云直播间自动关闭,不做处理
+    void destroyLiveRoom();
+
+    boolean tryDestroyLiveRoom(LiveRoom liveRoom);
 }
 

+ 2 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java

@@ -1062,7 +1062,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
             schedule.setEndTime(date.getEndTime());
             schedule.setCourseGroupId(groupId);
             schedule.setType(CourseScheduleEnum.PRACTICE.getCode());
-            schedule.classNum(i + 1);
+            schedule.setClassNum(i + 1);
             schedule.setTeacherId(scheduleDto.getTeacherId());
             schedule.setLock(1);
             schedule.setStatus(CourseScheduleEnum.NOT_START.getCode());
@@ -2056,7 +2056,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
             CourseSchedule schedule = new CourseSchedule();
             schedule.setCourseGroupId(courseGroup.getId());
             schedule.setType(CourseScheduleEnum.PIANO_ROOM_CLASS.getCode());
-            schedule.classNum(i + 1);
+            schedule.setClassNum(i + 1);
             schedule.setTeacherId(teacherId);
             schedule.setClassDate(DateUtil.trunc(courseTimeEntity.getStartTime()));
             schedule.setStartTime(courseTimeEntity.getStartTime());

+ 549 - 90
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java

@@ -1,6 +1,5 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.COOLESHOW;
 import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_HEART_BEAT;
 import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_INFO;
 import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_LIKE;
@@ -15,14 +14,10 @@ import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.USER_ID;
 import static com.yonge.cooleshow.common.constant.SysConfigConstant.DESTROY_EXPIRED_LIVE_ROOM_MINUTE;
 import static com.yonge.cooleshow.common.constant.SysConfigConstant.PRE_CREATE_LIVE_ROOM_MINUTE;
 
+import java.text.MessageFormat;
 import java.time.LocalDate;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
+import java.time.ZoneId;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
@@ -31,10 +26,20 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.middleware.live.LivePluginContext;
+import com.microsvc.toolkit.middleware.live.LivePluginService;
+import com.microsvc.toolkit.middleware.live.impl.RongCloudLivePlugin;
+import com.microsvc.toolkit.middleware.live.impl.TencentCloudLivePlugin;
+import com.microsvc.toolkit.middleware.live.message.*;
+import com.yonge.cooleshow.biz.dal.dto.TencentData;
+import com.yonge.cooleshow.biz.dal.entity.*;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
+import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
+import com.yonge.cooleshow.common.enums.EGroupDefinedDataType;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
 import org.redisson.api.RBucket;
 import org.redisson.api.RMap;
 import org.redisson.api.RedissonClient;
@@ -52,17 +57,6 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.LiveRoomDao;
-import com.yonge.cooleshow.biz.dal.entity.CourseSchedule;
-import com.yonge.cooleshow.biz.dal.entity.CourseScheduleStudentPayment;
-import com.yonge.cooleshow.biz.dal.entity.CourseScheduleTeacherSalary;
-import com.yonge.cooleshow.biz.dal.entity.IMApiResultInfo;
-import com.yonge.cooleshow.biz.dal.entity.ImRoomMessage;
-import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
-import com.yonge.cooleshow.biz.dal.entity.LiveRoom;
-import com.yonge.cooleshow.biz.dal.entity.RoomInfoCache;
-import com.yonge.cooleshow.biz.dal.entity.RoomUserInfoCache;
-import com.yonge.cooleshow.biz.dal.entity.StudentAttendance;
-import com.yonge.cooleshow.biz.dal.entity.TeacherAttendance;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.CourseScheduleEnum;
 import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
@@ -70,17 +64,6 @@ import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.OrderStatusEnum;
 import com.yonge.cooleshow.biz.dal.enums.RoomTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.TeacherSalaryEnum;
-import com.yonge.cooleshow.biz.dal.service.CourseGroupService;
-import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
-import com.yonge.cooleshow.biz.dal.service.CourseScheduleStudentPaymentService;
-import com.yonge.cooleshow.biz.dal.service.CourseScheduleTeacherSalaryService;
-import com.yonge.cooleshow.biz.dal.service.LiveBroadcastRoomMemberService;
-import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
-import com.yonge.cooleshow.biz.dal.service.StudentAttendanceService;
-import com.yonge.cooleshow.biz.dal.service.SysConfigService;
-import com.yonge.cooleshow.biz.dal.service.SysMessageService;
-import com.yonge.cooleshow.biz.dal.service.TeacherAttendanceService;
-import com.yonge.cooleshow.biz.dal.service.UserOrderService;
 import com.yonge.cooleshow.biz.dal.support.IMHelper;
 import com.yonge.cooleshow.biz.dal.support.WrapperUtil;
 import com.yonge.cooleshow.biz.dal.vo.TeacherLivingInfoVo;
@@ -131,6 +114,14 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
     private LiveBroadcastRoomMemberService liveBroadcastRoomMemberService;
     @Autowired
     private SysUserMapper sysUserMapper;
+    @Autowired
+    private LivePluginContext livePluginContext;
+
+    @Autowired
+    private LiveRoomService liveRoomService;
+
+    @Autowired
+    private LiveRoomVideoService liveRoomVideoService;
     /**
      * 生成房间UID
      *
@@ -138,7 +129,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
      * <p> en :房间类型
      */
     private final BiFunction<Long, RoomTypeEnum, String> GenRoomUid = (userId, en) -> String
-            .join("-", COOLESHOW, en.getCode(), userId.toString(), new Date().getTime() + "");
+            .join("-", en.getCode(), userId.toString(), new Date().getTime() + "");
 
     @Override
     public LiveRoomDao getDao() {
@@ -253,38 +244,49 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         RoomTypeEnum en = RoomTypeEnum.LIVE;
         Date now = new Date();
         courseScheduleList.forEach(c -> {
-            //课程开始时间-提前创建时间 = 创建房间时间
-            Date createRoomTime = DateUtil.addMinutes(c.getStartTime(), -Integer.parseInt(preCreateRoomMinute));
-            if (now.getTime() < createRoomTime.getTime()) {
-                return;
-            }
-            //避免重复创建直播间
-            int count = this.count(Wrappers.<LiveRoom>lambdaQuery().eq(LiveRoom::getCourseId, c.getId()));
-            if (count > 0) {
-                return;
+            try {
+                liveRoomService.updateLiveCourseSchedule(preCreateRoomMinute, titleMap, remarkMap, en, now, c);
+            } catch (Exception e) {
+                log.error("createCourseLiveRoom>>>生成直播间失败:{}", c, e);
             }
-            LiveRoom room = new LiveRoom();
-            room.setCourseGroupId(c.getCourseGroupId());
-            room.setCourseId(c.getId());
-            room.setRoomUid(GenRoomUid.apply(c.getTeacherId(), en));
-            room.setRoomTitle(titleMap.get(c.getCourseGroupId()));
-            room.setLiveRemark(remarkMap.get(c.getCourseGroupId()));
-            room.setSpeakerId(c.getTeacherId());
-            room.setLiveStartTime(c.getStartTime());
-            room.setLiveEndTime(c.getEndTime());
-            room.setLiveState(1);
-            room.setRoomState(0);
-            room.setType(en.getCode());
-            room.setCreatedBy(-2L);
-            room.setCreatedTime(now);
-            this.save(room);
-            //去融云创建房间及创建房间缓存信息
-            this.createLiveRoomInfo(room);
-            //开课提醒
-            this.pushLiveCreateRoom(room);
         });
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateLiveCourseSchedule(String preCreateRoomMinute, Map<Long, String> titleMap, Map<Long, String> remarkMap, RoomTypeEnum en, Date now, CourseSchedule c) {
+        //课程开始时间-提前创建时间 = 创建房间时间
+        Date createRoomTime = DateUtil.addMinutes(c.getStartTime(), -Integer.parseInt(preCreateRoomMinute));
+        if (now.getTime() < createRoomTime.getTime()) {
+            return;
+        }
+        //避免重复创建直播间
+        if (StringUtils.isNotBlank(c.getRoomUid())) {
+            log.info("createCourseLiveRoom>>>roomUid:{} 已存在", c.getRoomUid());
+            return;
+        }
+        LiveRoom room = new LiveRoom();
+        room.setRoomUid(GenRoomUid.apply(c.getTeacherId(), en));
+        room.setRoomTitle(titleMap.get(c.getCourseGroupId()));
+        room.setLiveRemark(remarkMap.get(c.getCourseGroupId()));
+        room.setSpeakerId(c.getTeacherId());
+        room.setLiveStartTime(c.getStartTime());
+        room.setLiveEndTime(c.getEndTime());
+        room.setLiveState(1);
+        room.setRoomState(0);
+        room.setType(en.getCode());
+        room.setCreatedBy(-2L);
+        room.setCreatedTime(now);
+        this.save(room);
+        // 更新课程房间信息
+        c.setRoomUid(room.getRoomUid());
+        courseScheduleService.updateById(c);
+        //去融云创建房间及创建房间缓存信息
+        this.createLiveRoomInfo(room);
+        //开课提醒
+        this.pushLiveCreateRoom(room);
+    }
+
     /**
      * 开课提醒
      */
@@ -342,8 +344,6 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         RoomTypeEnum en = RoomTypeEnum.TEMP;
         String roomUid = GenRoomUid.apply(teacherId, en);
         LiveRoom room = new LiveRoom();
-        room.setCourseGroupId(-1L);
-        room.setCourseId(-1L);
         room.setRoomUid(roomUid);
         room.setRoomTitle(roomTitle);
         room.setLiveRemark(liveRemark);
@@ -380,12 +380,111 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             //生成主讲人信息
             this.createRoomInfoCache(room, sysUser);
             //去融云创建房间
-            this.createLiveRoom(room.getRoomUid(), room.getRoomTitle());
+
+            //去融云创建房间
+            LivePluginService pluginService = livePluginContext.getPluginService(room.getServiceProvider());
+
+            // 注册主播用户信息到三方平台
+            pluginService.register(sysUser.getId().toString(), sysUser.getUsername(), sysUser.getAvatar());
+            // 创建直播间IM群
+            pluginService.chatRoomCreate(room.getRoomUid(), room.getRoomTitle(),sysUser.getId().toString());
+            // 腾讯云直播,提前生成录制规则
+            if (room.getServiceProvider().equals(TencentCloudLivePlugin.PLUGIN_NAME)) {
+
+                DateTime now = DateTime.now();
+
+                RTCRequest.RecordStart recordStart = RTCRequest.RecordStart.builder()
+                        .streamName(MessageFormat.format("{0}_{1}", room.getRoomUid(), room.getSpeakerId().toString()))
+                        .extra("")
+                        .startTime(now.toDateTime().getMillis())
+                        .endTime(now.plusDays(1).toDateTime().getMillis())
+                        .build();
+
+                if (StringUtils.isBlank(room.getVideoRecord())) {
+
+                    // 生成录制任务
+                    log.info("createLiveRoom>>>>>> recordStart:{}", recordStart.jsonString());
+                    RTCRoom.RecordResp resp = pluginService.rtcRoomRecordStart(recordStart);
+
+                    // 设置录制任务Id
+                    LiveRoom update = new LiveRoom();
+                    update.setId(room.getId());
+                    if (StringUtils.isBlank(room.getVideoRecord())) {
+                        update.setVideoRecord(resp.getRecordId());
+                    } else {
+                        update.setVideoRecord(room.getVideoRecord() + "," + resp.getRecordId());
+                    }
+                    updateById(update);
+                }
+                // 设置直播间房间属性默认值
+                setDefaultRoomDefinedInfo(room);
+            }
         } catch (Exception e) {
             throw new BizException("创建直播间失败!", e.getCause());
         }
     }
 
+
+    private void setDefaultRoomDefinedInfo(LiveRoom roomVo) {
+        List<TencentRequest.ChatRoomGroupDefinedData> appDefinedData = new ArrayList<>();
+        Arrays.stream(EGroupDefinedDataType.values()).forEach(key -> {
+            switch (key) {
+                case ANCHOR_STATUS:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("OFFLINE")
+                            .build());
+                    break;
+                case GLOBAL_BAN:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("OFF")
+                            .build());
+                    break;
+                case LIKES:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("0")
+                            .build());
+                    break;
+
+                case MEMBER_ONLINE:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("0")
+                            .build());
+                    break;
+                case MEMBER_TOTAL:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("0")
+                            .build());
+                    break;
+                case LIVE_STATUS:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("OFF")
+                            .build());
+                    break;
+                case ANCHOR_CAMERA:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("ON")
+                            .build());
+                    break;
+            }
+        });
+        try {
+            livePluginContext.getPluginService(roomVo.getServiceProvider())
+                    .chatRoomGroupDefinedData(TencentRequest.ChatRoomGroup.builder()
+                            .groupId(roomVo.getRoomUid())
+                            .appDefinedData(appDefinedData)
+                            .build());
+        } catch (Exception e) {
+            log.error("设置直播群配置失败", e);
+        }
+    }
+
     //生成主讲人信息
     private void createRoomInfoCache(LiveRoom room, SysUser sysUser) {
         RoomInfoCache roomCache = new RoomInfoCache();
@@ -404,8 +503,8 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         roomCache.setExpiredMinute(Integer.parseInt(configValue));
         roomCache.setExpiredCloseMinute(Integer.parseInt(expireCloseMinute));
         roomCache.setRoomType(room.getType());
-        roomCache.setCourseId(room.getCourseId());
-        roomCache.setCourseGroupId(room.getCourseGroupId());
+//        roomCache.setCourseId(room.getCourseId());
+//        roomCache.setCourseGroupId(room.getCourseGroupId());
 
         //写入房间信息
         getLiveRoomInfo(room.getRoomUid()).set(roomCache, 2L, TimeUnit.DAYS);
@@ -439,6 +538,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
     /**
      * 定时任务-清理过期的房间
      */
+    @Override
     public void destroyExpiredLiveRoom() {
         //查询房间过期时间
         String expiredMinuteStr = sysConfigService.findConfigValue(DESTROY_EXPIRED_LIVE_ROOM_MINUTE);
@@ -464,7 +564,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             }
             //当前时间 大于 直播间过期时间
             if (now.getTime() >= expiredDate.getTime()) {
-                this.destroyLiveRoom(room);
+                liveRoomService.destroyLiveRoom(room.getRoomUid());
             }
         });
     }
@@ -481,6 +581,20 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         this.destroyLiveRoom(liveRoom);
     }
 
+
+    private CourseSchedule getCourseScheduleByRoomUid(String roomUid) {
+        CourseSchedule one = courseScheduleService.lambdaQuery()
+                .eq(CourseSchedule::getRoomUid, roomUid)
+                .last("limit 1")
+                .one();
+        if (Objects.isNull(one)) {
+            one = new CourseSchedule();
+            one.setId(-1L);
+            one.setCourseGroupId(-1L);
+        }
+
+        return one;
+    }
     /**
      * 关闭直播间
      *
@@ -490,11 +604,12 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         if (Objects.isNull(room)) {
             return;
         }
+        CourseSchedule schedule = getCourseScheduleByRoomUid(room.getRoomUid());
         //直播课
         if (room.getType().equals(RoomTypeEnum.LIVE.getCode())) {
             //查询老师分润表
             List<CourseScheduleTeacherSalary> salaryList = courseScheduleTeacherSalaryService.list(Wrappers.<CourseScheduleTeacherSalary>lambdaQuery()
-                    .eq(CourseScheduleTeacherSalary::getCourseScheduleId, room.getCourseId())
+                    .eq(CourseScheduleTeacherSalary::getCourseScheduleId,schedule.getId())
             );
             if (CollectionUtils.isEmpty(salaryList)) {
                 return;
@@ -532,7 +647,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         //给老师签退
         teacherAttendanceService.update(Wrappers.<TeacherAttendance>lambdaUpdate()
                 .eq(TeacherAttendance::getTeacherId, room.getSpeakerId())
-                .eq(TeacherAttendance::getCourseScheduleId, room.getCourseId())
+                .eq(TeacherAttendance::getCourseScheduleId, schedule.getId())
                 .set(TeacherAttendance::getSignOutTime, now));
         //删除老师与房间关联关系
         deleteUserRoomCache.accept(speakerIdStr);
@@ -544,7 +659,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             //观看者签退
             studentAttendanceService.update(Wrappers.<StudentAttendance>lambdaUpdate()
                     .eq(StudentAttendance::getStudentId, id)
-                    .eq(StudentAttendance::getCourseScheduleId, room.getCourseId())
+                    .eq(StudentAttendance::getCourseScheduleId,schedule.getId())
                     .set(StudentAttendance::getSignOutTime, now));
         });
         //删除房间在线人员缓存
@@ -603,7 +718,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (!userRoom.isExists()) {
                 return;
             }
-            String roomUid = userRoom.get();
+            String roomUid = Optional.ofNullable(user.getRoomUid()).orElse(userRoom.get());
 
             Date now = new Date();
             //获取当前用户状态变更的时间
@@ -613,6 +728,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
                 //缓存的时间比当前传入时间大则放弃这条数据
                 long cacheTime = userStateTimeCache.get();
                 if (cacheTime > userStateTime) {
+                    log.debug("opsRoom>>>> cacheTime > userStateTime, cacheTime: {}, userStateTime: {}", cacheTime, userStateTime);
                     return;
                 }
             }
@@ -623,6 +739,19 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (!roomInfoCache.isExists()) {
                 return;
             }
+
+            // 查询房间信息
+            LiveRoom roomVo = getByRoomUid(roomUid);
+            if (Objects.isNull(roomVo)) {
+                log.info("opsRoom>>>> roomVo is null, userState: {}", JSON.toJSONString(userState));
+                return;
+            }
+
+            // 腾讯云直播不需要处理融云im的消息
+            if (roomVo.getServiceProvider().equals(TencentCloudLivePlugin.PLUGIN_NAME) && StringUtils.isNotBlank(user.getClientIp())) {
+                return;
+            }
+
             RoomInfoCache roomInfo = roomInfoCache.get();
             // 查询userId是不是主讲人 ,如果是主讲人则返回
             if (roomInfo.getSpeakerId().toString().equals(userIdStr)) {
@@ -654,13 +783,14 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (!onlineUserInfo.isExists()) {
                 return;
             }
+            CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
             liveBroadcastRoomMemberService.saveRecord(roomUid,userId, InOrOutEnum.OUT, YesOrNoEnum.NO);
             //从在线人员列表删除该人员
             onlineUserInfo.fastRemove(userId);
             //学员退出 写学生考勤表
             studentAttendanceService.update(Wrappers.<StudentAttendance>lambdaUpdate()
                     .eq(StudentAttendance::getStudentId, userId)
-                    .eq(StudentAttendance::getCourseScheduleId, roomInfo.getCourseId())
+                    .eq(StudentAttendance::getCourseScheduleId, schedule.getId())
                     .set(StudentAttendance::getSignOutTime, now));
             //向直播间发送当前在线人数消息
             this.sendOnlineUserCount(roomUid, userId, onlineUserInfo.size());
@@ -682,16 +812,26 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
 
     }
 
+    private LiveRoom getByRoomUid(String roomUid) {
+        return this.lambdaQuery()
+                .eq(LiveRoom::getRoomUid, roomUid)
+                .last("limit 1")
+                .one()  ;
+    }
+
     /**
      * 主讲人
      */
     private void opsSpeaker(RBucket<RoomInfoCache> roomInfoCache, ImUserStateSync user, Date now, String userIdStr) {
         RoomInfoCache roomInfo = roomInfoCache.get();
         String roomUid = roomInfo.getRoomUid();
+
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         //进退房间写老师考勤表
         TeacherAttendance teacherAttendance = teacherAttendanceService.getOne(Wrappers.<TeacherAttendance>lambdaQuery()
                 .eq(TeacherAttendance::getTeacherId, userIdStr)
-                .eq(TeacherAttendance::getCourseScheduleId, roomInfo.getCourseId()));
+                .eq(TeacherAttendance::getCourseScheduleId, schedule.getId()));
 
         //最后一次进入房间的clientIp
         RBucket<String> lastClientIp = redissonClient.getBucket(LIVE_USER_LAST_CLIENT_IP.replace(ROOM_UID, roomUid).replace(USER_ID, userIdStr));
@@ -703,9 +843,9 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (StringUtils.isNotBlank(user.getClientIp())) {
                 lastClientIp.set(user.getClientIp());
             }
-            //查询老师是否有进入过放假,没有则写老师考勤表的进入时间
+            //查询老师是否有进入过房间,没有则写老师考勤表的进入时间
             if (Objects.isNull(teacherAttendance)) {
-                this.setTeacherAttendance(Long.parseLong(userIdStr), roomInfo.getCourseGroupId(), roomInfo.getCourseId());
+                this.setTeacherAttendance(Long.parseLong(userIdStr), schedule.getCourseGroupId(), schedule.getId());
             }
             roomInfoCache.set(roomInfo);
             log.info("opsRoom>>>> join speakerCache {}", JSONObject.toJSONString(roomInfo));
@@ -731,8 +871,8 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             teacherAttendance = new TeacherAttendance();
             teacherAttendance.setTeacherId(Long.parseLong(userIdStr));
             teacherAttendance.setCourseGroupType(CourseScheduleEnum.LIVE.getCode());
-            teacherAttendance.setCourseGroupId(roomInfo.getCourseGroupId());
-            teacherAttendance.setCourseScheduleId(roomInfo.getCourseId());
+            teacherAttendance.setCourseGroupId(schedule.getCourseGroupId());
+            teacherAttendance.setCourseScheduleId(schedule.getId());
             teacherAttendance.setSignInTime(now);
             teacherAttendance.setSignOutTime(now);
             teacherAttendance.setCreateTime(now);
@@ -745,6 +885,29 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         }
         log.info("opsRoom>>>> exit speakerCache {}", JSONObject.toJSONString(roomInfo));
         roomInfoCache.set(roomInfo);
+
+
+        // 设置直播群组自定义数据
+        setGroupDefinedData(getByRoomUid(roomUid),EGroupDefinedDataType.ANCHOR_STATUS,"OFFLINE");
+
+    }
+
+    private void setGroupDefinedData(LiveRoom roomVo, EGroupDefinedDataType type, String value) {
+        List<TencentRequest.ChatRoomGroupDefinedData> appDefinedData = new ArrayList<>();
+        appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                .key(type.getCode())
+                .value(value)
+                .build());
+        try {
+            livePluginContext.getPluginService(roomVo.getServiceProvider())
+                    .chatRoomGroupDefinedData(TencentRequest.ChatRoomGroup.builder()
+                            .groupId(roomVo.getRoomUid())
+                            .appDefinedData(appDefinedData)
+                            .build());
+            log.info("设置直播群配置成功 roomUid:{}, data {}",roomVo.getRoomUid(), JSONObject.toJSONString(appDefinedData));
+        } catch (Exception e) {
+            log.error("设置直播群配置失败", e);
+        }
     }
 
     /**
@@ -780,12 +943,14 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
      */
     public RoomInfoCache speakerJoinRoom(String roomUid) {
         RoomInfoCache roomInfo = this.speakerCheckRoomInfo(roomUid);
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         Date now = new Date();
         roomInfo.setSpeakerState(0);
         roomInfo.setJoinRoomTime(now);
         liveBroadcastRoomMemberService.saveRecord(roomUid, roomInfo.getSpeakerId(), InOrOutEnum.IN, YesOrNoEnum.NO);
         //查询老师是否有进入过,没有则写老师考勤表的进入时间
-        this.setTeacherAttendance(roomInfo.getSpeakerId(), roomInfo.getCourseGroupId(), roomInfo.getCourseId());
+        this.setTeacherAttendance(roomInfo.getSpeakerId(), schedule.getCourseGroupId(), schedule.getId());
         //记录当前用户对应的房间uid
         redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, roomInfo.getSpeakerId().toString())).set(roomUid, 2L, TimeUnit.DAYS);
         return roomInfo;
@@ -820,8 +985,11 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             userInfo.setUserName(sysUser.getRealName());
             userInfo.setFirstJoinTime(now);
         }
+
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         //查询学生是否有进入过,没有则写学生考勤表的进入时间
-        this.setStudentAttendance(userId, roomInfo.getCourseGroupId(), roomInfo.getCourseId());
+        this.setStudentAttendance(userId, schedule.getCourseGroupId(), schedule.getId());
         userInfo.setDynamicJoinTime(now);
         //用户json信息
         String userJsonStr = JSONObject.toJSONString(userInfo);
@@ -848,13 +1016,15 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             throw new BizException("直播还未开始或已结束!");
         }
         RoomInfoCache roomInfo = roomInfoCache.get();
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         //校验观看者是否买过课,如果是非临时直播间,则校验是否已经买过课
         if (!roomInfo.getRoomType().equals(RoomTypeEnum.TEMP.getCode())) {
             // 查询该学员成功购买课程
             CourseScheduleStudentPayment studentPayment = courseScheduleStudentPaymentService.getOne(Wrappers.<CourseScheduleStudentPayment>lambdaQuery()
                     .eq(CourseScheduleStudentPayment::getUserId, sysUser.getId())
-                    .eq(CourseScheduleStudentPayment::getCourseId, roomInfo.getCourseId())
-                    .eq(CourseScheduleStudentPayment::getCourseGroupId, roomInfo.getCourseGroupId())
+                    .eq(CourseScheduleStudentPayment::getCourseId, schedule.getId())
+                    .eq(CourseScheduleStudentPayment::getCourseGroupId, schedule.getCourseGroupId())
                     .eq(CourseScheduleStudentPayment::getCourseType, CourseScheduleEnum.LIVE.getCode()));
             if (Objects.nonNull(studentPayment)) {
                 UserOrderVo detail = userOrderService.detail(studentPayment.getOrderNo(), studentPayment.getUserId());
@@ -967,16 +1137,41 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
     public void publishRoomMessage(ImRoomMessage message) {
         String msgStr = JSONObject.toJSONString(message);
         log.info("publishRoomMessage message : {}", msgStr);
-        IMApiResultInfo resultInfo;
+        // 消息发送用户
+        LiveRoomMessage.MessageUser messageUser = null;
+        SysUser sysUser = sysUserFeignService.queryUserById(Long.parseLong(message.getFromUserId()));
+        if (Objects.nonNull(sysUser)) {
+            // 发送用户信息
+            messageUser = LiveRoomMessage.MessageUser
+                    .builder()
+                    .sendUserId(sysUser.getId().toString())
+                    .sendUserName(sysUser.getUsername())
+                    .avatarUrl(sysUser.getAvatar())
+                    .build();
+        }
+
+        LiveRoomMessage.MessageContent messageContent = LiveRoomMessage.MessageContent
+                .builder()
+                .targetId(sysUser.getId().toString())
+                .sendUserInfo(messageUser)
+                .build();
+
+        LiveRoomMessage build = LiveRoomMessage.builder()
+                .isIncludeSender(1)
+                .objectName(message.getObjectName())
+                .fromUserId(message.getFromUserId())
+                .toChatRoomId(message.getToChatroomId())
+                .content(messageContent)
+                .build();
         try {
-            resultInfo = imHelper.publishRoomMessage(message.getFromUserId(), message.getToChatroomId(), message);
+
+            LivePluginService pluginService = livePluginContext.getPluginService(message.getServiceProvider());
+            pluginService.sendChatRoomMessage(build);
+            log.info("sendLiveRoomLoginOutMessage>>>> looker {} : roomId={}, userId={}",message.getObjectName(), message.getToChatroomId(), message.getFromUserId());
         } catch (Exception e) {
-            throw new BizException("消息发送失败" + e.getMessage());
-        }
-        if (!resultInfo.isSuccess()) {
-            throw new BizException("消息发送失败!" + resultInfo.getErrorMessage());
+            log.error("sendLiveRoomMessage>>>>  looker error {} {}", message.getObjectName(),e.getMessage());
+            log.error("sendLiveRoomMessage>>>>  looker error sendMessage {} : {} : : roomId={}, userId={}",message.getObjectName(), message, message.getToChatroomId(), message.getFromUserId());
         }
-        log.info("publishRoomMessage success: {}", msgStr);
     }
 
     private SysUser getSysUser(Long userId) {
@@ -1129,10 +1324,10 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         return baseMapper.getLiveInRoomBySpeakerId(speakerId);
     }
 
-	@Override
-	public List<TeacherLivingInfoVo> queryTeacherLivingList() {
-		return baseMapper.queryTeacherLivingList();
-	}
+    @Override
+    public List<TeacherLivingInfoVo> queryTeacherLivingList() {
+        return baseMapper.queryTeacherLivingList();
+    }
     @Override
     public void whetherMic(String roomUid, Integer whetherMic) {
         //校验房间是否存在
@@ -1181,5 +1376,269 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
 
         return roomWrapper;
     }
+
+
+    /**
+     * 生成直播录制信息
+     *
+     * @param event TencentData.CallbackSteamRecordEvent
+     */
+    @Override
+    public void createLiveRoomVideoRecord(TencentData.CallbackSteamRecordEvent event) {
+
+        // 直播间ROOM_UID
+        String roomId = event.getStreamId().split("_")[0];
+
+        // 录制开始时间
+        long startTime =  (event.getEndTime() - event.getDuration()) * 1000L;
+        //保存切片
+        LiveRoomVideo imLiveRoomVideo = initImLiveRoomVideo(roomId, event.getTaskId(), DateTime.now().toDate());
+        imLiveRoomVideo.setStartTime(new DateTime(startTime).toDate());
+        imLiveRoomVideo.setEndTime(new DateTime(event.getEndTime() * 1000L).toDate());
+        imLiveRoomVideo.setUrl(event.getVideoUrl());
+        imLiveRoomVideo.setType(2);
+
+        // 回放记录已存在,直接忽略
+        LiveRoomVideo video = liveRoomVideoService.lambdaQuery()
+                .eq(LiveRoomVideo::getRoomUid, imLiveRoomVideo.getRoomUid())
+                .eq(LiveRoomVideo::getRecordId, imLiveRoomVideo.getRecordId())
+                .last("LIMIT 1")
+                .one();
+        if (Objects.isNull(video)) {
+
+            LiveRoom room = this.getByRoomUid(imLiveRoomVideo.getRoomUid());
+
+            if (org.apache.commons.lang.StringUtils.isBlank(room.getVideoRecord()) || Arrays.stream(room.getVideoRecord().split(","))
+                    .anyMatch(x -> x.equals(imLiveRoomVideo.getRecordId()))) {
+                liveRoomVideoService.save(imLiveRoomVideo);
+            }
+
+        }
+
+    }
+    private LiveRoomVideo initImLiveRoomVideo(String roomId, String recordId, Date now) {
+        LiveRoomVideo video = new LiveRoomVideo();
+        video.setRoomUid(roomId);
+        video.setRecordId(recordId);
+        video.setStartTime(now);
+        video.setType(0);
+        video.setCreatedTime(now);
+        return video;
+    }
+
+
+    /**
+     * 直播群成员在线状态回调处理
+     *
+     */
+    @Override
+    @Transactional
+    public void callbackOnMemberStateChange(TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange) {
+
+        if (callbackOnMemberStateChange == null) {
+            return;
+        }
+
+        int onlineStatus = callbackOnMemberStateChange.getEventType().equals("Online")?1:0;
+
+        // 用户id
+        if (CollectionUtils.isEmpty(callbackOnMemberStateChange.getMemberList())) {
+            return;
+        }
+        List<Long> userIds = callbackOnMemberStateChange.getMemberList()
+                .stream()
+                .map(o -> Long.parseLong(o.getMemberAccount()))
+                .collect(Collectors.toList());
+
+        List<ImUserStateSync> imUserStates = new ArrayList<>();
+        for (Long userId : userIds) {
+
+            ImUserStateSync imUserState = new ImUserStateSync();
+            imUserState.setUserid(userId.toString());
+            imUserState.setStatus(onlineStatus == 1?"0":"1");
+            imUserState.setOs(callbackOnMemberStateChange.getOptPlatform());
+            imUserState.setTime(new Date().getTime());
+            imUserState.setRoomUid(callbackOnMemberStateChange.getGroupId());
+            imUserStates.add(imUserState);
+        }
+        opsRoom(imUserStates);
+    }
+
+    @Override
+    @Transactional
+    public void callbackAfterMemberExit(TencentData.CallbackAfterMemberExit callbackAfterMemberExit) {
+        if (callbackAfterMemberExit == null) {
+            return;
+        }
+        // 用户id
+        if (CollectionUtils.isEmpty(callbackAfterMemberExit.getExitMemberList())) {
+            return;
+        }
+        List<Long> userIds = callbackAfterMemberExit.getExitMemberList()
+                .stream()
+                .map(o -> Long.parseLong(o.getMemberAccount()))
+                .collect(Collectors.toList());
+
+
+        List<ImUserStateSync> imUserStates = new ArrayList<>();
+        for (Long userId : userIds) {
+            ImUserStateSync imUserState = new ImUserStateSync();
+            imUserState.setUserid(userId.toString());
+            imUserState.setStatus("3");
+            imUserState.setOs(callbackAfterMemberExit.getOptPlatform());
+            imUserState.setTime(callbackAfterMemberExit.getEventTime().atZone(ZoneId.systemDefault()).toEpochSecond());
+            imUserState.setRoomUid(callbackAfterMemberExit.getGroupId());
+            imUserStates.add(imUserState);
+        }
+        opsRoom(imUserStates);
+    }
+
+    @Override
+    @Transactional
+    public void callbackAfterNewMemberJoin(TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin) {
+        if (callbackAfterNewMemberJoin == null) {
+            return;
+        }
+
+        // 用户id
+        if (CollectionUtils.isEmpty(callbackAfterNewMemberJoin.getNewMemberList())) {
+            return;
+        }
+        List<Long> userIds = callbackAfterNewMemberJoin.getNewMemberList()
+                .stream()
+                .map(o -> Long.parseLong(o.getMemberAccount()))
+                .collect(Collectors.toList());
+
+        List<ImUserStateSync> imUserStates = new ArrayList<>();
+        for (Long userId : userIds) {
+            ImUserStateSync imUserState = new ImUserStateSync();
+            imUserState.setUserid(userId.toString());
+            imUserState.setStatus("0");
+            imUserState.setOs(callbackAfterNewMemberJoin.getOptPlatform());
+            imUserState.setTime(callbackAfterNewMemberJoin.getEventTime().atZone(ZoneId.systemDefault()).toEpochSecond());
+            imUserState.setRoomUid(callbackAfterNewMemberJoin.getGroupId());
+            imUserStates.add(imUserState);
+        }
+        opsRoom(imUserStates);
+    }
+
+    // 定时任务凌晨2点,关闭腾讯直播间, 融云直播间自动关闭,不做处理
+    @Override
+    public void destroyLiveRoom() {
+        List<LiveRoom> liveRooms = this.lambdaQuery()
+                .eq(LiveRoom::getLiveState, 2)
+                .ne(LiveRoom::getRoomState,2)
+                .list();
+        for (LiveRoom liveRoom : liveRooms) {
+            liveRoomService.tryDestroyLiveRoom(liveRoom);
+        }
+    }
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean tryDestroyLiveRoom(LiveRoom liveRoom) {
+        LivePluginService pluginService = livePluginContext.getPluginService(
+                liveRoom.getServiceProvider());
+        try {
+            if (pluginService == null) {
+                log.error("查询直播间流失败,未找到对应的插件");
+                return false;
+            }
+            if (pluginService.pluginName().equals(TencentCloudLivePlugin.PLUGIN_NAME)) {
+                TencentWrapper.LiveStreamState liveStreamState = pluginService.liveStreamState(
+                        getStreamId(liveRoom.getRoomUid(), liveRoom.getSpeakerId()));
+                if (liveStreamState == null) {
+                    log.error("查询直播间流失败,返回结果为空");
+                    return false;
+                }
+                log.info("查询直播间流状态:{},roomUid:{}", JSON.toJSONString(liveStreamState), liveRoom.getRoomUid());
+                if (!"active".equals(liveStreamState.getStreamState())) {
+                    return roomDestroy(liveRoom);
+                }
+            } else if (pluginService.pluginName().equals(RongCloudLivePlugin.PLUGIN_NAME)) {
+                // 融云走原有逻辑 融云自动销毁
+                // 销毁状态改为2
+                LiveRoom liveRoomUpdate = new LiveRoom();
+                liveRoomUpdate.setId(liveRoom.getId());
+                liveRoomUpdate.setRoomState(2);
+                liveRoomService.updateById(liveRoomUpdate);
+            }
+        } catch (Exception e) {
+
+            log.error("查询直播间流失败", e);
+            return false;
+        }
+        return true;
+    }
+
+    private String getStreamId(String roomUid, Long speakerId) {
+        return roomUid + "_" + speakerId;
+    }
+
+
+    public boolean roomDestroy(LiveRoom room) {
+        //10秒内同一个房间不能重复销毁-防重复销毁
+        RBucket<Object> bucket = redissonClient.getBucket("IM:ROOMDESTROY:" + room.getRoomUid());
+        if (!bucket.trySet(1, 10, TimeUnit.SECONDS)) {
+            return false;
+        }
+        log.error("roomDestroy>>>> room : {}", JSONObject.toJSONString(room));
+        String roomUid = room.getRoomUid();
+
+        //将房间状态改为已销毁
+        LiveRoom liveRoomUpdate = new LiveRoom();
+        liveRoomUpdate.setId(room.getId());
+
+        Date date = new Date();
+        liveRoomUpdate.setRoomState(2);
+        liveRoomUpdate.setLiveState(2);
+        Long userId = -1L;
+        try {
+            userId = getSysUser().getId();
+        } catch (Exception ignored) {
+        }
+        liveRoomUpdate.setUpdatedBy(userId);
+        liveRoomUpdate.setUpdatedTime(date);
+        liveRoomUpdate.setLiveEndTime(date);
+
+        this.updateById(liveRoomUpdate);
+
+
+        //向聊天室发自定义消息踢出所有人
+        try {
+            LivePluginService pluginService = livePluginContext.getPluginService(room.getServiceProvider());
+
+            //销毁直播间
+            pluginService.chatRoomDestroy(roomUid);
+
+            TencentWrapper.LiveStreamState liveStreamState = pluginService.liveStreamState(
+                    getStreamId(room.getRoomUid(), room.getSpeakerId()));
+            if (liveStreamState == null) {
+                log.error("查询直播间流失败,返回结果为空");
+            } else if ("active".equals(liveStreamState.getStreamState())) {
+                pluginService.liveStreamStop(getStreamId(room.getRoomUid(), room.getSpeakerId()));
+            }
+
+            // 录制任务Id
+            if (room.getServiceProvider().equals(TencentCloudLivePlugin.PLUGIN_NAME)) {
+
+                List<String> collect = liveRoomService.lambdaQuery()
+                        .eq(LiveRoom::getRoomUid, roomUid).list().stream()
+                        .map(LiveRoom::getVideoRecord)
+                        .filter(StringUtils::isNotEmpty)
+                        .distinct().collect(Collectors.toList());
+
+                for (String taskId : collect) {
+                    // 删除录制任务
+                    pluginService.rtcRoomRecordStop(taskId);
+                }
+            }
+        } catch (Exception e) {
+            log.error("roomDestroy>>>> errorMsg{}", e.getMessage(), e.getCause());
+            return false;
+        }
+        return true;
+    }
 }
 

+ 0 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomVideoServiceImpl.java

@@ -113,8 +113,6 @@ public class LiveRoomVideoServiceImpl extends ServiceImpl<LiveRoomVideoDao, Live
     private void insertVideo(String fileUrl, String recordId, Integer notifyType, LiveRoom room) {
         Date now = new Date();
         LiveRoomVideo roomVideo = new LiveRoomVideo();
-        roomVideo.setCourseGroupId(room.getCourseGroupId());
-        roomVideo.setCourseId(room.getCourseId());
         roomVideo.setRoomUid(room.getRoomUid());
         roomVideo.setRecordId(recordId);
         roomVideo.setUrl(fileUrl);

+ 1 - 1
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/task/TaskController.java

@@ -55,7 +55,7 @@ public class TaskController extends BaseController {
         return HttpResponseResult.succeed();
     }
 
-    @ApiOperation("定时任务-销毁房间-直播间")
+    @ApiOperation("定时任务-关闭-直播间")
     @GetMapping("/destroyExpiredLiveRoom")
     public void destroyExpiredLiveRoom() {
         liveRoomService.destroyExpiredLiveRoom();