|  | @@ -0,0 +1,493 @@
 | 
	
		
			
				|  |  | +package com.zanxiang.game.module.manage.websocket;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 | 
	
		
			
				|  |  | +import com.zanxiang.erp.base.pojo.TokenInfo;
 | 
	
		
			
				|  |  | +import com.zanxiang.erp.security.util.SecurityUtil;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.enums.KfOperateEnum;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.enums.KfRoomMsgOwnerEnum;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.pojo.params.KfWebSocketMsgParam;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.service.IKfQuickReplyService;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.service.IKfRoomMsgService;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.service.IKfRoomService;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.service.IKfSessionUserService;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.service.api.KfWxApiService;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.manage.utils.FileUtil;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.mybatis.entity.KfRoom;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.mybatis.entity.KfRoomMsg;
 | 
	
		
			
				|  |  | +import com.zanxiang.game.module.mybatis.entity.KfSessionUser;
 | 
	
		
			
				|  |  | +import com.zanxiang.module.redis.service.IDistributedLockComponent;
 | 
	
		
			
				|  |  | +import com.zanxiang.module.util.JsonUtil;
 | 
	
		
			
				|  |  | +import lombok.extern.slf4j.Slf4j;
 | 
	
		
			
				|  |  | +import org.apache.logging.log4j.util.Strings;
 | 
	
		
			
				|  |  | +import org.springframework.beans.factory.annotation.Autowired;
 | 
	
		
			
				|  |  | +import org.springframework.context.annotation.Lazy;
 | 
	
		
			
				|  |  | +import org.springframework.stereotype.Component;
 | 
	
		
			
				|  |  | +import org.springframework.web.socket.*;
 | 
	
		
			
				|  |  | +import reactor.util.function.Tuple2;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import java.io.IOException;
 | 
	
		
			
				|  |  | +import java.time.LocalDateTime;
 | 
	
		
			
				|  |  | +import java.util.HashMap;
 | 
	
		
			
				|  |  | +import java.util.List;
 | 
	
		
			
				|  |  | +import java.util.Map;
 | 
	
		
			
				|  |  | +import java.util.Objects;
 | 
	
		
			
				|  |  | +import java.util.concurrent.TimeUnit;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @author : lingfeng
 | 
	
		
			
				|  |  | + * @time : 2024-02-23
 | 
	
		
			
				|  |  | + * @description : 客服消息Websocket处理器
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +@Slf4j
 | 
	
		
			
				|  |  | +@Lazy(value = false)
 | 
	
		
			
				|  |  | +@Component
 | 
	
		
			
				|  |  | +public class KfMsgWebsocketHandler implements WebSocketHandler {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private KfMsgWebSocketSessionRegistry kfMsgWebSocketSessionRegistry;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private IKfRoomService kfRoomService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private IKfSessionUserService kfSessionUserService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private IKfRoomMsgService kfRoomMsgService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private IDistributedLockComponent distributedLockComponent;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private KfWxApiService wxApiService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private IKfQuickReplyService kfQuickReplyService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * websocket连接建立成功
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 收到消息
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
 | 
	
		
			
				|  |  | +        String msgStr = message.getPayload().toString();
 | 
	
		
			
				|  |  | +        //消息解析
 | 
	
		
			
				|  |  | +        KfWebSocketMsgParam param = JsonUtil.toObj(msgStr, KfWebSocketMsgParam.class);
 | 
	
		
			
				|  |  | +        //消息类型
 | 
	
		
			
				|  |  | +        KfWebSocketMsgEnum webSocketMsgType = param.getWebSocketMsgType();
 | 
	
		
			
				|  |  | +        //游戏id
 | 
	
		
			
				|  |  | +        Long gameId = param.getGameId();
 | 
	
		
			
				|  |  | +        //参数验证不通过, 直接结束
 | 
	
		
			
				|  |  | +        if (!this.paramCheck(session, webSocketMsgType, param.getToken(), gameId)) {
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //处理不同类型的消息
 | 
	
		
			
				|  |  | +        switch (webSocketMsgType) {
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_KF_HAND_SHAKE:
 | 
	
		
			
				|  |  | +                kfHandShake(session, webSocketMsgType);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_KF_CREATE_CONNECT:
 | 
	
		
			
				|  |  | +                kfCreateConnect(session, webSocketMsgType, gameId);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_USER_CONNECT_JOIN:
 | 
	
		
			
				|  |  | +                userConnectJoin(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_ROOM_HISTORY:
 | 
	
		
			
				|  |  | +                msgRoomHistory(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_KF_SEND:
 | 
	
		
			
				|  |  | +                kfSendMsg(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_FINISH_ROOM_LIST:
 | 
	
		
			
				|  |  | +                finishRoomList(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_FINISH_SESSION:
 | 
	
		
			
				|  |  | +                kfFinishSession(session, webSocketMsgType, gameId, param.getRoomId());
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_GET_USER:
 | 
	
		
			
				|  |  | +                getUser(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_GET_ROLE_LIST:
 | 
	
		
			
				|  |  | +                getRoleList(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_GET_ORDER_LIST:
 | 
	
		
			
				|  |  | +                getOrderList(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case WEBSOCKET_MSG_QUICK_REPLY:
 | 
	
		
			
				|  |  | +                quickReply(session, param);
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            default:
 | 
	
		
			
				|  |  | +                this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "参数错误, 未知的消息类型"));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 消息传输发生错误
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 连接被关闭
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
 | 
	
		
			
				|  |  | +        //关闭连接
 | 
	
		
			
				|  |  | +        session.close();
 | 
	
		
			
				|  |  | +        //判断会话是否保存
 | 
	
		
			
				|  |  | +        Object kfUserIdObject = session.getAttributes().get("kfUserId");
 | 
	
		
			
				|  |  | +        if (kfUserIdObject == null) {
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //从session中获取客服id
 | 
	
		
			
				|  |  | +        Long kfUserId = Long.valueOf(kfUserIdObject.toString());
 | 
	
		
			
				|  |  | +        //移除连接
 | 
	
		
			
				|  |  | +        kfMsgWebSocketSessionRegistry.removeSession(kfUserId);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public boolean supportsPartialMessages() {
 | 
	
		
			
				|  |  | +        return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void quickReply(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        KfWebSocketMsgParam.QuickReplyBean quickReplyBean = param.getQuickReplyBean();
 | 
	
		
			
				|  |  | +        if (quickReplyBean == null) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                    "快捷回复参数错误, 参数对象不可为空, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //查询
 | 
	
		
			
				|  |  | +        if (Objects.equals(quickReplyBean.getKfOperateEnum(), KfOperateEnum.KF_OPERATE_SELECT)) {
 | 
	
		
			
				|  |  | +            Tuple2<KfWebSocketMsgDTO.PageBean, List<KfWebSocketMsgDTO.QuickReplyBean>> tuple2 = kfQuickReplyService
 | 
	
		
			
				|  |  | +                    .quickReplyList(param.getPage());
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                    .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                    .page(tuple2.getT1())
 | 
	
		
			
				|  |  | +                    .quickReplyList(tuple2.getT2())
 | 
	
		
			
				|  |  | +                    .build());
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //处理不同类型的消息
 | 
	
		
			
				|  |  | +        Boolean result = null;
 | 
	
		
			
				|  |  | +        switch (quickReplyBean.getKfOperateEnum()) {
 | 
	
		
			
				|  |  | +            case KF_OPERATE_ADD:
 | 
	
		
			
				|  |  | +                result = kfQuickReplyService.quickReplyAdd(param.getQuickReplyBean());
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case KF_OPERATE_DELETE:
 | 
	
		
			
				|  |  | +                result = kfQuickReplyService.quickReplyDelete(param.getQuickReplyBean());
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            case KF_OPERATE_UPDATE:
 | 
	
		
			
				|  |  | +                result = kfQuickReplyService.quickReplyUpdate(param.getQuickReplyBean());
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  | +            default:
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (result != null && result) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.ok(param.getWebSocketMsgType()));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                "快捷回复操作失败, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void getUser(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        if (Strings.isBlank(param.getOpenId())) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                    "获取玩家信息参数错误, openId不可为空, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        KfWebSocketMsgDTO.UserBean userBean = kfRoomService.getUserBean(param.getOpenId());
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .user(userBean)
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void getRoleList(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        if (Strings.isBlank(param.getOpenId())) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                    "获取玩家角色列表参数错误, openId不可为空, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        Tuple2<KfWebSocketMsgDTO.PageBean, List<KfWebSocketMsgDTO.GameRoleBean>> tuple2 = kfRoomService
 | 
	
		
			
				|  |  | +                .getRoleBeanList(param.getOpenId(), param.getPage());
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .page(tuple2.getT1())
 | 
	
		
			
				|  |  | +                .roleList(tuple2.getT2())
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void getOrderList(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        if (Strings.isBlank(param.getOpenId())) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                    "获取玩家订单列表参数错误, openId不可为空, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        Tuple2<KfWebSocketMsgDTO.PageBean, List<KfWebSocketMsgDTO.OrderBean>> tuple2 = kfRoomService
 | 
	
		
			
				|  |  | +                .getOrderBeanList(param.getOpenId(), param.getPage());
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .page(tuple2.getT1())
 | 
	
		
			
				|  |  | +                .orderList(tuple2.getT2())
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void kfSendMsg(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        if (param.getRoomId() == null) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "参数错误,roomId为空"));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        KfRoom kfRoom = kfRoomService.getById(param.getRoomId());
 | 
	
		
			
				|  |  | +        if (kfRoom == null) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "参数错误,房间信息不存在"));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        KfWebSocketMsgParam.MsgContentBean msgContent = param.getMsgContent();
 | 
	
		
			
				|  |  | +        if (msgContent == null) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "参数错误,消息内容不可为空"));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //发送消息
 | 
	
		
			
				|  |  | +        Map<String, Object> msgParamMap = new HashMap<>(3);
 | 
	
		
			
				|  |  | +        msgParamMap.put("touser", kfRoom.getOpenId());
 | 
	
		
			
				|  |  | +        //文本
 | 
	
		
			
				|  |  | +        if (Objects.equals(msgContent.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT)) {
 | 
	
		
			
				|  |  | +            Map<String, Object> textMap = new HashMap<>(1);
 | 
	
		
			
				|  |  | +            textMap.put("content", msgContent.getText());
 | 
	
		
			
				|  |  | +            msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue());
 | 
	
		
			
				|  |  | +            msgParamMap.put(KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue(), textMap);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //图片
 | 
	
		
			
				|  |  | +        if (Objects.equals(msgContent.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE)) {
 | 
	
		
			
				|  |  | +            Map<String, Object> imageMap = new HashMap<>(1);
 | 
	
		
			
				|  |  | +            //图片上传到腾讯, 转成 media_id
 | 
	
		
			
				|  |  | +            Tuple2<Long, String> tuple2 = wxApiService.mediaUpload(param.getGameId(),
 | 
	
		
			
				|  |  | +                    FileUtil.urlToMultipartFile(msgContent.getImage()));
 | 
	
		
			
				|  |  | +            //素材上传失败, 通知返回通知客户端
 | 
	
		
			
				|  |  | +            if (tuple2.getT1() != 0) {
 | 
	
		
			
				|  |  | +                this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), tuple2.getT2()));
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            imageMap.put("media_id", tuple2.getT2());
 | 
	
		
			
				|  |  | +            msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue());
 | 
	
		
			
				|  |  | +            msgParamMap.put(KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue(), imageMap);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //调腾讯接口发送消息
 | 
	
		
			
				|  |  | +        Tuple2<Long, String> tuple2 = wxApiService.sendCustomMessageApi(param.getGameId(), msgParamMap);
 | 
	
		
			
				|  |  | +        //发送失败, 通知返回通知客户端
 | 
	
		
			
				|  |  | +        if (tuple2.getT1() != 0) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), tuple2.getT2()));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //发送成功, 消息入库
 | 
	
		
			
				|  |  | +        kfRoomMsgService.sendMsgSave(param.getGameId(), kfRoom, msgContent);
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.ok(param.getWebSocketMsgType()));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void kfFinishSession(WebSocketSession session, KfWebSocketMsgEnum webSocketMsgType, Long gameId, Long roomId) {
 | 
	
		
			
				|  |  | +        //房间在线状态更新
 | 
	
		
			
				|  |  | +        kfRoomService.update(new LambdaUpdateWrapper<KfRoom>()
 | 
	
		
			
				|  |  | +                .set(KfRoom::getOnline, Boolean.FALSE)
 | 
	
		
			
				|  |  | +                .set(KfRoom::getUpdateTime, LocalDateTime.now())
 | 
	
		
			
				|  |  | +                .eq(KfRoom::getId, roomId));
 | 
	
		
			
				|  |  | +        //推送完整的已链接房间列表
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.RoomBean> onlineRoomList = kfRoomService.getOnlineRoomList(gameId);
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(webSocketMsgType)
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .gameId(gameId)
 | 
	
		
			
				|  |  | +                .roomList(onlineRoomList)
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void finishRoomList(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        Tuple2<KfWebSocketMsgDTO.PageBean, List<KfWebSocketMsgDTO.RoomBean>> tuple2 = kfRoomService
 | 
	
		
			
				|  |  | +                .getFinishRoomList(param.getGameId(), param.getPage());
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .gameId(param.getGameId())
 | 
	
		
			
				|  |  | +                .page(tuple2.getT1())
 | 
	
		
			
				|  |  | +                .roomList(tuple2.getT2())
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void msgRoomHistory(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        if (param.getRoomId() == null) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                    "获取房间历史消息参数错误, roomId不可为空, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //分页获取房间消息列表
 | 
	
		
			
				|  |  | +        Tuple2<KfWebSocketMsgDTO.PageBean, List<KfWebSocketMsgDTO.RoomMsgBean>> tuple2 = kfRoomMsgService
 | 
	
		
			
				|  |  | +                .msgRoomHistory(param.getRoomId(), param.getPage());
 | 
	
		
			
				|  |  | +        //房间信息设置
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.RoomBean> roomList = kfRoomService.getRoomByRoomId(param.getRoomId());
 | 
	
		
			
				|  |  | +        //游戏列表
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.GameBean> gameList = kfRoomService.getKfGameByGameId(param.getGameId());
 | 
	
		
			
				|  |  | +        //发送消息
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .page(tuple2.getT1())
 | 
	
		
			
				|  |  | +                .gameId(param.getGameId())
 | 
	
		
			
				|  |  | +                .roomId(param.getRoomId())
 | 
	
		
			
				|  |  | +                .gameList(gameList)
 | 
	
		
			
				|  |  | +                .roomList(roomList)
 | 
	
		
			
				|  |  | +                .roomMsgList(tuple2.getT2())
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void userConnectJoin(WebSocketSession session, KfWebSocketMsgParam param) {
 | 
	
		
			
				|  |  | +        if (Strings.isBlank(param.getOpenId())) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                    "接入玩家参数错误, openId不可为空, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //触发玩家接入线程锁
 | 
	
		
			
				|  |  | +        if (!distributedLockComponent.doLock(RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + param.getOpenId(),
 | 
	
		
			
				|  |  | +                0L, 1L, TimeUnit.MINUTES)) {
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
 | 
	
		
			
				|  |  | +                    "玩家已被其他客服接入, param : " + JsonUtil.toString(param)));
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //玩家更新
 | 
	
		
			
				|  |  | +        kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
 | 
	
		
			
				|  |  | +                .set(KfSessionUser::getIsWait, Boolean.FALSE)
 | 
	
		
			
				|  |  | +                .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
 | 
	
		
			
				|  |  | +                .eq(KfSessionUser::getOpenId, param.getOpenId())
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        //玩家信息
 | 
	
		
			
				|  |  | +        KfSessionUser kfSessionUser = kfSessionUserService.getById(param.getOpenId());
 | 
	
		
			
				|  |  | +        //房间更新
 | 
	
		
			
				|  |  | +        Long roomId = kfRoomService.userJoinRoom(param.getOpenId(), param.getGameId());
 | 
	
		
			
				|  |  | +        //玩家未读消息更新到房间
 | 
	
		
			
				|  |  | +        kfRoomMsgService.update(new LambdaUpdateWrapper<KfRoomMsg>()
 | 
	
		
			
				|  |  | +                .set(KfRoomMsg::getRoomId, roomId)
 | 
	
		
			
				|  |  | +                .set(KfRoomMsg::getUserId, kfSessionUser == null ? null : kfSessionUser.getUserId())
 | 
	
		
			
				|  |  | +                .set(KfRoomMsg::getKfUserId, SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .set(KfRoomMsg::getUpdateTime, LocalDateTime.now())
 | 
	
		
			
				|  |  | +                .eq(KfRoomMsg::getOpenId, param.getOpenId())
 | 
	
		
			
				|  |  | +                .eq(KfRoomMsg::getGameId, param.getGameId())
 | 
	
		
			
				|  |  | +                .ne(KfRoomMsg::getMsgOwner, KfRoomMsgOwnerEnum.KF_MSG_OWNER_KF.getValue())
 | 
	
		
			
				|  |  | +                .eq(KfRoomMsg::getReadStatus, Boolean.FALSE));
 | 
	
		
			
				|  |  | +        //发送消息, 给该客服返回完整的已接入房间列表
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.RoomBean> roomList = kfRoomService.getOnlineRoomList(param.getGameId());
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .gameId(param.getGameId())
 | 
	
		
			
				|  |  | +                .roomList(roomList)
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +        //发送消息, 给所有在线客服推送完整待接入列表
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.WaitUserBean> waitUserList = kfSessionUserService.getWaitUserList(param.getGameId());
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(param.getWebSocketMsgType())
 | 
	
		
			
				|  |  | +                .gameId(param.getGameId())
 | 
	
		
			
				|  |  | +                .waitUserList(waitUserList)
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +        //释放锁
 | 
	
		
			
				|  |  | +        distributedLockComponent.unlock(RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + param.getOpenId());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void kfCreateConnect(WebSocketSession session, KfWebSocketMsgEnum msgTypeEnum, Long gameId) {
 | 
	
		
			
				|  |  | +        //获取待接入列表
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.WaitUserBean> waitUserList = kfSessionUserService.getWaitUserList(gameId);
 | 
	
		
			
				|  |  | +        //获取已接入房间列表
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.RoomBean> roomList = kfRoomService.getOnlineRoomList(gameId);
 | 
	
		
			
				|  |  | +        //发送消息
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(msgTypeEnum)
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .gameId(gameId)
 | 
	
		
			
				|  |  | +                .waitUserList(waitUserList)
 | 
	
		
			
				|  |  | +                .roomList(roomList)
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void kfHandShake(WebSocketSession session, KfWebSocketMsgEnum msgTypeEnum) {
 | 
	
		
			
				|  |  | +        //获取游戏列表
 | 
	
		
			
				|  |  | +        List<KfWebSocketMsgDTO.GameBean> gameList = kfRoomService.getKfGameList();
 | 
	
		
			
				|  |  | +        //发送消息
 | 
	
		
			
				|  |  | +        this.sendMessage(session, KfWebSocketMsgDTO.builder()
 | 
	
		
			
				|  |  | +                .webSocketMsgType(msgTypeEnum)
 | 
	
		
			
				|  |  | +                .kfUserId(SecurityUtil.getUserId())
 | 
	
		
			
				|  |  | +                .gameList(gameList)
 | 
	
		
			
				|  |  | +                .build());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private boolean paramCheck(WebSocketSession session, KfWebSocketMsgEnum webSocketMsgType, String token, Long gameId) {
 | 
	
		
			
				|  |  | +        //gameId参数为空
 | 
	
		
			
				|  |  | +        if (gameId == null) {
 | 
	
		
			
				|  |  | +            //心跳和握手消息, 不需要携带gameId
 | 
	
		
			
				|  |  | +            if (!Objects.equals(KfWebSocketMsgEnum.WEBSOCKET_MSG_KF_HAND_SHAKE, webSocketMsgType)
 | 
	
		
			
				|  |  | +                    && !Objects.equals(KfWebSocketMsgEnum.WEBSOCKET_MSG_QUICK_REPLY, webSocketMsgType)) {
 | 
	
		
			
				|  |  | +                this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "参数错误, gameId为空"));
 | 
	
		
			
				|  |  | +                return Boolean.FALSE;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //令牌为空
 | 
	
		
			
				|  |  | +        if (Strings.isBlank(token)) {
 | 
	
		
			
				|  |  | +            log.error("非法参数, token令牌和客服id不可为空");
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "非法参数, token令牌和kfUserId不可为空"));
 | 
	
		
			
				|  |  | +            return Boolean.FALSE;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //令牌验证
 | 
	
		
			
				|  |  | +        TokenInfo tokenInfo;
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            tokenInfo = SecurityUtil.parseToken(token);
 | 
	
		
			
				|  |  | +        } catch (Exception e) {
 | 
	
		
			
				|  |  | +            log.error("token验证异常, token : {}, e : {}", token, e.getMessage());
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "token验证异常, " + e.getMessage()));
 | 
	
		
			
				|  |  | +            return Boolean.FALSE;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (tokenInfo == null) {
 | 
	
		
			
				|  |  | +            log.error("参数错误, 令牌验证不通过, token : {}", token);
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "参数错误, 令牌验证不通过"));
 | 
	
		
			
				|  |  | +            return Boolean.FALSE;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //将token设置到当前线程
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            SecurityUtil.fillToken(token);
 | 
	
		
			
				|  |  | +        } catch (Exception e) {
 | 
	
		
			
				|  |  | +            log.error("token刷新到本地线程失败, token : {}, e : {}", token, e.getMessage());
 | 
	
		
			
				|  |  | +            this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "token刷新到本地线程失败"));
 | 
	
		
			
				|  |  | +            return Boolean.FALSE;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //添加会话
 | 
	
		
			
				|  |  | +        WebSocketSession webSocketSession = kfMsgWebSocketSessionRegistry.getSession(SecurityUtil.getUserId());
 | 
	
		
			
				|  |  | +        if (webSocketSession == null) {
 | 
	
		
			
				|  |  | +            session.getAttributes().put("kfUserId", SecurityUtil.getUserId());
 | 
	
		
			
				|  |  | +            kfMsgWebSocketSessionRegistry.addSession(SecurityUtil.getUserId(), session);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //返回验证通过
 | 
	
		
			
				|  |  | +        return Boolean.TRUE;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * session直接发送消息
 | 
	
		
			
				|  |  | +     *
 | 
	
		
			
				|  |  | +     * @param session : 会话对象
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private void sendMessage(WebSocketSession session, KfWebSocketMsgDTO kfWebSocketMsgDTO) {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            session.sendMessage(new TextMessage(JsonUtil.toString(kfWebSocketMsgDTO)));
 | 
	
		
			
				|  |  | +        } catch (IOException ignored) {
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |