瀏覽代碼

Merge branch 'feature/0803-im' of http://git.dayaedu.com/yonge/cooleshow into feature/0803-im

zouxuan 2 年之前
父節點
當前提交
56ac665177
共有 41 個文件被更改,包括 2739 次插入178 次删除
  1. 32 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/live/ELiveViewMode.java
  2. 40 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/live/EUseScene.java
  3. 3 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/ImGroupController.java
  4. 13 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImController.java
  5. 4 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/constant/LiveRoomConstant.java
  6. 4 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupDao.java
  7. 0 27
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImNetworkRoomMemberStatusMapper.java
  8. 2 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/LiveBroadcastRoomMemberDao.java
  9. 4 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImHistoryMessage.java
  10. 77 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImLiveBroadcastRoomMember.java
  11. 53 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveBroadcastRoomData.java
  12. 37 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java
  13. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java
  14. 55 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/ImLiveBroadcastRoomMemberMapper.java
  15. 27 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/LiveBroadcastRoomDataMapper.java
  16. 10 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java
  17. 51 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImLiveBroadcastRoomMemberService.java
  18. 43 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveBroadcastRoomDataService.java
  19. 60 11
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java
  20. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberAuditServiceImpl.java
  21. 0 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberServiceImpl.java
  22. 281 9
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupServiceImpl.java
  23. 164 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImLiveBroadcastRoomMemberServiceImpl.java
  24. 66 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveBroadcastRoomDataServiceImpl.java
  25. 789 84
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java
  26. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherAuthEntryRecordServiceImpl.java
  27. 5 13
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherServiceImpl.java
  28. 183 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/ImHistoryMessageWrapper.java
  29. 139 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/liveroom/ImLiveBroadcastRoomMemberWrapper.java
  30. 63 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/liveroom/LiveBroadcastRoomDataWrapper.java
  31. 161 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/liveroom/LiveRoomWrapper.java
  32. 9 2
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMapper.xml
  33. 1 1
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMemberMapper.xml
  34. 101 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomMemberMapper.xml
  35. 24 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/LiveBroadcastRoomDataMapper.xml
  36. 5 1
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/CourseHomeworkController.java
  37. 71 0
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/ImLiveBroadcastRoomController.java
  38. 4 8
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/StudentLiveRoomController.java
  39. 6 1
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/CourseHomeworkController.java
  40. 140 0
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherImLiveBroadcastRoomController.java
  41. 9 15
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherLiveRoomController.java

+ 32 - 0
cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/live/ELiveViewMode.java

@@ -0,0 +1,32 @@
+package com.yonge.cooleshow.common.enums.live;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+import lombok.Getter;
+
+/**
+ * 直播观看模式
+ */
+@Getter
+public enum ELiveViewMode implements BaseEnum<String, ELiveViewMode> {
+
+    LOGIN("登陆"),
+    VISITOR("游客"),
+    ;
+
+    private final String describe;
+
+    @EnumValue
+    private final String code;
+
+    ELiveViewMode(String describe) {
+        this.describe = describe;
+
+        this.code = this.name();
+    }
+
+    @Override
+    public String getCode() {
+        return code;
+    }
+}

+ 40 - 0
cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/live/EUseScene.java

@@ -0,0 +1,40 @@
+package com.yonge.cooleshow.common.enums.live;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+public enum EUseScene implements BaseEnum<String, EUseScene> {
+
+    /**
+     * 正常
+     */
+    NORMAL( "正常"),
+
+    /**
+     * 音乐
+     */
+    MUSIC( "音乐"),
+
+    ;
+
+    @EnumValue
+    private String code;
+
+    private String msg;
+
+    EUseScene(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+
+    /**
+     * 获取枚举类的code值
+     *
+     * @return
+     */
+    @Override
+    public String getCode() {
+        return this.code;
+    }
+}

+ 3 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/ImGroupController.java

@@ -149,7 +149,10 @@ public class ImGroupController extends BaseController {
             //获取融云消息
             //List<ImGroup> list = imGroupService.lambdaQuery().last("limit "+(page-1)*size+","+size).list();
             info = imGroupService.getRongYunInfo(page,size);
+            //IM导入
             imGroupService.importInfo(info);
+            //为已导入数据更改标识
+            imGroupService.updateStatus(info);
         }
     }
 

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

@@ -200,10 +200,23 @@ public class ImController extends BaseController {
         // 断流事件通知
         if (event.getEventType() == 0) {
             imUserState.setStatus("3");
+
+            // 更新推流时长
+            if (StringUtils.isNotBlank(event.getPushDuration()) && event.getPushDuration().matches("\\d+")) {
+                // 更新直播推流时长
+                liveRoomService.updateLiveRoomPushStreamTime(event);
+            }
+
+            // 自动关闭录制
+            liveRoomService.closeLive(getRoomUid(event.getStreamId()), getSpeakerId(event.getStreamId()),event.getSequence());
+            // 同步点赞数
+            liveRoomService.syncLikeCount(getRoomUid(event.getStreamId()));
         }
 
         // 推流事件通知
         if (event.getEventType() == 1) {
+            // 自动开启录制
+            liveRoomService.startLive(getRoomUid(event.getStreamId()), getSpeakerId(event.getStreamId()),event.getSequence());
 
             imUserState.setStatus("0");
         }

+ 4 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/constant/LiveRoomConstant.java

@@ -37,5 +37,9 @@ public interface LiveRoomConstant {
 
     //正在临时直播间的房间列表
     String TEACHER_TEMP_LIVE_ROOM = String.join(":", COOLESHOW, "TEACHER_TEMP_LIVE_ROOM");
+    //主讲人信息
+    String LIVE_SPEAKER_INFO = String.join(":", "IM:LIVE_SPEAKER_INFO", ROOM_UID, USER_ID);
+    //计算人员观看时长锁
+    public static final String LIVE_LOOK_LOCK = String.join(":", "IM:LIVE_LOOK_LOCK", ROOM_UID);
 
 }

+ 4 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupDao.java

@@ -33,6 +33,10 @@ public interface ImGroupDao extends BaseMapper<ImGroup> {
     //获取融云数据
     List<ImHistoryMessage> selectAll(@Param("result") int result, @Param("size") int size);
 
+    //查询总数据量
     int queryCount();
+
+    //更新状态
+    void updateStatus(@Param("info") List<ImHistoryMessage> info);
 }
 

+ 0 - 27
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImNetworkRoomMemberStatusMapper.java

@@ -1,27 +0,0 @@
-package com.yonge.cooleshow.biz.dal.dao;
-
-import java.util.List;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.apache.ibatis.annotations.Param;
-import org.springframework.stereotype.Repository;
-import com.yonge.cooleshow.biz.dal.entity.ImNetworkRoomMemberStatus;
-import com.yonge.cooleshow.biz.dal.wrapper.ImNetworkRoomMemberStatusWrapper;
-
-/**
- * 网络教室成员状态表
- * 2023-08-14 15:47:02
- */
-@Repository
-public interface ImNetworkRoomMemberStatusMapper extends BaseMapper<ImNetworkRoomMemberStatus> {
-
-	/**
-	 * 分页查询
-	 * @param page IPage<ImNetworkRoomMemberStatusWrapper.ImNetworkRoomMemberStatus>
-	 * @param param ImNetworkRoomMemberStatusWrapper.ImNetworkRoomMemberStatusQuery
-	 * @return List<ImNetworkRoomMemberStatusWrapper.ImNetworkRoomMemberStatus>
-	 */
-	List<ImNetworkRoomMemberStatus> selectPage(@Param("page") IPage<ImNetworkRoomMemberStatus> page, @Param("param") ImNetworkRoomMemberStatusWrapper.ImNetworkRoomMemberStatusQuery param);
-	
-}

+ 2 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/LiveBroadcastRoomMemberDao.java

@@ -4,6 +4,7 @@ import java.util.List;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper;
 import org.apache.ibatis.annotations.Param;
 import com.yonge.cooleshow.biz.dal.entity.LiveBroadcastRoomMember;
 import com.yonge.cooleshow.biz.dal.vo.LiveBroadcastRoomMemberVo;
@@ -26,5 +27,5 @@ public interface LiveBroadcastRoomMemberDao extends BaseMapper<LiveBroadcastRoom
      * @return: com.yonge.cooleshow.biz.dal.vo.LiveBroadcastRoomMemberVo
 	 */
 	List<LiveBroadcastRoomMemberVo> selectPage(@Param("page") IPage page, @Param("param") LiveBroadcastRoomMemberSearch liveBroadcastRoomMember);
-	
+
 }

+ 4 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImHistoryMessage.java

@@ -88,4 +88,8 @@ public class ImHistoryMessage implements Serializable {
     @TableField(value = "appId_")
     private String appId;
 
+    @ApiModelProperty("导入状态,1 (已导入)  0(未导入)")
+    @TableField(value = "status_")
+    private Integer status;
+
 }

+ 77 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImLiveBroadcastRoomMember.java

@@ -0,0 +1,77 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import lombok.Data;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 直播间人员关系表
+ * 2023-08-15 15:50:17
+ */
+@Data
+@ApiModel(" ImLiveBroadcastRoomMember-直播间人员关系表")
+@TableName("im_live_broadcast_room_member")
+public class ImLiveBroadcastRoomMember implements Serializable {
+
+    @ApiModelProperty("主键") 
+	    @TableId(value = "id_", type = IdType.AUTO)
+        private Long id;
+
+    @ApiModelProperty("房间编号") 
+	@TableField(value = "room_uid_")
+    private String roomUid;
+
+    @ApiModelProperty("进入房间的人员id/学生id") 
+	@TableField(value = "user_id_")
+    private Long userId;
+
+    @ApiModelProperty("进入房间时间") 
+	@TableField(value = "join_time_")
+    private Date joinTime;
+
+    @ApiModelProperty("累计观看时间/以分钟为单位") 
+	@TableField(value = "total_time_")
+    private Integer totalTime;
+
+    @ApiModelProperty("在线状态;0离线;1在线") 
+	@TableField(value = "online_status_")
+    private Integer onlineStatus;
+
+    @ApiModelProperty("禁言状态:0取消;1禁言") 
+	@TableField(value = "ban_status_")
+    private Integer banStatus;
+
+    @ApiModelProperty("直播状态: 0离开;1观看") 
+	@TableField(value = "live_room_status_")
+    private Integer liveRoomStatus;
+
+    @ApiModelProperty("连麦状态 0:未申请1:申请连麦中2:连麦中") 
+	@TableField(value = "whether_mic_status_")
+    private Integer whetherMicStatus;
+
+    @ApiModelProperty("是否能上麦:0否;1是") 
+	@TableField(value = "microphone_flag_")
+    private Boolean microphoneFlag;
+
+    @ApiModelProperty("游客凭据") 
+	@TableField(value = "fingerprint_")
+    private String fingerprint;
+
+    @ApiModelProperty("游客名称") 
+	@TableField(value = "visitor_name_")
+    private String visitorName;
+
+    @ApiModelProperty("创建时间") 
+	@TableField(value = "create_time_")
+    private Date createTime;
+
+}

+ 53 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveBroadcastRoomData.java

@@ -0,0 +1,53 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import lombok.Data;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 直播房间数据表
+ * 2023-08-15 15:50:17
+ */
+@Data
+@ApiModel(" LiveBroadcastRoomData-直播房间数据表")
+@TableName("live_broadcast_room_data")
+public class LiveBroadcastRoomData implements Serializable {
+
+    @ApiModelProperty("主键") 
+	    @TableId(value = "id_", type = IdType.AUTO)
+        private Integer id;
+
+    @ApiModelProperty("房间uid") 
+	@TableField(value = "room_uid_")
+    private String roomUid;
+
+    @ApiModelProperty("直播时长") 
+	@TableField(value = "live_time_")
+    private Integer liveTime;
+
+    @ApiModelProperty("累计点赞数") 
+	@TableField(value = "like_num_")
+    private Integer likeNum;
+
+    @ApiModelProperty("即时观看人员数") 
+	@TableField(value = "look_user_num_")
+    private Integer lookUserNum;
+
+    @ApiModelProperty("累计观看总人员数") 
+	@TableField(value = "total_user_num_")
+    private Integer totalUserNum;
+
+    @ApiModelProperty("更新时间") 
+	@TableField(value = "updated_time_")
+    private Date updatedTime;
+
+}

+ 37 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java

