基于Vue+SpringBoot的在线群聊系统的设计与实现

qq_28188421 2022-01-17 14:54:47

 

1. 需求分析

  • 登录:用户正确输入账号、密码后即可登录成功,并进入系统主页面。若输入错误,则给出错误提示。
  • 注册:用户正确输入账号、密码、昵称等信息后即可注册一个账号。若账号已存在,则注册失败,并给出错误提示。
  • 加入群聊:用户通过输入关键字来搜索群聊,搜索结果为群聊名中含有给定关键字的群聊,然后用户可以加入其中的某个或某些群聊。
  • 退出群聊:用户进入某个群聊后,可以点击“离开”按钮退出这个群聊。
  • 创建群聊:用户可以创建群聊,创建时需要输入群聊名称。
  • 接收消息:用户可以收到群聊中其他用户发送的消息。
  • 发送消息:用户可以发送消息,群聊中其他用户均可接收到。

 

2. 设计与实现

2.1 数据设计

主要有三种数据实体:

  • 用户实体(User),保存用户的基本信息
  • 群聊实体(Group),保存群聊的基本信息
  • 消息实体(Message),保存某个群聊下的全部消息。

采用MongoDB数据库保存这些实体,它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能。

2.2 前端

前端主要包括三个页面:登录页面、注册页面、聊天页面(主页面)。

前端页面主要通过 Vue 框架实现:

  • 为了快速搭建页面而选用了 ElementUI 组件库。
  • 使用 Vuex 保存一些频繁使用的公共变量,如用户信息、用户加入的群聊信息等。
  • 使用 VueRouter 来管理前端路由。

2.3 后端

后端使用 Java + SpringBoot框架实现。

后端采用经典的三层架构:Controller层负责接收前端的请求并将结果发送给前端;Service层负责具体的业务实现;Repository层负责与数据库的交互。

2.3.1 接口设计

接口设计尽量采用了RESTful风格。

(1)UserController

@RequestMapping("/users")
public class UserController {
    ......
    /* 登录 */
    @PostMapping("login")
    public JsonResult<User> login(@RequestBody LoginUserInfo loginUserInfo) {......}

    /* 注册 */
    @PostMapping("register")
    public JsonResult<User> register(@RequestBody RegisterUserInfo registerUserInfo) {......}

    /* 获取用户加入的全部群聊信息 */
    @GetMapping("{userId}/groups")
    public JsonResult<List<Group>> getAllGroups(@PathVariable("userId") String userId) {......}
    
    /* 加入一个群聊 */
    @PostMapping("{userId}/groups/{groupId}")
    public JsonResult<Group> joinGroup(@PathVariable("userId") String userId, @PathVariable("groupId") String groupId) {......}

    /* 退出一个群聊 */
    @DeleteMapping("{userId}/groups/{groupId}")
    public JsonResult<Boolean> removeGroup(@PathVariable("userId") String userId, @PathVariable("groupId") String groupId) {......}    
}

(2)GroupController

@RequestMapping("/groups")
public class GroupController {
    ......
    /* 获取某个群聊的信息 */
    @GetMapping("{id}")
    public JsonResult<GroupDetail> getGroup(@PathVariable("id") String groupId) {......}

    /* 获取名字包含关键词的群聊信息 */
    @GetMapping("")
    public JsonResult<List<Group>> searchGroups(@PathParam("keywords") String keywords) {......}

    /* 创建一个群聊 */
    @PostMapping("")
    public JsonResult<Group> createGroup(@RequestBody NewGroupInfo groupInfo) {......}
}

2.3.2 功能实现

(1)登录

/* UserController */
@PostMapping("login")
public JsonResult<User> login(@RequestBody LoginUserInfo loginUserInfo) {
    User user = userService.login(loginUserInfo);
    if (user == null) {
        return ResultTool.fail(ResultCode.USER_CREDENTIALS_ERROR, new User());
    } else {
        return ResultTool.success(user);
    }
}

/* UserService */
public User login(LoginUserInfo userInfo) {
    return userRepository.findByAccountAndPassword(userInfo.getAccount(), userInfo.getPassword());
}

(2)注册

/* UserController */
@PostMapping("register")
public JsonResult<User> register(@RequestBody RegisterUserInfo registerUserInfo) {
    User user = userService.register(registerUserInfo);
    if (user == null) {
        return ResultTool.fail(ResultCode.COMMON_FAIL, new User());
    } else {
        return ResultTool.success(user);
    }
}

/* UserService */
public User register(RegisterUserInfo userInfo) {
    User user = new User();
    BeanUtil.copyProperties(userInfo, user);
    user.setGroups(new ArrayList<>());
    return userRepository.insert(user);
}

(3)加入群聊

public void joinGroup(String userId, String groupId) {
    /* 在群聊中加入此用户 */
    Group group = groupRepository.findById(groupId).orElse(new Group());
    User user = userRepository.findById(userId).orElse(new User());
    group.getMembers().add(new GroupUser(userId, user.getName()));
    groupRepository.save(group);

    /* 在用户信息中加入群聊 */
    user.getGroups().add(groupId);
    userRepository.save(user);
}

