1.会话管理
此处谈到的会话(虽然也是叫做 session),这个会话是特指在聊天过程中,产生的"业务上的会话”而不是 servlet/spring 里面内置的会话,会话是计算机中一个非常广义的概念
【每次你发起一个聊天,都相当于创建了一个会话
这个会话里就包含了一些"人"和一些"消息
比如我和 同学A 聊天, 就产生一 会话我再和同学 B 聊天, 也会产生一个会话,】
【此处的会话要想管理起来,势必要放到数据库中进行保存.(持久化存储)】
2.会话的数据库设计
【实体】
- 会话
- 用户
- 消息
会话 和 用户【多对多】
一个会话里会包含多个用户,
一个用户也能出现在多个会话中。会话 和 消息 【一对多】
一个会话里会包含多条消息条消息只能从属于一个会话,
2.1 会话表
先创建一个会话表
-- 会话表
drop table if exists message_session;
create table message_session (
sessionId int primary key auto_increment,
-- 上次访问时间
lastTime datetime
);
insert into message_session values(1, now());
insert into message_session values(2, now());
2.2 会话和用户的关联表
-- 会话-⽤⼾ 关联表
drop table if exists message_session_user;
create table message_session_user (
sessionId int,
userId int
);
insert into message_session_user values(1, 1), (1, 2);
insert into message_session_user values(2, 1), (2, 3);
后续写到消息功能的时候,再来考虑消息表和会话之间的关联
一个会话里,可以有两个用户,也可以有多个用户!有两个用户的会话,称为"单聊”有多个用户的会话,称为"群聊”
3.会话管理的核心内容
1.获取会话信息
2.新增会话
3.1 获取会话信息
1.约定前后端接口
2.前端操作
针对会话的标题这里,只是简化处理了,此处就只是单纯的使用了第0个会话中的用户的名字作为了会话标题.
暂时没有考虑群聊的情况.
如果想考虑群聊,需要给会话表新增个属性,会话的标题,
会话的最后一条消息,可能比较长,但是会话预览的部分,不适合显示太长内容就可以根据长度,进行一个截断操作.
判定下消息长度是否超过 10.如果超过, 就只取前 10 个字符,作为预览结果.
当用户点击会话列表中的元素的时候,
1.把当前会话设置成"高亮"/active 状态
2.获取到该会话中的历史消息,并且显示到右侧区域如何实现设置高亮的效果,
1)先获取到所有的会话的li标签了
2)循环遍历这些li标签.
3)依次对比,看现在这个li标签是否是正在点击的标签,如果是,就给 className 属性设置成 selected; 如果不是,就清空 className
4.后端代码
结果表示当前用户(用户的登录状态),有哪些会话每个会话都包含哪些好友信息
4.1 MODEL
要求与约定的响应报文一致
@Data
public class MessageSession {
private Integer sessionId;
private List<Friend> friends;
private String lastMessage;
}
4.2 MAPPER
package com.example.demo.mapper;
import com.example.demo.model.Friend;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MessageSessionMapper {
//1.根据 userId 获取到该用户都在哪些会话中存在。返回结果是一组 sessionId
List<Integer> getSessionIdsByUserId(Integer usenId);
//2.根据 sessionId 再来查询这个会话都包含了哪些用户.(刨除最初的自己)
List<Friend> getFriendBySessionId(Integer sessionId,Integer selfUserId);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.mapper.MessageSessionMapper">
<!-- 给定一个用户看当前包含哪些sessionId-->
<!-- 降序排序-->
<select id="getSessionIdsByUserId" resultType="java.lang.Integer">
select sessionId from message_session
where sessionId in
(select sessionId from message_session_user where userId=#{userId})
order by lastTime desc
</select>
<!-- 给会话id看包含哪些好友-->
<select id="getFriendBySessionId" resultType="com.example.demo.model.Friend">
<!-- 与friend类相互匹配-->
select userId as friendId,username as friendName from user
where userId in
(select userId from message_session_user where sessionId = #{sessionId} and userId!=#{selfUserId})
</select>
</mapper>
4.3 CONTROLLER
package com.example.demo.controller;
import com.example.demo.config.Result;
import com.example.demo.constant.Constant;
import com.example.demo.mapper.MessageSessionMapper;
import com.example.demo.model.MessageSession;
import com.example.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/session")
public class MessageSessionController {
@Autowired
private MessageSessionMapper messageSessionMapper;
@RequestMapping("/getSessionMessage")
public Result getSessionMessage(HttpSession httpSession){
//0.先构造返回的数据
List<MessageSession> messageSessionList=new ArrayList<>();
//1.获取到当前用户的userId(从spring的session 中获取)
User user=(User) httpSession.getAttribute(Constant.USERINFO_SESSION_KEY);
Integer userId= user.getUserId();
//拦截器已经做了相关操作
// 2.根据userId 查询数据库,查出来有哪些会话id
List<Integer> sessionId=messageSessionMapper.getSessionIdsByUserId(userId);
// 3.遍历会话id,查询出每个会话里涉及到的好友都有谁
for(Integer userIds:sessionId){
/*public class MessageSession {
private Integer sessionId;
private List<Friend> friends;
private String lastMessage;
}*/
MessageSession messageSession=new MessageSession();
messageSession.setSessionId(userIds);
messageSession.setFriends(messageSessionMapper.getFriendBySessionId(userIds,userId));
// 4.遍历会话id,查询出每个会话的最后一条消息
messageSession.setLastMessage("最后一条消息");
// 最终目标就是构造出一个MessageSession 对象数组
messageSessionList.add(messageSession);
}
return Result.success(messageSessionList);
}
}
4.4 POSTMAN测试
成功!!!
5.前端代码
<!-- 会话列表 -->
<ul class="list" id="session-list">
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li class="selected">
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
<li>
<h3>lisi</h3>
<p>晚上吃啥</p>
</li>
</ul>
function getSessionList() {
$.ajax({
type: 'get',
url: '/session/getSessionMessage',
success: function(result) {
body=result.data;
// 1. 清空之前的会话列表
let sessionListUL = document.querySelector('#session-list');
sessionListUL.innerHTML = '';
// 2. 遍历响应的数组, 针对结果来构造页面
for (let session of body) {
// 针对 lastMessage 的长度进行截断处理
if (session.lastMessage.length > 10) {
session.lastMessage = session.lastMessage.substring(0, 10) + '...';
}
// 构造一个li标签
let li = document.createElement('li');
// 会话id保存起来,以备后用
// 把会话 id 保存到 li 标签的自定义属性中.
li.setAttribute('message-session-id', session.sessionId);
li.innerHTML = '<h3>' + session.friends[0].friendName + '</h3>'
+ '<p>' + session.lastMessage + '</p>';
sessionListUL.appendChild(li);
// 给 li 标签新增点击事件
li.onclick = function() {
// 这个写法, 就能保证, 点击哪个 li 标签
// 此处对应的 clickSession 函数的参数就能拿到哪个 li 标签.
clickSession(li);
}
}
}
});
}
getSessionList();
测试!!!
成功!!!