@@ -4,6 +4,8 @@ package com.yonge.cooleshow.biz.dal.entity;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.common.enums.live.ELiveViewMode;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -36,6 +38,12 @@ public class LiveRoom implements Serializable {
     @ApiModelProperty(value = "主讲人id/老师id")
     private Long speakerId;
 
+
+
+    @TableField("client_type_")
+    @ApiModelProperty("用户类型 TEACHER 老师 ")
+    private ClientEnum clientType;
+
     @TableField("room_uid_")
     @ApiModelProperty(value = "房间编号")
     private String roomUid;
@@ -76,6 +84,35 @@ public class LiveRoom implements Serializable {
     @ApiModelProperty(value = "房间类型 live直播课  temp临时直播间")
     private String type;
 
+    @TableField("pre_template_")
+    @ApiModelProperty(value = "预热模版")
+    private String preTemplate;
+
+
+    @TableField("live_total_time_")
+    @ApiModelProperty(value = "直播时长")
+    private Integer liveTotalTime;
+
+    @TableField("os_")
+    @ApiModelProperty(value = "播出端-  pc网页端 移动端mobile")
+    private String os;
+
+    @TableField("speaker_status_")
+    @ApiModelProperty(value = "主播状态: 0离开;1在播")
+    private Integer speakerStatus;
+
+    @TableField("push_status_")
+    @ApiModelProperty(value = "推流状态: 0 暂停; 1在播")
+    private Integer pushStatus;
+
+    @TableField("ban_status_")
+    @ApiModelProperty(value = "禁言状态: 0 取消;1禁言")
+    private Integer banStatus;
+
+    @TableField("view_mode_")
+    @ApiModelProperty("直播观看模式")
+    private ELiveViewMode viewMode;
+
     @TableField("cover_pic_")
     @ApiModelProperty(value = "封面图片")
     private String coverPic;

+ 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;

+ 55 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/ImLiveBroadcastRoomMemberMapper.java

@@ -0,0 +1,55 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+import com.yonge.cooleshow.biz.dal.entity.ImLiveBroadcastRoomMember;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper;
+
+/**
+ * 直播间人员关系表
+ * 2023-08-15 15:50:17
+ */
+@Repository
+public interface ImLiveBroadcastRoomMemberMapper extends BaseMapper<ImLiveBroadcastRoomMember> {
+
+	/**
+	 * 分页查询
+	 * @param page IPage<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember>
+	 * @param param ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMemberQuery
+	 * @return List<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember>
+	 */
+	List<ImLiveBroadcastRoomMember> selectPage(@Param("page") IPage<ImLiveBroadcastRoomMember> page, @Param("param") ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMemberQuery param);
+
+    /**
+     * 查询房间人数
+     *
+     * @param roomUid 房间id
+     */
+    ImLiveBroadcastRoomMemberWrapper.RoomMemberNumDto queryMemberNum(@Param("roomUid") String roomUid);
+
+    /**
+     * 修改用户连麦状态
+     *
+     * @param roomUid          直播房间id
+     * @param userId           用户id
+     * @param whetherMicStatus 连麦状态
+     */
+    void userWhetherMic(@Param("roomUid") String roomUid, @Param("userId") Long userId, @Param(
+            "whetherMicStatus") Integer whetherMicStatus);
+
+    /**
+     * 学生声部
+     * @param studentIds 学生编号
+     * @return List<ImLiveBroadcastRoomMemberVo>
+     */
+    List<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> selectStudentSubject(@Param("studentIds") List<Integer> studentIds);
+
+    IPage<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> queryMemberPage(Page<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> pageInfo, @Param("param") Map<String, Object> param);
+}

+ 27 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/LiveBroadcastRoomDataMapper.java

@@ -0,0 +1,27 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+import com.yonge.cooleshow.biz.dal.entity.LiveBroadcastRoomData;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveBroadcastRoomDataWrapper;
+
+/**
+ * 直播房间数据表
+ * 2023-08-15 15:50:17
+ */
+@Repository
+public interface LiveBroadcastRoomDataMapper extends BaseMapper<LiveBroadcastRoomData> {
+
+	/**
+	 * 分页查询
+	 * @param page IPage<LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData>
+	 * @param param LiveBroadcastRoomDataWrapper.LiveBroadcastRoomDataQuery
+	 * @return List<LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData>
+	 */
+	List<LiveBroadcastRoomData> selectPage(@Param("page") IPage<LiveBroadcastRoomData> page, @Param("param") LiveBroadcastRoomDataWrapper.LiveBroadcastRoomDataQuery param);
+	
+}

+ 10 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java

@@ -171,7 +171,17 @@ public interface ImGroupService extends IService<ImGroup> {
      */
     void importUser();
 
+    /**
+     * 计算未导入数据的总条数
+     * @return
+     */
     int queryCount();
 
+    /**
+     * 更新已修改数据的状态
+     * @param info
+     */
+    void updateStatus(List<ImHistoryMessage> info);
+
 }
 

+ 51 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImLiveBroadcastRoomMemberService.java

@@ -0,0 +1,51 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper;
+import com.yonge.cooleshow.biz.dal.entity.ImLiveBroadcastRoomMember;
+import com.yonge.toolset.base.page.PageInfo;
+
+import java.util.Map;
+
+/**
+ * 直播间人员关系表
+ * 2023-08-15 15:50:17
+ */
+public interface ImLiveBroadcastRoomMemberService extends IService<ImLiveBroadcastRoomMember>  {
+
+	/**
+     * 查询详情
+     * @param id 详情ID
+     * @return ImLiveBroadcastRoomMember
+     */
+	ImLiveBroadcastRoomMember detail(Long id);
+
+    /**
+     * 分页查询
+     * @param page IPage<ImLiveBroadcastRoomMember>
+     * @param query ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMemberQuery
+     * @return IPage<ImLiveBroadcastRoomMember>
+     */
+    IPage<ImLiveBroadcastRoomMember> selectPage(IPage<ImLiveBroadcastRoomMember> page, ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMemberQuery query);
+	
+    /**
+     * 添加
+     * @param imLiveBroadcastRoomMember ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember
+     * @return Boolean
+     */
+     Boolean add(ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember imLiveBroadcastRoomMember);   
+
+    /**
+     * 更新
+     * @param imLiveBroadcastRoomMember ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember
+     * @return Boolean
+     */
+     Boolean update(ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember imLiveBroadcastRoomMember);
+
+
+    /**
+     * 查询房间观看人员详情
+     */
+    PageInfo<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> queryRoomMember(Map<String, Object> param);
+}

+ 43 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveBroadcastRoomDataService.java

@@ -0,0 +1,43 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveBroadcastRoomDataWrapper;
+import com.yonge.cooleshow.biz.dal.entity.LiveBroadcastRoomData;
+
+/**
+ * 直播房间数据表
+ * 2023-08-15 15:50:17
+ */
+public interface LiveBroadcastRoomDataService extends IService<LiveBroadcastRoomData>  {
+
+	/**
+     * 查询详情
+     * @param id 详情ID
+     * @return LiveBroadcastRoomData
+     */
+	LiveBroadcastRoomData detail(Long id);
+
+    /**
+     * 分页查询
+     * @param page IPage<LiveBroadcastRoomData>
+     * @param query LiveBroadcastRoomDataWrapper.LiveBroadcastRoomDataQuery
+     * @return IPage<LiveBroadcastRoomData>
+     */
+    IPage<LiveBroadcastRoomData> selectPage(IPage<LiveBroadcastRoomData> page, LiveBroadcastRoomDataWrapper.LiveBroadcastRoomDataQuery query);
+	
+    /**
+     * 添加
+     * @param liveBroadcastRoomData LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData
+     * @return Boolean
+     */
+     Boolean add(LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData liveBroadcastRoomData);   
+
+    /**
+     * 更新
+     * @param liveBroadcastRoomData LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData
+     * @return Boolean
+     */
+     Boolean update(LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData liveBroadcastRoomData);
+     
+}

+ 60 - 11
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java

@@ -5,6 +5,7 @@ import java.util.List;
 import java.util.Map;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.microsvc.toolkit.middleware.live.message.TencentWrapper;
 import com.yonge.cooleshow.biz.dal.dao.LiveRoomDao;
 import com.yonge.cooleshow.biz.dal.dto.LiveRoomStatus;
 import com.yonge.cooleshow.biz.dal.dto.TencentData;
@@ -13,7 +14,6 @@ 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)表服务接口
@@ -43,14 +43,14 @@ public interface LiveRoomService extends IService<LiveRoom> {
      */
     RoomInfoCache studentCheckRoomInfo(String roomUid);
 
+
     /**
      * 主讲人查询并校验房间信息
      *
      * @param roomUid 房间uid
-     * @param userId  主讲人id
      * @return
      */
-    RoomInfoCache speakerCheckRoomInfo(String roomUid);
+    LiveRoomWrapper.LiveRoomVo speakerCheckRoomInfo(String roomUid);
 
     /**
      * 定时任务创建直播间
@@ -92,13 +92,6 @@ public interface LiveRoomService extends IService<LiveRoom> {
      */
     void syncLike(String roomUid, Integer likeNum);
 
-    /**
-     * 主讲人-进入房间
-     *
-     * @param roomUid 房间uid
-     * @param userId  主讲人id
-     */
-    RoomInfoCache speakerJoinRoom(String roomUid);
 
     /**
      * 观看者-进入房间
@@ -106,7 +99,7 @@ public interface LiveRoomService extends IService<LiveRoom> {
      * @param roomUid 房间uid
      * @param userId  观看者id
      */
-    RoomInfoCache joinRoom(String roomUid, Long userId);
+    void joinRoom(String roomUid, Long userId);
 
     /**
      * 发送消息
@@ -182,5 +175,61 @@ public interface LiveRoomService extends IService<LiveRoom> {
      */
     Boolean updateRoomStatus(LiveRoomStatus status);
 
+    /**
+     * 进入直播间检查数据
+     *
+     * @param roomUid 房间uid
+     * @param userId  用户id
+     * @param osType  默认学生端  0:pc端 1:学生端 2:老师端
+     */
+    LiveRoomWrapper.LiveRoomVo queryRoomAndCheck(String roomUid, Long userId, int osType);
+
+    /**
+     * 直播间推流状态查询
+     *
+     * @param roomUid 房间号
+     */
+    TencentWrapper.LiveStreamState roomLiveStreamStatus(String roomUid);
+
+
+    /**
+     * 查询直播间信息
+     *
+     * @param roomUid 直播间uid
+     */
+    LiveRoomWrapper.LiveRoomVo queryRoomInfo(String roomUid);
+
+    /**
+     * 同步直播间点赞数
+     *
+     * @param roomUid 直播间uid
+     * @return int
+     */
+    int syncLikeCount(String roomUid);
+
+
+    List<LiveRoomWrapper.BaseRoomUserVo> queryRoomLimitOnlineUserInfo(String roomUid);
+
+
+    /**
+     * 修改用户连麦状态
+     *
+     * @param roomUid 直播房间id
+     * @param userId 用户id
+     * @param whetherMicStatus 连麦状态
+     */
+    void userWhetherMic(String roomUid, Long userId, Integer whetherMicStatus);
+
+    void joinRoom(String roomUid, Long userId, Boolean microphoneFlag);
+
+    /**
+     * 更新直播推流时间
+     * @param event TencentData.CallbackStreamStateEvent
+     */
+    void updateLiveRoomPushStreamTime(TencentData.CallbackStreamStateEvent event);
+
+    void closeLive(String roomUid, Integer speakerId, String sequence);
+
+    void startLive(String roomUid, Integer speakerId, String sequence);
 }
 

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberAuditServiceImpl.java

@@ -81,7 +81,7 @@ public class ImGroupMemberAuditServiceImpl extends ServiceImpl<ImGroupMemberAudi
         }
         Date date = new Date();
         //是否自动通过审核
-        imGroupMemberAudit.setAuditStatus(imGroup.getAutoPassFlag()?AuditStatusEnum.OPEN:AuditStatusEnum.AUDITING);
+        imGroupMemberAudit.setAuditStatus(imGroup.getAutoPassFlag() && autoJoin?AuditStatusEnum.OPEN:AuditStatusEnum.AUDITING);
         imGroupMemberAudit.setUpdateTime(date);
         imGroupMemberAudit.setCreateTime(date);
         baseMapper.insert(imGroupMemberAudit);

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

@@ -196,8 +196,6 @@ public class ImGroupMemberServiceImpl extends ServiceImpl<ImGroupMemberDao, ImGr
 
             for (ImGroupMember item : members) {
 
-                item.setImUserId(String.valueOf(item.getUserId()));
-
                 if (ImGroupMemberRoleType.STUDENT == item.getRoleType()) {
                     item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
                 }else if(ImGroupMemberRoleType.TEACHER == item.getRoleType()){

+ 281 - 9
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupServiceImpl.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.Lists;
+import com.microsvc.toolkit.middleware.common.http.ImageUtil;
 import com.microsvc.toolkit.middleware.im.ImPluginContext;
 import com.microsvc.toolkit.middleware.im.message.GroupMemberWrapper;
 import com.microsvc.toolkit.middleware.im.message.MessageWrapper;
@@ -35,8 +36,10 @@ import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.support.IMHelper;
 import com.yonge.cooleshow.biz.dal.vo.StudentVo;
 import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
+import com.yonge.cooleshow.biz.dal.wrapper.ImHistoryMessageWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
 import com.yonge.cooleshow.common.constant.SysConfigConstant;
+import com.yonge.cooleshow.common.entity.UploadReturnBean;
 import com.yonge.toolset.base.exception.BizException;
 import com.yonge.toolset.base.util.ThreadPool;
 import com.yonge.toolset.mybatis.support.PageUtil;
@@ -45,6 +48,7 @@ import io.rong.methods.message.history.History;
 import io.rong.models.Result;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.checkerframework.checker.units.qual.A;
 import org.redisson.api.RBucket;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
@@ -56,11 +60,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
+import java.io.*;
 import java.nio.charset.StandardCharsets;
 import java.text.MessageFormat;
 import java.text.ParseException;
@@ -127,6 +127,9 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
     @Autowired
     private RTCRoomPluginContext rtcRoomPluginContext;
 
+    @Autowired
+    private UploadFileService uploadFileService;
+
 
     @Value("${cn.rongcloud.im.appkey}")
     private String appKey;
@@ -722,11 +725,57 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
 
                 JSONObject jsonObject = JSONObject.parseObject(i.getContent());
                 if (i.getClassname().equals("RC:TxtMsg")) {
+                    //文本
                     TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimTextElem(jsonObject.getString("content"));
                     list.add(body1);
                 } else if (i.getClassname().equals("RC:ImgMsg")) {
+                    //图片
                     TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimImageElem(jsonObject.getString("imageUri"));
                     list.add(body1);
+                } else if (i.getClassname().equals("RC:GIFMsg")) {
+                    //GIf
+                    TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimImageElem(jsonObject.getString("remoteUrl"));
+                    list.add(body1);
+                } else if (i.getClassname().equals("RC:HQVCMsg")){
+                    //语音
+                    String remoteUrl = jsonObject.getString("remoteUrl");
+                    int duration = jsonObject.getInteger("duration");
+                    TencentRequest.MessageBody body1 = getTimSoundElem(remoteUrl,duration);
+                    list.add(body1);
+                } else if(i.getClassname().equals("RC:FileMsg")){
+                    //文件
+                    String fileUrl = jsonObject.getString("fileUrl");
+                    String size = jsonObject.getString("size");
+                    String name = jsonObject.getString("name");
+
+                    TencentRequest.MessageBody body1 = getTimFileElem(fileUrl,size,name);
+                    list.add(body1);
+                } else if(i.getClassname().equals("RC:SightMsg")){
+                    //视频
+                    String sightUrl = jsonObject.getString("sightUrl");
+                    String size = jsonObject.getString("size");
+                    int duration = jsonObject.getInteger("duration");
+                    String content = jsonObject.getString("content");
+                    TencentRequest.MessageBody body1 = null;
+                    try {
+                        body1 = getTimVideoFileElem(sightUrl,size,duration,content);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                    list.add(body1);
+                } else if(i.getClassname().equals("RC:LBSMsg")) {
+                    //位置
+                    int latitude = jsonObject.getInteger("latitude");
+                    int longitude = jsonObject.getInteger("longitude");
+                    String poi = jsonObject.getString("poi");
+                    TencentRequest.MessageBody body1 = getTimLocationElem(latitude, longitude, poi);
+                    list.add(body1);
+                } else if (i.getClassname().equals("RC:ImgTextMsg")){
+                    //图文
+                    TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimTextElem(jsonObject.getString("content"));
+                    TencentRequest.MessageBody body2 = CustomerServiceBatchSendingServiceImpl.getTimImageElem(jsonObject.getString("imageUri"));
+                    list.add(body1);
+                    list.add(body2);
                 } else {
                     return;
                 }
@@ -739,13 +788,14 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
                     item.setMsgContent(i.getContent());
                     item.setMsgType(i.getClassname());
                 });*/
-                if (list.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:TxtMsg")) {
+
+               /* if (list.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:TxtMsg")) {
                     //文本对象
                     list.forEach(item -> item.setMsgType("TIMTextElem"));
                 } else if (list.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:ImgMsg")) {
                     //图文对象
                     list.forEach(item -> item.setMsgType("TIMImageElem"));
-                }
+                }*/
                 privateImportMessage.setTencentMessageBody(list);
                 try {
                     imPluginContext.getPluginService().importPrivateMessage(privateImportMessage);
@@ -796,11 +846,66 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
                         });*/
                 JSONObject jsonObject = JSONObject.parseObject(i.getContent());
                 if (i.getClassname().equals("RC:TxtMsg")) {
+                    //文本
                     TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimTextElem(jsonObject.getString("content"));
                     bodyList.add(body1);
                 } else if (i.getClassname().equals("RC:ImgMsg")) {
+                    //图片  GIf
                     TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimImageElem(jsonObject.getString("imageUri"));
                     bodyList.add(body1);
+                } else if (i.getClassname().equals("RC:GIFMsg")) {
+                    //GIf
+                    String remoteUrl = jsonObject.getString("remoteUrl");
+                    int gifDataSize = jsonObject.getInteger("gifDataSize");
+                    int width = jsonObject.getInteger("width");
+                    int height = jsonObject.getInteger("height");
+                    TencentRequest.MessageBody body1 = getTimGifElem(remoteUrl,gifDataSize,width,height);
+                    bodyList.add(body1);
+                } else if (i.getClassname().equals("RC:HQVCMsg")){
+                    //语音
+                    String remoteUrl = jsonObject.getString("remoteUrl");
+                    int duration = jsonObject.getInteger("duration");
+                    TencentRequest.MessageBody body1 = getTimSoundElem(remoteUrl,duration);
+                    bodyList.add(body1);
+                } else if(i.getClassname().equals("RC:FileMsg")){
+                    //文件
+                    String fileUrl = jsonObject.getString("fileUrl");
+                    String size = jsonObject.getString("size");
+                    String name = jsonObject.getString("name");
+
+                    TencentRequest.MessageBody body1 = getTimFileElem(fileUrl,size,name);
+                    bodyList.add(body1);
+                } else if(i.getClassname().equals("RC:SightMsg")){
+                    //视频
+                    String sightUrl = jsonObject.getString("sightUrl");
+                    String size = jsonObject.getString("size");
+                    int duration = jsonObject.getInteger("duration");
+                    String content = jsonObject.getString("content");
+                    TencentRequest.MessageBody body1 = null;
+                    try {
+                        body1 = getTimVideoFileElem(sightUrl,size,duration,content);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                    bodyList.add(body1);
+                } else if(i.getClassname().equals("RC:LBSMsg")) {
+                    //位置
+                    int latitude = jsonObject.getInteger("latitude");
+                    int longitude = jsonObject.getInteger("longitude");
+                    String poi = jsonObject.getString("poi");
+                    TencentRequest.MessageBody body1 = getTimLocationElem(latitude, longitude, poi);
+                    bodyList.add(body1);
+                } else if (i.getClassname().equals("RC:ImgTextMsg")){
+                    //图文
+                    TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimTextElem(jsonObject.getString("content"));
+                    TencentRequest.MessageBody body2 = CustomerServiceBatchSendingServiceImpl.getTimImageElem(jsonObject.getString("imageUri"));
+                    bodyList.add(body1);
+                    bodyList.add(body2);
+                } else if (i.getClassname().equals("RC:CombineMsg")){
+                    //合并转发
+                    String remoteUrl = jsonObject.getString("remoteUrl");
+                    int conversationType = jsonObject.getInteger("conversationType");
+                    getTimRelayElem();
                 } else {
                     return;
                 }
@@ -808,12 +913,12 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
                 body.setMsgType(i.getClassname());*/
 
                 //文本对象
-                if (bodyList.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:TxtMsg")) {
+                /*if (bodyList.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:TxtMsg")) {
                     bodyList.forEach(item -> item.setMsgType("TIMTextElem"));
                 } else if (bodyList.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:ImgMsg")) {
                     //暂未支持图文对象
                     bodyList.forEach(item -> item.setMsgType("TIMCustomElem"));
-                }
+                }*/
 
                 data1.setTencentMessageBody(bodyList);
 
@@ -829,6 +934,163 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         });
     }
 
