mirror of
https://github.com/zongzibinbin/MallChat.git
synced 2026-03-13 21:53:41 +08:00
fix:
1.登录集群改造 2.netty取消上下文
This commit is contained in:
@@ -95,24 +95,26 @@ INSERT INTO `room` VALUES (1, '抹茶群聊', 1, '2023-03-25 22:30:07.328', '202
|
|||||||
-- Table structure for user
|
-- Table structure for user
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
DROP TABLE IF EXISTS `user`;
|
DROP TABLE IF EXISTS `user`;
|
||||||
CREATE TABLE `user` (
|
CREATE TABLE `user` (
|
||||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户id',
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id',
|
||||||
`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户昵称',
|
`name` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户昵称',
|
||||||
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户头像',
|
`avatar` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户头像',
|
||||||
`sex` int(11) NULL DEFAULT NULL COMMENT '性别 1为男性,2为女性',
|
`sex` int(11) DEFAULT NULL COMMENT '性别 1为男性,2为女性',
|
||||||
`open_id` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '微信openid用户标识',
|
`open_id` char(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '微信openid用户标识',
|
||||||
|
`active_status` int(11) DEFAULT '2' COMMENT '在线状态 1在线 2离线',
|
||||||
`last_opt_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '最后上下线时间',
|
`last_opt_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '最后上下线时间',
|
||||||
`ip_info` json NULL COMMENT 'ip信息',
|
`ip_info` json DEFAULT NULL COMMENT 'ip信息',
|
||||||
`item_id` bigint(20) NULL DEFAULT NULL COMMENT '佩戴的徽章id',
|
`item_id` bigint(20) DEFAULT NULL COMMENT '佩戴的徽章id',
|
||||||
`status` int(11) DEFAULT "0" COMMENT '使用状态 0.正常 1拉黑',
|
`status` int(11) DEFAULT '0' COMMENT '使用状态 0.正常 1拉黑',
|
||||||
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
UNIQUE INDEX `uniq_open_id`(`open_id`) USING BTREE,
|
UNIQUE KEY `uniq_open_id` (`open_id`) USING BTREE,
|
||||||
UNIQUE INDEX `uniq_name`(`name`) USING BTREE,
|
UNIQUE KEY `uniq_name` (`name`) USING BTREE,
|
||||||
INDEX `idx_create_time`(`create_time`) USING BTREE,
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
INDEX `idx_update_time`(`update_time`) USING BTREE
|
KEY `idx_update_time` (`update_time`) USING BTREE,
|
||||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
|
KEY `idx_active_status_last_opt_time` (`active_status`,`last_opt_time`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=20000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='用户表';
|
||||||
INSERT INTO `mallchat`.`user` (`id`, `name`, `avatar`, `sex`, `open_id`, `last_opt_time`, `ip_info`, `item_id`, `status`, `create_time`, `update_time`) VALUES (10001, 'ChatGPT', 'https://img1.baidu.com/it/u=3613958228,3522035000&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500', 0, '??', '2023-06-29 17:03:03.357', NULL, NULL, 0, '2023-06-29 17:03:03.357', '2023-07-01 14:56:10.271');
|
INSERT INTO `mallchat`.`user` (`id`, `name`, `avatar`, `sex`, `open_id`, `last_opt_time`, `ip_info`, `item_id`, `status`, `create_time`, `update_time`) VALUES (10001, 'ChatGPT', 'https://img1.baidu.com/it/u=3613958228,3522035000&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500', 0, '??', '2023-06-29 17:03:03.357', NULL, NULL, 0, '2023-06-29 17:03:03.357', '2023-07-01 14:56:10.271');
|
||||||
INSERT INTO `mallchat`.`user` (`id`, `name`, `avatar`, `sex`, `open_id`, `last_opt_time`, `ip_info`, `item_id`, `status`, `create_time`, `update_time`) VALUES (10002, 'ChatGLM2', 'http://mms1.baidu.com/it/u=1979830414,2984779047&fm=253&app=138&f=JPEG&fmt=auto&q=75?w=500&h=500', NULL, '450', '2023-07-01 11:58:24.605', NULL, NULL, 0, '2023-07-01 11:58:24.605', '2023-07-01 12:02:56.900');
|
INSERT INTO `mallchat`.`user` (`id`, `name`, `avatar`, `sex`, `open_id`, `last_opt_time`, `ip_info`, `item_id`, `status`, `create_time`, `update_time`) VALUES (10002, 'ChatGLM2', 'http://mms1.baidu.com/it/u=1979830414,2984779047&fm=253&app=138&f=JPEG&fmt=auto&q=75?w=500&h=500', NULL, '450', '2023-07-01 11:58:24.605', NULL, NULL, 0, '2023-07-01 11:58:24.605', '2023-07-01 12:02:56.900');
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@@ -203,3 +205,126 @@ CREATE TABLE `user_emoji` (
|
|||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
KEY `IDX_USER_EMOJIS_UID` (`uid`)
|
KEY `IDX_USER_EMOJIS_UID` (`uid`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='用户表情包';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='用户表情包';
|
||||||
|
###单聊群聊功能
|
||||||
|
DROP TABLE IF EXISTS `user_apply`;
|
||||||
|
CREATE TABLE `user_apply` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`uid` bigint(20) NOT NULL COMMENT '申请人uid',
|
||||||
|
`type` int(11) NOT NULL COMMENT '申请类型 1加好友',
|
||||||
|
`target_id` bigint(20) NOT NULL COMMENT '接收人uid',
|
||||||
|
`msg` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '申请信息',
|
||||||
|
`status` int(11) NOT NULL COMMENT '申请状态 1待审批 2同意',
|
||||||
|
`read_status` int(11) NOT NULL COMMENT '阅读状态 1未读 2已读',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `idx_target_id_uid_status` (`target_id`,`uid`,`status`) USING BTREE,
|
||||||
|
KEY `idx_target_id` (`target_id`) USING BTREE,
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
|
KEY `idx_update_time` (`update_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户申请表';
|
||||||
|
DROP TABLE IF EXISTS `user_friend`;
|
||||||
|
CREATE TABLE `user_friend` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`uid` bigint(20) NOT NULL COMMENT 'uid',
|
||||||
|
`friend_uid` bigint(20) NOT NULL COMMENT '好友uid',
|
||||||
|
`delete_status` int(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除(0-正常,1-删除)',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `idx_uid_friend_uid` (`uid`,`friend_uid`) USING BTREE,
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
|
KEY `idx_update_time` (`update_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户联系人表';
|
||||||
|
DROP TABLE IF EXISTS `room`;
|
||||||
|
CREATE TABLE `room` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`type` int(11) NOT NULL COMMENT '房间类型 1群聊 2单聊',
|
||||||
|
`hot_flag` int(11) DEFAULT '0' COMMENT '是否全员展示 0否 1是',
|
||||||
|
`active_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '群最后消息的更新时间(热点群不需要写扩散,只更新这里)',
|
||||||
|
`last_msg_id` bigint(20) DEFAULT NULL COMMENT '会话中的最后一条消息id',
|
||||||
|
`ext_json` json DEFAULT NULL COMMENT '额外信息(根据不同类型房间有不同存储的东西)',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
|
KEY `idx_update_time` (`update_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间表';
|
||||||
|
DROP TABLE IF EXISTS `room_friend`;
|
||||||
|
CREATE TABLE `room_friend` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`room_id` bigint(20) NOT NULL COMMENT '房间id',
|
||||||
|
`uid1` bigint(20) NOT NULL COMMENT 'uid1(更小的uid)',
|
||||||
|
`uid2` bigint(20) NOT NULL COMMENT 'uid2(更大的uid)',
|
||||||
|
`room_key` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '房间key由两个uid拼接,先做排序uid1_uid2',
|
||||||
|
`status` int(11) NOT NULL COMMENT '房间状态 0正常 1禁用(删好友了禁用)',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
UNIQUE KEY `room_key` (`room_key`) USING BTREE,
|
||||||
|
KEY `idx_room_id` (`room_id`) USING BTREE,
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
|
KEY `idx_update_time` (`update_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='单聊房间表';
|
||||||
|
DROP TABLE IF EXISTS `room_group`;
|
||||||
|
CREATE TABLE `room_group` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`room_id` bigint(20) NOT NULL COMMENT '房间id',
|
||||||
|
`name` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '群名称',
|
||||||
|
`avatar` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '群头像',
|
||||||
|
`ext_json` json DEFAULT NULL COMMENT '额外信息(根据不同类型房间有不同存储的东西)',
|
||||||
|
`delete_status` int(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除(0-正常,1-删除)',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `idx_room_id` (`room_id`) USING BTREE,
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
|
KEY `idx_update_time` (`update_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='群聊房间表';
|
||||||
|
DROP TABLE IF EXISTS `group_member`;
|
||||||
|
CREATE TABLE `group_member` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`group_id` bigint(20) NOT NULL COMMENT '群主id',
|
||||||
|
`uid` bigint(20) NOT NULL COMMENT '成员uid',
|
||||||
|
`role` int(11) NOT NULL COMMENT '成员角色 1群主 2管理员 3普通成员',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `idx_group_id_role` (`group_id`,`role`) USING BTREE,
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
|
KEY `idx_update_time` (`update_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='群成员表';
|
||||||
|
DROP TABLE IF EXISTS `contact`;
|
||||||
|
CREATE TABLE `contact` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`uid` bigint(20) NOT NULL COMMENT 'uid',
|
||||||
|
`room_id` bigint(20) NOT NULL COMMENT '房间id',
|
||||||
|
`read_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '阅读到的时间',
|
||||||
|
`active_time` datetime(3) DEFAULT NULL COMMENT '会话内消息最后更新的时间(只有普通会话需要维护,全员会话不需要维护)',
|
||||||
|
`last_msg_id` bigint(20) DEFAULT NULL COMMENT '会话最新消息id',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
UNIQUE KEY `uniq_uid_room_id` (`uid`,`room_id`) USING BTREE,
|
||||||
|
KEY `idx_room_id_read_time` (`room_id`,`read_time`) USING BTREE,
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE,
|
||||||
|
KEY `idx_update_time` (`update_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会话列表';
|
||||||
|
|
||||||
|
INSERT INTO `user` (`id`, `name`, `avatar`, `sex`, `open_id`, `last_opt_time`, `ip_info`, `item_id`, `status`, `create_time`, `update_time`) VALUES (1, '系统消息', 'http://mms1.baidu.com/it/u=1979830414,2984779047&fm=253&app=138&f=JPEG&fmt=auto&q=75?w=500&h=500', NULL, '0', '2023-07-01 11:58:24.605', NULL, NULL, 0, '2023-07-01 11:58:24.605', '2023-07-01 12:02:56.900');
|
||||||
|
insert INTO `room`(`id`,`type`,`hot_flag`) values (1,1,1);
|
||||||
|
insert INTO `room_group`(`id`,`room_id`,`name`,`avatar`) values (1,1,'抹茶全员群','https://mallchat.cn/assets/logo-e81cd252.jpeg');
|
||||||
|
DROP TABLE IF EXISTS `secure_invoke_record`;
|
||||||
|
CREATE TABLE `secure_invoke_record` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||||
|
`secure_invoke_json` json NOT NULL COMMENT '请求快照参数json',
|
||||||
|
`status` tinyint(8) NOT NULL COMMENT '状态 1待执行 2已失败',
|
||||||
|
`next_retry_time` datetime(3) NOT NULL COMMENT '下一次重试的时间',
|
||||||
|
`retry_times` int(11) NOT NULL COMMENT '已经重试的次数',
|
||||||
|
`max_retry_times` int(11) NOT NULL COMMENT '最大重试次数',
|
||||||
|
`fail_reason` text COMMENT '执行失败的堆栈',
|
||||||
|
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||||
|
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `idx_next_retry_time` (`next_retry_time`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='本地消息表';
|
||||||
@@ -34,6 +34,8 @@ public class ChatMessageResp {
|
|||||||
public static class Message {
|
public static class Message {
|
||||||
@ApiModelProperty("消息id")
|
@ApiModelProperty("消息id")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
@ApiModelProperty("房间id")
|
||||||
|
private Long roomId;
|
||||||
@ApiModelProperty("消息发送时间")
|
@ApiModelProperty("消息发送时间")
|
||||||
private Date sendTime;
|
private Date sendTime;
|
||||||
@ApiModelProperty("消息类型 1正常文本 2.撤回消息")
|
@ApiModelProperty("消息类型 1正常文本 2.撤回消息")
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ public interface MQConstant {
|
|||||||
/**
|
/**
|
||||||
* (授权完成后)登录信息mq
|
* (授权完成后)登录信息mq
|
||||||
*/
|
*/
|
||||||
String LOGIN_MSG_TOPIC = "login_send_msg";
|
String LOGIN_MSG_TOPIC = "user_login_send_msg";
|
||||||
String LOGIN_MSG_GROUP = "login_send_msg_group";
|
String LOGIN_MSG_GROUP = "user_login_send_msg_group";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫码成功 信息发送mq
|
* 扫码成功 信息发送mq
|
||||||
*/
|
*/
|
||||||
String SCAN_MSG_TOPIC = "scan_send_msg";
|
String SCAN_MSG_TOPIC = "user_scan_send_msg";
|
||||||
String SCAN_MSG_GROUP = "scan_send_msg_group";
|
String SCAN_MSG_GROUP = "user_scan_send_msg_group";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
package com.abin.mallchat.common.common.domain.dto;
|
package com.abin.mallchat.common.common.domain.dto;
|
||||||
|
|
||||||
import com.abin.mallchat.common.user.domain.enums.WSBaseResp;
|
|
||||||
import com.abin.mallchat.common.user.domain.enums.WSPushTypeEnum;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@@ -15,18 +11,11 @@ import java.io.Serializable;
|
|||||||
* Date: 2023-08-12
|
* Date: 2023-08-12
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@AllArgsConstructor
|
||||||
public class LoginMessageDTO implements Serializable {
|
public class LoginMessageDTO implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
private Long uid;
|
||||||
* 微信公众号获得扫码事件后,发送给我方的回调信息
|
private Integer code;
|
||||||
*/
|
|
||||||
private WxMpXmlMessage wxMpXmlMessage ;
|
|
||||||
|
|
||||||
public LoginMessageDTO(WxMpXmlMessage wxMpXmlMessage) {
|
|
||||||
this.wxMpXmlMessage = wxMpXmlMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.abin.mallchat.common.common.domain.dto;
|
package com.abin.mallchat.common.common.domain.dto;
|
||||||
|
|
||||||
import com.abin.mallchat.common.user.domain.enums.WSBaseResp;
|
|
||||||
import com.abin.mallchat.common.user.domain.enums.WSPushTypeEnum;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -17,17 +15,9 @@ import java.io.Serializable;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class ScanSuccessMessageDTO implements Serializable {
|
public class ScanSuccessMessageDTO implements Serializable {
|
||||||
/**
|
|
||||||
* 推送的ws消息
|
|
||||||
*/
|
|
||||||
private WSBaseResp<?> wsBaseMsg;
|
|
||||||
/**
|
/**
|
||||||
* 推送的uid
|
* 推送的uid
|
||||||
*/
|
*/
|
||||||
private Integer loginCode;
|
private Integer code;
|
||||||
|
|
||||||
public ScanSuccessMessageDTO(Integer loginCode, WSBaseResp<?> wsBaseMsg) {
|
|
||||||
this.loginCode = loginCode;
|
|
||||||
this.wsBaseMsg = wsBaseMsg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
package com.abin.mallchat.common.common.utils;
|
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Description: Cache管理器
|
|
||||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
|
||||||
* Date: 2023-04-05
|
|
||||||
*/
|
|
||||||
public class CacheHolder {
|
|
||||||
|
|
||||||
private static final Long MAX_MUM_SIZE = 10000L;
|
|
||||||
|
|
||||||
private static final Duration EXPIRE_TIME = Duration.ofHours(1);
|
|
||||||
/**
|
|
||||||
* 所有请求登录的code与channel关系
|
|
||||||
*/
|
|
||||||
public static final Cache<Integer, Channel> WAIT_LOGIN_MAP = Caffeine.newBuilder()
|
|
||||||
.expireAfterWrite(EXPIRE_TIME)
|
|
||||||
.maximumSize(MAX_MUM_SIZE)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
@@ -1,23 +1,14 @@
|
|||||||
package com.abin.mallchat.custom.user.consumer;
|
package com.abin.mallchat.custom.user.consumer;
|
||||||
|
|
||||||
import com.abin.mallchat.common.common.constant.MQConstant;
|
import com.abin.mallchat.common.common.constant.MQConstant;
|
||||||
import com.abin.mallchat.common.common.constant.RedisKey;
|
|
||||||
import com.abin.mallchat.common.common.domain.dto.LoginMessageDTO;
|
import com.abin.mallchat.common.common.domain.dto.LoginMessageDTO;
|
||||||
import com.abin.mallchat.common.common.utils.CacheHolder;
|
|
||||||
import com.abin.mallchat.common.user.dao.UserDao;
|
|
||||||
import com.abin.mallchat.common.user.domain.entity.User;
|
|
||||||
import com.abin.mallchat.custom.user.service.WebSocketService;
|
import com.abin.mallchat.custom.user.service.WebSocketService;
|
||||||
import com.abin.mallchat.custom.user.service.WxMsgService;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
|
||||||
import org.apache.rocketmq.spring.annotation.MessageModel;
|
import org.apache.rocketmq.spring.annotation.MessageModel;
|
||||||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||||
import org.apache.rocketmq.spring.core.RocketMQListener;
|
import org.apache.rocketmq.spring.core.RocketMQListener;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description: 在本地服务上找寻对应channel,将对应用户登陆,并触发所有用户收到上线事件
|
* Description: 在本地服务上找寻对应channel,将对应用户登陆,并触发所有用户收到上线事件
|
||||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||||
@@ -27,30 +18,12 @@ import java.util.Objects;
|
|||||||
@Component
|
@Component
|
||||||
public class MsgLoginConsumer implements RocketMQListener<LoginMessageDTO> {
|
public class MsgLoginConsumer implements RocketMQListener<LoginMessageDTO> {
|
||||||
@Autowired
|
@Autowired
|
||||||
private WxMsgService wxMsgService;
|
private WebSocketService webSocketService;
|
||||||
@Autowired
|
|
||||||
private UserDao userDao;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(LoginMessageDTO loginMessageDTO) {
|
public void onMessage(LoginMessageDTO loginMessageDTO) {
|
||||||
WxMpXmlMessage wxMpXmlMessage = loginMessageDTO.getWxMpXmlMessage();
|
//尝试登录登录
|
||||||
//给二维码绑定的登录code
|
webSocketService.scanLoginSuccess(loginMessageDTO.getCode(), loginMessageDTO.getUid());
|
||||||
Integer eventKey = Integer.parseInt(getEventKey(wxMpXmlMessage));
|
|
||||||
//本地未储存对应的channel,则结束
|
|
||||||
Channel channel = CacheHolder.WAIT_LOGIN_MAP.getIfPresent(eventKey);
|
|
||||||
if (Objects.isNull(channel)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//查询openid对应的用户(必然存在)
|
|
||||||
String openid = wxMpXmlMessage.getFromUser();
|
|
||||||
User user = userDao.getByOpenId(openid);
|
|
||||||
//登录,并且清除缓存
|
|
||||||
wxMsgService.login(user.getId(), eventKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getEventKey(WxMpXmlMessage wxMpXmlMessage) {
|
|
||||||
//扫码关注的渠道事件有前缀,需要去除
|
|
||||||
return wxMpXmlMessage.getEventKey().replace("qrscene_", "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
package com.abin.mallchat.custom.user.consumer;
|
package com.abin.mallchat.custom.user.consumer;
|
||||||
|
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import com.abin.mallchat.common.common.constant.MQConstant;
|
import com.abin.mallchat.common.common.constant.MQConstant;
|
||||||
import com.abin.mallchat.common.common.domain.dto.ScanSuccessMessageDTO;
|
import com.abin.mallchat.common.common.domain.dto.ScanSuccessMessageDTO;
|
||||||
import com.abin.mallchat.common.common.utils.CacheHolder;
|
|
||||||
import com.abin.mallchat.common.user.dao.UserDao;
|
|
||||||
import com.abin.mallchat.common.user.domain.entity.User;
|
|
||||||
import com.abin.mallchat.custom.user.service.WebSocketService;
|
import com.abin.mallchat.custom.user.service.WebSocketService;
|
||||||
import com.abin.mallchat.custom.user.service.WxMsgService;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
|
||||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
|
||||||
import org.apache.rocketmq.spring.annotation.MessageModel;
|
import org.apache.rocketmq.spring.annotation.MessageModel;
|
||||||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||||
import org.apache.rocketmq.spring.core.RocketMQListener;
|
import org.apache.rocketmq.spring.core.RocketMQListener;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description: 将扫码成功的信息发送给对应的用户,等待授权
|
* Description: 将扫码成功的信息发送给对应的用户,等待授权
|
||||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||||
@@ -29,22 +19,10 @@ import java.util.Objects;
|
|||||||
public class ScanSuccessConsumer implements RocketMQListener<ScanSuccessMessageDTO> {
|
public class ScanSuccessConsumer implements RocketMQListener<ScanSuccessMessageDTO> {
|
||||||
@Autowired
|
@Autowired
|
||||||
private WebSocketService webSocketService;
|
private WebSocketService webSocketService;
|
||||||
@Autowired
|
|
||||||
private WxMsgService wxMsgService;
|
|
||||||
@Autowired
|
|
||||||
private UserDao userDao;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(ScanSuccessMessageDTO scanSuccessMessageDTO) {
|
public void onMessage(ScanSuccessMessageDTO scanSuccessMessageDTO) {
|
||||||
Integer loginCode = scanSuccessMessageDTO.getLoginCode();
|
webSocketService.scanSuccess(scanSuccessMessageDTO.getCode());
|
||||||
//本地未储存对应的channel,则结束
|
|
||||||
Channel channel = CacheHolder.WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
|
||||||
if (Objects.isNull(channel)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//给正在等待登陆的channel发送扫码成功的消息,等待授权
|
|
||||||
channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(scanSuccessMessageDTO.getWsBaseMsg())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
||||||
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
|
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
|
||||||
import me.chanjar.weixin.common.error.WxErrorException;
|
|
||||||
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
|
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
|
||||||
import me.chanjar.weixin.mp.api.WxMpService;
|
import me.chanjar.weixin.mp.api.WxMpService;
|
||||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||||
@@ -50,7 +49,7 @@ public class WxPortalController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/callBack")
|
@GetMapping("/callBack")
|
||||||
public RedirectView callBack(@RequestParam String code) throws WxErrorException {
|
public RedirectView callBack(@RequestParam String code) {
|
||||||
try {
|
try {
|
||||||
WxOAuth2AccessToken accessToken = wxService.getOAuth2Service().getAccessToken(code);
|
WxOAuth2AccessToken accessToken = wxService.getOAuth2Service().getAccessToken(code);
|
||||||
WxOAuth2UserInfo userInfo = wxService.getOAuth2Service().getUserInfo(accessToken, "zh_CN");
|
WxOAuth2UserInfo userInfo = wxService.getOAuth2Service().getUserInfo(accessToken, "zh_CN");
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.abin.mallchat.custom.user.service;
|
package com.abin.mallchat.custom.user.service;
|
||||||
|
|
||||||
import com.abin.mallchat.common.user.domain.entity.User;
|
|
||||||
import com.abin.mallchat.common.user.domain.enums.WSBaseResp;
|
import com.abin.mallchat.common.user.domain.enums.WSBaseResp;
|
||||||
import com.abin.mallchat.common.user.domain.vo.request.ws.WSAuthorize;
|
import com.abin.mallchat.common.user.domain.vo.request.ws.WSAuthorize;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
@@ -37,19 +36,15 @@ public interface WebSocketService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫码用户登录成功通知,清除本地Cache中的loginCode和channel的关系
|
* 扫码用户登录成功通知,清除本地Cache中的loginCode和channel的关系
|
||||||
*
|
|
||||||
* @param loginCode
|
|
||||||
* @param user
|
|
||||||
* @param token
|
|
||||||
*/
|
*/
|
||||||
Boolean scanLoginSuccess(Integer loginCode, User user, String token);
|
Boolean scanLoginSuccess(Integer loginCode, Long uid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知用户扫码成功
|
* 通知用户扫码成功
|
||||||
*
|
*
|
||||||
* @param loginCode
|
* @param loginCode
|
||||||
*/
|
*/
|
||||||
Boolean scanSuccess(Integer loginCode, Long uid);
|
Boolean scanSuccess(Integer loginCode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 推动消息给所有在线的人
|
* 推动消息给所有在线的人
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import cn.hutool.core.util.RandomUtil;
|
|||||||
import com.abin.mallchat.common.common.constant.MQConstant;
|
import com.abin.mallchat.common.common.constant.MQConstant;
|
||||||
import com.abin.mallchat.common.common.constant.RedisKey;
|
import com.abin.mallchat.common.common.constant.RedisKey;
|
||||||
import com.abin.mallchat.common.common.domain.dto.LoginMessageDTO;
|
import com.abin.mallchat.common.common.domain.dto.LoginMessageDTO;
|
||||||
import com.abin.mallchat.common.common.utils.CacheHolder;
|
import com.abin.mallchat.common.common.domain.dto.ScanSuccessMessageDTO;
|
||||||
import com.abin.mallchat.common.common.utils.RedisUtils;
|
import com.abin.mallchat.common.common.utils.RedisUtils;
|
||||||
import com.abin.mallchat.common.user.dao.UserDao;
|
import com.abin.mallchat.common.user.dao.UserDao;
|
||||||
import com.abin.mallchat.common.user.domain.entity.User;
|
import com.abin.mallchat.common.user.domain.entity.User;
|
||||||
import com.abin.mallchat.custom.user.service.adapter.TextBuilder;
|
import com.abin.mallchat.custom.user.service.adapter.TextBuilder;
|
||||||
import com.abin.mallchat.custom.user.service.adapter.UserAdapter;
|
import com.abin.mallchat.custom.user.service.adapter.UserAdapter;
|
||||||
import com.abin.mallchat.transaction.service.MQProducer;
|
import com.abin.mallchat.transaction.service.MQProducer;
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
||||||
import me.chanjar.weixin.mp.api.WxMpService;
|
import me.chanjar.weixin.mp.api.WxMpService;
|
||||||
@@ -20,14 +19,11 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,22 +37,14 @@ public class WxMsgService {
|
|||||||
/**
|
/**
|
||||||
* 用户的openId和前端登录场景code的映射关系
|
* 用户的openId和前端登录场景code的映射关系
|
||||||
*/
|
*/
|
||||||
private static final ConcurrentHashMap<String, Integer> OPENID_EVENT_CODE_MAP = new ConcurrentHashMap<>();
|
|
||||||
private static final String URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
|
private static final String URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
|
||||||
@Value("${wx.mp.callback}")
|
@Value("${wx.mp.callback}")
|
||||||
private String callback;
|
private String callback;
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserDao userDao;
|
private UserDao userDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Lazy
|
|
||||||
private WebSocketService webSocketService;
|
|
||||||
@Autowired
|
|
||||||
private LoginService loginService;
|
|
||||||
@Autowired
|
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
|
||||||
@Autowired
|
|
||||||
private MQProducer mqProducer;
|
private MQProducer mqProducer;
|
||||||
|
|
||||||
public WxMpXmlOutMessage scan(WxMpService wxMpService, WxMpXmlMessage wxMpXmlMessage) {
|
public WxMpXmlOutMessage scan(WxMpService wxMpService, WxMpXmlMessage wxMpXmlMessage) {
|
||||||
@@ -65,14 +53,7 @@ public class WxMsgService {
|
|||||||
User user = userDao.getByOpenId(openid);
|
User user = userDao.getByOpenId(openid);
|
||||||
//如果已经注册,直接登录成功
|
//如果已经注册,直接登录成功
|
||||||
if (Objects.nonNull(user) && StringUtils.isNotEmpty(user.getAvatar())) {
|
if (Objects.nonNull(user) && StringUtils.isNotEmpty(user.getAvatar())) {
|
||||||
Channel channel = CacheHolder.WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
mqProducer.sendMsg(MQConstant.LOGIN_MSG_TOPIC, new LoginMessageDTO(user.getId(), loginCode));
|
||||||
//要么在本地登录,否则利用mq广播到到所有服务上尝试登录
|
|
||||||
if (Objects.nonNull(channel)) {
|
|
||||||
String token = loginService.login(user.getId());
|
|
||||||
webSocketService.scanLoginSuccess(loginCode, user, token);
|
|
||||||
}else {
|
|
||||||
mqProducer.sendMsg(MQConstant.LOGIN_MSG_TOPIC, new LoginMessageDTO(wxMpXmlMessage));
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,12 +62,10 @@ public class WxMsgService {
|
|||||||
user = User.builder().openId(openid).build();
|
user = User.builder().openId(openid).build();
|
||||||
userService.register(user);
|
userService.register(user);
|
||||||
}
|
}
|
||||||
Long uid = user.getId();
|
//在redis中保存openid和场景code的关系,后续才能通知到前端,旧版数据没有清除,这里设置了过期时间
|
||||||
|
|
||||||
//在 redis中保存openid和场景code的关系,后续才能通知到前端,旧版数据没有清除,这里设置了过期时间
|
|
||||||
RedisUtils.set(RedisKey.getKey(RedisKey.OPEN_ID_STRING, openid), loginCode, 60, TimeUnit.MINUTES);
|
RedisUtils.set(RedisKey.getKey(RedisKey.OPEN_ID_STRING, openid), loginCode, 60, TimeUnit.MINUTES);
|
||||||
//授权流程,给用户发送授权消息,并且异步通知前端扫码成功(如非本地channel,使用MQ通知某服务对前端进行通知扫码成功)
|
//授权流程,给用户发送授权消息,并且异步通知前端扫码成功,等待授权
|
||||||
threadPoolTaskExecutor.execute(() -> webSocketService.scanSuccess(loginCode, uid));
|
mqProducer.sendMsg(MQConstant.SCAN_MSG_TOPIC, new ScanSuccessMessageDTO(loginCode));
|
||||||
String skipUrl = String.format(URL, wxMpService.getWxMpConfigStorage().getAppId(), URLEncoder.encode(callback + "/wx/portal/public/callBack"));
|
String skipUrl = String.format(URL, wxMpService.getWxMpConfigStorage().getAppId(), URLEncoder.encode(callback + "/wx/portal/public/callBack"));
|
||||||
WxMpXmlOutMessage.TEXT().build();
|
WxMpXmlOutMessage.TEXT().build();
|
||||||
return new TextBuilder().build("请点击链接授权:<a href=\"" + skipUrl + "\">登录</a>", wxMpXmlMessage, wxMpService);
|
return new TextBuilder().build("请点击链接授权:<a href=\"" + skipUrl + "\">登录</a>", wxMpXmlMessage, wxMpService);
|
||||||
@@ -108,20 +87,10 @@ public class WxMsgService {
|
|||||||
if (StringUtils.isEmpty(user.getName())) {
|
if (StringUtils.isEmpty(user.getName())) {
|
||||||
fillUserInfo(user.getId(), userInfo);
|
fillUserInfo(user.getId(), userInfo);
|
||||||
}
|
}
|
||||||
//找到对应的
|
//找到对应的code
|
||||||
Integer eventKey = RedisUtils.get(RedisKey.getKey(RedisKey.OPEN_ID_STRING, userInfo.getOpenid()), Integer.class);
|
Integer code = RedisUtils.get(RedisKey.getKey(RedisKey.OPEN_ID_STRING, userInfo.getOpenid()), Integer.class);
|
||||||
//如果channel就在本地,直接登录
|
//发送登录成功事件
|
||||||
Channel channel = CacheHolder.WAIT_LOGIN_MAP.getIfPresent(eventKey);
|
mqProducer.sendMsg(MQConstant.LOGIN_MSG_TOPIC, new LoginMessageDTO(user.getId(), code));
|
||||||
if (Objects.nonNull(channel)) {
|
|
||||||
login(user.getId(), eventKey);
|
|
||||||
}else {
|
|
||||||
//如果channel不在本地,利用mq广播到到所有服务上,尝试进行登录
|
|
||||||
//手动生成一个WxMpXmlMessage
|
|
||||||
WxMpXmlMessage wxMpXmlMessage = new WxMpXmlMessage();
|
|
||||||
wxMpXmlMessage.setFromUser(userInfo.getOpenid());
|
|
||||||
wxMpXmlMessage.setEventKey("qrscene_"+eventKey);
|
|
||||||
mqProducer.sendMsg(MQConstant.LOGIN_MSG_TOPIC, new LoginMessageDTO(wxMpXmlMessage));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillUserInfo(Long uid, WxOAuth2UserInfo userInfo) {
|
private void fillUserInfo(Long uid, WxOAuth2UserInfo userInfo) {
|
||||||
@@ -138,12 +107,4 @@ public class WxMsgService {
|
|||||||
update.setName("名字重置" + RandomUtil.randomInt(100000));
|
update.setName("名字重置" + RandomUtil.randomInt(100000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void login(Long uid, Integer eventKey) {
|
|
||||||
User user = userDao.getById(uid);
|
|
||||||
//调用用户登录模块
|
|
||||||
String token = loginService.login(uid);
|
|
||||||
//推送前端登录成功
|
|
||||||
webSocketService.scanLoginSuccess(eventKey, user, token);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.abin.mallchat.common.common.annotation.FrequencyControl;
|
import com.abin.mallchat.common.common.annotation.FrequencyControl;
|
||||||
import com.abin.mallchat.common.common.config.ThreadPoolConfig;
|
import com.abin.mallchat.common.common.config.ThreadPoolConfig;
|
||||||
import com.abin.mallchat.common.common.constant.MQConstant;
|
|
||||||
import com.abin.mallchat.common.common.constant.RedisKey;
|
import com.abin.mallchat.common.common.constant.RedisKey;
|
||||||
import com.abin.mallchat.common.common.domain.dto.ScanSuccessMessageDTO;
|
|
||||||
import com.abin.mallchat.common.common.event.UserOfflineEvent;
|
import com.abin.mallchat.common.common.event.UserOfflineEvent;
|
||||||
import com.abin.mallchat.common.common.event.UserOnlineEvent;
|
import com.abin.mallchat.common.common.event.UserOnlineEvent;
|
||||||
import com.abin.mallchat.common.common.utils.CacheHolder;
|
|
||||||
import com.abin.mallchat.common.common.utils.RedisUtils;
|
import com.abin.mallchat.common.common.utils.RedisUtils;
|
||||||
import com.abin.mallchat.common.user.dao.UserDao;
|
import com.abin.mallchat.common.user.dao.UserDao;
|
||||||
import com.abin.mallchat.common.user.domain.entity.User;
|
import com.abin.mallchat.common.user.domain.entity.User;
|
||||||
@@ -25,6 +22,8 @@ import com.abin.mallchat.custom.user.service.WebSocketService;
|
|||||||
import com.abin.mallchat.custom.user.service.adapter.WSAdapter;
|
import com.abin.mallchat.custom.user.service.adapter.WSAdapter;
|
||||||
import com.abin.mallchat.custom.user.websocket.NettyUtil;
|
import com.abin.mallchat.custom.user.websocket.NettyUtil;
|
||||||
import com.abin.mallchat.transaction.service.MQProducer;
|
import com.abin.mallchat.transaction.service.MQProducer;
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@@ -38,12 +37,12 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.*;
|
import java.util.Date;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.Condition;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description: websocket处理类
|
* Description: websocket处理类
|
||||||
@@ -55,7 +54,14 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
public class WebSocketServiceImpl implements WebSocketService {
|
public class WebSocketServiceImpl implements WebSocketService {
|
||||||
|
|
||||||
private static final Duration EXPIRE_TIME = Duration.ofHours(1);
|
private static final Duration EXPIRE_TIME = Duration.ofHours(1);
|
||||||
|
private static final Long MAX_MUM_SIZE = 10000L;
|
||||||
|
/**
|
||||||
|
* 所有请求登录的code与channel关系
|
||||||
|
*/
|
||||||
|
public static final Cache<Integer, Channel> WAIT_LOGIN_MAP = Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(EXPIRE_TIME)
|
||||||
|
.maximumSize(MAX_MUM_SIZE)
|
||||||
|
.build();
|
||||||
/**
|
/**
|
||||||
* 所有已连接的websocket连接列表和一些额外参数
|
* 所有已连接的websocket连接列表和一些额外参数
|
||||||
*/
|
*/
|
||||||
@@ -68,6 +74,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
|||||||
public static ConcurrentHashMap<Channel, WSChannelExtraDTO> getOnlineMap() {
|
public static ConcurrentHashMap<Channel, WSChannelExtraDTO> getOnlineMap() {
|
||||||
return ONLINE_WS_MAP;
|
return ONLINE_WS_MAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* redis保存loginCode的key
|
* redis保存loginCode的key
|
||||||
*/
|
*/
|
||||||
@@ -97,7 +104,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
|||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
@FrequencyControl(time = 1000, count = 50, spEl = "T(com.abin.mallchat.common.common.utils.RequestHolder).get().getIp()")
|
@FrequencyControl(time = 1000, count = 50, spEl = "T(com.abin.mallchat.custom.user.websocket.NettyUtil).getAttr(#channel,T(com.abin.mallchat.custom.user.websocket.NettyUtil).IP)")
|
||||||
public void handleLoginReq(Channel channel) {
|
public void handleLoginReq(Channel channel) {
|
||||||
//生成随机不重复的登录码,并将channel存在本地cache中
|
//生成随机不重复的登录码,并将channel存在本地cache中
|
||||||
Integer code = generateLoginCode(channel);
|
Integer code = generateLoginCode(channel);
|
||||||
@@ -114,13 +121,13 @@ public class WebSocketServiceImpl implements WebSocketService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Integer generateLoginCode(Channel channel) {
|
private Integer generateLoginCode(Channel channel) {
|
||||||
int inc = 0;
|
int inc;
|
||||||
do {
|
do {
|
||||||
//本地cache时间必须比redis key过期时间短,否则会出现并发问题
|
//本地cache时间必须比redis key过期时间短,否则会出现并发问题
|
||||||
inc = RedisUtils.integerInc(RedisKey.getKey(LOGIN_CODE), 61, TimeUnit.MINUTES);
|
inc = RedisUtils.integerInc(RedisKey.getKey(LOGIN_CODE), (int) EXPIRE_TIME.toMinutes(), TimeUnit.MINUTES);
|
||||||
} while (CacheHolder.WAIT_LOGIN_MAP.asMap().containsKey(inc));
|
} while (WAIT_LOGIN_MAP.asMap().containsKey(inc));
|
||||||
//储存一份在本地
|
//储存一份在本地
|
||||||
CacheHolder.WAIT_LOGIN_MAP.put(inc, channel);
|
WAIT_LOGIN_MAP.put(inc, channel);
|
||||||
return inc;
|
return inc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +137,6 @@ public class WebSocketServiceImpl implements WebSocketService {
|
|||||||
* @param channel
|
* @param channel
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
// @FrequencyControl(time = 10, count = 5, spEl = "T(com.abin.mallchat.common.common.utils.RequestHolder).get().getIp()")
|
|
||||||
public void connect(Channel channel) {
|
public void connect(Channel channel) {
|
||||||
ONLINE_WS_MAP.put(channel, new WSChannelExtraDTO());
|
ONLINE_WS_MAP.put(channel, new WSChannelExtraDTO());
|
||||||
}
|
}
|
||||||
@@ -207,30 +213,30 @@ public class WebSocketServiceImpl implements WebSocketService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean scanLoginSuccess(Integer loginCode, User user, String token) {
|
public Boolean scanLoginSuccess(Integer loginCode, Long uid) {
|
||||||
//发送消息
|
//确认连接在该机器
|
||||||
Channel channel = CacheHolder.WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
Channel channel = WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
||||||
if (Objects.isNull(channel)) {
|
if (Objects.isNull(channel)) {
|
||||||
return Boolean.FALSE;
|
return Boolean.FALSE;
|
||||||
}
|
}
|
||||||
|
User user = userDao.getById(uid);
|
||||||
//移除code
|
//移除code
|
||||||
CacheHolder.WAIT_LOGIN_MAP.invalidate(loginCode);
|
WAIT_LOGIN_MAP.invalidate(loginCode);
|
||||||
|
//调用用户登录模块
|
||||||
|
String token = loginService.login(uid);
|
||||||
//用户登录
|
//用户登录
|
||||||
loginSuccess(channel, user, token);
|
loginSuccess(channel, user, token);
|
||||||
return true;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean scanSuccess(Integer loginCode, Long uid) {
|
public Boolean scanSuccess(Integer loginCode) {
|
||||||
Channel channel = CacheHolder.WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
Channel channel = WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
||||||
if (Objects.nonNull(channel)) {
|
if (Objects.nonNull(channel)) {
|
||||||
sendMsg(channel, WSAdapter.buildScanSuccessResp());
|
sendMsg(channel, WSAdapter.buildScanSuccessResp());
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}else {
|
|
||||||
//广播通知次channel服务扫码成功
|
|
||||||
mqProducer.sendMsg(MQConstant.SCAN_MSG_TOPIC, new ScanSuccessMessageDTO(loginCode, WSAdapter.buildScanSuccessResp()));
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
}
|
||||||
|
return Boolean.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -277,7 +283,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给本地channel发送消息
|
* 给本地channel发送消息
|
||||||
*
|
*
|
||||||
* @param channel
|
* @param channel
|
||||||
* @param wsBaseResp
|
* @param wsBaseResp
|
||||||
@@ -286,39 +292,4 @@ public class WebSocketServiceImpl implements WebSocketService {
|
|||||||
channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(wsBaseResp)));
|
channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(wsBaseResp)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 案例证明ConcurrentHashMap#entrySet的值不是快照数据
|
|
||||||
*
|
|
||||||
* @param args
|
|
||||||
* @throws InterruptedException
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) throws InterruptedException {
|
|
||||||
ReentrantLock reentrantLock = new ReentrantLock();
|
|
||||||
Condition condition = reentrantLock.newCondition();
|
|
||||||
ConcurrentHashMap<Integer, Integer> a = new ConcurrentHashMap<>();
|
|
||||||
a.put(1, 1);
|
|
||||||
a.put(2, 2);
|
|
||||||
new Thread(() -> {
|
|
||||||
reentrantLock.lock();
|
|
||||||
Set<Map.Entry<Integer, Integer>> entries = a.entrySet();
|
|
||||||
System.out.println(entries);
|
|
||||||
try {
|
|
||||||
condition.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
System.out.println(entries);
|
|
||||||
reentrantLock.unlock();
|
|
||||||
|
|
||||||
}).start();
|
|
||||||
Thread.sleep(1000);
|
|
||||||
reentrantLock.lock();
|
|
||||||
a.put(3, 3);
|
|
||||||
System.out.println("haha");
|
|
||||||
condition.signalAll();
|
|
||||||
reentrantLock.unlock();
|
|
||||||
Thread.sleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.abin.mallchat.custom.user.websocket;
|
|
||||||
|
|
||||||
import com.abin.mallchat.common.common.constant.MDCKey;
|
|
||||||
import com.abin.mallchat.common.common.domain.dto.RequestInfo;
|
|
||||||
import com.abin.mallchat.common.common.utils.RequestHolder;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.slf4j.MDC;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class NettyCollectorHandler extends ChannelInboundHandlerAdapter {
|
|
||||||
@Override
|
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
|
||||||
String tid = UUID.randomUUID().toString();
|
|
||||||
MDC.put(MDCKey.TID, tid);
|
|
||||||
RequestInfo info = new RequestInfo();
|
|
||||||
info.setUid(NettyUtil.getAttr(ctx.channel(), NettyUtil.UID));
|
|
||||||
info.setIp(NettyUtil.getAttr(ctx.channel(), NettyUtil.IP));
|
|
||||||
RequestHolder.set(info);
|
|
||||||
|
|
||||||
ctx.fireChannelRead(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -80,7 +80,6 @@ public class NettyWebSocketServer {
|
|||||||
pipeline.addLast(new HttpObjectAggregator(8192));
|
pipeline.addLast(new HttpObjectAggregator(8192));
|
||||||
//保存用户ip
|
//保存用户ip
|
||||||
pipeline.addLast(new HttpHeadersHandler());
|
pipeline.addLast(new HttpHeadersHandler());
|
||||||
pipeline.addLast(new NettyCollectorHandler());
|
|
||||||
/**
|
/**
|
||||||
* 说明:
|
* 说明:
|
||||||
* 1. 对于 WebSocket,它的数据是以帧frame 的形式传递的;
|
* 1. 对于 WebSocket,它的数据是以帧frame 的形式传递的;
|
||||||
|
|||||||
@@ -100,10 +100,6 @@ public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler<Tex
|
|||||||
break;
|
break;
|
||||||
case HEARTBEAT:
|
case HEARTBEAT:
|
||||||
break;
|
break;
|
||||||
case AUTHORIZE:
|
|
||||||
this.webSocketService.authorize(ctx.channel(), JSONUtil.toBean(wsBaseReq.getData(), WSAuthorize.class));
|
|
||||||
log.info("主动认证 = " + msg.text());
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
log.info("未知类型");
|
log.info("未知类型");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user