|
@@ -1,23 +1,15 @@
|
|
package com.yonge.cooleshow.biz.dal.service.impl;
|
|
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.DESTROY_EXPIRED_LIVE_ROOM_MINUTE;
|
|
import static com.yonge.cooleshow.common.constant.SysConfigConstant.PRE_CREATE_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.text.MessageFormat;
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
import java.time.ZoneId;
|
|
import java.time.ZoneId;
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.BiPredicate;
|
|
import java.util.function.BiPredicate;
|
|
@@ -26,6 +18,7 @@ import java.util.function.Function;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
+import com.fasterxml.jackson.annotation.JsonFormat;
|
|
import com.microsvc.toolkit.middleware.live.LivePluginContext;
|
|
import com.microsvc.toolkit.middleware.live.LivePluginContext;
|
|
import com.microsvc.toolkit.middleware.live.LivePluginService;
|
|
import com.microsvc.toolkit.middleware.live.LivePluginService;
|
|
import com.microsvc.toolkit.middleware.live.impl.RongCloudLivePlugin;
|
|
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.entity.*;
|
|
import com.yonge.cooleshow.biz.dal.enums.live.EAnchorStatus;
|
|
import com.yonge.cooleshow.biz.dal.enums.live.EAnchorStatus;
|
|
import com.yonge.cooleshow.biz.dal.enums.live.EOnOffStatus;
|
|
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.mapper.SysUserMapper;
|
|
import com.yonge.cooleshow.biz.dal.service.*;
|
|
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.biz.dal.wrapper.liveroom.LiveRoomWrapper;
|
|
import com.yonge.cooleshow.common.enums.EGroupDefinedDataType;
|
|
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.collections.CollectionUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.joda.time.DateTime;
|
|
import org.joda.time.DateTime;
|
|
@@ -125,6 +122,17 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
private LiveRoomVideoService liveRoomVideoService;
|
|
private LiveRoomVideoService liveRoomVideoService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private LiveBroadcastRoomDataService liveBroadcastRoomDataService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ImLiveBroadcastRoomMemberMapper imLiveBroadcastRoomMemberMapper;
|
|
|
|
+ @Autowired
|
|
|
|
+ private ImLiveBroadcastRoomMemberService imLiveBroadcastRoomMemberService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ImGroupService imGroupService;
|
|
/**
|
|
/**
|
|
* 生成房间UID
|
|
* 生成房间UID
|
|
*
|
|
*
|
|
@@ -181,10 +189,13 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
|
* @return
|
|
* @return
|
|
*/
|
|
*/
|
|
@Override
|
|
@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)) {
|
|
if (Objects.isNull(liveRoom)) {
|
|
log.error("teacherCheckRoomInfo>>>live not start roomUid: {} userId:{}", roomUid, userId);
|
|
log.error("teacherCheckRoomInfo>>>live not start roomUid: {} userId:{}", roomUid, userId);
|
|
throw new BizException("直播还未开始!");
|
|
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);
|
|
log.error("teacherCheckRoomInfo>>>live not start roomUid: {} userId:{}", roomUid, userId);
|
|
throw new BizException("直播还未开始!");
|
|
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);
|
|
log.error("teacherCheckRoomInfo>>>not speaker roomUid: {} userId:{}", roomUid, userId);
|
|
throw new BizException("您不是该直播间的主讲人!");
|
|
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
|
|
@Override
|
|
@@ -715,6 +792,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
|
if (StringUtils.isBlank(user.getStatus())) {
|
|
if (StringUtils.isBlank(user.getStatus())) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+ Date now = new Date();
|
|
String userIdStr = user.getUserid();
|
|
String userIdStr = user.getUserid();
|
|
//获取当前用户所在房间的uid
|
|
//获取当前用户所在房间的uid
|
|
RBucket<String> userRoom = redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userIdStr));
|
|
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());
|
|
String roomUid = Optional.ofNullable(user.getRoomUid()).orElse(userRoom.get());
|
|
|
|
|
|
- Date now = new Date();
|
|
|
|
//获取当前用户状态变更的时间
|
|
//获取当前用户状态变更的时间
|
|
long userStateTime = Optional.ofNullable(user.getTime()).orElse(now.getTime());
|
|
long userStateTime = Optional.ofNullable(user.getTime()).orElse(now.getTime());
|
|
RBucket<Long> userStateTimeCache = redissonClient.getBucket(LIVE_USER_LAST_TIME.replace(USER_ID, userIdStr));
|
|
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::getStudentId, userId)
|
|
.eq(StudentAttendance::getCourseScheduleId, schedule.getId())
|
|
.eq(StudentAttendance::getCourseScheduleId, schedule.getId())
|
|
.set(StudentAttendance::getSignOutTime, now));
|
|
.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());
|
|
this.sendOnlineUserCount(roomUid, userId, onlineUserInfo.size());
|
|
log.info("opsRoom>>>> looker userInfo: {}", userJsonStr);
|
|
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());
|
|
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 roomUid 房间uid
|
|
* @param userId 用户id 观看者id
|
|
* @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) {
|
|
if (room == null) {
|
|
throw new BizException("直播间不存在");
|
|
throw new BizException("直播间不存在");
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ room.setId(room.getId());
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ room.setBanStatus(liveRoom.getBanStatus());
|
|
// 设置直播群组自定义数据
|
|
// 设置直播群组自定义数据
|
|
if (liveRoom.getSpeakerStatus() != null) {
|
|
if (liveRoom.getSpeakerStatus() != null) {
|
|
|
|
+ room.setSpeakerStatus(liveRoom.getSpeakerStatus());
|
|
setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_STATUS,
|
|
setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_STATUS,
|
|
liveRoom.getSpeakerStatus() == 0 ? EAnchorStatus.OFFLINE.getCode() : EAnchorStatus.ONLINE.getCode());
|
|
liveRoom.getSpeakerStatus() == 0 ? EAnchorStatus.OFFLINE.getCode() : EAnchorStatus.ONLINE.getCode());
|
|
}
|
|
}
|
|
|
|
|
|
if (liveRoom.getPushStatus() != null) {
|
|
if (liveRoom.getPushStatus() != null) {
|
|
|
|
+ room.setPushStatus(liveRoom.getPushStatus());
|
|
// 设置推流状态
|
|
// 设置推流状态
|
|
setGroupDefinedData(room, EGroupDefinedDataType.LIVE_STATUS, liveRoom.getPushStatus() == 1 ?
|
|
setGroupDefinedData(room, EGroupDefinedDataType.LIVE_STATUS, liveRoom.getPushStatus() == 1 ?
|
|
EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
|
|
EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
|
|
@@ -1671,6 +1712,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
|
|
|
|
|
|
|
|
|
if (liveRoom.getBanStatus() != null) {
|
|
if (liveRoom.getBanStatus() != null) {
|
|
|
|
+ room.setBanStatus(liveRoom.getBanStatus());
|
|
setGroupDefinedData(room, EGroupDefinedDataType.GLOBAL_BAN, liveRoom.getBanStatus() == 1 ?
|
|
setGroupDefinedData(room, EGroupDefinedDataType.GLOBAL_BAN, liveRoom.getBanStatus() == 1 ?
|
|
EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
|
|
EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
|
|
}
|
|
}
|
|
@@ -1686,8 +1728,671 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
|
setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_MIC,
|
|
setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_MIC,
|
|
liveRoom.getMicStatus() == 1 ? EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
|
|
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;
|
|
|
|
+
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|