+    private TencentRequest.MessageBody getTimGifElem(String remoteUr,int size,int width,int height) {
+        if (StringUtils.isEmpty(remoteUr) && size == 0 && width == 0 && height == 0) {
+            return null;
+        }
+
+        // 腾讯云消息
+        // 腾讯云消息
+        ImageUtil.ImageInfo imageReq = ImageUtil.ImageInfo.builder()
+                .imgUrl(remoteUr)
+                .suffix(remoteUr.substring(remoteUr.lastIndexOf(".") + 1))
+                .build();
+
+        // 获取图片基本信息
+        ImageUtil.imageToBase64(imageReq);
+
+        // 图片格式。 JPG = 1 GIF = 2 PNG = 3 BMP = 4 其他 = 255
+        // 图片类型。原图 = 1,大图 = 2,缩略图 = 3
+        int imageFormat = 1;
+        if ("PNG".equalsIgnoreCase(imageReq.getSuffix())) {
+            imageFormat = 3;
+        }
+        if ("GIF".equalsIgnoreCase(imageReq.getSuffix())) {
+            imageFormat = 2;
+        }
+        return TencentRequest.MessageBody.builder()
+                .msgType("TIMImageElem")
+                .msgContent(TencentRequest.ImageMessageBody.builder()
+                        .uuid(imageReq.getMd5())
+                        .imageFormat(imageFormat)
+                        .type(1)
+                        .height(height)
+                        .width(width)
+                        .url(remoteUr)
+                        .size(imageReq.getSize()).build())
+                .build();
+
+    }
+
+    //构建合并转发请求体
+    private TencentRequest.MessageBody getTimRelayElem() {
+        ImHistoryMessageWrapper.TimRelayElem timRelayElem = new ImHistoryMessageWrapper.TimRelayElem();
+        //todo
+        //timRelayElem.setMsgNum();
+
+        TencentRequest.MessageBody messageBody = new TencentRequest.MessageBody();
+        messageBody.setMsgType("TIMRelayElem");
+        messageBody.setMsgContent(timRelayElem);
+        return  messageBody;
+    }
+
+    //构建位置请求体
+    private TencentRequest.MessageBody getTimLocationElem(int latitude, int longitude, String poi) {
+        if (latitude == 0 && longitude == 0 &&  StringUtils.isEmpty(poi)) {
+            return null;
+        }
+        ImHistoryMessageWrapper.TimLocationElem timLocationElem = new ImHistoryMessageWrapper.TimLocationElem();
+        timLocationElem.setDesc(poi);
+        timLocationElem.setLatitude(latitude);
+        timLocationElem.setLongitude(longitude);
+
+        TencentRequest.MessageBody messageBody = new TencentRequest.MessageBody();
+        messageBody.setMsgType("TIMLocationElem");
+        messageBody.setMsgContent(timLocationElem);
+        return  messageBody;
+    }
+
+    //构建视频请求体
+    public  TencentRequest.MessageBody getTimVideoFileElem(String sightUrl, String size, int duration, String content) throws IOException {
+        if (StringUtils.isEmpty(sightUrl) && StringUtils.isEmpty(sightUrl) && duration == 0) {
+            return null;
+        }
+        ByteArrayInputStream bais = null;
+        String  format = "";
+        //判断是否有前缀
+        if (content.contains(",")){
+            String[] split = content.split(",");
+            String s = split[0];
+            format = s.substring(s.indexOf("/")+ 1,s.indexOf(";"));
+            content = split[1];
+        } else {
+            format = "JPG";
+        }
+
+        //获取JDK8里的解码器Base64.Decoder,将base64字符串转为字节数组
+        byte[] bytes = Base64.getDecoder().decode(content);
+        //构建字节数组输入流
+        bais = new ByteArrayInputStream(bytes);
+
+        UploadReturnBean uploadReturnBean = uploadFileService.uploadFile(bais, format);
+        String url = uploadReturnBean.getUrl();
+        //BufferedImage read = ImageIO.read(bais);
+
+
+
+        ImageUtil.ImageInfo imageReq = ImageUtil.ImageInfo.builder()
+                .imgUrl(url)
+                .suffix(format)
+                .build();
+
+
+        ImHistoryMessageWrapper.TimVideoFileElem timVideoFileElem = new ImHistoryMessageWrapper.TimVideoFileElem();
+        timVideoFileElem.setVideoUrl(sightUrl);
+        timVideoFileElem.setVideoUUID(UUID.randomUUID().toString());
+        timVideoFileElem.setVideoSize(Integer.valueOf(size));
+        timVideoFileElem.setVideoSecond(duration);
+        timVideoFileElem.setVideoFormat("mp4");
+        timVideoFileElem.setVideoDownloadFlag(2);
+        timVideoFileElem.setThumbUrl(url);
+        timVideoFileElem.setThumbUUID(imageReq.getMd5());
+        timVideoFileElem.setThumbWidth(imageReq.getWidth());
+        timVideoFileElem.setThumbHeight(imageReq.getHeight());
+        timVideoFileElem.setThumbSize((imageReq.getSize().intValue()));
+        timVideoFileElem.setThumbDownloadFlag(2);
+        timVideoFileElem.setThumbFormat(format);
+
+        TencentRequest.MessageBody messageBody = new TencentRequest.MessageBody();
+        messageBody.setMsgType("TIMVideoFileElem");
+        messageBody.setMsgContent(timVideoFileElem);
+        return  messageBody;
+    }
+
+    //构建文件请求体
+    public static TencentRequest.MessageBody getTimFileElem(String fileUrl, String size, String name) {
+        if (StringUtils.isEmpty(fileUrl) && StringUtils.isEmpty(size) &&  StringUtils.isEmpty(name)) {
+            return null;
+        }
+        ImHistoryMessageWrapper.TimFileElem timFileElem = new ImHistoryMessageWrapper.TimFileElem();
+        timFileElem.setUrl(fileUrl);
+        timFileElem.setUUID(UUID.randomUUID().toString());
+        timFileElem.setFileName(name);
+        timFileElem.setFileSize(Integer.valueOf(size));
+        timFileElem.setDownload_Flag(2);
+
+        TencentRequest.MessageBody messageBody = new TencentRequest.MessageBody();
+        messageBody.setMsgType("TIMFileElem");
+        messageBody.setMsgContent(timFileElem);
+        return  messageBody;
+    }
+
+    //构造语音请求体
+    public static TencentRequest.MessageBody getTimSoundElem(String remoteUrl,int duration) {
+        if (StringUtils.isEmpty(remoteUrl) && duration == 0) {
+            return null;
+        }
+
+        ImHistoryMessageWrapper.TimSoundElem timSoundElem = new ImHistoryMessageWrapper.TimSoundElem();
+        timSoundElem.setUrl(remoteUrl);
+        timSoundElem.setUUID(UUID.randomUUID().toString());
+        timSoundElem.setSecond(duration);
+        timSoundElem.setDownload_Flag(2);
+
+        TencentRequest.MessageBody messageBody = new TencentRequest.MessageBody();
+        messageBody.setMsgType("TIMSoundElem");
+        messageBody.setMsgContent(timSoundElem);
+        return  messageBody;
+    }
+
 
     private History getHistory() {
         RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
@@ -919,6 +1181,16 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         }
     }
 
+    /**
+     * 更新已修改数据的状态
+     * @param info
+     */
+    @Override
+    public void updateStatus(List<ImHistoryMessage> info) {
+        getDao().updateStatus(info);
+    }
+
+
     private void groupTransfer(List<ImGroup> records) {
         String fansIcon = sysConfigService.findConfigValue(SysConfigConstant.ICON_FANS_GROUP_DEFAULT);
         String courseIcon = sysConfigService.findConfigValue(SysConfigConstant.ICON_COURSE_GROUP_DEFAULT);

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

@@ -0,0 +1,164 @@
+package com.yonge.cooleshow.biz.dal.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.LiveRoom;
+import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.beans.BeanUtils;
+import lombok.extern.slf4j.Slf4j;
+import com.yonge.cooleshow.biz.dal.entity.ImLiveBroadcastRoomMember;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper;
+import com.yonge.cooleshow.biz.dal.mapper.ImLiveBroadcastRoomMemberMapper;
+import com.yonge.cooleshow.biz.dal.service.ImLiveBroadcastRoomMemberService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * 直播间人员关系表
+ * 2023-08-15 15:50:17
+ */
+@Slf4j
+@Service
+public class ImLiveBroadcastRoomMemberServiceImpl extends ServiceImpl<ImLiveBroadcastRoomMemberMapper, ImLiveBroadcastRoomMember> implements ImLiveBroadcastRoomMemberService {
+
+    @Autowired
+    private LiveRoomService liveRoomService;
+
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+
+	/**
+     * 查询详情
+     * @param id 详情ID
+     * @return ImLiveBroadcastRoomMember
+     */
+	@Override
+    public ImLiveBroadcastRoomMember detail(Long id) {
+        
+        return baseMapper.selectById(id);
+    }
+    
+    /**
+     * 分页查询
+     * @param page IPage<ImLiveBroadcastRoomMember>
+     * @param query ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMemberQuery
+     * @return IPage<ImLiveBroadcastRoomMember>
+     */
+    @Override
+    public IPage<ImLiveBroadcastRoomMember> selectPage(IPage<ImLiveBroadcastRoomMember> page, ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMemberQuery query) {
+        
+        return page.setRecords(baseMapper.selectPage(page, query));
+    }
+	
+    /**
+     * 添加
+     * @param imLiveBroadcastRoomMember ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember
+     * @return Boolean
+     */
+    @Override
+    public Boolean add(ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember imLiveBroadcastRoomMember) {    	
+        
+        return this.save(JSON.parseObject(imLiveBroadcastRoomMember.jsonString(), ImLiveBroadcastRoomMember.class));
+    }
+
+    /**
+     * 更新
+     * @param imLiveBroadcastRoomMember ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember
+     * @return Boolean
+     */
+    @Override
+    public Boolean update(ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember imLiveBroadcastRoomMember){
+
+        return this.updateById(JSON.parseObject(imLiveBroadcastRoomMember.jsonString(), ImLiveBroadcastRoomMember.class));       
+    }
+
+    @Override
+    public PageInfo<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> queryRoomMember(Map<String, Object> param) {
+        Page<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> pageInfo = PageUtil.getPageInfo(param);
+
+        IPage<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> page = baseMapper.queryMemberPage(pageInfo, param);
+
+        List<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember> records = page.getRecords();
+
+        if (CollectionUtils.isNotEmpty(records)) {
+
+            // 游客默认头像
+
+            String roomUid = MapUtils.getString(param, "roomUid", RandomStringUtils.random(5));
+            // 直播间信息
+            LiveRoom room =liveRoomService.lambdaQuery()
+                    .eq(LiveRoom::getRoomUid, roomUid)
+                    .last("LIMIT 1")
+                    .one();
+            if (Objects.isNull(room)) {
+                throw new BizException("直播间不存在");
+            }
+            // 直播间主讲老师
+            SysUser speaker = sysUserFeignService.queryUserById(room.getSpeakerId());
+
+
+            List<Integer> studentIds = records.stream()
+                    .filter(x -> StringUtils.isBlank(x.getFingerprint()))
+                    .map(ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember::getStudentId)
+                    .filter(Objects::nonNull)
+                    .distinct().collect(Collectors.toList());
+
+            // 学生报名乐团
+            Map<Integer, String> musicGroupMap = Maps.newConcurrentMap();
+            // 学生声部名称
+            Map<Integer, String> subjectMap = Maps.newConcurrentMap();
+
+            // 直播客户端查询在线用户信息时,忽略学生相关信息查询
+            if (MapUtils.getIntValue(param, "rows", 10) < 100) {
+
+                for (List<Integer> item : Lists.partition(studentIds, 500)) {
+                    // 并发查询学生信息
+                    Lists.partition(item, 50).parallelStream().forEach(data -> {
+
+                        // 学生声部名称
+                        Map<Integer, String> collect = baseMapper.selectStudentSubject(studentIds).stream()
+                                .filter(x -> Objects.nonNull(x) && Objects.nonNull(x.getStudentId()))
+                                .collect(Collectors.toMap(ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember::getStudentId, x -> Optional.ofNullable(x.getSubName()).orElse(""), (o, n) -> n));
+                        if (MapUtils.isNotEmpty(collect)) {
+                            subjectMap.putAll(collect);
+                        }
+                    });
+                }
+            }
+
+            for (ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember item : records) {
+
+                if (StringUtils.isNotEmpty(item.getFingerprint())) {
+
+                } else {
+                    // 设置默认数据
+                    item.setSubName(subjectMap.getOrDefault(item.getStudentId(), ""));
+                    item.setRoomTitle(room.getRoomTitle());
+                    item.setSpeakerName(speaker.getRealName());
+                }
+
+            }
+        }
+
+        return PageUtil.pageInfo(page);
+    }
+}

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

@@ -0,0 +1,66 @@
+package com.yonge.cooleshow.biz.dal.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.beans.BeanUtils;
+import lombok.extern.slf4j.Slf4j;
+import com.yonge.cooleshow.biz.dal.entity.LiveBroadcastRoomData;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveBroadcastRoomDataWrapper;
+import com.yonge.cooleshow.biz.dal.mapper.LiveBroadcastRoomDataMapper;
+import com.yonge.cooleshow.biz.dal.service.LiveBroadcastRoomDataService;
+
+/**
+ * 直播房间数据表
+ * 2023-08-15 15:50:17
+ */
+@Slf4j
+@Service
+public class LiveBroadcastRoomDataServiceImpl extends ServiceImpl<LiveBroadcastRoomDataMapper, LiveBroadcastRoomData> implements LiveBroadcastRoomDataService {
+
+	/**
+     * 查询详情
+     * @param id 详情ID
+     * @return LiveBroadcastRoomData
+     */
+	@Override
+    public LiveBroadcastRoomData detail(Long id) {
+        
+        return baseMapper.selectById(id);
+    }
+    
+    /**
+     * 分页查询
+     * @param page IPage<LiveBroadcastRoomData>
+     * @param query LiveBroadcastRoomDataWrapper.LiveBroadcastRoomDataQuery
+     * @return IPage<LiveBroadcastRoomData>
+     */
+    @Override
+    public IPage<LiveBroadcastRoomData> selectPage(IPage<LiveBroadcastRoomData> page, LiveBroadcastRoomDataWrapper.LiveBroadcastRoomDataQuery query) {
+        
+        return page.setRecords(baseMapper.selectPage(page, query));
+    }
+	
+    /**
+     * 添加
+     * @param liveBroadcastRoomData LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData
+     * @return Boolean
+     */
+    @Override
+    public Boolean add(LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData liveBroadcastRoomData) {    	
+        
+        return this.save(JSON.parseObject(liveBroadcastRoomData.jsonString(), LiveBroadcastRoomData.class));
+    }
+
+    /**
+     * 更新
+     * @param liveBroadcastRoomData LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData
+     * @return Boolean
+     */
+    @Override
+    public Boolean update(LiveBroadcastRoomDataWrapper.LiveBroadcastRoomData liveBroadcastRoomData){
+
+        return this.updateById(JSON.parseObject(liveBroadcastRoomData.jsonString(), LiveBroadcastRoomData.class));       
+    }
+}

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

@@ -1,23 +1,15 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
-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;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_ONLINE_USER_LIST;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_TOTAL_USER_LIST;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_USER_LAST_CLIENT_IP;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_USER_LAST_TIME;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_USER_ROOM;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.ROOM_UID;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.TEACHER_TEMP_LIVE_ROOM;
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.USER_ID;
+import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.*;
 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.io.Serializable;
 import java.text.MessageFormat;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
@@ -26,6 +18,7 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.microsvc.toolkit.middleware.live.LivePluginContext;
 import com.microsvc.toolkit.middleware.live.LivePluginService;
 import com.microsvc.toolkit.middleware.live.impl.RongCloudLivePlugin;
@@ -36,10 +29,14 @@ import com.yonge.cooleshow.biz.dal.dto.TencentData;
 import com.yonge.cooleshow.biz.dal.entity.*;
 import com.yonge.cooleshow.biz.dal.enums.live.EAnchorStatus;
 import com.yonge.cooleshow.biz.dal.enums.live.EOnOffStatus;
+import com.yonge.cooleshow.biz.dal.mapper.ImLiveBroadcastRoomMemberMapper;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
 import com.yonge.cooleshow.biz.dal.service.*;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
 import com.yonge.cooleshow.common.enums.EGroupDefinedDataType;
+import com.yonge.toolset.payment.util.DistributedLock;
+import lombok.Data;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.joda.time.DateTime;
@@ -125,6 +122,17 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
 
     @Autowired
     private LiveRoomVideoService liveRoomVideoService;
+
+    @Autowired
+    private LiveBroadcastRoomDataService liveBroadcastRoomDataService;
+
+    @Autowired
+    private ImLiveBroadcastRoomMemberMapper imLiveBroadcastRoomMemberMapper;
+    @Autowired
+    private ImLiveBroadcastRoomMemberService imLiveBroadcastRoomMemberService;
+
+    @Autowired
+    private ImGroupService imGroupService;
     /**
      * 生成房间UID
      *
@@ -181,10 +189,13 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
      * @return
      */
     @Override
-    public RoomInfoCache speakerCheckRoomInfo(String roomUid) {
-        Long userId = this.getSysUser().getId();
-        LiveRoom liveRoom = this.getOne(Wrappers.<LiveRoom>lambdaQuery()
-                .eq(LiveRoom::getRoomUid, roomUid));
+    @Transactional(rollbackFor = Exception.class)
+    public LiveRoomWrapper.LiveRoomVo speakerCheckRoomInfo(String roomUid) {
+
+        SysUser sysUser = getSysUser();
+        Long userId = sysUser.getId();
+        LiveRoomWrapper.LiveRoomVo liveRoom = queryRoomInfo(roomUid);
+        LiveRoom room = getByRoomUid(roomUid);
         if (Objects.isNull(liveRoom)) {
             log.error("teacherCheckRoomInfo>>>live not start  roomUid: {} userId:{}", roomUid, userId);
             throw new BizException("直播还未开始!");
@@ -198,18 +209,84 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             log.error("teacherCheckRoomInfo>>>live not start  roomUid: {} userId:{}", roomUid, userId);
             throw new BizException("直播还未开始!");
         }
-        RoomInfoCache roomInfo = roomInfoCache.get();
+        Date now = new Date();
         //校验进入房间的是否是该直播间的主讲人
-        if (!roomInfo.getSpeakerId().equals(userId)) {
+        if (!liveRoom.getSpeakerId().equals(userId)) {
             log.error("teacherCheckRoomInfo>>>not speaker  roomUid: {} userId:{}", roomUid, userId);
             throw new BizException("您不是该直播间的主讲人!");
         }
-        roomInfo.setLikeNum(getLike(roomUid));
-        roomInfo.setLookNum(getNum.apply(this::getOnlineUserCache, roomUid));
-        return roomInfo;
+        //获取直播间信息
+        RBucket<RoomSpeakerInfo> speakerCache = getRoomSpeakerInfoCache(roomUid, userId.toString());
+        if (!speakerCache.isExists()) {
+
+            //没有主讲人信息则生成一个
+            createSpeakerInfo(this.getById(liveRoom.getId()), sysUser);
+
+            // 查询主播缓存信息
+            speakerCache = getRoomSpeakerInfoCache(roomUid, userId.toString());
+        }
+        RoomSpeakerInfo speakerInfo = speakerCache.get();
+        speakerInfo.setJoinRoomTime(now);
+        speakerCache.set(speakerInfo);
+
+        //记录用户当前房间uid
+        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userId.toString())).set(roomUid, 12L, TimeUnit.HOURS);
+
+        // 更新直播间主播状态
+        lambdaUpdate()
+                .eq(LiveRoom::getRoomUid, roomUid)
+                .eq(LiveRoom::getSpeakerId, userId)
+                .set(LiveRoom::getSpeakerStatus, 1)
+                .update();
+
+        // 设置直播群组自定义数据
+        setGroupDefinedData(room,EGroupDefinedDataType.ANCHOR_STATUS,EAnchorStatus.ONLINE.getCode());
+        return liveRoom;
     }
 
     /**
+     * 获取房间主讲人消息
+     *
+     * @param roomUid 房间uid
+     * @param userId  用户id
+     */
+    private RBucket<RoomSpeakerInfo> getRoomSpeakerInfoCache(String roomUid, String userId) {
+        return redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, userId).replace(ROOM_UID, roomUid));
+    }
+
+
+    //生成主讲人信息
+    private void createSpeakerInfo(LiveRoom room, SysUser sysUser) {
+        Date now = new Date();
+        RoomSpeakerInfo speakerInfo = new RoomSpeakerInfo();
+        speakerInfo.setSpeakerId(sysUser.getId());
+        speakerInfo.setSpeakerName(sysUser.getRealName());
+        speakerInfo.setCreateRoomTime(now);
+        speakerInfo.setRoomUid(room.getRoomUid());
+        speakerInfo.setTotalLiveTime(0);
+
+        //可以录制视频
+        speakerInfo.setWhetherVideo(0);
+        // 设置模板
+        speakerInfo.setVideoTemplate(1);
+
+        speakerInfo.setOs(room.getOs());
+        //写入主讲人信息
+        getRoomSpeakerInfoCache(room.getRoomUid(), room.getSpeakerId().toString()).set(speakerInfo);
+
+        //生成0点赞
+        redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, room.getRoomUid())).set(0);
+
+        //修改房间状态为开始
+        LiveRoom liveRoomUpdate = new LiveRoom();
+        liveRoomUpdate.setId(room.getId());
+        liveRoomUpdate.setLiveState(1);
+        liveRoomUpdate.setUpdatedTime(now);
+        liveRoomUpdate.setUpdatedBy(-2L);
+        liveRoomUpdate.setSpeakerStatus(1);
+        baseMapper.updateById(liveRoomUpdate);
+    }
+    /**
      * 定时任务创建直播间
      */
     @Override
