|
|
@@ -1,8 +1,6 @@
|
|
|
package com.fs.live.websocket.handle;
|
|
|
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
-import com.fs.his.domain.FsUser;
|
|
|
-import com.fs.his.service.IFsUserService;
|
|
|
import com.fs.hisStore.domain.FsUserScrm;
|
|
|
import com.fs.hisStore.service.IFsUserScrmService;
|
|
|
import com.fs.live.websocket.bean.SendMsgVo;
|
|
|
@@ -14,7 +12,7 @@ import com.fs.live.domain.LiveWatchUser;
|
|
|
import com.fs.live.service.ILiveMsgService;
|
|
|
import com.fs.live.service.ILiveService;
|
|
|
import com.fs.live.service.ILiveWatchUserService;
|
|
|
-import com.fs.live.vo.LiveWatchUserVO;
|
|
|
+import com.fs.live.websocket.auth.TenantChannelContext;
|
|
|
import io.netty.channel.*;
|
|
|
import io.netty.channel.group.ChannelGroup;
|
|
|
import io.netty.channel.group.DefaultChannelGroup;
|
|
|
@@ -36,7 +34,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
@Slf4j
|
|
|
public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
|
|
|
|
|
- // 容器
|
|
|
private final static ConcurrentHashMap<Long, CopyOnWriteArrayList<Channel>> adminRooms = new ConcurrentHashMap<>();
|
|
|
private final static ConcurrentHashMap<Long, ConcurrentHashMap<Long, Channel>> rooms = new ConcurrentHashMap<>();
|
|
|
private final static ConcurrentHashMap<Long, ChannelGroup> roomGroups = new ConcurrentHashMap<>();
|
|
|
@@ -44,128 +41,68 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
|
|
|
private final static ILiveWatchUserService liveWatchUserService = SpringUtils.getBean(ILiveWatchUserService.class);
|
|
|
private final static ILiveMsgService liveMsgService = SpringUtils.getBean(ILiveMsgService.class);
|
|
|
private final static IFsUserScrmService fsUserService = SpringUtils.getBean(IFsUserScrmService.class);
|
|
|
+ private final static TenantChannelContext tenantChannelContext = SpringUtils.getBean(TenantChannelContext.class);
|
|
|
|
|
|
/**
|
|
|
- * 处理握手
|
|
|
- * @param ctx 连接
|
|
|
- * @param evt 数据
|
|
|
- * @throws Exception 异常
|
|
|
+ * 处理握手完成事件
|
|
|
*/
|
|
|
@Override
|
|
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
|
|
-
|
|
|
- // 处理 WebSocket 握手完成事件
|
|
|
if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
|
|
|
Long userId = ctx.channel().attr(AttrConstant.ATTR_USER_ID).get();
|
|
|
Long liveId = ctx.channel().attr(AttrConstant.ATTR_LIVE_ID).get();
|
|
|
Long userType = ctx.channel().attr(AttrConstant.ATTR_USER_TYPE).get();
|
|
|
-
|
|
|
-
|
|
|
- if (Objects.isNull(liveService.selectLiveByLiveId(liveId))) {
|
|
|
- ctx.channel().writeAndFlush(new TextWebSocketFrame("Error: 未找到直播间")).addListener(ChannelFutureListener.CLOSE);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- Map<Long, Channel> room = getRoom(liveId);
|
|
|
- List<Channel> adminRoom = getAdminRoom(liveId);
|
|
|
- ChannelGroup roomGroup = getRoomGroup(liveId);
|
|
|
- roomGroup.add(ctx.channel());
|
|
|
-
|
|
|
- if (userType == 0) {
|
|
|
-
|
|
|
-
|
|
|
- FsUserScrm fsUser = fsUserService.selectFsUserByUserId(userId);
|
|
|
- // 加入房间
|
|
|
- LiveWatchUser liveWatchUser = liveWatchUserService.joinWithoutLocation(fsUser,liveId, userId);
|
|
|
- room.put(userId, ctx.channel());
|
|
|
- if (Objects.isNull(fsUser)) {
|
|
|
- ctx.channel().writeAndFlush(new TextWebSocketFrame("Error: 用户信息错误")).addListener(ChannelFutureListener.CLOSE);
|
|
|
+ try {
|
|
|
+ tenantChannelContext.activate(ctx.channel());
|
|
|
+ if (Objects.isNull(liveService.selectLiveByLiveId(liveId))) {
|
|
|
+ ctx.channel().writeAndFlush(new TextWebSocketFrame("Error: 未找到直播间")).addListener(ChannelFutureListener.CLOSE);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
- SendMsgVo sendMsgVo = new SendMsgVo();
|
|
|
- sendMsgVo.setLiveId(liveId);
|
|
|
- sendMsgVo.setUserId(userId);
|
|
|
- sendMsgVo.setUserType(userType);
|
|
|
- sendMsgVo.setCmd("entry");
|
|
|
- sendMsgVo.setMsg("用户进入");
|
|
|
- sendMsgVo.setData(JSONObject.toJSONString(liveWatchUser));
|
|
|
- sendMsgVo.setNickName(fsUser.getNickname());
|
|
|
- sendMsgVo.setAvatar(fsUser.getAvatar());
|
|
|
-
|
|
|
- // 广播连接消息
|
|
|
- broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
- } else if (userType == 1) {
|
|
|
- adminRoom.add(ctx.channel());
|
|
|
+ Map<Long, Channel> room = getRoom(liveId);
|
|
|
+ List<Channel> adminRoom = getAdminRoom(liveId);
|
|
|
+ ChannelGroup roomGroup = getRoomGroup(liveId);
|
|
|
+ roomGroup.add(ctx.channel());
|
|
|
+ if (userType == 0) {
|
|
|
+ FsUserScrm fsUser = fsUserService.selectFsUserByUserId(userId);
|
|
|
+ LiveWatchUser liveWatchUser = liveWatchUserService.joinWithoutLocation(fsUser, liveId, userId);
|
|
|
+ room.put(userId, ctx.channel());
|
|
|
+ if (Objects.isNull(fsUser)) {
|
|
|
+ ctx.channel().writeAndFlush(new TextWebSocketFrame("Error: 用户信息错误")).addListener(ChannelFutureListener.CLOSE);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ SendMsgVo sendMsgVo = new SendMsgVo();
|
|
|
+ sendMsgVo.setLiveId(liveId);
|
|
|
+ sendMsgVo.setUserId(userId);
|
|
|
+ sendMsgVo.setUserType(userType);
|
|
|
+ sendMsgVo.setCmd("entry");
|
|
|
+ sendMsgVo.setMsg("用户进入");
|
|
|
+ sendMsgVo.setData(JSONObject.toJSONString(liveWatchUser));
|
|
|
+ sendMsgVo.setNickName(fsUser.getNickname());
|
|
|
+ sendMsgVo.setAvatar(fsUser.getAvatar());
|
|
|
+ broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
+ } else if (userType == 1) {
|
|
|
+ adminRoom.add(ctx.channel());
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ tenantChannelContext.clear();
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取房间
|
|
|
- * @param liveId 直播间ID
|
|
|
- * @return 容器
|
|
|
- */
|
|
|
- private CopyOnWriteArrayList<Channel> getAdminRoom(Long liveId) {
|
|
|
- return adminRooms.computeIfAbsent(liveId, k -> new CopyOnWriteArrayList<>());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取房间
|
|
|
- * @param liveId 直播间ID
|
|
|
- * @return 容器
|
|
|
- */
|
|
|
- private ConcurrentHashMap<Long, Channel> getRoom(Long liveId) {
|
|
|
- return rooms.computeIfAbsent(liveId, k -> new ConcurrentHashMap<>());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取房间用户组
|
|
|
- * @param liveId 直播间ID
|
|
|
- * @return 用户组
|
|
|
- */
|
|
|
- private ChannelGroup getRoomGroup(Long liveId) {
|
|
|
- return roomGroups.computeIfAbsent(liveId, k -> new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 发送广播
|
|
|
- * @param liveId 直播间ID
|
|
|
- * @param msg 消息
|
|
|
- */
|
|
|
- private void broadcastMessage(Long liveId, String msg) {
|
|
|
- getRoomGroup(liveId).writeAndFlush(new TextWebSocketFrame(msg));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 发送指定消息
|
|
|
- * @param channel 连接
|
|
|
- * @param message 消息
|
|
|
- */
|
|
|
- private void sendMessage(Channel channel, String message) {
|
|
|
- channel.writeAndFlush(new TextWebSocketFrame(message));
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 接收消息
|
|
|
- * @param channelHandlerContext 连接
|
|
|
- * @param textWebSocketFrame 消息
|
|
|
- * @throws Exception 异常
|
|
|
*/
|
|
|
@Override
|
|
|
- protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
|
|
|
-
|
|
|
- Long liveId = channelHandlerContext.channel().attr(AttrConstant.ATTR_LIVE_ID).get();
|
|
|
- Long userType = channelHandlerContext.channel().attr(AttrConstant.ATTR_USER_TYPE).get();
|
|
|
-
|
|
|
- SendMsgVo msg = JSONObject.parseObject( textWebSocketFrame.text(), SendMsgVo.class);
|
|
|
- if(msg.isOn()) return;
|
|
|
+ protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception {
|
|
|
+ Long liveId = ctx.channel().attr(AttrConstant.ATTR_LIVE_ID).get();
|
|
|
+ Long userType = ctx.channel().attr(AttrConstant.ATTR_USER_TYPE).get();
|
|
|
+ SendMsgVo msg = JSONObject.parseObject(textWebSocketFrame.text(), SendMsgVo.class);
|
|
|
+ if (msg.isOn()) return;
|
|
|
try {
|
|
|
+ tenantChannelContext.activate(ctx.channel());
|
|
|
switch (msg.getCmd()) {
|
|
|
case "heartbeat":
|
|
|
- sendMessage(channelHandlerContext.channel(), JSONObject.toJSONString(R.ok().put("data", msg)));
|
|
|
+ sendMessage(ctx.channel(), JSONObject.toJSONString(R.ok().put("data", msg)));
|
|
|
break;
|
|
|
case "sendMsg":
|
|
|
LiveMsg liveMsg = new LiveMsg();
|
|
|
@@ -175,96 +112,97 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
|
|
|
liveMsg.setAvatar(msg.getAvatar());
|
|
|
liveMsg.setMsg(msg.getMsg());
|
|
|
liveMsg.setCreateTime(new Date());
|
|
|
-
|
|
|
if (userType == 0) {
|
|
|
Map<String, Integer> liveFlagWithCache = liveWatchUserService.getLiveFlagWithCache(liveId);
|
|
|
- LiveWatchUser liveWatchUser = liveWatchUserService.selectLiveWatchUserByFlag(msg.getLiveId(), msg.getUserId(), liveFlagWithCache.get("liveFlag"), liveFlagWithCache.get("replayFlag"));
|
|
|
- if(liveWatchUser != null && liveWatchUser.getMsgStatus() == 1){
|
|
|
- sendMessage(channelHandlerContext.channel(), JSONObject.toJSONString(R.error("你以被禁言")));
|
|
|
+ LiveWatchUser liveWatchUser = liveWatchUserService.selectLiveWatchUserByFlag(
|
|
|
+ msg.getLiveId(), msg.getUserId(),
|
|
|
+ liveFlagWithCache.get("liveFlag"), liveFlagWithCache.get("replayFlag"));
|
|
|
+ if (liveWatchUser != null && liveWatchUser.getMsgStatus() == 1) {
|
|
|
+ sendMessage(ctx.channel(), JSONObject.toJSONString(R.error("你以被禁言")));
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
liveMsgService.insertLiveMsg(liveMsg);
|
|
|
}
|
|
|
-
|
|
|
msg.setOn(true);
|
|
|
msg.setData(JSONObject.toJSONString(liveMsg));
|
|
|
-
|
|
|
- // 广播消息
|
|
|
broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
|
|
|
break;
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
log.error("webSocket 消息处理失败 msg: {}", e.getMessage(), e);
|
|
|
+ } finally {
|
|
|
+ tenantChannelContext.clear();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 断开连接
|
|
|
- * @param ctx 连接
|
|
|
- * @throws Exception 异常
|
|
|
*/
|
|
|
@Override
|
|
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
|
-
|
|
|
Long userId = ctx.channel().attr(AttrConstant.ATTR_USER_ID).get();
|
|
|
Long liveId = ctx.channel().attr(AttrConstant.ATTR_LIVE_ID).get();
|
|
|
Long userType = ctx.channel().attr(AttrConstant.ATTR_USER_TYPE).get();
|
|
|
-
|
|
|
if (Objects.isNull(userId) || Objects.isNull(liveId) || Objects.isNull(userType)) {
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- Map<Long, Channel> room = getRoom(liveId);
|
|
|
- List<Channel> adminRoom = getAdminRoom(liveId);
|
|
|
- ChannelGroup roomGroup = getRoomGroup(liveId);
|
|
|
-
|
|
|
- if (userType == 0) {
|
|
|
- FsUserScrm fsUser = fsUserService.selectFsUserByUserId(userId);
|
|
|
- LiveWatchUser close = liveWatchUserService.close(fsUser,liveId, userId);
|
|
|
- room.remove(userId);
|
|
|
-
|
|
|
- if (room.isEmpty()) {
|
|
|
- rooms.remove(liveId);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- SendMsgVo sendMsgVo = new SendMsgVo();
|
|
|
- sendMsgVo.setLiveId(liveId);
|
|
|
- sendMsgVo.setUserId(userId);
|
|
|
- sendMsgVo.setUserType(userType);
|
|
|
- sendMsgVo.setCmd("out");
|
|
|
- sendMsgVo.setMsg("用户离开");
|
|
|
- sendMsgVo.setData(JSONObject.toJSONString(close));
|
|
|
- sendMsgVo.setNickName(fsUser.getNickname());
|
|
|
- sendMsgVo.setAvatar(fsUser.getAvatar());
|
|
|
-
|
|
|
- // 广播离开消息
|
|
|
- broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
- } else {
|
|
|
- adminRoom.remove(ctx.channel());
|
|
|
- if (adminRoom.isEmpty()) {
|
|
|
- adminRooms.remove(liveId);
|
|
|
+ try {
|
|
|
+ tenantChannelContext.activate(ctx.channel());
|
|
|
+ Map<Long, Channel> room = getRoom(liveId);
|
|
|
+ List<Channel> adminRoom = getAdminRoom(liveId);
|
|
|
+ ChannelGroup roomGroup = getRoomGroup(liveId);
|
|
|
+ if (userType == 0) {
|
|
|
+ FsUserScrm fsUser = fsUserService.selectFsUserByUserId(userId);
|
|
|
+ LiveWatchUser close = liveWatchUserService.close(fsUser, liveId, userId);
|
|
|
+ room.remove(userId);
|
|
|
+ if (room.isEmpty()) rooms.remove(liveId);
|
|
|
+ SendMsgVo sendMsgVo = new SendMsgVo();
|
|
|
+ sendMsgVo.setLiveId(liveId);
|
|
|
+ sendMsgVo.setUserId(userId);
|
|
|
+ sendMsgVo.setUserType(userType);
|
|
|
+ sendMsgVo.setCmd("out");
|
|
|
+ sendMsgVo.setMsg("用户离开");
|
|
|
+ sendMsgVo.setData(JSONObject.toJSONString(close));
|
|
|
+ sendMsgVo.setNickName(fsUser.getNickname());
|
|
|
+ sendMsgVo.setAvatar(fsUser.getAvatar());
|
|
|
+ broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
+ } else {
|
|
|
+ adminRoom.remove(ctx.channel());
|
|
|
+ if (adminRoom.isEmpty()) adminRooms.remove(liveId);
|
|
|
}
|
|
|
+ roomGroup.remove(ctx.channel());
|
|
|
+ if (roomGroup.isEmpty()) roomGroups.remove(liveId);
|
|
|
+ } finally {
|
|
|
+ tenantChannelContext.clear();
|
|
|
}
|
|
|
- roomGroup.remove(ctx.channel());
|
|
|
- if (roomGroup.isEmpty()) {
|
|
|
- roomGroups.remove(liveId);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 连接异常
|
|
|
- * @param ctx 连接
|
|
|
- * @param cause 原因
|
|
|
- * @throws Exception 异常
|
|
|
*/
|
|
|
@Override
|
|
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
|
log.error("连接异常 msg: {}", cause.getMessage(), cause);
|
|
|
ctx.close();
|
|
|
}
|
|
|
+
|
|
|
+ private CopyOnWriteArrayList<Channel> getAdminRoom(Long liveId) {
|
|
|
+ return adminRooms.computeIfAbsent(liveId, k -> new CopyOnWriteArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ private ConcurrentHashMap<Long, Channel> getRoom(Long liveId) {
|
|
|
+ return rooms.computeIfAbsent(liveId, k -> new ConcurrentHashMap<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ private ChannelGroup getRoomGroup(Long liveId) {
|
|
|
+ return roomGroups.computeIfAbsent(liveId, k -> new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void broadcastMessage(Long liveId, String msg) {
|
|
|
+ getRoomGroup(liveId).writeAndFlush(new TextWebSocketFrame(msg));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sendMessage(Channel channel, String message) {
|
|
|
+ channel.writeAndFlush(new TextWebSocketFrame(message));
|
|
|
+ }
|
|
|
}
|