Browse Source

Merge branch 'feature/0511-live' into master_saas

shangke 1 year ago
parent
commit
44674a7b84

+ 1 - 0
.gitignore

@@ -6,6 +6,7 @@
 .settings
 .project
 *.iml
+*.log
 /lib/
 
 ### 忽略子模块的文件 ###

+ 23 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/dto/ImLiveBroadcastRoomDto.java

@@ -1,8 +1,8 @@
 package com.ym.mec.biz.dal.dto;
 
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.ym.mec.auth.api.enums.SysUserType;
 import com.ym.mec.biz.dal.enums.EUseScene;
+import com.ym.mec.biz.dal.enums.live.ELiveViewMode;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -67,6 +67,12 @@ public class ImLiveBroadcastRoomDto implements Serializable {
     @ApiModelProperty(value = "服务提供方 rongCloud tencentCloud")
     private String serviceProvider;
 
+    @ApiModelProperty("直播观看模式")
+    private ELiveViewMode viewMode;
+
+    @ApiModelProperty("购物车标题")
+    private String shoppingTitle;
+
     @ApiModel(value = "房间配置")
     public static class RoomConfig implements Serializable {
 
@@ -229,5 +235,21 @@ public class ImLiveBroadcastRoomDto implements Serializable {
     public void setServiceProvider(String serviceProvider) {
         this.serviceProvider = serviceProvider;
     }
+
+    public ELiveViewMode getViewMode() {
+        return viewMode;
+    }
+
+    public void setViewMode(ELiveViewMode viewMode) {
+        this.viewMode = viewMode;
+    }
+
+    public String getShoppingTitle() {
+        return shoppingTitle;
+    }
+
+    public void setShoppingTitle(String shoppingTitle) {
+        this.shoppingTitle = shoppingTitle;
+    }
 }
 

+ 25 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoom.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.ym.mec.auth.api.enums.SysUserType;
 import com.ym.mec.biz.dal.enums.EUseScene;
+import com.ym.mec.biz.dal.enums.live.ELiveViewMode;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -114,6 +115,14 @@ public class ImLiveBroadcastRoom implements Serializable {
     @ApiModelProperty(value = "禁言状态: 0 取消;1禁言")
     private Integer banStatus;
 
+    @TableField("view_mode_")
+    @ApiModelProperty("直播观看模式")
+    private ELiveViewMode viewMode;
+
+    @TableField("shopping_title_")
+    @ApiModelProperty("购物车标题")
+    private String shoppingTitle;
+
     @TableField("created_by_")
     @ApiModelProperty(value = "创建人")
     private Integer createdBy;
@@ -358,5 +367,21 @@ public class ImLiveBroadcastRoom implements Serializable {
     public void setLiveTotalTime(Integer liveTotalTime) {
         this.liveTotalTime = liveTotalTime;
     }
+
+    public ELiveViewMode getViewMode() {
+        return viewMode;
+    }
+
+    public void setViewMode(ELiveViewMode viewMode) {
+        this.viewMode = viewMode;
+    }
+
+    public String getShoppingTitle() {
+        return shoppingTitle;
+    }
+
+    public void setShoppingTitle(String shoppingTitle) {
+        this.shoppingTitle = shoppingTitle;
+    }
 }
 

+ 24 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoomMember.java

@@ -60,6 +60,14 @@ public class ImLiveBroadcastRoomMember implements Serializable {
     @ApiModelProperty(value = "连麦状态 0:未申请1:申请连麦中2:连麦中")
     private Integer whetherMicStatus;
 
+    @TableField("fingerprint_")
+    @ApiModelProperty("游客凭据")
+    private String fingerprint;
+
+    @TableField("visitor_name_")
+    @ApiModelProperty("游客名称")
+    private String visitorName;
+
     @TableField("create_time_")
     @ApiModelProperty(value = "创建时间")
     private Date createTime;
@@ -153,5 +161,21 @@ public class ImLiveBroadcastRoomMember implements Serializable {
     public void setCreateTime(Date createTime) {
         this.createTime = createTime;
     }
+
+    public String getFingerprint() {
+        return fingerprint;
+    }
+
+    public void setFingerprint(String fingerprint) {
+        this.fingerprint = fingerprint;
+    }
+
+    public String getVisitorName() {
+        return visitorName;
+    }
+
+    public void setVisitorName(String visitorName) {
+        this.visitorName = visitorName;
+    }
 }
 

+ 32 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/enums/live/ELiveViewMode.java

@@ -0,0 +1,32 @@
+package com.ym.mec.biz.dal.enums.live;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.ym.mec.common.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;
+    }
+}

+ 22 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/vo/ImLiveBroadcastRoomVo.java

@@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import com.microsvc.toolkit.middleware.live.message.LiveRoomConfig;
 import com.ym.mec.auth.api.enums.SysUserType;
 import com.ym.mec.biz.dal.enums.EUseScene;
+import com.ym.mec.biz.dal.enums.live.ELiveViewMode;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -127,6 +128,11 @@ public class ImLiveBroadcastRoomVo implements Serializable {
     @ApiModelProperty("视频数量")
     private Integer videoNum;
 
+    @ApiModelProperty("直播观看模式")
+    private ELiveViewMode viewMode;
+
+    @ApiModelProperty("购物车标题")
+    private String shoppingTitle;
 
     @ApiModelProperty("使用场景 正常:NORMAL 音乐:MUSIC")
     private EUseScene useScene;
@@ -434,5 +440,21 @@ public class ImLiveBroadcastRoomVo implements Serializable {
     public void setLiveTotalTime(Integer liveTotalTime) {
         this.liveTotalTime = liveTotalTime;
     }
+
+    public ELiveViewMode getViewMode() {
+        return viewMode;
+    }
+
+    public void setViewMode(ELiveViewMode viewMode) {
+        this.viewMode = viewMode;
+    }
+
+    public String getShoppingTitle() {
+        return shoppingTitle;
+    }
+
+    public void setShoppingTitle(String shoppingTitle) {
+        this.shoppingTitle = shoppingTitle;
+    }
 }
 

+ 11 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/vo/RoomUserInfoVo.java

@@ -1,6 +1,7 @@
 package com.ym.mec.biz.dal.vo;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -24,6 +25,9 @@ public class RoomUserInfoVo extends BaseRoomUserVo implements Serializable  {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date dynamicLookTime;
 
+    @ApiModelProperty("用户头象")
+    private String avatar;
+
     public Integer getTotalViewTime() {
         return totalViewTime;
     }
@@ -56,4 +60,11 @@ public class RoomUserInfoVo extends BaseRoomUserVo implements Serializable  {
         this.tenantId = tenantId;
     }
 
+    public String getAvatar() {
+        return avatar;
+    }
+
+    public void setAvatar(String avatar) {
+        this.avatar = avatar;
+    }
 }

+ 24 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveBroadcastRoomService.java

@@ -12,6 +12,7 @@ import com.ym.mec.biz.dal.vo.BaseRoomUserVo;
 import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomVo;
 import com.ym.mec.biz.dal.vo.RoomReservationUserVo;
 import com.ym.mec.biz.dal.vo.LiveRoomGoodsOrderVo;
+import com.ym.mec.biz.dal.vo.RoomUserInfoVo;
 import com.ym.mec.common.entity.ImUserState;
 import com.ym.mec.common.page.PageInfo;
 
@@ -32,6 +33,14 @@ public interface ImLiveBroadcastRoomService extends IService<ImLiveBroadcastRoom
 
     ImLiveBroadcastRoomVo queryRoomAndCheck(String roomUid, Integer userId, Integer osType);
 
+    /**
+     * 游客直播间信息
+     * @param roomUid 直播间编号
+     * @param userId 游客编号
+     * @return ImLiveBroadcastRoomVo
+     */
+    ImLiveBroadcastRoomVo visitorRoomInfo(String roomUid, Integer userId);
+
     ImLiveBroadcastRoomVo queryRoomInfo(String roomUid);
 
     void add(ImLiveBroadcastRoomDto dto);
@@ -77,6 +86,21 @@ public interface ImLiveBroadcastRoomService extends IService<ImLiveBroadcastRoom
 
     void joinRoom(String roomUid, Integer userId);
 
+    /**
+     * 游客加入直播间
+     * @param roomUid 直播间编号
+     * @param userId 游客编号
+     */
+    void visitorJoinRoom(String roomUid, Integer userId);
+
+    /**
+     * 游客加入直播间凭据
+     * @param roomUid 直播间编号
+     * @param fingerprint 游客凭据
+     * @return RoomUserInfoVo
+     */
+    RoomUserInfoVo getVisitorCredentials(String roomUid, String fingerprint);
+
     void startLive(String roomUid, Integer userId,String videoResolution);
 
     void startLive(String roomUid, Integer userId, String videoResolution, String sequence);

+ 3 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/SysConfigService.java

@@ -400,6 +400,9 @@ public interface SysConfigService extends BaseService<Long, SysConfig> {
     // 学校端默认用户头象
     String USER_DEFAULT_AVATAR = "user_default_avatar";
 
+    // 游客默认头像
+    String VISITOR_DEFAULT_AVATAR = "visitor_default_avatar";
+
     static void checkActivityDate(String startTimeStr, String endTimeStr) {
         if(StringUtils.isEmpty(startTimeStr) || StringUtils.isEmpty(startTimeStr)){
             return;

+ 335 - 40
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomServiceImpl.java

@@ -39,6 +39,7 @@ import com.ym.mec.biz.dal.enums.EAnchorStatus;
 import com.ym.mec.biz.dal.enums.EGroupDefinedDataType;
 import com.ym.mec.biz.dal.enums.EOnOffStatus;
 import com.ym.mec.biz.dal.enums.MessageTypeEnum;
+import com.ym.mec.biz.dal.enums.live.ELiveViewMode;
 import com.ym.mec.biz.dal.page.LiveRoomGoodsOrderQueryInfo;
 import com.ym.mec.biz.dal.vo.*;
 import com.ym.mec.biz.redisson.RedissonMessageService;
@@ -60,6 +61,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.jetbrains.annotations.NotNull;
 import org.joda.time.DateTime;
+import org.redisson.api.RAtomicLong;
 import org.redisson.api.RBucket;
 import org.redisson.api.RLock;
 import org.redisson.api.RMap;
@@ -145,6 +147,8 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
     public static final String LIVE_USER_STATE_TIME = String.join(":", "IM:LIVE_USER_STATE_TIME", USER_ID);
     //主讲人信息
     public static final String LIVE_SPEAKER_INFO = String.join(":", "IM:LIVE_SPEAKER_INFO", ROOM_UID, USER_ID);
+    // 直播间游客信息
+    public static final String LIVE_VISITOR_INFO = String.join(":", "IM:LIVE_VISITOR_INFO", ROOM_UID, USER_ID);
     //主讲人最近一次加入房间的clientIp
     public static final String LIVE_SPEAKER_LAST_CLIENT_IP = String.join(":", "IM:LIVE_SPEAKER_LAST_CLIENT_IP", ROOM_UID, USER_ID);
     //直播提前开始时间
@@ -161,20 +165,25 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
      */
     @Override
     public ImLiveBroadcastRoomVo queryRoomAndCheck(String roomUid, Integer userId, Integer osType) {
+
         SysUser sysUser = Optional.ofNullable(userId)
                 .map(this::getSysUser)
                 .orElseGet(this::getSysUser);
+
         // 默认学生端查询
         osType = Optional.ofNullable(osType).orElse(1);
+
+        // 移动端-学生端
         if (osType == 1) {
             //学生端
             return studentQueryRoomAndCheck(roomUid, sysUser);
-        } else if (osType == 2) {
+        }
+        // 移动端-老师端
+        if (osType == 2) {
             //老师端
             return teacherQueryRoomAndCheck(roomUid, sysUser);
         }
-
-        // 直播助手
+        // PC端-直播助手
         if (osType == 3) {
             // 校验直播间是否存在
             ImLiveBroadcastRoomVo vo = Optional.ofNullable(roomUid).map(this::queryRoomInfo)
@@ -188,6 +197,57 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         return queryRoomAndCheck(roomUid, sysUser);
     }
 
+    /**
+     * 游客直播间信息
+     *
+     * @param roomUid 直播间编号
+     * @param userId  游客编号
+     * @return ImLiveBroadcastRoomVo
+     */
+    @Override
+    public ImLiveBroadcastRoomVo visitorRoomInfo(String roomUid, Integer userId) {
+
+        // 直播间游客信息
+        RMap<Integer, String> roomTotalUser = getTotalUserCache(roomUid);
+        if (!roomTotalUser.containsKey(userId)) {
+            throw new BizException("您无法观看该直播,请刷新重试");
+        }
+
+        // 游客信息
+        RoomUserInfoVo userInfo = JSONObject.toJavaObject(JSONObject.parseObject(roomTotalUser.get(userId)),
+                RoomUserInfoVo.class);
+
+        // 游客直播间信息校验
+        ImLiveBroadcastRoomVo roomVo = getVisitorLiveBroadcastRoomVo(roomUid);
+
+        // 用户直播间黑名单
+        roomVo.setBlacklistFlag(0);
+        //黑名单查询-查询当前用户是否在黑名单中
+        int count = imLiveRoomBlackService.count(Wrappers.<ImLiveRoomBlack>lambdaQuery()
+                .eq(ImLiveRoomBlack::getRoomUid, roomVo.getRoomUid())
+                .eq(ImLiveRoomBlack::getUserId, userInfo.getUserId()));
+        if (count > 0) {
+            roomVo.setBlacklistFlag(1);
+        }
+
+        LivePluginService pluginService = livePluginContext.getPluginService(roomVo.getServiceProvider());
+        // 直播房间配置信息
+        String userSig = "";
+        try {
+            // 生成聊天签名
+            userSig = pluginService.register(userInfo.getUserId().toString(), userInfo.getUserName(), userInfo.getAvatar());
+
+        } catch (Exception e) {
+            log.error("直播房间游客注册失败: userId={}", userInfo.getUserId(), e);
+        }
+
+        // 直播房间统计信息
+        getRoomData(roomVo);
+
+        // 直播间配置信息
+        return roomVo.userSig(userSig).liveRoomConfig(pluginService.getLiveRoomConfig());
+    }
+
     public ImLiveBroadcastRoomVo studentQueryRoomAndCheck(String roomUid, SysUser sysUser) {
         //如果是学生端,则需要检查是否有权限进入
         Map<String, Object> param = new HashMap<>();
@@ -1004,7 +1064,7 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         userid = setFromUserId(userid,roomVo);
         // 消息发送用户
         LiveRoomMessage.MessageUser messageUser = null;
-        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        SysUser sysUser = getRoomSysUser(Integer.parseInt(userid), roomVo.getRoomUid());
         if (Objects.nonNull(sysUser)) {
             // 发送用户信息
             messageUser = LiveRoomMessage.MessageUser
@@ -1059,7 +1119,7 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         String fromUserId = setFromUserId(userid, roomVo);
         // 消息发送用户
         LiveRoomMessage.MessageUser messageUser = null;
-        SysUser sysUser = sysUserFeignService.queryUserById(Integer.parseInt(userid));
+        SysUser sysUser = getRoomSysUser(Integer.parseInt(userid), roomVo.getRoomUid());
         if (Objects.nonNull(sysUser)) {
             // 发送用户信息
             messageUser = LiveRoomMessage.MessageUser
@@ -1118,7 +1178,7 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         // 直播间统计数据
         getRoomData(roomVo);
 
-        String userId = setFromUserId(fromUserId.toString(),roomVo);
+        String userId = setFromUserId(fromUserId.toString(), roomVo);
         // 缓存JoinRoom用户信息到redis
         RBucket<Object> bucket = redissonClient.getBucket(RedissonMessageService.LIVE_ROOM_MEMBER + roomUid);
         if (!bucket.isExists()) {
@@ -1129,7 +1189,7 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
 
         // 消息发送用户
         LiveRoomMessage.MessageUser messageUser = null;
-        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        SysUser sysUser = getRoomSysUser(fromUserId, roomUid);
         if (Objects.nonNull(sysUser)) {
             // 发送用户信息
             messageUser = LiveRoomMessage.MessageUser
@@ -1383,13 +1443,26 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
             throw new BizException("当前用户所在机构没有加入直播间的权限");
         }
 
+        // 加入直播间用户信息
+        joinRoomUserInfo(roomUid, userId, imLiveBroadcastRoomVo);
+    }
+
+    /**
+     * 加入直播间用户信息
+     * @param roomUid 直播间编号
+     * @param userId 用户编号
+     * @param imLiveBroadcastRoomVo ImLiveBroadcastRoomVo
+     */
+    private void joinRoomUserInfo(String roomUid, Integer userId, ImLiveBroadcastRoomVo imLiveBroadcastRoomVo) {
+
         //记录用户当前房间uid
         redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userId.toString())).set(roomUid, 12L, TimeUnit.HOURS);
-        //房间累计用户信息-指只要进入到该房间的用户都要记录
-        RMap<Integer, String> roomTotalUser = getTotalUserCache(roomUid);
+
         //判断是否第一次进房间
         RoomUserInfoVo userInfo;
-        Date now = new Date();
+
+        //房间累计用户信息-指只要进入到该房间的用户都要记录
+        RMap<Integer, String> roomTotalUser = getTotalUserCache(roomUid);
         if (roomTotalUser.containsKey(userId)) {
             //多次进入更新动态进入时间
             userInfo = JSONObject.toJavaObject(JSONObject.parseObject(roomTotalUser.get(userId)), RoomUserInfoVo.class);
@@ -1403,7 +1476,7 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
             if (Objects.isNull(liveRoomMember)) {
 
                 // 初次进入房间
-                getLiveRoomUserInfo(userId, imLiveBroadcastRoomVo, now);
+                getLiveRoomUserInfo(userId, imLiveBroadcastRoomVo);
             } else {
                 // 更新直播间用户信息
                 ImLiveBroadcastRoomMember roomMember = new ImLiveBroadcastRoomMember();
@@ -1416,17 +1489,20 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
 
         } else {
             //第一次进该房间 写入用户首次进入时间
-            userInfo = getLiveRoomUserInfo(userId, imLiveBroadcastRoomVo, now);
+            userInfo = getLiveRoomUserInfo(userId, imLiveBroadcastRoomVo);
         }
+
         //查询主讲人信息
         RBucket<RoomSpeakerInfo> speakerCache = getRoomSpeakerInfoCache(roomUid, imLiveBroadcastRoomVo.getSpeakerId().toString());
         if (speakerCache.isExists()) {
             //如果用户进来时主讲人已经开启直播则修改学生观看时间
             Integer state = speakerCache.get().getState();
             if (Objects.nonNull(state) && state == 0 && imLiveBroadcastRoomVo.getPushStatus() == 1) {
-                userInfo.setDynamicLookTime(now);
+                userInfo.setDynamicLookTime(DateTime.now().toDate());
             }
         }
+
+        // 直播间用户、在线用户数据缓存
         roomTotalUser.fastPut(userId, JSONObject.toJSONString(userInfo));
         //在线人员列表
         RMap<Integer, String> onlineUserInfo = getOnlineUserCache(roomUid);
@@ -1441,12 +1517,185 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
             this.sendBlackJoinRoom(userId, imLiveBroadcastRoomVo);
         }
 
-
-        log.info("join sendOnlineUserCount>>>>   roomUid: {}  fromUserId:{}  count:{}", roomUid, userId, onlineUserInfo.size());
+        log.info("joinRoom>>>> sendOnlineUserCount>>>>   roomUid: {}  fromUserId:{}  count:{}", roomUid, userId, onlineUserInfo.size());
         log.info("joinRoom>>>> userInfo: {}", JSONObject.toJSONString(userInfo));
     }
 
     /**
+     * 游客加入直播间
+     *
+     * @param roomUid 直播间编号
+     * @param userId  游客编号
+     */
+    @Override
+    public void visitorJoinRoom(String roomUid, Integer userId) {
+
+        // 游客直播间信息
+        ImLiveBroadcastRoomVo roomVo = getVisitorLiveBroadcastRoomVo(roomUid);
+
+        // 加入直播间用户信息
+        joinRoomUserInfo(roomUid, userId, roomVo);
+    }
+
+    /**
+     * 游客访问直播间信息
+     *
+     * @param roomUid 直播间编号
+     */
+    private ImLiveBroadcastRoomVo getVisitorLiveBroadcastRoomVo(String roomUid) {
+        // 查询直播间信息
+        ImLiveBroadcastRoomVo roomVo = getImLiveBroadcastRoomVo(roomUid);
+        if (Objects.isNull(roomVo) || roomVo.getRoomState() == 1) {
+            log.info("visitorJoinRoom>>>> joinRoom LIVE_ROOM_CANCEL roomUid: {}", roomUid);
+            throw new BizException("直播间不存在");
+        }
+
+        // 判断当前直播间观看模式是否为游客模式且,直播状态
+        if (ELiveViewMode.VISITOR != roomVo.getViewMode()) {
+            log.info("visitorJoinRoom>>>> joinRoom LOGOUT roomUid: {} viewMode: {}", roomUid, roomVo.getViewMode());
+            throw new BizException("请登陆");
+        }
+
+        // 判断当前直播间是否在直播中,直播状态 0未开始 1开始 2结束
+        if (roomVo.getLiveState() == 0) {
+            log.info("visitorJoinRoom>>>> joinRoom LIVE_NOT_START roomUid: {} liveState: {}", roomUid, roomVo.getLiveState());
+
+            Date liveStartTime = DateUtil.addMinutes(roomVo.getLiveStartTime(), -PRE_LIVE_TIME_MINUTE);
+            throw new BizException(DateUtil.format(liveStartTime, "yyyy年MM月dd日 HH点mm分") + " 可进入直播间准备");
+        }
+        if (roomVo.getLiveState() == 2) {
+            log.info("visitorJoinRoom>>>> joinRoom LIVE_CLOSED roomUid: {} liveState: {}", roomUid, roomVo.getLiveState());
+
+            //如果直播结束了还是推广状态则将推广修改为取消
+            if (roomVo.getPopularize() == 1) {
+                this.update(Wrappers.<ImLiveBroadcastRoom>lambdaUpdate()
+                        .set(ImLiveBroadcastRoom::getPopularize, 0)
+                        .eq(ImLiveBroadcastRoom::getId, roomVo.getId()));
+            }
+
+            throw new BizException("直播已结束!");
+        }
+
+        return roomVo;
+    }
+
+    /**
+     * 游客加入直播间凭据
+     *
+     * @param roomUid     直播间编号
+     * @param fingerprint 游客凭据
+     * @return RoomUserInfoVo
+     */
+    @Override
+    public RoomUserInfoVo getVisitorCredentials(String roomUid, String fingerprint) {
+
+        // 游客直播间信息校验
+        ImLiveBroadcastRoomVo roomVo = getVisitorLiveBroadcastRoomVo(roomUid);
+
+        // 为当前游客生成惟一用户ID
+        String cacheKey = MessageFormat.format("fingerprint:{0}:{1}", roomUid, fingerprint);
+        RBucket<Object> bucket = redissonClient.getBucket(cacheKey);
+
+        Integer userId;
+        if (bucket.isExists()) {
+
+            // 查询游客用户ID
+            userId = Optional.ofNullable(bucket.get()).map(x -> (int) x).orElse(-1);
+        } else {
+
+            String today = DateTime.now().toString("yyMMdd");
+            // 用户Id缓存key
+            String userIdKey = MessageFormat.format("fingerprint:userId:{0}", today);
+
+            RAtomicLong atomicLong = redissonClient.getAtomicLong(userIdKey);
+            if (!atomicLong.isExists()) {
+                // 设置默认初始化值
+                atomicLong.set(10000);
+                // 设置缓存失效时间
+                atomicLong.expire(1L, TimeUnit.DAYS);
+            }
+
+            // 生成游客用户ID
+            userId = (int) atomicLong.getAndIncrement();
+            // 设置用户ID缓存
+            bucket.set(userId, 12L, TimeUnit.HOURS);
+        }
+
+        //房间累计用户信息-指只要进入到该房间的用户都要记录
+        RMap<Integer, String> roomTotalUser = getTotalUserCache(roomUid);
+
+        RoomUserInfoVo userInfo;
+        if (roomTotalUser.containsKey(userId)) {
+            //多次进入更新动态进入时间
+            userInfo = JSONObject.toJavaObject(JSONObject.parseObject(roomTotalUser.get(userId)), RoomUserInfoVo.class);
+
+            Integer exists = liveBroadcastRoomMemberService.lambdaQuery()
+                    .eq(ImLiveBroadcastRoomMember::getTenantId, userInfo.getTenantId())
+                    .eq(ImLiveBroadcastRoomMember::getRoomUid, roomUid)
+                    .eq(ImLiveBroadcastRoomMember::getUserId, userInfo.getUserId())
+                    .count();
+
+            if (exists <= 0) {
+                // 初次进入房间
+                getLiveRoomVisitorInfo(userId, roomVo, fingerprint);
+            }
+
+        } else {
+            //第一次进该房间 写入用户首次进入时间
+            userInfo = getLiveRoomVisitorInfo(userId, roomVo, fingerprint);
+        }
+
+        // 缓存用户信息
+        roomTotalUser.fastPut(userId, JSONObject.toJSONString(userInfo));
+
+        // 游客缓存信息
+        getRoomVisitorInfoCache(roomUid, userId.toString()).set(userInfo, 12L, TimeUnit.HOURS);
+
+        // 返回直播间访客信息
+        return userInfo;
+    }
+
+    /**
+     * 获取直播间游客信息
+     * @param userId 游客编号
+     * @param imLiveBroadcastRoomVo ImLiveBroadcastRoomVo
+     * @param fingerprint 游客凭据
+     * @return RoomUserInfoVo
+     */
+    private RoomUserInfoVo getLiveRoomVisitorInfo(Integer userId, ImLiveBroadcastRoomVo imLiveBroadcastRoomVo, String fingerprint) {
+
+        RoomUserInfoVo userInfo = new RoomUserInfoVo();
+        // 生成游客账号信息,缓存信息
+        userInfo.setTenantId(-1);
+        userInfo.setUserId(userId);
+        userInfo.setUserName(MessageFormat.format("游客{0}", String.valueOf(userId)));
+        userInfo.setTotalViewTime(0);
+        userInfo.setFirstJoinTime(DateTime.now().toDate());
+
+        // 游客默认头像
+        String defaultAvatar = sysConfigDao.findByParamName(SysConfigService.VISITOR_DEFAULT_AVATAR).getParanValue();
+        userInfo.setAvatar(Optional.ofNullable(defaultAvatar).orElse(""));
+
+        // 记录直播间用户信息
+        ImLiveBroadcastRoomMember roomMember = new ImLiveBroadcastRoomMember();
+        roomMember.setTenantId(userInfo.getTenantId());
+        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.setFingerprint(fingerprint);
+        roomMember.setVisitorName(userInfo.getUserName());
+
+        // 保存直播间用户信息
+        liveBroadcastRoomMemberService.save(roomMember);
+
+        return userInfo;
+    }
+
+    /**
      * 发送黑名单进入消息
      * @param userId 用户id
      */
@@ -1462,18 +1711,21 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         String fromUserid = setFromUserId(userId.toString(),roomVo);
         // 消息发送用户
         LiveRoomMessage.MessageUser messageUser = null;
-        SysUser sysUser = sysUserFeignService.queryUserById(userId);
+
+        // 直播间访问用户信息
+        SysUser sysUser = getRoomSysUser(userId, roomUid);
         if (Objects.nonNull(sysUser)) {
             // 发送用户信息
             messageUser = LiveRoomMessage.MessageUser
-                .builder()
-                .sendUserId(sysUser.getId().toString())
-                .sendUserName(sysUser.getUsername())
-                .avatarUrl(sysUser.getAvatar())
-                .blackFlag(true)
-                .build();
+                    .builder()
+                    .sendUserId(sysUser.getId().toString())
+                    .sendUserName(sysUser.getUsername())
+                    .avatarUrl(sysUser.getAvatar())
+                    .blackFlag(true)
+                    .build();
         }
 
+
         LiveRoomMessage.MessageContent messageContent = LiveRoomMessage.MessageContent
             .builder()
             .sendUserInfo(messageUser)
@@ -1492,23 +1744,53 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
             livePluginContext.getPluginService(roomVo.getServiceProvider()).sendChatRoomMessage(message);
             log.info("sendBlackJoinRoom>>>> message: {}", JSONObject.toJSONString(message));
         } catch (Exception e) {
-            log.error("sendBlackJoinRoom>>>> error {}", e.getMessage());
             log.error("sendBlackJoinRoom>>>> sendMessage {} :", JSONObject.toJSONString(message));
+            log.error("sendBlackJoinRoom>>>> error", e);
+        }
+    }
+
+    /**
+     * 直播间访问用户信息
+     * @param userId 用户编号
+     * @param roomUid 直播间编号
+     * @return SysUser
+     */
+    private SysUser getRoomSysUser(Integer userId, String roomUid) {
+
+        SysUser sysUser = null;
+        // 直播间游客或学生用户匹配
+        RBucket<RoomUserInfoVo> visitorInfoCache = getRoomVisitorInfoCache(roomUid, userId.toString());
+        if (getRoomVisitorInfoCache(roomUid, userId.toString()).isExists()) {
+
+            // 游客身份信息
+            RoomUserInfoVo userInfoVo = visitorInfoCache.get();
+            if (Objects.nonNull(userInfoVo)) {
+                sysUser = new SysUser();
+                sysUser.setId(userInfoVo.getUserId());
+                sysUser.setUsername(userInfoVo.getUserName());
+                sysUser.setAvatar(userInfoVo.getAvatar());
+            }
+
+        } else {
+
+            // 登录用户信息
+            sysUser = sysUserFeignService.queryUserById(userId);
         }
+
+        return sysUser;
     }
 
     /**
      * 获取直播间用户信息
      * @param userId 用户id
      * @param imLiveBroadcastRoomVo 直播间信息
-     * @param now 当前时间
      * @return RoomUserInfoVo
      */
     @NotNull
-    private RoomUserInfoVo getLiveRoomUserInfo(Integer userId, ImLiveBroadcastRoomVo imLiveBroadcastRoomVo, Date now) {
+    private RoomUserInfoVo getLiveRoomUserInfo(Integer userId, ImLiveBroadcastRoomVo imLiveBroadcastRoomVo) {
 
         RoomUserInfoVo userInfo = getUserInfo(userId);
-        userInfo.setFirstJoinTime(now);
+        userInfo.setFirstJoinTime(DateTime.now().toDate());
         userInfo.setTotalViewTime(0);
 
         // 记录直播间用户信息
@@ -2087,18 +2369,20 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         }
     }
 
+    /**
+     * 直播间统计数据
+     * 观看数、点赞数,在线人数
+     * @param roomVo ImLiveBroadcastRoomVo
+     */
     private void getRoomData(ImLiveBroadcastRoomVo roomVo) {
-        //点赞数
-        Object like = redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomVo.getRoomUid())).get();
-        if (Objects.isNull(like)) {
-            like = 0;
-        }
 
-        like = syncLikeCount(roomVo.getRoomUid());
+        // 同步点赞数
+        int likeNums = syncLikeCount(roomVo.getRoomUid());
 
-        roomVo.setLikeNum((int) like);
+        roomVo.setLikeNum(likeNums);
         roomVo.setTotalLookNum(0);
         roomVo.setLookNum(0);
+
         //累计总用户数量
         // roomVo.setTotalLookNum(getNum.apply(this::getTotalUserCache, roomVo.getRoomUid()));
         // //在房间观看用户数量
@@ -2606,24 +2890,24 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
      * 同步直播间点赞数
      *
      * @param roomUid 直播间uid
-     * @return
+     * @return int
      */
     @Override
-    @Transactional
     public int syncLikeCount(String roomUid) {
+
+        // 直播间信息
         ImLiveBroadcastRoomVo room = getImLiveBroadcastRoomVo(roomUid);
         if (room == null) {
             return 0;
         }
 
+        // 融云直播间
         if (room.getServiceProvider().equals(RongCloudLivePlugin.PLUGIN_NAME)) {
             //点赞数
-            Object like = redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).get();
-            if (Objects.isNull(like)) {
-                like = 0;
-            }
-            return (int) like;
+            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 {
@@ -2648,6 +2932,7 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
                                                                       .eq(ImLiveBroadcastRoomData::getRoomUid, roomUid)
                                                                       .last("limit 1")
                                                                       .one();
+            // 同步点赞数
             if (one != null) {
                 ImLiveBroadcastRoomData imLiveBroadcastRoomData = new ImLiveBroadcastRoomData();
                 imLiveBroadcastRoomData.setId(one.getId());
@@ -2735,6 +3020,16 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         return redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, userId).replace(ROOM_UID, roomUid));
     }
 
+    /**
+     * 直播间游客信息
+     * @param roomUid 直播间编号
+     * @param userId 游客编号
+     * @return RBucket<RoomUserInfoVo>
+     */
+    private RBucket<RoomUserInfoVo> getRoomVisitorInfoCache(String roomUid, String userId) {
+        return redissonClient.getBucket(LIVE_VISITOR_INFO.replace(USER_ID, userId).replace(ROOM_UID, roomUid));
+    }
+
     private RoomUserInfoVo getUserInfo(Integer userId) {
         RoomUserInfoVo userInfo = new RoomUserInfoVo();
         userInfo.setUserId(userId);

+ 2 - 0
mec-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomMapper.xml

@@ -65,6 +65,8 @@
         a.ban_status_ AS banStatus,
         a.created_time_ AS createdTime,
         a.service_provider_ as serviceProvider,
+        a.view_mode_ AS viewMode,
+        a.shopping_title_ AS shoppingTitle,
         a.use_scene_ as useScene
         from im_live_broadcast_room as a
         left join tenant_info AS t on a.tenant_id_ = t.id_

+ 2 - 0
mec-biz/src/main/resources/config/mybatis/ImLiveRoomReservationMapper.xml

@@ -46,6 +46,8 @@
         a.created_time_ AS createdTime,
         a.service_provider_ as serviceProvider,
         a.popularize_type_ AS popularizeType,
+        a.view_mode_ AS viewMode,
+        a.shopping_title_ AS shoppingTitle,
         IF(d.user_id_ is null, 0, 1) as reserve
         from (
         select *

+ 1 - 1
mec-student/src/main/java/com/ym/mec/student/config/ResourceServerConfig.java

@@ -48,7 +48,7 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
                 "/musicGroup/getGradeList",
                 "/studentCompetition/get","/musicGroup/preRegister",
                 "/tenantInfo/queryTenantInfoByOrgan/**",
-                "/subject/list","/tenantApply/add","/questionnaireUserResult/add","/questionnaireTopic/getDetail",
+                "/subject/list","/tenantApply/add","/questionnaireUserResult/add","/questionnaireTopic/getDetail", "/open/**",
                 "/musicEnlightenmentQuestionnaire/addEnlightenmentQuestionnaire", "/musicEnlightenmentQuestionnaire/getUserMusicEnlightenmentQuestionnaire").permitAll().anyRequest().authenticated().and().httpBasic();
     }
 

+ 93 - 0
mec-student/src/main/java/com/ym/mec/student/controller/open/OpenLiveBroadcastRoomController.java

@@ -0,0 +1,93 @@
+package com.ym.mec.student.controller.open;
+
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomVo;
+import com.ym.mec.biz.dal.vo.RoomUserInfoVo;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomService;
+import com.ym.mec.common.controller.BaseController;
+import com.ym.mec.common.entity.HttpResponseResult;
+import com.ym.mec.common.entity.ImUserState;
+import com.ym.mec.common.exception.BizException;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 直播房间开放接口
+ *
+ */
+@Api(tags = "直播房间开放接口")
+@RestController
+@RequestMapping("/open/liveBroadcastRoom")
+public class OpenLiveBroadcastRoomController extends BaseController {
+
+    /**
+     * 服务对象
+     */
+    @Resource
+    private ImLiveBroadcastRoomService imLiveBroadcastRoomService;
+
+    @ApiOperation("直播间游客访问凭据")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "roomUid", value = "直播间编号", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "fingerprint", value = "游客凭据", required = true, dataType = "String")
+    })
+    @GetMapping("/visitorCredentials")
+    public HttpResponseResult<RoomUserInfoVo> visitorCredentials(@RequestParam String roomUid,
+                                                                 @RequestParam String fingerprint) {
+
+        // 游客访问直播间凭据信息
+        return succeed(imLiveBroadcastRoomService.getVisitorCredentials(roomUid, fingerprint));
+    }
+
+
+    @ApiOperation("游客查询房间信息")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "roomUid", value = "房间编号", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "userId", value = "游客编号", required = true, dataType = "Integer"),
+    })
+    @GetMapping("/visitorRoomInfo")
+    public HttpResponseResult<ImLiveBroadcastRoomVo> visitorRoomInfo(@RequestParam String roomUid, @RequestParam Integer userId) {
+
+        return succeed(imLiveBroadcastRoomService.visitorRoomInfo(roomUid, userId));
+    }
+
+    @ApiOperation("游客-进入房间")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "roomUid", value = "房间编号", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "userId", value = "游客编号", required = true, dataType = "Integer"),
+    })
+    @GetMapping("/visitorJoinRoom")
+    public HttpResponseResult<Object> visitorJoinRoom(@RequestParam String roomUid, @RequestParam Integer userId) {
+
+        // 校验请求参数
+        if (StringUtils.isAnyBlank(roomUid) || Objects.isNull(userId)) {
+            throw new BizException("请求参数错误");
+        }
+
+        // 游客加入直播间
+        imLiveBroadcastRoomService.visitorJoinRoom(roomUid, userId);
+
+        return succeed();
+    }
+
+    @ApiOperation("游客退出直播间")
+    @PostMapping("/visitorQuitRoom")
+    public HttpResponseResult<Object> visitorQuitRoom(@RequestBody List<ImUserState> userState) {
+        imLiveBroadcastRoomService.opsRoom(userState);
+        return succeed();
+    }
+
+}
+