@@ -715,6 +792,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (StringUtils.isBlank(user.getStatus())) {
                 return;
             }
+            Date now = new Date();
             String userIdStr = user.getUserid();
             //获取当前用户所在房间的uid
             RBucket<String> userRoom = redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userIdStr));
@@ -723,7 +801,6 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             }
             String roomUid = Optional.ofNullable(user.getRoomUid()).orElse(userRoom.get());
 
-            Date now = new Date();
             //获取当前用户状态变更的时间
             long userStateTime = Optional.ofNullable(user.getTime()).orElse(now.getTime());
             RBucket<Long> userStateTimeCache = redissonClient.getBucket(LIVE_USER_LAST_TIME.replace(USER_ID, userIdStr));
@@ -795,6 +872,23 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
                     .eq(StudentAttendance::getStudentId, userId)
                     .eq(StudentAttendance::getCourseScheduleId, schedule.getId())
                     .set(StudentAttendance::getSignOutTime, now));
+
+            // 更新用户离线状态
+            ImLiveBroadcastRoomMember roomMember = new ImLiveBroadcastRoomMember();
+            roomMember.setOnlineStatus(0);
+
+            // 用户离开直播间
+            if (user.getStatus().equals("3")) {
+                // 直播间用户离开状态
+                roomMember.setLiveRoomStatus(0);
+
+            }
+            // 更新用户在线状态为离线
+            imLiveBroadcastRoomMemberService.lambdaUpdate()
+                    .eq(ImLiveBroadcastRoomMember::getRoomUid, roomVo.getRoomUid())
+                    .eq(ImLiveBroadcastRoomMember::getUserId, userId)
+                    .update(roomMember);
+
             //向直播间发送当前在线人数消息
             this.sendOnlineUserCount(roomUid, userId, onlineUserInfo.size());
             log.info("opsRoom>>>> looker userInfo: {}", userJsonStr);
@@ -939,25 +1033,6 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         redissonClient.getBucket(LIVE_ROOM_HEART_BEAT.replace(ROOM_UID, roomUid)).set(new Date());
     }
 
-    /**
-     * 主讲人-进入房间
-     *
-     * @param roomUid 房间uid
-     */
-    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(), schedule.getCourseGroupId(), schedule.getId());
-        //记录当前用户对应的房间uid
-        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, roomInfo.getSpeakerId().toString())).set(roomUid, 2L, TimeUnit.DAYS);
-        return roomInfo;
-    }
 
     /**
      * 观看者-进入房间
@@ -965,50 +1040,9 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
      * @param roomUid 房间uid
      * @param userId  用户id 观看者id
      */
-    public RoomInfoCache joinRoom(String roomUid, Long userId) {
-        Optional.ofNullable(roomUid).orElseThrow(() -> new BizException("房间编号不能为空!"));
-        Optional.ofNullable(userId).orElseThrow(() -> new BizException("人员编号不能为空!"));
-        //获取进入房间人员信息
-        SysUser sysUser = this.getSysUser(userId);
-        //校验信息
-        RoomInfoCache roomInfo = this.checkStudentRoom(roomUid, sysUser);
-        Date now = new Date();
-        //房间累计用户信息-指只要进入到该房间的用户都要记录
-        RMap<Long, String> roomTotalUser = this.getTotalUserCache(roomUid);
-        //判断是否第一次进房间
-        RoomUserInfoCache userInfo;
-        liveBroadcastRoomMemberService.saveRecord(roomUid,userId, InOrOutEnum.IN, YesOrNoEnum.YES);
-        if (roomTotalUser.containsKey(userId)) {
-            //多次进入更新动态进入时间
-            userInfo = JSONObject.toJavaObject(JSONObject.parseObject(roomTotalUser.get(userId)), RoomUserInfoCache.class);
-        } else {
-            //第一次进该房间 写入用户首次进入时间
-            userInfo = new RoomUserInfoCache();
-            userInfo.setUserId(sysUser.getId());
-            userInfo.setUserName(sysUser.getRealName());
-            userInfo.setFirstJoinTime(now);
-        }
+    public void joinRoom(String roomUid, Long userId) {
 
-
-        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
-        //查询学生是否有进入过,没有则写学生考勤表的进入时间
-        this.setStudentAttendance(userId, schedule.getCourseGroupId(), schedule.getId());
-        userInfo.setDynamicJoinTime(now);
-        //用户json信息
-        String userJsonStr = JSONObject.toJSONString(userInfo);
-        //写入人员缓存
-        roomTotalUser.fastPut(userId, userJsonStr);
-        //写入在线人员缓存
-        RMap<Long, String> onlineUserCache = this.getOnlineUserCache(roomUid);
-        onlineUserCache.fastPut(userId, userJsonStr);
-        log.info("joinRoom>>>> userInfo: {}", userJsonStr);
-        //向直播间发送当前在线人数消息
-        this.sendOnlineUserCount(roomUid, userId, onlineUserCache.size());
-        log.info("join sendOnlineUserCount>>>> param is null   roomUid: {}  fromUserId:{}  count:{}", roomUid, userId, onlineUserCache.size());
-        log.info("joinRoom>>>> userInfo: {}", userJsonStr);
-        //记录当前用户对应的房间uid
-        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userId.toString())).set(roomUid, 2L, TimeUnit.DAYS);
-        return roomInfo;
+        joinRoom(roomUid, userId, true);
     }
 
     //校验学生与房间的关系
@@ -1657,13 +1691,20 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         if (room == null) {
             throw new BizException("直播间不存在");
         }