(4)离开群聊

public Boolean leaveGroup(String userId, String groupId) {
    /* 从群聊中删除此用户 */
    Group group = groupRepository.findById(groupId).orElse(null);
    if (group == null) {return true;}
    List<GroupUser> members = group.getMembers();
    members.removeIf(user -> user.getId().equals(userId));
    group.setMembers(members);

    /* 如果群聊中没人了,删除群聊 */
    if (members.size() == 0) {
        groupRepository.deleteById(groupId);
    } else {
        groupRepository.save(group);
    }

    /* 从用户信息中删除群聊 */
    User user = userRepository.findById(userId).orElse(null);
    if (user == null) {return false;}
    List<String> groups = user.getGroups();
    groups.remove(groupId);
    userRepository.save(user);
    return true;
}

(5)创建群聊

public Group createGroup(NewGroupInfo newGroup) {
    Group group = new Group();
    BeanUtil.copyProperties(newGroup, group);
    group.setAdminNickname(group.getAdminName());

    /* 初始化群成员 */
    List<GroupUser> members = new ArrayList<>();
    members.add(new GroupUser(newGroup.getAdminId(), newGroup.getAdminName()));
    group.setMembers(members);

    /* 初始化群消息 */
    Message message = new Message();
    messageRepository.insert(message);
    group.setMessagesId(message.getId());

    /* 插入群组 */
    groupRepository.insert(group);

    /* 群主新增群组 */
    mongoTemplate.updateMulti(
        new Query(Criteria.where("_id").is(newGroup.getAdminId())),
        new Update().push("groups").value(group.getId()),
        "users");
    return group;
}

(6)聊天

聊天功能的实现使用到了WebSocket库,它是一种在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在SpringBoot框架中,只需完成一些对接口的编程,框架就可以自动在合适的时候调用这些接口。

创建一个MyWebSocket类,它有一个Map类型的静态成员变量clients,其中存放了当前所有连接到服务器的会话,键为用户ID,值为会话信息。

MyWebSocket类还实现了WebSocket的一系列接口:

  • onOpen:客户端连接时触发。需将<用户ID,会话信息>保存至clients中
  • onClose:客户端关闭连接时触发。将用户会话信息从clients中去除。
  • onMessage:服务器收到客户端发来的消息触发。需要将消息推送给群聊中的其他用户。

这里着重讲述onMessage的实现。

websocket请求路径为“/websocket/{用户ID}”,用户发来的消息格式为:<群聊ID>#<消息内容>,用户ID可以从请求路径参数中获得。

系统收到用户发来的消息后,取出用户ID、群聊ID、消息内容。然后根据群聊ID从数据库中取得群聊信息,再从群聊信息中获得所有群聊成员ID,最后将消息依次转发给这些群员。转发的消息格式为“<消息发送者ID>#<消息内容>”

@OnMessage
public void onMessage(String message, @PathParam("userId") String userId) {
    int idx = message.indexOf("#");
    String groupId = message.substring(0, idx);
    message = message.substring(idx + 1);

    /* 消息保存至数据库 */
    Message.Record record = messageService.storeMessage(userId, message, groupId);

    /* 向在线用户群发消息 */
    Query query = new Query(Criteria.where("_id").is(groupId));
    query.fields().include("members");
    Group group = mongoTemplate.findOne(query, Group.class, "groups");

    message = groupId + "#" + JSONUtil.toJsonStr(record);
    for (GroupUser member: group.getMembers()) {
        String memberId = member.getId();
        if (clients.containsKey(memberId)) {
            clients.get(memberId).getAsyncRemote().sendText(message);
        }
    }
}

3. 项目演示

(1)注册

(2)登录

(3)加入群聊

(4)聊天

 

作者:NP368

...全文
602 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
考研学习分享功能的描述可以涵盖以下几个主要模块,旨在为考研学生提供一个互动、资源共享、经验交流的平台: 1. 用户注册与个人信息管理 学生可以通过邮箱或手机号注册账户,填写个人信息,如姓名、专业、目标院校等。 用户可设置学习目标和进度,方便记录自己的学习历程。 2. 学习资料共享 用户可以上传、下载考研相关学习资料,如教材、真题、笔记、复习计划等。 提供文件分类功能,按学科、院校、难度等进行整理,方便用户查找。 支持多种文件格式,如PDF、Word、Excel、图片等。 3. 复习经验分享 学生可以发布自己的复习经验文章,分享复习方法、备考心得、时间管理技巧等。 提供文章评论和互动功能,其他学生可以点赞、评论、提问,促进经验交流。 设置专栏或专题,帮助学生快速找到自己感兴趣的复习内容。 4. 考研小组与社交功能 学生可以创建或加入学习小组,组内成员可共享资料、讨论问题、互相鼓励。 提供私信、群聊功能,方便学员在小组内进行实时讨论和交流。 支持设置小组学习目标和定期检查进度,增加学习动力。 5. 在线课程与讲座 提供考研各科目(如英语、数学、政治等)的在线课程资源,用户可以报名参加。

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