简单实现消息小红点

消息推送

消息推送一般分为移动端的消息推送和web端消息的推送,这里主要将web端消息的推送,实现我们常见的小红点;

环境部署

后端

SpringBoot 2.3.7

依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

启动服务

  1. 配置类

@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
  1. 服务类

@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocketServer {

private static MessageChatService messageChatService;

@Autowired
public void setMessageChatService(MessageChatService messageChatService) {
this.messageChatService = messageChatService;
}

/**
* 连接成功的方法
*
* @param session
* @param userId
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
ManagerWebSocketSession.addOnlineUser(userId, session);
}

/**
* 断开连接的方法
*/
@OnClose
public void onClose(Session session, @PathParam("userId") String userId) {
ManagerWebSocketSession.removeOnlineUser(userId);
log.info("用户sessionId为{}退出了", session.getId());
}

/**
* 收到客户端的信息
*
* @param message
* @param session
*/
@OnMessage
public void onMessage(String message, Session session) {
messageChatService.sendPrivateMessage(message);
log.info("收到的信息为:{}, sessionId为{}", message, session.getId());
}

/**
* 错误处理
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.warn("用户sessionId为{}发生了错误,错误原因为{}", session.getId(), error.toString());
}
}
  1. 管理session类

@Slf4j
public class ManagerWebSocketSession {

// 存储用户连接的session
private static final ConcurrentHashMap<String, Session> webSocketSessionMap = new ConcurrentHashMap<>();

// 在线用户的数量
private static final AtomicInteger onlineUsersCount = new AtomicInteger();

/**
* 添加在线用户
* @param userId
* @param session
*/
public static void addOnlineUser(String userId, Session session) {
// 存储用户的session
if(!webSocketSessionMap.containsKey(userId)) {
webSocketSessionMap.put(userId, session);
log.info("用户id为{},sessionId为{}", userId, session.getId());
onlineUsersCount.getAndIncrement();
}
}

/**
* 获取用户的session
* @param userId
* @return
*/
public static Session getSessionByUserId(String userId) {
return webSocketSessionMap.getOrDefault(userId, null);
}

/**
* 移除在线用户
* @param userId
*/
public static void removeOnlineUser(String userId) {
if (webSocketSessionMap.containsKey(userId)) {
webSocketSessionMap.remove(userId);
onlineUsersCount.decrementAndGet();
}
}

/**
* 判断用户是否在线
* @param userId
* @return
*/
public static boolean isUserOnline(String userId) {
return webSocketSessionMap.containsKey(userId);
}

/**
* 获取session的集合
* @return
*/
public static ConcurrentHashMap<String, Session> getWebSocketSessionMap() {
return webSocketSessionMap;
}

/**
* 获取在线人数
* @return
*/
public static int getOnlineUsersCount() {
return onlineUsersCount.get();
}
}

前端

基于React的实现

const WebSocket = () => {
const [webSocket, setWebSocket] = useState(new WebSocket(WEB_SOCKET_URL + getUserId()))

const openWebSocket = () => {
if (!window.WebSocket) {
alert('浏览器不支持websocket')
return
}
webSocket.onopen = (ev) => {
console.log('开启websocket')
}

webSocket.onclose = (ev) => {
console.log(`关闭websocket 关闭的原因:${ev.reason},关闭的状态码:${ev.code}`)
}

webSocket.onmessage = (ev) => {
console.log(`收到客户端的消息为${ev.data}`)

}
}

return (
<>
<div></div>
</>
);
};

export default WebSocket;


存储系统设计

数据库表

create table message
(
id bigint auto_increment comment '自增主键'
primary key,
from_id varchar(255) not null comment '发送者id',
to_id varchar(255) not null comment '接收者id',
message_type tinyint not null comment '消息类型',
owner_id bigint default -1 not null comment '资源类型',
message_content varchar(512) null comment '消息内容',
is_read char default '0' null comment '是否已读 1-已读 0-未读',
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
)
comment '消息表';


Redis键值设计

我这里考虑使用hash数据结构,一共有三类消息(评论,点赞,关注),所以可以使用三个hash,对应的三个key分别是:

message:reminder:comment

message:reminder:like

message:reminder:follow

每个hash里面的hashkey 为userId,值为对应用户所接受到的消息个数即可

实现逻辑

后端


简单实现消息小红点

  1. 当用户评论、点赞、关注之后,调用方法封装消息体,并存入数据库(便于未来寻找详细的消息信息)

  2. 然后通过websocket把评论、点赞、关注的消息传递到客户端

  3. 如果不在线,就把消息的个数存入到Redis中,用户登录后再请求固定的接口即可

  4. 如果在线,就让前端重新渲染,让红点+1即可

若用户持续在线,使用websocket就可以实时地将消息发送到客户端,客户端就可以根据消息的数量显示小红点的数量;

发送消息的代码

 /**
* 发送消息
* @param fromId 发送者
* @param toId 接受者
* @param content 内容
* @param ownerId 资源类型
* @param messageType 消息类型
*/
public void sendRedDotReminder(String fromId, String toId, String content, Long ownerId, int messageType) {


// 将信息存入数据库
storeMessage2DB(fromId, toId, content, ownerId, messageType);
// 构造信息传递对象
MessageTypeVo messageTypeVo = new MessageTypeVo(messageType, toId);
String toJson = GsonUtils.gson.toJson(messageTypeVo, MessageTypeVo.class);

Session session = ManagerWebSocketSession.getSessionByUserId(toId);

// 如果在线
if (session != null) {
try {
session.getBasicRemote().sendText(toJson);
} catch (IOException e) {
e.printStackTrace();
}
} else {
// 不在线就 存储进入redis中 存储数量即可
String messageKey = hashMap.get(messageType);

Boolean aBoolean = redisTemplate.opsForHash().putIfAbsent(messageKey, toId, 1);
if (!aBoolean) {
redisTemplate.opsForHash().increment(messageKey, toId, 1);
}
}
}

前端

以下是实现的用例图

简单实现消息小红点

  1. 用户点击相应的红点,则取消;

  2. 如果点入子列表,要删除Redis中存储的数量,然后加载具体的消息内容即可;


原文始发于微信公众号(EzreaLwj):简单实现消息小红点

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/244300.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!