+
+        room.setId(room.getId());
+
+
+        room.setBanStatus(liveRoom.getBanStatus());
         // 设置直播群组自定义数据
         if (liveRoom.getSpeakerStatus() != null) {
+            room.setSpeakerStatus(liveRoom.getSpeakerStatus());
             setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_STATUS,
                     liveRoom.getSpeakerStatus() == 0 ? EAnchorStatus.OFFLINE.getCode() : EAnchorStatus.ONLINE.getCode());
         }
 
         if (liveRoom.getPushStatus() != null) {
+            room.setPushStatus(liveRoom.getPushStatus());
             // 设置推流状态
             setGroupDefinedData(room, EGroupDefinedDataType.LIVE_STATUS, liveRoom.getPushStatus() == 1 ?
                     EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
@@ -1671,6 +1712,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
 
 
         if (liveRoom.getBanStatus() != null) {
+            room.setBanStatus(liveRoom.getBanStatus());
             setGroupDefinedData(room, EGroupDefinedDataType.GLOBAL_BAN, liveRoom.getBanStatus() == 1 ?
                     EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
         }
@@ -1686,8 +1728,671 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_MIC,
                     liveRoom.getMicStatus() == 1 ? EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
         }
-        return true;
+
+        return this.updateById(room);
+    }
+
+    /**
+     * 进入直播间检查数据
+     *
+     * @param roomUid 房间uid
+     * @param userId  用户id
+     * @param osType  默认学生端  0:pc端 1:学生端 2:老师端
+     */
+    @Override
+    public LiveRoomWrapper.LiveRoomVo queryRoomAndCheck(String roomUid, Long userId, int osType) {
+
+        SysUser sysUser = Optional.ofNullable(userId)
+                .map(this::getSysUser)
+                .orElseGet(this::getSysUser);
+
+        // 默认学生端查询
+        osType = Optional.ofNullable(osType).orElse(1);
+        ClientEnum clientEnum = ClientEnum.TEACHER;
+
+        // 移动端-学生端
+        if (osType == 1) {
+            clientEnum = ClientEnum.STUDENT;
+            //学生端
+            this.checkStudentRoom(roomUid, sysUser);
+        }
+        // 移动端-老师端
+        if (osType == 2) {
+            //老师端
+//            return teacherQueryRoomAndCheck(roomUid, sysUser);
+        }
+        // PC端-直播助手
+        if (osType == 3) {
+            // 校验直播间是否存在
+            LiveRoomWrapper.LiveRoomVo vo = Optional.ofNullable(roomUid).map(this::queryRoomInfo)
+                    .orElseThrow(() -> new BizException("直播间不存在"));
+
+            if (vo.getOs().equals("mobile")) {
+                throw new BizException("该直播间仅可在手机端直播");
+            }
+        }
+
+        return this.queryRoomAndCheck(queryRoomInfo(roomUid), sysUser,clientEnum);
+    }
+
+
+    public LiveRoomWrapper.LiveRoomVo queryRoomAndCheck(LiveRoomWrapper.LiveRoomVo room, SysUser sysUser,ClientEnum clientType) {
+
+
+        if (room.getLiveState() == 2) {
+            //如果直播结束了还是推广状态则将推广修改为取消
+            throw new BizException("直播已结束!");
+        }
+
+        if (room.getRoomState() == 1) {
+            throw new BizException("直播间不存在");
+        }
+
+
+        room.setBlacklistFlag(0);
+
+        LivePluginService pluginService = livePluginContext.getPluginService(room.getServiceProvider());
+        // 直播房间配置信息
+        String userSig = "";
+        try {
+            userSig = pluginService.register(imGroupService.getImUserId(sysUser.getId().toString(),clientType.getCode()), sysUser.getUsername(),
+                    sysUser.getAvatar());
+        } catch (Exception e) {
+            log.error("直播房间用户注册失败: userId={}", sysUser.getId(), e);
+        }
+
+        // 直播房间统计信息
+        getRoomData(room);
+        room.setUserSig(userSig);
+        room.setLiveRoomConfig(pluginService.getLiveRoomConfig());
+
+        // 直播间配置信息
+        return room;
+    }
+
+
+    /**
+     * 直播间推流状态查询
+     *
+     * @param roomUid 直播间ID
+     * @return TencentWrapper.LiveStreamState
+     */
+    @Override
+    public TencentWrapper.LiveStreamState roomLiveStreamStatus(String roomUid) {
+
+        // 直播间信息
+        LiveRoomWrapper.LiveRoomVo roomVo = queryRoomInfo(roomUid);
+        if (Objects.isNull(roomVo)) {
+            throw new BizException("无效的直播间ID");
+        }
+
+        TencentWrapper.LiveStreamState liveStreamState = TencentWrapper.LiveStreamState
+                .builder()
+                .streamState("inactive")
+                .requestId("")
+                .build();
+        // 直播间推流状态
+        try {
+            LivePluginService pluginService = livePluginContext.getPluginService(roomVo.getServiceProvider());
+
+            liveStreamState = pluginService.liveStreamState(getStreamId(roomUid, roomVo.getSpeakerId()));
+        } catch (Exception e) {
+            log.error("roomLiveStreamStatus roomUid={}", roomUid, e);
+        }
+
+        return liveStreamState;
+    }
+
+
+    /**
+     * 查询直播间信息
+     *
+     * @param roomUid 直播间uid
+     */
+    @Override
+    public LiveRoomWrapper.LiveRoomVo queryRoomInfo(String roomUid) {
+        LiveRoom liveRoom = getByRoomUid(roomUid);
+        if (Objects.isNull(liveRoom)) {
+            throw new BizException("直播间不存在");
+        }
+        LiveRoomWrapper.LiveRoomVo liveRoomVo = JSON.parseObject(JSON.toJSONString(liveRoom), LiveRoomWrapper.LiveRoomVo.class);
+        return getRoomData(liveRoomVo);
+    }
+
+    private LiveRoomWrapper.LiveRoomVo getRoomData(LiveRoomWrapper.LiveRoomVo roomVo) {
+
+        // 同步点赞数
+        int likeNums = syncLikeCount(roomVo.getRoomUid());
+
+        roomVo.setLikeNum(likeNums);
+        roomVo.setTotalLookNum(0);
+        roomVo.setLookNum(0);
+
+        ImLiveBroadcastRoomMemberWrapper.RoomMemberNumDto roomMemberNumDto = imLiveBroadcastRoomMemberMapper.queryMemberNum(roomVo.getRoomUid());
+        if (roomMemberNumDto == null) {
+            roomMemberNumDto = new ImLiveBroadcastRoomMemberWrapper.RoomMemberNumDto();
+            roomMemberNumDto.setTotalNum(0);
+            roomMemberNumDto.setOnlineNum(0);
+        }
+        roomVo.setTotalLookNum(roomMemberNumDto.getTotalNum() == null? 0 : roomMemberNumDto.getTotalNum());
+        roomVo.setLookNum(roomMemberNumDto.getOnlineNum() == null? 0 : roomMemberNumDto.getOnlineNum());
+
+        // 直播视频数
+        List<LiveRoomVideo> imLiveRoomVideoVos = liveRoomVideoService.queryVideo(roomVo.getRoomUid());
+        if (CollectionUtils.isNotEmpty(imLiveRoomVideoVos)) {
+            roomVo.setVideoNum(imLiveRoomVideoVos.size());
+        } else {
+            roomVo.setVideoNum(0);
+        }
+        return roomVo;
+    }
+
+
+    /**
+     * 同步直播间点赞数
+     *
+     * @param roomUid 直播间uid
+     * @return int
+     */
+    @Override
+    public int syncLikeCount(String roomUid) {
+
+        // 直播间信息
+        LiveRoom room = getByRoomUid(roomUid);
+        if (room == null) {
+            return 0;
+        }
+
+        // 融云直播间
+        if (room.getServiceProvider().equals(RongCloudLivePlugin.PLUGIN_NAME)) {
+            //点赞数
+            return (int) Optional.ofNullable(redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).get()).orElse(0);
+        }
+
+
+        // 腾讯云直播
+        LivePluginService pluginService = livePluginContext.getPluginService(room.getServiceProvider());
+        List<TencentWrapper.ChatRoomGroupCounter> chatRoomGroupCounters = null;
+        try {
+            chatRoomGroupCounters = pluginService.chatRoomGroupCounterDataList(
+                    room.getRoomUid());
+        } catch (Exception e) {
+            log.error("同步直播间点赞数失败", e);
+        }
+        if (CollectionUtils.isEmpty(chatRoomGroupCounters)) {
+            return 0;
+        }
+        Optional<TencentWrapper.ChatRoomGroupCounter> first = chatRoomGroupCounters.stream()
+                .filter(a -> a.getKey()
+                        .equals(
+                                EGroupDefinedDataType.LIKES.getCode()))
+                .findFirst();
+
+        if (first.isPresent()) {
+            int i = Integer.parseInt(first.get().getValue());
+
+            LiveBroadcastRoomData one = liveBroadcastRoomDataService.lambdaQuery()
+                    .eq(LiveBroadcastRoomData::getRoomUid, roomUid)
+                    .last("limit 1")
+                    .one();
+            // 同步点赞数
+            if (one != null) {
+                LiveBroadcastRoomData imLiveBroadcastRoomData = new LiveBroadcastRoomData();
+                imLiveBroadcastRoomData.setId(one.getId());
+                imLiveBroadcastRoomData.setLikeNum(i);
+                liveBroadcastRoomDataService.updateById(imLiveBroadcastRoomData);
+            }
+
+            return i;
+        }
+        return 0;
+    }
+
+
+    /**
+     * 查询直播间在线的用户
+     *
+     * @param roomUid 直播间uid
+     */
+    @Override
+    public List<LiveRoomWrapper.BaseRoomUserVo> queryRoomLimitOnlineUserInfo(String roomUid) {
+        RMap<Long, String> onlineUserCache = getOnlineUserCache(roomUid);
+        return onlineUserCache.values().parallelStream()
+                .map(a -> JSONObject.toJavaObject(JSONObject.parseObject(a), LiveRoomWrapper.BaseRoomUserVo.class))
+                .limit(200)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 修改用户连麦状态
+     *
+     * @param roomUid          直播房间id
+     * @param userId           用户id
+     * @param whetherMicStatus 连麦状态
+     */
+    @Override
+    public void userWhetherMic(String roomUid, Long userId, Integer whetherMicStatus) {
+        imLiveBroadcastRoomMemberMapper.userWhetherMic(roomUid, userId, whetherMicStatus);
+    }
+
+    @Override
+    public void joinRoom(String roomUid, Long userId, Boolean microphoneFlag) {
+        Optional.ofNullable(roomUid).orElseThrow(() -> new BizException("房间编号不能为空!"));
+        Optional.ofNullable(userId).orElseThrow(() -> new BizException("人员编号不能为空!"));
+
+        LiveRoomWrapper.LiveRoomVo liveRoomVo = queryRoomInfo(roomUid);
+
+        //记录用户当前房间uid
+        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userId.toString())).set(roomUid, 12L, TimeUnit.HOURS);
+
+        //判断是否第一次进房间
+        ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo userInfo;
+
+        //房间累计用户信息-指只要进入到该房间的用户都要记录
+        RMap<Long, String> roomTotalUser = getTotalUserCache(roomUid);
+        if (roomTotalUser.containsKey(userId)) {
+            //多次进入更新动态进入时间
+            userInfo = JSONObject.toJavaObject(JSONObject.parseObject(roomTotalUser.get(userId)), ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo.class);
+
+            ImLiveBroadcastRoomMember liveRoomMember = imLiveBroadcastRoomMemberService.lambdaQuery()
+                    .eq(ImLiveBroadcastRoomMember::getRoomUid, liveRoomVo.getRoomUid())
+                    .eq(ImLiveBroadcastRoomMember::getUserId, userInfo.getUserId())
+                    .list().stream().findFirst().orElse(null);
+
+            if (Objects.isNull(liveRoomMember)) {
+
+                // 初次进入房间
+                getLiveRoomUserInfo(userId, liveRoomVo, microphoneFlag);
+            } else {
+                // 更新直播间用户信息
+                ImLiveBroadcastRoomMember roomMember = new ImLiveBroadcastRoomMember();
+                roomMember.setId(liveRoomMember.getId());
+                roomMember.setOnlineStatus(1);
+                roomMember.setLiveRoomStatus(1);
+                roomMember.setMicrophoneFlag(microphoneFlag);
+
+                imLiveBroadcastRoomMemberService.updateById(roomMember);
+            }
+
+        } else {
+            //第一次进该房间 写入用户首次进入时间
+            userInfo = getLiveRoomUserInfo(userId, liveRoomVo, microphoneFlag);
+        }
+
+        //查询主讲人信息
+        RBucket<RoomSpeakerInfo> speakerCache = getRoomSpeakerInfoCache(roomUid, liveRoomVo.getSpeakerId().toString());
+        if (speakerCache.isExists()) {
+            //如果用户进来时主讲人已经开启直播则修改学生观看时间
+            Integer state = speakerCache.get().getState();
+            if (Objects.nonNull(state) && state == 0 && liveRoomVo.getPushStatus() == 1) {
+                userInfo.setDynamicLookTime(DateTime.now().toDate());
+            }
+        }
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
+        //查询学生是否有进入过,没有则写学生考勤表的进入时间
+        this.setStudentAttendance(userId, schedule.getCourseGroupId(), schedule.getId());
+        //用户json信息
+        String userJsonStr = JSONObject.toJSONString(userInfo);
+        //写入人员缓存
+        roomTotalUser.fastPut(userId, userJsonStr);
+        //写入在线人员缓存
+        RMap<Long, String> onlineUserCache = this.getOnlineUserCache(roomUid);
+        onlineUserCache.fastPut(userId, userJsonStr);
+        log.info("joinRoom>>>> userInfo: {}", userJsonStr);
+        //向直播间发送当前在线人数消息
+        this.sendOnlineUserCount(roomUid, userId, onlineUserCache.size());
+        log.info("join sendOnlineUserCount>>>> param is null   roomUid: {}  fromUserId:{}  count:{}", roomUid, userId, onlineUserCache.size());
+        log.info("joinRoom>>>> userInfo: {}", userJsonStr);
+        //记录当前用户对应的房间uid
+        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userId.toString())).set(roomUid, 2L, TimeUnit.DAYS);
     }
 
+    /**
+     * 更新直播推流时间
+     *
+     * @param event TencentData.CallbackStreamStateEvent
+     */
+    @Override
+    public void updateLiveRoomPushStreamTime(TencentData.CallbackStreamStateEvent event) {
+
+        // 直播间ROOM_UID
+        String roomId = event.getStreamId().split("_")[0];
+
+        LiveRoom room = getByRoomUid(roomId);
+        if (Objects.isNull(room)) {
+            log.warn("updateLiveRoomPushStreamTime 无效的直播间ID, roomId:{}", roomId);
+            return;
+        }
+
+        // 更新直播时长
+        LiveRoom entity = new LiveRoom();
+        // 主键ID
+        entity.setId(room.getId());
+        // 直播累积播放时长
+        int liveTotalTime = Optional.ofNullable(room.getLiveTotalTime()).orElse(0) + Integer.parseInt(event.getPushDuration());
+        // 累积播放时长
+        entity.setLiveTotalTime(liveTotalTime);
+
+        updateById(entity);
+    }
+
+    @Override
+    public void closeLive(String roomUid, Integer userId, String sequence) {
+        //查询房间主播信息
+        RBucket<RoomSpeakerInfo> speakerCache = getRoomSpeakerInfoCache(roomUid, userId.toString());
+        if (!speakerCache.isExists()) {
+            return;
+        }
+        RoomSpeakerInfo roomSpeakerInfo = speakerCache.get();
+
+        //关闭直播
+        if (sequence.equals(roomSpeakerInfo.getSequence())) {
+            setPushStatus(roomSpeakerInfo.getRoomUid(), 0);
+        }
+
+
+        speakerCache.set(roomSpeakerInfo);
+
+        Date now = new Date();
+        roomSpeakerInfo.setEndLiveTime(now);
+        roomSpeakerInfo.setState(1);
+        //如果开播时间和本次操作结束播放时间小于1分钟则不计算观看时间
+        int liveMinutes = getLookMillisecond(roomSpeakerInfo.getStartLiveTime(), null);
+        if (liveMinutes > 1) {
+            //写入本次直播时长
+            roomSpeakerInfo.setTotalLiveTime(getLookMillisecond(roomSpeakerInfo.getStartLiveTime(), roomSpeakerInfo.getTotalLiveTime()));
+            //关闭直播后异步执行计算房间人员观看时长
+            CompletableFuture.runAsync(() -> this.asyncOpsLiveLookTime(roomSpeakerInfo.getRoomUid(), 2, now));
+        }
+        //计算完后将开始直播时间设置为空,待下次开启后再计算
+        roomSpeakerInfo.setStartLiveTime(null);
+        log.info("closeLive>>>> roomSpeakerInfo: {}", JSONObject.toJSONString(roomSpeakerInfo));
+    }
+
+
+    @Override
+    public void startLive(String roomUid, Integer userId, String sequence) {
+        //查询房间信息
+        RBucket<RoomSpeakerInfo> speakerCache = getRoomSpeakerInfoCache(roomUid, userId.toString());
+        if (!speakerCache.isExists()) {
+            return;
+        }
+        RoomSpeakerInfo roomSpeakerInfo = speakerCache.get();
+        //已是直播状态则直接返回
+        if (roomSpeakerInfo.getState() ==  0) {
+            return;
+        }
+
+
+        // 设置推流状态房间信息
+        setPushStatus(roomSpeakerInfo.getRoomUid(), 1);
+
+        //开始录制视频
+        try {
+            // 查询房间信息
+            LiveRoom imLiveBroadcastRoomVo = getByRoomUid(roomUid);
+            if (Objects.isNull(imLiveBroadcastRoomVo)) {
+                log.info("opsRoom>>>> startRecord error roomUid: {}", roomUid);
+                return;
+            }
+            LivePluginService pluginService = livePluginContext.getPluginService(imLiveBroadcastRoomVo.getServiceProvider());
+            RTCRoom rtcRoom = pluginService.rtcRoomInfo(roomSpeakerInfo.getRoomUid());
+            if (rtcRoom == null) {
+                log.info("startRecord>>>> rtcRoom is null");
+                return;
+            }
+
+            if (imLiveBroadcastRoomVo.getServiceProvider().equals(TencentCloudLivePlugin.PLUGIN_NAME) && roomSpeakerInfo.getVideoTemplate() == 0) {
+                // 直播开始时间
+                DateTime dateTime = new DateTime(imLiveBroadcastRoomVo.getLiveStartTime());
+
+                RTCRoom.RecordResp recordResp = pluginService.rtcRoomRecordStart(RTCRequest.RecordStart.builder()
+                        .startTime(dateTime.getMillis() )
+                        .endTime(dateTime.plusDays(1).getMillis() )
+                        .streamName(MessageFormat.format("{0}_{1}", imLiveBroadcastRoomVo.getRoomUid(), String.valueOf(imLiveBroadcastRoomVo.getSpeakerId())))
+                        .build());
+                roomSpeakerInfo.setVideoTemplate(1);
+
+                log.info("startLive 直播录录开启: recordResp={}", JSON.toJSONString(recordResp));
+            }
+
+
+        } catch (Exception e) {
+            log.error("startRecord error: {}", e.getMessage());
+        }
+        Date now = new Date();
+        //开始直播
+        roomSpeakerInfo.setState(0);
+        roomSpeakerInfo.setSequence(sequence);
+        roomSpeakerInfo.setStartLiveTime(now);
+        speakerCache.set(roomSpeakerInfo);
+        log.info("startLive>>>> roomSpeakerInfo: {}", JSONObject.toJSONString(roomSpeakerInfo));
+        //主播开启直播,查询所有在直播间的用户并写入观看时间
+        CompletableFuture.runAsync(() -> this.asyncOpsLiveLookTime(roomUid, 1, now));
+    }
+
+
+    /**
+     * 计算观看时长差-毫秒数
+     *
+     * @param startDT    开始时间
+     * @param nowMinutes 现在观看时长
+     */
+    private int getLookMillisecond(Date startDT, Integer nowMinutes) {
+        return getLookMillisecond(startDT, new Date(), nowMinutes);
+    }
+    /**
+     * 计算观看时长差-毫秒数
+     *
+     * @param startDT    开始时间
+     * @param endDT      结束时间
+     * @param nowMinutes 现在观看时长
+     */
+    private int getLookMillisecond(Date startDT, Date endDT, Integer nowMinutes) {
+        if (Objects.isNull(nowMinutes)) {
+            nowMinutes = 0;
+        }
+        if (Objects.isNull(startDT)) {
+            return nowMinutes;
+        }
+        if (startDT.getTime() > endDT.getTime()) {
+            return nowMinutes;
+        }
+        //课程结束时间-课程开始时间
+        long durationTime = endDT.getTime() - startDT.getTime();
+        //相差多少分钟
+        int minutesBetween = new Long(durationTime).intValue();
+        minutesBetween += nowMinutes;
+        return Math.max(minutesBetween, 0);
+    }
+
+
+    /**
+     * 打开/关闭直播后-异步计算房间人员观看时长
+     *
+     * @param roomUid 房间uid
+     * @param type    type 1:开始直播-开始录像     2:关闭直播关闭录像
+     */
+    private void asyncOpsLiveLookTime(String roomUid, Integer type, Date now) {
+        //加锁-避免快速点击开启直播和关闭直后异步执行后直播数据错误
+        boolean b = DistributedLock.of(redissonClient).runIfLockCanGet(LIVE_LOOK_LOCK.replace(ROOM_UID, roomUid), () -> {
+            //查询所有在直播间的用户并计算观看时长
+            RMap<Long, String> roomTotalUser = getTotalUserCache(roomUid);
+            if (!roomTotalUser.isExists()) {
+                return;
+            }
+            //查询在线人员列表
+            RMap<Long, String> onlineUserInfo = getOnlineUserCache(roomUid);
+            boolean onlineUserInfoExists = onlineUserInfo.isExists();
+            roomTotalUser.forEach((id, userInfoStr) -> {
+                ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo userInfo = JSONObject.toJavaObject(JSONObject.parseObject(userInfoStr), ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo.class);
+                //获取当前用户是否在房间状态 true在 false不在
+                if (onlineUserInfoExists && onlineUserInfo.containsKey(id)) {
+                    if (type.equals(1)) {
+                        //开启直播后对当前在房间的用户写入观看时间
+                        userInfo.setDynamicLookTime(now);
+                    } else if (type.equals(2)) {
+                        userInfo.setTotalViewTime(getLookMinutes(userInfo.getDynamicLookTime(), now, userInfo.getTotalViewTime()));
+                        userInfo.setDynamicLookTime(null);
+                    } else {
+                        return;
+                    }
+                    roomTotalUser.fastPut(id, JSONObject.toJSONString(userInfo));
+                }
+            });
+        }, 2, TimeUnit.MINUTES);
+        if (!b) {
+            try {
+                Thread.sleep(200L);
+            } catch (InterruptedException e) {
+                log.error("asyncOpsLiveLookTime error: {}", e.getMessage());
+            }
+            this.asyncOpsLiveLookTime(roomUid, type, now);
+        }
+    }
+
+
+    /**
+     * 计算观看时长差-分钟数不满一分钟为0
+     *
+     * @param startDT    开始时间
+     * @param endDT      结束时间
+     * @param nowMinutes 现在观看时长
+     */
+    private int getLookMinutes(Date startDT, Date endDT, Integer nowMinutes) {
+        if (Objects.isNull(nowMinutes)) {
+            nowMinutes = 0;
+        }
+        if (Objects.isNull(startDT)) {
+            return nowMinutes;
+        }
+        if (startDT.getTime() > endDT.getTime()) {
+            return nowMinutes;
+        }
+        //课程结束时间-课程开始时间
+        long durationTime = endDT.getTime() - startDT.getTime();
+        //相差多少分钟
+        int minutesBetween = new Long(durationTime / 1000 / 60).intValue();
+        minutesBetween += nowMinutes;
+        return Math.max(minutesBetween, 0);
+    }
+
+    /**
+     * 设置推流状态房间信息
+     * @param roomUid 房间uid
+     * @param status 0:关闭直播 1:开启直播
+     */
+    private void setPushStatus(String roomUid, Integer status) {
+        if (status == null) {
+            return;
+        }
+        LiveRoom liveRoom = getByRoomUid(roomUid);
+        if (liveRoom == null) {
+            log.warn("setPushStatus liveRoom is null");
+            return;
+        }
+
+        LiveRoom imLiveBroadcastRoom = new LiveRoom();
+        imLiveBroadcastRoom.setId(liveRoom.getId());
+        imLiveBroadcastRoom.setPushStatus(status);
+        this.updateById(imLiveBroadcastRoom);
+        setGroupDefinedData(liveRoom,EGroupDefinedDataType.LIVE_STATUS,status == 1?
+                EOnOffStatus.ON.getCode():EOnOffStatus.OFF.getCode());
+
+    }
+
+    private ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo getLiveRoomUserInfo(Long userId, LiveRoomWrapper.LiveRoomVo imLiveBroadcastRoomVo, Boolean microphoneFlag) {
+
+        ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo userInfo = getUserInfo(userId);
+        userInfo.setFirstJoinTime(DateTime.now().toDate());
+        userInfo.setTotalViewTime(0);
+
+        // 记录直播间用户信息
+        ImLiveBroadcastRoomMember roomMember = new ImLiveBroadcastRoomMember();
+        roomMember.setRoomUid(imLiveBroadcastRoomVo.getRoomUid());
+        roomMember.setUserId(userInfo.getUserId());
+        roomMember.setJoinTime(DateTime.now().toDate());
+        roomMember.setTotalTime(0);
+        roomMember.setOnlineStatus(1);
+        roomMember.setBanStatus(0);
+        roomMember.setLiveRoomStatus(1);
+        roomMember.setMicrophoneFlag(microphoneFlag);
+
+        ImLiveBroadcastRoomMember liveRoomMember = imLiveBroadcastRoomMemberService.lambdaQuery()
+                .eq(ImLiveBroadcastRoomMember::getRoomUid, imLiveBroadcastRoomVo.getRoomUid())
+                .eq(ImLiveBroadcastRoomMember::getUserId, userInfo.getUserId())
+                .list().stream().findFirst().orElse(null);
+        if (Objects.nonNull(liveRoomMember)) {
+            return userInfo;
+        }
+
+
+        // 保存直播间用户信息
+        imLiveBroadcastRoomMemberService.save(roomMember);
+
+        return userInfo;
+    }
+
+    private ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo getUserInfo(Long userId) {
+        ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo userInfo = new ImLiveBroadcastRoomMemberWrapper.RoomUserInfoVo();
+        userInfo.setUserId(userId);
+        SysUser sysUser = sysUserFeignService.queryUserById(userId);
+        String name = userId.toString();
+        if (Objects.nonNull(sysUser)) {
+            name = sysUser.getPhone();
+            if (sysUser.getUserType().contains("STUDENT")) {
+                name = sysUser.getUsername();
+            } else {
+                if (StringUtils.isNotBlank(sysUser.getRealName())) {
+                    name = sysUser.getRealName();
+                }
+            }
+        }
+        userInfo.setUserName(name);
+        return userInfo;
+    }
+
+
+    /**
+     * 主讲人信息
+     */
+    @Data
+    public static class RoomSpeakerInfo implements Serializable {
+        //主讲人id
+        private Long speakerId;
+        //主讲人名称
+        private String speakerName;
+        //直播状态 0 直播中 1关闭直播
+        private Integer state;
+        //房间uid
+        private String roomUid;
+        // 直播流sequence
+        private String sequence;
+
+        //房间创建时间
+        private Date createRoomTime;
+        //进入房间时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date joinRoomTime;
+        //开始直播时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date startLiveTime;
+        //开始直播时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date endLiveTime;
+        //退出房间时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date exitRoomTime;
+        //总直播时间
+        private Integer totalLiveTime = 0;
+        //是否录像 0允许 1不允许
+        private Integer whetherVideo;
+        //播出端-  pc网页端 移动端mobile
+        private String os = "pc";
+
+        // 腾讯视频模板 0: 没有 1:有
+        private Integer videoTemplate;
+
+    }
 }
 

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherAuthEntryRecordServiceImpl.java

@@ -172,7 +172,7 @@ public class TeacherAuthEntryRecordServiceImpl extends ServiceImpl<TeacherAuthEn
                     ImGroup imGroup = new ImGroup();
                     imGroup.setName(MessageFormat.format("{0}的粉丝群", teacher.getUsername()));
                     imGroup.setIntroduce("");
-                    imGroup.setAutoPassFlag(true);
+                    imGroup.setAutoPassFlag(false);
                     imGroup.setCreateBy(teacher.getUserId());
 
                     imGroupService.create(imGroup);

+ 5 - 13
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherServiceImpl.java

@@ -10,7 +10,7 @@ import javax.annotation.Resource;
 
 import com.google.common.collect.Lists;
 import com.yonge.cooleshow.biz.dal.queryInfo.TeacherQueryInfo;
-import com.yonge.cooleshow.biz.dal.service.SubjectService;
+import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper;
 import org.redisson.api.RMap;
@@ -49,17 +49,6 @@ import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.TeacherTagEnum;
 import com.yonge.cooleshow.biz.dal.enums.TeacherTypeEnum;
-import com.yonge.cooleshow.biz.dal.service.StudentService;
-import com.yonge.cooleshow.biz.dal.service.StudentStarService;
-import com.yonge.cooleshow.biz.dal.service.SysConfigService;
-import com.yonge.cooleshow.biz.dal.service.SysMessageService;
-import com.yonge.cooleshow.biz.dal.service.TeacherAuthEntryRecordService;
-import com.yonge.cooleshow.biz.dal.service.TeacherAuthMusicianRecordService;
-import com.yonge.cooleshow.biz.dal.service.TeacherService;
-import com.yonge.cooleshow.biz.dal.service.TeacherStyleVideoService;
-import com.yonge.cooleshow.biz.dal.service.TeacherTotalService;
-import com.yonge.cooleshow.biz.dal.service.UserAccountService;
-import com.yonge.cooleshow.biz.dal.service.UserFirstTimeService;
 import com.yonge.cooleshow.biz.dal.vo.HotTeacherVo;
 import com.yonge.cooleshow.biz.dal.vo.MyFens;
 import com.yonge.cooleshow.biz.dal.vo.TeacherAuthEntryRecordVo;
@@ -120,6 +109,9 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
     @Autowired
     private SysMessageService sysMessageService;
 
