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.*;
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.*;
import com.zanxiang.game.module.manage.service.api.KfWxApiService;
import com.zanxiang.game.module.manage.utils.FileUtil;
import com.zanxiang.game.module.manage.utils.RedisUtil;
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.game.module.mybatis.entity.KfSystemReply;
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.data.redis.core.RedisTemplate;
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.*;

/**
 * @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 IKfSystemReplyService kfSystemReplyService;

    @Autowired
    private KfWxApiService kfWxApiService;

    @Autowired
    private KfWxApiService wxApiService;

    @Autowired
    private IGameAuthRoleService gameAuthRoleService;

    @Autowired
    private IKfQuickReplyService kfQuickReplyService;

    @Autowired
    private RedisUtil<String> redisUtil;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * websocket连接建立成功
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    }

    /**
     * 收到消息
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        try {
            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, "参数错误, 未知的消息类型"));
            }
        } catch (Exception e) {
            log.error("Websocket消息处理出现异常, e : {}", e.getMessage());
        } finally {
            SecurityUtil.destroy();
        }
    }

    /**
     * 消息传输发生错误
     */
    @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(), param.getGameId());
        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.getGameId(), 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.getGameId(), 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) {
        //查询房间信息
        KfRoom kfRoom = kfRoomService.getById(roomId);
        if (kfRoom == null) {
            this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "参数错误,房间信息不存在"));
            return;
        }
        //房间在线状态更新
        kfRoomService.update(new LambdaUpdateWrapper<KfRoom>()
                .set(KfRoom::getOnline, Boolean.FALSE)
                .set(KfRoom::getUpdateTime, LocalDateTime.now())
                .eq(KfRoom::getId, roomId)
        );
        //玩家信息更新
        kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
                .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
                .eq(KfSessionUser::getOpenId, kfRoom.getOpenId())
                .eq(KfSessionUser::getGameId, kfRoom.getGameId())
        );
        //房间消息全部变成已读状态
        kfRoomMsgService.update(new LambdaUpdateWrapper<KfRoomMsg>()
                .set(KfRoomMsg::getReadStatus, Boolean.TRUE)
                .set(KfRoomMsg::getUpdateTime, LocalDateTime.now())
                .eq(KfRoomMsg::getReadStatus, Boolean.FALSE)
                .eq(KfRoomMsg::getRoomId, roomId)
        );
        //推送完整的已链接房间列表
        List<KfWebSocketMsgDTO.RoomBean> onlineRoomList = kfRoomService.getOnlineRoomList(gameId);
        this.sendMessage(session, KfWebSocketMsgDTO.builder()
                .webSocketMsgType(webSocketMsgType)
                .kfUserId(SecurityUtil.getUserId())
                .gameId(gameId)
                .roomList(onlineRoomList)
                .build());
        //以防锁依然存在, 尝试释放锁, 锁如果不存在, 会抛出异常, 直接捕获不处理
        String lockKey = RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + kfRoom.getOpenId() + "_" + gameId;
        try {
            log.error("结束会话释放锁, key : {}", lockKey);
            redisUtil.deleteCache(lockKey);
        } catch (Exception e) {
            log.error("结束会话释放锁异常, key : {}, e : {}", lockKey, e.getMessage());
        }
    }

    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(), SecurityUtil.getUserId());
        //发送消息
        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 (!gameAuthRoleService.isCustomer()) {
            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
                    "非客服人员, 不可接待玩家, param : " + JsonUtil.toString(param)));
            return;
        }
        //玩家信息, 判断玩家是否已经被接入
        KfSessionUser kfSessionUser = kfSessionUserService.getById(param.getOpenId(), param.getGameId());
        if (!kfSessionUser.getIsWait()) {
            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "玩家非待接入状态(已被其他客服接入)"));
            return;
        }
        //触发玩家接入线程锁
        String lockKey = RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + param.getOpenId() + "_" + param.getGameId();
        log.error("玩家接入触发线程锁, key : {}", lockKey);
        if (!redisUtil.setIfAbsent(lockKey, lockKey, ExpireTimeEnum.FIVE_MIN.getTime())) {
            log.error("玩家接入锁碰撞, 未接入成功, key : {}", lockKey);
            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "玩家非待接入状态,(其他客服已抢先接入)"));
            return;
        }
        //玩家更新为已接入
        kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
                .set(KfSessionUser::getIsWait, Boolean.FALSE)
                .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
                .eq(KfSessionUser::getOpenId, param.getOpenId())
                .eq(KfSessionUser::getGameId, param.getGameId())
        );
        //房间更新
        Long roomId = kfRoomService.userJoinRoom(param.getOpenId(), param.getGameId());
        //玩家未读消息, 系统消息更新到房间
        kfRoomMsgService.update(new LambdaUpdateWrapper<KfRoomMsg>()
                .set(KfRoomMsg::getRoomId, roomId)
                .set(KfRoomMsg::getKfUserId, SecurityUtil.getUserId())
                .set(kfSessionUser.getUserId() != null, KfRoomMsg::getUserId, kfSessionUser.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.pushMessage(KfWebSocketMsgDTO.builder()
                .webSocketMsgType(KfWebSocketMsgEnum.WEBSOCKET_MSG_WAIT_LIST)
                .gameId(param.getGameId())
                .waitUserList(waitUserList)
                .build());
        //发送欢迎语
        this.sendWelcomeMsg(param.getGameId(), param.getOpenId(), roomId, kfSessionUser.getUserId());
    }

    private void sendWelcomeMsg(Long gameId, String openId, Long roomId, Long userId) {
        //获取自动回复配置
        KfSystemReply kfSystemReply = kfSystemReplyService.getById(gameId);
        if (kfSystemReply == null || Strings.isBlank(kfSystemReply.getWorkSysReply())) {
            return;
        }
        //休息时间, 发送指定消息
        Map<String, Object> textMap = new HashMap<>(1);
        textMap.put("content", kfSystemReply.getWorkSysReply());
        Map<String, Object> msgParamMap = new HashMap<>(3);
        msgParamMap.put("touser", openId);
        msgParamMap.put("text", textMap);
        msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue());
        Tuple2<Long, String> tuple2 = kfWxApiService.sendCustomMessageApi(gameId, msgParamMap);
        //发送失败
        if (!Objects.equals(tuple2.getT1(), 0L)) {
            return;
        }
        //保存消息
        Map<String, Object> kfRoomMsgMap = new HashMap<>(1);
        kfRoomMsgMap.put("text", kfSystemReply.getWorkSysReply());
        kfRoomMsgService.save(this.transform(openId, gameId, roomId, userId, JsonUtil.toString(kfRoomMsgMap)));
    }

    private KfRoomMsg transform(String openId, Long gameId, Long roomId, Long userId, String msgContent) {
        return KfRoomMsg.builder()
                .msgId(UUID.randomUUID().toString().replace("-", ""))
                .msgType(KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue())
                .gameId(gameId)
                .openId(openId)
                .userId(userId)
                .readStatus(Boolean.TRUE)
                .roomId(roomId)
                .kfUserId(SecurityUtil.getUserId())
                .msgOwner(KfRoomMsgOwnerEnum.KF_MSG_OWNER_SYSTEM.getValue())
                .content(msgContent)
                .source(msgContent)
                .sortTime(System.currentTimeMillis())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
    }

    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) {
        }
    }

    /**
     * 消息发送到redis广播
     */
    private void pushMessage(KfWebSocketMsgDTO kfWebSocketMsgDTO) {
        redisTemplate.convertAndSend(RedisKeyConstant.KF_MSG_REDIS_LISTEN_TOPIC, JsonUtil.toString(kfWebSocketMsgDTO));
    }
}
