聊天室快速访问
继上次完成聊天室的历史记录功能后,我又想着实现聊天记录的已读未读功能。(轻喷。。)

开始之前

首先我看了抖音和钉钉这两款应用的消息已读未读功能的呈现效果。首先是抖音,在聊天界面,给好友发送完消息后,消息界面的最右下角有一个“已发送”标记,这时候是属于对方未读,如果对方上线并别点开了和你的聊天界面,你和他的聊天界面上,那个“已发送”就变成了“已读”,所以抖音并不会每一条消息上面都显示“已读”或者“未读”。而钉钉,由于其专注于办公,这个已读未读功能就“变态”多了,每一条发出去的消息都会显示对方是已读还是未读。

学谁?

当然了,我只能通过抖音或者钉钉在功能上呈现出来的效果来结合自己掌握的知识来推断他们大概的实现过程,真正的实现可比想的复杂多了,所以我也是照葫芦画瓢,做一个能用的精简版已读未读功能。既然是照葫芦画瓢,那就学钉钉吧,做一个“变态版”,每一条消息都显示“已读”或者“未读”。

思考

A和B聊天,如果A发送一条消息给B,怎么知道B已读呢?当然是B收到消息后,发送一个“已读回执”,A接收到已读回执后,更新自己的UI,把“未读”改为“已读”。

啊!挺简单啊。。。

可,,可是,如果B不在线,或者B在线,但是没有打开和A的聊天界面,那B不也是未读A的消息吗?如果B不在线,A发完消息后下线了,这时B上线了,查看消息后,发送已读回执给A,可是A已经下线了啊,怎么保持已读状态?此外,消息的已读和未读状态要保持的话,是该给消息添加一个属性,标识已读未读吗?如果添加属性,意味着我以前的代码也得改?比如一些操作数据库的代码和逻辑。

啊!

我怎么做

首先,并不需要给消息添加一个已读未读标识,只记录一个B最近已读A消息的时间即可。这样怎么就行呢?

1.设计“已读回执”结构

已读回执很简单,就是两个id和一个时间戳,id分别是读消息的A用户id、发消息的B用户id,时间戳是A最近一次读取B的消息的时间。这个已读回执是存到Redis中去的,最终会通过定时任务在凌晨三点持久化到数据库。例如id为3的用户发送已读回执给id为5的用户,则Redis中保存一个Value(key:’3-4-readTime’, ×××)

2.“已读回执”怎么用

给一个场景A和B两个用户,进行对话。我们来分情况讨论一下吧:

Redis宕机了怎么办

和历史记录的实现一样,对于我这个单服务器系统,那就只能直接保存到数据库了。另外Redis持久化策略可以在下次启动的时候恢复数据。

遇到的坑

这个思路是没什么复杂的,但是实施起来就有很多小细节了。在一个坑上面花了很多时间。就是js获取的时间戳,在java中转成Timestamp的时候出错,导致我在调试的时候一旦发送已读回执,接收已读回执的那个客户端就会断连。最后发现是Timestemp这个东东,不能用强转字符串来得到,大意了,需要通过Timestamp.valueOf(×××)来把一个字符串转为时间戳,而且这个传入的字符串需要以yyyy-MM-dd HH:mm:ss的格式来的。当时写太快了,就用(Timestamp)强制转换。。。

截图:
image