+    @Autowired
+    private ImGroupService imGroupService;
+
     @Override
     public TeacherVo detail(Long userId) {
         TeacherVo detail = baseMapper.detail(userId);
@@ -192,7 +184,7 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
         BeanUtils.copyProperties(teacher, teacherHomeVo);
 
         // 设置IM用户ID
-        teacherHomeVo.setImUserId(String.valueOf(teacher.getUserId()));
+        teacherHomeVo.setImUserId(imGroupService.getImUserId(userId.toString(),ClientEnum.TEACHER.getCode()));
 
         teacherHomeVo.setDegreeDate(teacher.getDegreeDate());
         teacherHomeVo.setTeacherDate(teacher.getTeacherDate());

+ 183 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/ImHistoryMessageWrapper.java

@@ -0,0 +1,183 @@
+package com.yonge.cooleshow.biz.dal.wrapper;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+public class ImHistoryMessageWrapper {
+
+
+    @Data
+    @ApiModel("ImHistoryMessage-iM语音消息")
+    public static class TimSoundElem {
+
+        @ApiModelProperty("语音下载地址,可通过该 URL 地址直接下载相应语音。")
+        private String Url;
+
+        @ApiModelProperty("语音的唯一标识,客户端用于索引语音的键值")
+        private String UUID;
+
+        @ApiModelProperty("语音数据大小,单位:字节。")
+        private Integer Size;
+
+        @ApiModelProperty("语音时长,单位:秒。")
+        private Integer Second;
+
+        @ApiModelProperty("语音下载方式标记。目前 Download_Flag 取值只能为2,表示可通过Url字段值的 URL 地址直接下载语音。")
+        private Integer Download_Flag;
+
+    }
+
+    @Data
+    @ApiModel("ImHistoryMessage-iM文件")
+    public static class TimFileElem {
+
+        @ApiModelProperty("文件下载地址,可通过该 URL 地址直接下载相应文件。")
+        private String Url;
+
+        @ApiModelProperty("文件的唯一标识,客户端用于索引文件的键值。")
+        private String UUID;
+
+        @ApiModelProperty("文件数据大小,单位:字节。")
+        private Integer FileSize;
+
+        @ApiModelProperty("文件名称。")
+        private String FileName;
+
+        @ApiModelProperty("文件下载方式标记。目前 Download_Flag 取值只能为2,表示可通过Url字段值的 URL 地址直接下载文件。")
+        private Integer Download_Flag;
+
+    }
+
+    @Data
+    @ApiModel("ImHistoryMessage-iM视频")
+    public static class TimVideoFileElem {
+
+        @ApiModelProperty("视频下载地址。可通过该 URL 地址直接下载相应视频。")
+        private String VideoUrl;
+
+        @ApiModelProperty("文视频的唯一标识,客户端用于索引视频的键值。")
+        private String VideoUUID;
+
+        @ApiModelProperty("视频数据大小,单位:字节。")
+        private Integer VideoSize;
+
+        @ApiModelProperty("视频时长,单位:秒。Web 端不支持获取视频时长,值为0")
+        private Integer VideoSecond;
+
+        @ApiModelProperty("视频格式,例如 mp4")
+        private String VideoFormat;
+
+        @ApiModelProperty("视频下载方式标记。目前 VideoDownloadFlag 取值只能为2,表示可通过 VideoUrl 字段值的 URL 地址直接下载视频。")
+        private Integer VideoDownloadFlag;
+
+        @ApiModelProperty("视频缩略图下载地址。可通过该 URL 地址直接下载相应视频缩略图。")
+        private String ThumbUrl;
+
+        @ApiModelProperty("视频缩略图的唯一标识,客户端用于索引视频缩略图的键值")
+        private String ThumbUUID;
+
+        @ApiModelProperty("缩略图大小,单位:字节。")
+        private Integer ThumbSize;
+
+        @ApiModelProperty("缩略图宽度,单位为像素。")
+        private Integer ThumbWidth;
+
+        @ApiModelProperty("缩略图高度,单位为像素。")
+        private Integer ThumbHeight;
+
+        @ApiModelProperty("缩略图格式,例如 JPG、BMP 等。")
+        private String ThumbFormat;
+
+        @ApiModelProperty("视频缩略图下载方式标记。目前 ThumbDownloadFlag 取值只能为2,表示可通过 ThumbUrl 字段值的 URL 地址直接下载视频缩略图。")
+        private Integer ThumbDownloadFlag;
+    }
+
+
+    @Data
+    @ApiModel("ImHistoryMessage-iM位置")
+    public static class TimLocationElem {
+
+        @ApiModelProperty("地理位置描述信息。")
+        private String Desc;
+
+        @ApiModelProperty("纬度。")
+        private Integer Latitude;
+
+        @ApiModelProperty("经度。")
+        private Integer Longitude;
+    }
+
+    @Data
+    @ApiModel("ImHistoryMessage-iM合并转发")
+    public static class TimRelayElem {
+
+        @ApiModelProperty("合并转发消息的标题。")
+        private String Title;
+
+        @ApiModelProperty("被转发的消息条数。")
+        private Integer MsgNum;
+
+        @ApiModelProperty("兼容文本。当不支持合并转发消息的老版本 SDK 收到此类消息时,IM 后台会将该消息转化成兼容文本再下发。")
+        private String CompatibleText;
+
+        @ApiModelProperty("合并消息的摘要列表,是一个 String 数组")
+        private String[] AbstractList;
+
+        @ApiModelProperty("消息列表。当被转发的消息长度之和小于等于12K时才会有此字段,此时不会有 JsonMsgKey 字段。")
+        private List<TimRelayElemList> MsgList;
+
+        @ApiModelProperty("合并转发的消息列表 Key。当被转发的消息长度之和大于12K时才会有此字段,此时不会有 MsgList 字段。")
+        private String JsonMsgKey;
+
+
+    }
+
+
+    @Data
+    @ApiModel("ImHistoryMessage-iM合并转发消息列表")
+    public static class TimRelayElemList {
+
+        @ApiModelProperty("消息发送方 UserID。被转发的消息为单聊或群聊时都有此字段。")
+        private String From_Account;
+
+        @ApiModelProperty("消息接收方 UserID。被转发的消息为单聊才有此字段。")
+        private String To_Account;
+
+        @ApiModelProperty("群 ID。被转发的消息为群聊才有此字段。")
+        private String GroupId;
+
+        @ApiModelProperty("消息序列号(32位无符号整数)。")
+        private Integer MsgSeq;
+
+        @ApiModelProperty("消息随机数(32位无符号整数)。")
+        private Integer MsgRandom;
+
+        @ApiModelProperty("消息的秒级时间戳。")
+        private Integer MsgTimeStamp;
+
+        @ApiModelProperty("消息内容")
+        private List<TimRelayElemMessage> MsgBody;
+
+        @ApiModelProperty("消息自定义数据(云端保存,会发送到对端,程序卸载重装后还能拉取到)。")
+        private String CloudCustomData;
+
+    }
+
+    @Data
+    @ApiModel("ImHistoryMessage-iM合并转发消息内容体")
+    public static class TimRelayElemMessage {
+
+        @ApiModelProperty("消息类型")
+        private String msgType;
+
+        @ApiModelProperty("消息内容")
+        private Object msgContent;
+
+    }
+
+
+
+}

+ 139 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/liveroom/ImLiveBroadcastRoomMemberWrapper.java

@@ -0,0 +1,139 @@
+package com.yonge.cooleshow.biz.dal.wrapper.liveroom;
+
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Optional;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 直播间人员关系表
+ * 2023-08-15 15:50:17
+ */
+@ApiModel(value = "ImLiveBroadcastRoomMemberWrapper对象", description = "直播间人员关系表查询对象")
+public class ImLiveBroadcastRoomMemberWrapper {
+
+    @Data
+	@Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(" ImLiveBroadcastRoomMemberQuery-直播间人员关系表")
+    public static class ImLiveBroadcastRoomMemberQuery implements QueryInfo {
+    
+    	@ApiModelProperty("当前页")
+        private Integer page;
+        
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+        
+        @ApiModelProperty("关键字匹配")
+		private String keyword;
+        
+        public String getKeyword() {
+            return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
+        }
+        
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static ImLiveBroadcastRoomMemberQuery from(String json) {
+            return JSON.parseObject(json, ImLiveBroadcastRoomMemberQuery.class);
+        }
+    }  
+
+    @Data
+	@ApiModel(" ImLiveBroadcastRoomMember-直播间人员关系表")
+    public static class ImLiveBroadcastRoomMember {
+
+
+        @ApiModelProperty(value = "房间编号")
+        private String roomUid;
+
+        @ApiModelProperty(value = "房间标题")
+        private String roomTitle;
+
+        @ApiModelProperty(value = "主讲人")
+        private String speakerName;
+
+        @ApiModelProperty(value = "学生编号")
+        private Integer studentId;
+
+        @ApiModelProperty(value = "学生姓名")
+        private String studentName;
+
+        @ApiModelProperty(value = "学生头像")
+        private String avatar;
+
+        @ApiModelProperty(value = "手机号")
+        private String phone;
+
+        @ApiModelProperty(value = "声部名称")
+        private String subName;
+
+        @ApiModelProperty(value = "进入房间时间")
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+        private Date joinTime;
+
+        @ApiModelProperty(value = "累计观看时长")
+        private Integer totalViewTime;
+
+        @ApiModelProperty(value = "连麦状态 0:未申请1:申请连麦中2:连麦中")
+        private Integer whetherMicStatus;
+
+        @ApiModelProperty(value = "是否黑名单")
+        private boolean blackFlag;
+
+        @ApiModelProperty("游客凭据")
+        private String fingerprint;
+
+        @ApiModelProperty(value = "是否能上麦:false否;true是")
+        private boolean microphoneFlag;
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static ImLiveBroadcastRoomMember from(String json) {
+            return JSON.parseObject(json, ImLiveBroadcastRoomMember.class);
+        }
+	}
+
+    @Data
+    public static class RoomMemberNumDto {
+
+        private String roomUid;
+
+        private Integer onlineNum;
+
+        private Integer totalNum;
+    }
+
+
+    @Data
+    public static class RoomUserInfoVo extends LiveRoomWrapper.BaseRoomUserVo implements Serializable {
+
+        //累计观看时长
+        private Integer totalViewTime;
+
+        //首次进入时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+        private Date firstJoinTime;
+
+        //动态观看直播时间  主播每次开启直播后更新
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+        private Date dynamicLookTime;
+
+        @ApiModelProperty("用户头象")
+        private String avatar;
+    }
+}

+ 63 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/liveroom/LiveBroadcastRoomDataWrapper.java

@@ -0,0 +1,63 @@
+package com.yonge.cooleshow.biz.dal.wrapper.liveroom;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Optional;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 直播房间数据表
+ * 2023-08-15 15:50:17
+ */
+@ApiModel(value = "LiveBroadcastRoomDataWrapper对象", description = "直播房间数据表查询对象")
+public class LiveBroadcastRoomDataWrapper {
+
+    @Data
+	@Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(" LiveBroadcastRoomDataQuery-直播房间数据表")
+    public static class LiveBroadcastRoomDataQuery implements QueryInfo {
+    
+    	@ApiModelProperty("当前页")
+        private Integer page;
+        
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+        
+        @ApiModelProperty("关键字匹配")
+		private String keyword;
+        
+        public String getKeyword() {
+            return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
+        }
+        
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static LiveBroadcastRoomDataQuery from(String json) {
+            return JSON.parseObject(json, LiveBroadcastRoomDataQuery.class);
+        }
+    }  
+
+	@ApiModel(" LiveBroadcastRoomData-直播房间数据表")
+    public static class LiveBroadcastRoomData {
+        
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static LiveBroadcastRoomData from(String json) {
+            return JSON.parseObject(json, LiveBroadcastRoomData.class);
+        }
+	}
+
+}

+ 161 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/liveroom/LiveRoomWrapper.java

@@ -1,6 +1,13 @@
 package com.yonge.cooleshow.biz.dal.wrapper.liveroom;
 
 import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.microsvc.toolkit.middleware.live.message.LiveRoomConfig;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.common.enums.live.ELiveViewMode;
+import com.yonge.cooleshow.common.enums.live.EUseScene;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -74,4 +81,158 @@ public class LiveRoomWrapper implements Serializable {
         private String avatar;
         private String realName;
     }
+
+
+    /**
+     * 直播间老师信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class LiveRoomVo implements Serializable {
+        @ApiModelProperty(value = "主键")
+        private Integer id;
+
+        @ApiModelProperty(value = "房间编号")
+        private String roomUid;
+
+        @ApiModelProperty(value = "房间标题/最多12个字")
+        private String roomTitle;
+
+        @ApiModelProperty(value = "直播内容/最多200个字")
+        private String liveRemark;
+
+        @ApiModelProperty(value = "主讲人id/老师id")
+        private Long speakerId;
+
+        @ApiModelProperty("用户类型 TEACHER 老师")
+        private ClientEnum clientType;
+
+        @ApiModelProperty(value = "主讲人名称")
+        private String speakerName;
+
+        @ApiModelProperty(value = "主讲人头像")
+        private String speakerPic;
+
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+        @ApiModelProperty(value = "直播开始时间")
+        private Date liveStartTime;
+
+        @ApiModelProperty(value = "直播状态 0未开始 1开始 2结束")
+        private Integer liveState;
+
+        @ApiModelProperty(value = "直播时长")
+        private Integer liveTotalTime;
+
+        @ApiModelProperty(value = "房间状态 0正常 1已删除")
+        private Integer roomState;
+
+        @ApiModelProperty(value = "创建人")
+        private String createdByName;
+
+        @ApiModelProperty(value = "预热模版")
+        private String preTemplate;
+
+        @ApiModelProperty(value = "房间配置json格式-是否允许点赞-是否允许聊天互动-是否允许保存直播回放")
+        private String roomConfig;
+
+        @ApiModelProperty(value = "imToken")
+        private String imToken;
+
+        @ApiModelProperty(value = "播出端-  pc网页端 移动端mobile")
+        private String os;
+
+        @ApiModelProperty(value = "是否在首页推广 0否 1是 - 每个机构只能有一个直播间在首页推广")
+        private Integer popularize;
+
+        @ApiModelProperty(value = "是否预约过该直播间 0否 1是")
+        private Integer reserve;
+
+        @ApiModelProperty(value = "当前登录人是否是黑名单用户 0否 1是")
+        private Integer blacklistFlag = 0;
+
+        @ApiModelProperty(value = "直播间可见类型-全部可见 ALL / 部分可见 PRIVATE")
+        private String popularizeType;
+
+        @ApiModelProperty(value = "点赞数")
+        private Integer likeNum;
+        @ApiModelProperty(value = "当前观看人数")
+        private Integer lookNum;
+        @ApiModelProperty(value = "累计观看人数")
+        private Integer totalLookNum;
+
+        @ApiModelProperty("预约人数")
+        private Integer roomReservationNum;
+
+        @ApiModelProperty("直播间用户签名")
+        private String userSig;
+
+
+        @ApiModelProperty(value = "服务提供方 ")
+        private String serviceProvider;
+
+        @ApiModelProperty("直播配置")
+        private LiveRoomConfig liveRoomConfig;
+
+        @ApiModelProperty(value = "主播状态: 0离开;1在播")
+        private Integer speakerStatus;
+
+        @ApiModelProperty(value = "推流状态: 0 暂停; 1在播")
+        private Integer pushStatus;
+
+        @ApiModelProperty(value = "禁言状态: 0 取消;1禁言")
+        private Integer banStatus;
+
+        @ApiModelProperty("视频数量")
+        private Integer videoNum;
+
+        @ApiModelProperty("直播观看模式")
+        private ELiveViewMode viewMode;
+
+        @ApiModelProperty("购物车标题")
+        private String shoppingTitle;
+
+        @ApiModelProperty("使用场景 正常:NORMAL 音乐:MUSIC")
+        private EUseScene useScene;
+
+        @ApiModelProperty("课程结束后多少分钟关闭网络教室")
+        private String autoCloseNetworkRoomTime;
+
+        @ApiModelProperty("剩余时长")
+        private int surplusTime;
+
+        @ApiModelProperty("声部ID")
+        private Integer subjectId;
+
+
+        @ApiModelProperty("直播类型 NORMAL:普通直播  LIVE:直播课直播")
+        private String  groupType;
+
+
+        @TableField("video_record_")
+        @ApiModelProperty(value = "录制记录")
+        private String videoRecord;
+
+
+        @TableField("live_end_time_")
+        @ApiModelProperty(value = "直播结束时间")
+        private Date liveEndTime;
+
+        @TableField("type_")
+        @ApiModelProperty(value = "房间类型 live直播课  temp临时直播间")
+        private String type;
+
+
+        @ApiModelProperty(value = "封面图片")
+        private String coverPic;
+
+    }
+
+    @Data
+    public static class BaseRoomUserVo implements Serializable {
+
+        private Long userId;
+
+        private String userName;
+    }
 }

+ 9 - 2
cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMapper.xml

@@ -64,6 +64,12 @@
         SET member_num_ = igm.num_
         WHERE ig.id_ = igm.group_id_
     </update>
+    <update id="updateStatus">
+        update im_history_message set status_ = 1
+        where msgUID in <foreach collection="info" open="(" close=")" separator="," item="info">
+        #{info.msgUID}
+    </foreach>
+    </update>
     <select id="findAll" resultMap="BaseResultMap">
         SELECT ig.* FROM im_group ig
         LEFT JOIN im_group_member igm ON igm.group_id_ = ig.id_
@@ -97,7 +103,7 @@
                CASE WHEN igma.id_ IS NULL THEN 0 ELSE 1 END hasWaitAuditFlag
         FROM im_group ig
                  LEFT JOIN im_group_member igm ON (ig.id_ = igm.group_id_ AND igm.user_id_ = #{imGroupDto.userId} <if test="imGroupDto.roleType != null"> AND igm.role_type_ = #{imGroupDto.roleType} </if>)
-                 LEFT JOIN im_group_member_audit igma ON igma.group_id_ = ig.id_ AND igma.audit_status_ = 'AUDITING' AND igma.user_id_ = igm.user_id_
+                 LEFT JOIN im_group_member_audit igma ON igma.group_id_ = ig.id_ AND igma.audit_status_ = 'AUDITING' AND igma.user_id_ = #{imGroupDto.userId}
         WHERE ig.create_by_ = #{imGroupDto.createUserId}
         <if test="imGroupDto.type != null and imGroupDto.type != ''">
             AND ig.type_ = #{imGroupDto.type}
@@ -128,10 +134,11 @@
                appId_           AS appId
 
         from im_history_message
+        where status_ = 0
         limit #{result},#{size}
 
     </select>
     <select id="queryCount" resultType="java.lang.Integer">
-        SELECT COUNT(*) FROM im_history_message
+        SELECT COUNT(*) FROM im_history_message where status_ = 0
     </select>
 </mapper>

+ 1 - 1
cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMemberMapper.xml

@@ -51,7 +51,7 @@
         LIMIT 1
     </select>
     <select id="queryGroupMember" resultType="com.yonge.cooleshow.biz.dal.entity.ImGroupMember">
-        SELECT user_id_,group_id_ FROM im_group_member WHERE group_id_ = #{groupId}
+        SELECT user_id_,group_id_,role_type_ FROM im_group_member WHERE group_id_ = #{groupId}
     </select>
     <resultMap id="ImGroupMember" type="com.yonge.cooleshow.biz.dal.entity.ImGroupMember">
         <result property="groupId" column="group_id_"/>

+ 101 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomMemberMapper.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE  mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.yonge.cooleshow.biz.dal.mapper.ImLiveBroadcastRoomMemberMapper">
+
+	 
+    
+    <!-- 表字段 -->
+    <sql id="baseColumns">
+         t.id_ AS id
+        , t.room_uid_ AS roomUid
+        , t.user_id_ AS userId
+        , t.join_time_ AS joinTime
+        , t.total_time_ AS totalTime
+        , t.online_status_ AS onlineStatus
+        , t.ban_status_ AS banStatus
+        , t.live_room_status_ AS liveRoomStatus
+        , t.whether_mic_status_ AS whetherMicStatus
+        , t.microphone_flag_ AS microphoneFlag
+        , t.fingerprint_ AS fingerprint
+        , t.visitor_name_ AS visitorName
+        , t.create_time_ AS createTime
+        </sql> 
+    
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.entity.ImLiveBroadcastRoomMember">
+		SELECT         
+        	<include refid="baseColumns" />
+		FROM im_live_broadcast_room_member t
+	</select>
+
+
+    <select id="queryMemberNum"
+            resultType="com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper$RoomMemberNumDto">
+        select room_uid_ as roomUid,
+        count(1) as totalNum ,
+        sum(if(online_status_ = 1 and live_room_status_ = 1,1,0)) as onlineNum
+        from im_live_broadcast_room_member
+        where room_uid_ = #{roomUid}
+        group by room_uid_
+    </select>
+
+
+    <update id="userWhetherMic">
+        update im_live_broadcast_room_member
+        set whether_mic_status_ = #{whetherMicStatus}
+        where room_uid_ = #{roomUid} and user_id_ = #{userId}
+    </update>
+
+
+    <!--学生声部信息-->
+    <select id="selectStudentSubject"
+            resultType="com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper$ImLiveBroadcastRoomMember">
+        SELECT
+        st.user_id_ as studentId, b.name_ as subName
+        FROM
+        student st left join subject as b on st.subject_id_ = b.id_
+        WHERE st.user_id_ IN (<foreach collection="studentIds" separator="," item="item">#{item}</foreach>)
+    </select>
+
+    <select id="queryMemberPage"
+            resultType="com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper$ImLiveBroadcastRoomMember">
+
+        select
+        a.room_uid_ as roomUid,
+        a.user_id_ as studentId,
+        a.whether_mic_status_ as whetherMicStatus,
+        a.fingerprint_ as fingerprint,
+        IFNULL(su.username_, a.visitor_name_) as studentName,
+        su.avatar_ as avatar,
+        su.phone_ as phone,
+        a.join_time_ as joinTime,
+        a.total_time_ as totalViewTime,
+        a.microphone_flag_ as microphoneFlag
+        from
+        im_live_broadcast_room_member as a
+        left join sys_user as su on su.id_ = a.user_id_
+        where a.room_uid_ = #{param.roomUid}
+        <if test="param.search != null ">
+            AND (
+            a.user_id_ LIKE CONCAT('%', #{param.search},'%')
+            OR a.visitor_name_ LIKE CONCAT('%', #{param.search},'%')
+            OR su.username_ LIKE CONCAT('%', #{param.search},'%')
+            )
+        </if>
+        <if test="param.onlineStatus != null">
+            AND (a.online_status_ = #{param.onlineStatus} and a.live_room_status_ = 1)
+        </if>
+        <if test="param.whetherMicStatus != null">
+            and a.whether_mic_status_ = #{param.whetherMicStatus}
+        </if>
+        <if test="param.visitorQuery != null">
+            <if test="param.visitorQuery == 1">
+                AND a.fingerprint_ IS NOT NULL
+            </if>
+            <if test="param.visitorQuery == 0">
+                AND a.fingerprint_ IS NULL
+            </if>
+        </if>
+        group by a.user_id_
+        ORDER BY a.join_time_
+    </select>
+</mapper>

+ 24 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/LiveBroadcastRoomDataMapper.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE  mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.yonge.cooleshow.biz.dal.mapper.LiveBroadcastRoomDataMapper">
+
+	 
+    
+    <!-- 表字段 -->
+    <sql id="baseColumns">
+         t.id_ AS id
+        , t.room_uid_ AS roomUid
+        , t.live_time_ AS liveTime
+        , t.like_num_ AS likeNum
+        , t.look_user_num_ AS lookUserNum
+        , t.total_user_num_ AS totalUserNum
+        , t.updated_time_ AS updatedTime
+        </sql> 
+    
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.entity.LiveBroadcastRoomData">
+		SELECT         
+        	<include refid="baseColumns" />
+		FROM live_broadcast_room_data t
+	</select>
+    
+</mapper>

+ 5 - 1
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/CourseHomeworkController.java

@@ -60,7 +60,11 @@ public class CourseHomeworkController extends BaseController {
         SysUser sysUser = sysUserFeignService.queryUserInfo();
         HttpResponseResult info = checkCourseSchedule(courseId,sysUser);
         if (info != null) return info;
-        return succeed(courseHomeworkService.getCourseHomeworkDetailByCourseId(courseId, sysUser.getId()));
+        CourseHomeworkDetailVo detailVo = courseHomeworkService.getCourseHomeworkDetailByCourseId(courseId, sysUser.getId());
+        if (detailVo != null) {
+            detailVo.setImUserId(imGroupService.getImUserId(String.valueOf(detailVo.getTeacherId()),ClientEnum.TEACHER.name()));
+        }
+        return succeed(detailVo);
     }
 
     private HttpResponseResult checkCourseSchedule(Long courseId,SysUser sysUser) {

+ 71 - 0
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/ImLiveBroadcastRoomController.java

@@ -0,0 +1,71 @@
+package com.yonge.cooleshow.student.controller;
+
+import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
+import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
+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 io.swagger.annotations.ApiParam;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表控制层
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:04
+ */
+@Api(tags = "直播房间管理表")
+@RestController
+@RequestMapping("/imLiveBroadcastRoom")
+public class ImLiveBroadcastRoomController extends BaseController {
+    /**
+     * 服务对象
+     */
+    @Resource
+    private LiveRoomService liveRoomService;
+
+    @ApiOperation("学生端-查询房间信息")
+    @GetMapping("/queryRoomInfo")
+    public HttpResponseResult<LiveRoomWrapper.LiveRoomVo> queryRoomInfo(@ApiParam(value = "房间uid", required = true) String roomUid) {
+        return succeed(liveRoomService.queryRoomInfo(roomUid));
+    }
+
+
+    @ApiOperation("查询房间信息并校验房间是否合规")
+    @GetMapping("/queryRoom")
+    public HttpResponseResult<LiveRoomWrapper.LiveRoomVo> queryRoomAndCheck(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                                                       @ApiParam(value = "用户id", required = true) Long userId) {
+        return succeed(liveRoomService.queryRoomAndCheck(roomUid, userId, 1));
+    }
+
+    @PostMapping("/quitRoom")
+    public HttpResponseResult<Object> quitRoom(@RequestBody List<ImUserStateSync> userState) {
+        liveRoomService.opsRoom(userState);
+        return succeed();
+    }
+
+    @ApiOperation("学生-进入房间")
+    @GetMapping("/joinRoom")
+    public HttpResponseResult<Object> joinRoom(String roomUid, Long userId,Boolean microphoneFlag) {
+        if (microphoneFlag == null) {
+            microphoneFlag = true;
+        }
+        liveRoomService.joinRoom(roomUid, userId,microphoneFlag);
+        return succeed();
+    }
+
+    @ApiOperation("设置连麦状态")
+    @PutMapping("/userWhetherMic")
+    public HttpResponseResult<Object> userWhetherMic(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                                     @ApiParam(value = "用户id", required = true) Long userId,
+                                                     @ApiParam(value = "连麦状态 0:未申请1:申请连麦中2:连麦中", required = true) Integer whetherMicStatus) {
+        liveRoomService.userWhetherMic(roomUid,userId,whetherMicStatus);
+        return succeed();
+    }
+}
+

+ 4 - 8
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/StudentLiveRoomController.java

@@ -44,12 +44,7 @@ public class StudentLiveRoomController extends BaseController {
      */
     @Resource
     private LiveRoomService liveRoomService;
-    
-    @Autowired
-    private SysUserFeignService sysUserFeignService;
-    
-    @Autowired
-    private StudentService studentService;
+
 
     /**
      * 校验房间信息,及个人信息
@@ -63,8 +58,9 @@ public class StudentLiveRoomController extends BaseController {
 
     @ApiOperation("观看者-进入房间")
     @GetMapping(value = "/joinRoom")
-    public HttpResponseResult<RoomInfoCache> joinRoom(String roomUid, Long userId) {
-        return succeed(liveRoomService.joinRoom(roomUid, userId));
+    public HttpResponseResult<Object> joinRoom(String roomUid, Long userId) {
+        liveRoomService.joinRoom(roomUid, userId);
+        return succeed();
     }
 
     /**

+ 6 - 1
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/CourseHomeworkController.java

@@ -91,7 +91,12 @@ public class CourseHomeworkController extends BaseController {
         SysUser sysUser = sysUserFeignService.queryUserInfo();
         HttpResponseResult info = checkCourseSchedule(courseId,sysUser);
         if (info != null) return info;
-        return succeed(courseHomeworkService.getCourseHomeworkDetailByCourseId(courseId, studentId));
+        CourseHomeworkDetailVo detailVo = courseHomeworkService.getCourseHomeworkDetailByCourseId(courseId, studentId);
+        if (detailVo != null) {
+            detailVo.setImUserId(imGroupService.getImUserId(String.valueOf(detailVo.getStudentId()),ClientEnum.STUDENT.name()));
+        }
+
+        return succeed(detailVo);
     }
 
 

+ 140 - 0
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherImLiveBroadcastRoomController.java

@@ -0,0 +1,140 @@
+package com.yonge.cooleshow.teacher.controller;
+
+import com.microsvc.toolkit.middleware.live.message.TencentWrapper;
+import com.yonge.cooleshow.biz.dal.dto.LiveRoomStatus;
+import com.yonge.cooleshow.biz.dal.service.ImLiveBroadcastRoomMemberService;
+import com.yonge.cooleshow.biz.dal.service.LiveBroadcastRoomMemberService;
+import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.ImLiveBroadcastRoomMemberWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.toolset.base.page.PageInfo;
+import io.swagger.annotations.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表控制层
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:04
+ */
+@Api(tags = "直播房间管理表")
+@RestController
+@RequestMapping("/imLiveBroadcastRoom")
+public class TeacherImLiveBroadcastRoomController extends BaseController {
+    /**
+     * 服务对象
+     */
+    @Resource
+    private LiveRoomService liveRoomService;
+
+    @Autowired
+    private ImLiveBroadcastRoomMemberService imLiveBroadcastRoomMemberService;
+
+
+    @ApiOperation("查询房间信息并校验房间是否合规")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "roomUid", dataType = "String", value = "房间uid", required = true),
+            @ApiImplicitParam(name = "userId", dataType = "Integer", value = "用户id", required = true),
+            @ApiImplicitParam(name = "liveAssistant", dataType = "Boolean", value = "直播助手"),
+    })
+    @GetMapping("/queryRoom")
+    public HttpResponseResult<LiveRoomWrapper.LiveRoomVo> queryRoomAndCheck(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                                                 @ApiParam(value = "用户id", required = true) Long userId,
+                                                                 @RequestParam(defaultValue = "false") Boolean liveAssistant) {
+
+        // 请求来源业源
+        int osType = 2; // 老师端
+        if (Optional.ofNullable(liveAssistant).orElse(false)) {
+            osType = 3; // 直播助手
+        }
+
+        return succeed(liveRoomService.queryRoomAndCheck(roomUid, userId, osType));
+    }
+
+    @ApiOperation("关闭直播间")
+    @GetMapping(value = "/roomDestroy")
+    public HttpResponseResult<Object> roomDestroy(@ApiParam(value = "房间Uid", required = true) String roomUid) {
+        liveRoomService.destroyLiveRoom(roomUid);
+        return succeed();
+    }
+
+    @ApiOperation("同步点赞数量")
+    @GetMapping("/syncLike")
+    public HttpResponseResult<Object> syncLike(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                               @ApiParam(value = "点赞数", required = true) Integer likeNum) {
+        liveRoomService.syncLike(roomUid, likeNum);
+        return succeed();
+    }
+
+    @ApiOperation("主讲人进入房间")
+    @GetMapping("/speakerJoinRoom")
+    public HttpResponseResult<LiveRoomWrapper.LiveRoomVo> speakerJoinRoom(String roomUid) {
+        return succeed(liveRoomService.speakerCheckRoomInfo(roomUid));
+    }
+
+    @ApiOperation("获取房间人员")
+    @GetMapping("/queryRoomUserInfo")
+    public HttpResponseResult<List<LiveRoomWrapper.BaseRoomUserVo>> queryRoomUserInfo(@ApiParam(value = "房间uid", required = true) String roomUid) {
+        return succeed(liveRoomService.queryRoomLimitOnlineUserInfo(roomUid));
+    }
+
+    @ApiOperation("设置是否允许连麦")
+    @GetMapping("/whetherMic")
+    public HttpResponseResult<Object> whetherMic(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                                 @ApiParam(value = "是否连麦 0:是 1否", required = true) Integer whetherMic) {
+        liveRoomService.whetherMic(roomUid,whetherMic);
+        return succeed();
+    }
+
+
+    @ApiOperation("设置连麦状态")
+    @PutMapping("/userWhetherMic")
+    public HttpResponseResult<Object> userWhetherMic(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                                 @ApiParam(value = "用户id", required = true) Long userId,
+                                                 @ApiParam(value = "连麦状态 0:未申请1:申请连麦中2:连麦中", required = true) Integer whetherMicStatus) {
+        liveRoomService.userWhetherMic(roomUid,userId,whetherMicStatus);
+        return succeed();
+    }
+
+    @ApiOperation("分页查询直播间人员列表")
+    @ApiImplicitParams({
+      @ApiImplicitParam(name = "search", dataType = "String", value = "模糊搜索 学员编号姓名"),
+      @ApiImplicitParam(name = "roomUid", dataType = "String", value = "房间uid"),
+      @ApiImplicitParam(name = "onlineStatus", dataType = "String ", value = "0:离线 1:在线  不传是全部"),
+      @ApiImplicitParam(name = "whetherMicStatus", dataType = "String", value = "连麦状态 0:未申请1:申请连麦中2:连麦中"),
+      @ApiImplicitParam(name = "page", dataType = "Integer", value = "页数"),
+      @ApiImplicitParam(name = "rows", dataType = "Integer", value = "每页数量"),
+    })
+    @PostMapping("/queryRoomMember")
+    public HttpResponseResult<PageInfo<ImLiveBroadcastRoomMemberWrapper.ImLiveBroadcastRoomMember>> queryRoomMember(@RequestBody Map<String, Object> param) {
+        return succeed(imLiveBroadcastRoomMemberService.queryRoomMember(param));
+    }
+
+
+    @ApiOperation("更新直播间状态")
+    @PostMapping("/updateRoomStatus")
+    public HttpResponseResult<Boolean> updateRoomStatus(@RequestBody @Valid LiveRoomStatus status ) {
+        return succeed(liveRoomService.updateRoomStatus(status));
+    }
+
+    @ApiOperation("直播间推流状态")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "roomUid", dataType = "String", value = "房间Uid", required = true),
+    })
+    @GetMapping(value = "/liveStreamStatus")
+    public HttpResponseResult<TencentWrapper.LiveStreamState> roomLiveStream(String roomUid) {
+
+        // 直播间推流状态查询
+        return succeed(liveRoomService.roomLiveStreamStatus(roomUid));
+    }
+}
+

+ 9 - 15
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherLiveRoomController.java

@@ -48,15 +48,15 @@ public class TeacherLiveRoomController extends BaseController {
         return succeed(liveRoomService.queryPageRoom(param));
     }
 
-    /**
-     * 根据房间uid查询房间信息
-     *
-     * @param roomUid 房间uid
-     */
-    @GetMapping("/speakerCheckRoomInfo")
-    public HttpResponseResult<RoomInfoCache> speakerCheckRoomInfo(@RequestParam("roomUid") String roomUid) {
-        return succeed(liveRoomService.speakerCheckRoomInfo(roomUid));
-    }
+//    /**
+//     * 根据房间uid查询房间信息
+//     *
+//     * @param roomUid 房间uid
+//     */
+//    @GetMapping("/speakerCheckRoomInfo")
+//    public HttpResponseResult<RoomInfoCache> speakerCheckRoomInfo(@RequestParam("roomUid") String roomUid) {
+//        return succeed(liveRoomService.speakerCheckRoomInfo(roomUid));
+//    }
 
     @ApiImplicitParams({
             @ApiImplicitParam(name = "roomTitle", dataType = "String", value = "房间标题"),
@@ -78,12 +78,6 @@ public class TeacherLiveRoomController extends BaseController {
         return succeed();
     }
 
-    @ApiOperation("主讲人-进入房间")
-    @GetMapping(value = "/speakerJoinRoom")
-    public HttpResponseResult<RoomInfoCache> speakerJoinRoom(String roomUid) {
-        return succeed(liveRoomService.speakerJoinRoom(roomUid));
-    }
-
 
     @ApiOperation("设置是否允许连麦")
     @GetMapping("/whetherMic")