日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

基于Vue+Java实现的在线聊天APP系统设计与实现

發(fā)布時間:2023/12/8 vue 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于Vue+Java实现的在线聊天APP系统设计与实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

全套資料下載地址:https://download.csdn.net/download/sheziqiong/85595798

一、需求分析

1.核心用戶分析

在線聊天系統(tǒng)主要針對一些年輕用戶群體以及因為工作需求而對于實時交流以及非實時交流有較大需求的群里。就青年群體而言,這一用戶群體特征比較鮮明,其主要需求為基礎(chǔ)聊天需求以及一些能夠凸顯個性的功能需求。在線聊天對于青年人來說也逐漸成為一種主流的設(shè)計方式。年輕人們通過在線交流和好友印象的可以了解到對方的性格,而且可以通過相互添加好友保持關(guān)系。而對于有工作需求的人來說能夠?qū)崟r交流以及處理未讀消息就顯得十分重要。

2. 系統(tǒng)的主要功能的概述

首先未注冊的用戶可以注冊賬號,已經(jīng)注冊的用戶可以使用賬號密碼進行登錄。

用戶可以搜索好友,搜索之后可以進行添加好友

主界面分為兩個部分,一個部分為消息盒子,一個部分為好友盒子

消息盒子主要存放未讀消息,如果有一個好友向你發(fā)送消息你沒有點到聊天框里查看的話就會在消息盒子界面顯示

好友盒子顯示如下幾個部分,好友列表,添加好友的入口,個人信息的入口,朋友驗證的入口

所有的好友會在好友列表中展示,一開始所有的好友的在默認(rèn)分組。點擊好友之后可以進入好友的資料卡頁面

可以在好友資料卡中可以查看好友的基本消息,以及會顯示好友的印象,當(dāng)點擊某個印象標(biāo)簽的時候會提示你可以進行刪除。還可以在好友資料卡頁面點擊發(fā)送消息進入聊天窗口。除此之外右上角點擊之后可以有刪除好友,移動好友,添加標(biāo)簽的選項

刪除好友:點擊之后好友將被刪除,你可以通過再次發(fā)送驗證消息進行添加

移動好友:可以將好友移動到指定的分組,如果分組不存在則創(chuàng)建分組,若移動后分組內(nèi)沒有成員則刪除分組。

添加標(biāo)簽:可以為好友添加一個標(biāo)簽。

當(dāng)進入聊天框之后發(fā)送消息對方就可以發(fā)收到,點擊下載聊天記錄的按鈕就可以下載所有的聊天記錄,點擊刪除聊天記錄可以刪除和當(dāng)前用戶所有的云端記錄。

個人信息,在這里可以修改個人信息包括修改頭像,以及刪除別人給自己的標(biāo)簽,并且可以在此處退出登錄

3. 項目操作流程圖

4. 功能詳解

  • 登錄
  • 使用賬號密碼進行登錄,登錄成功之后跳轉(zhuǎn)到主頁面中的消息盒子的頁面

  • 注冊
  • 賬號采用郵箱格式,密碼要求大于八位

  • 消息盒子
  • 消息盒子顯示你的所有的未讀消息,一旦消息已讀就會從消息盒子中去除

  • 好友盒子
  • 好友盒子有如下這些部分組成:新的朋友,我的賬號,朋友驗證,好友列表

  • 好友列表
  • 按照分組展示所有的好友,點擊好友可以進入好友資料卡頁面

  • 朋友驗證
  • 當(dāng)你發(fā)送的請求別人已經(jīng)處理完了或者別人向你發(fā)送了請求的話此處會有一個紅點表示消息數(shù)量。點擊進入之后進入驗證消息模塊

  • 我的賬號
  • 點擊之后進入個人資料卡,在這里可以修改姓名,頭像,性別,頭像要求小于 30kb,年齡要求不能為負(fù)數(shù),性別要求只能是男或者女,還可以在此處刪除自己的標(biāo)簽,也可以退出登錄。

  • 新的朋友
  • 可以進行全局搜索,即不進行任何輸入直接回車可以顯示所有的好友,并且可以進行模糊搜索,只輸入名字的部分也可搜索到。并且可以添加年齡和性別的限制條件。點擊搜索結(jié)果可以進入好友資料卡。在這里可以填寫驗證消息,并且發(fā)送好友驗證,自己不能添加自己,不能添加以及添加的好友,如果已經(jīng)發(fā)送過依次請求對方為響應(yīng)也不能發(fā)送。當(dāng)這里發(fā)送之后對方的朋友驗證會出現(xiàn)紅點。

  • 驗證消息
  • 當(dāng)我們點擊朋友驗證之后,進入驗證消息頁面,如果我們發(fā)送的消息被處理了,則會有一個紅點標(biāo)記,別人發(fā)送的請求我們可以選擇拒絕和接受。如果我們進入了此頁面的話,如果存在我們發(fā)送的消息被處理了且我們自己之前未讀的,則會被設(shè)置為已讀。對于別人發(fā)給自己的請求,則必須在處理完之后才會被設(shè)置為已讀。

  • 好友資料卡
  • 顯示好友的基本信息,好友的標(biāo)簽,點擊標(biāo)簽可以進行刪除,并且可以在此頁面點擊發(fā)送消息進入聊天框進行聊天,此頁面中點擊右上角還可以進行刪除好友,移動好友,添加標(biāo)簽。

  • 刪除好友:將好友從列表中刪除,刪除后可以再次發(fā)送驗證消息
  • 移動好友
  • 輸入要移動的分組如果不存在則創(chuàng)建分組,若某個分組內(nèi)沒有了用戶則刪除分組,所有用戶默認(rèn)在默認(rèn)分組中

  • 添加標(biāo)簽
  • 可以對一個用戶添加一個標(biāo)簽,添加重復(fù)標(biāo)簽沒有用

  • 聊天界面
  • 聊天界面可以雙方可以實時發(fā)送消息,顯示的時候自己的消息在右側(cè),對方的消息在左側(cè),且按時間排序,點擊下載按鈕可以進行聊天記錄下載,點擊刪除按鈕可以刪除云端數(shù)據(jù)

    5. 系統(tǒng)的頂級用例圖

    6. 系統(tǒng)的原型圖設(shè)計

    原型圖主要是用圖片的形式站輸出之前的功能模塊,并且也是后面前端 UI 的主要依據(jù)

    登陸界面,注冊界面類似消息盒子界面

    聊天界面 好友列表界面

    搜索界面 好友申請界面

    個人資料頁面 驗證消息界面

    項目的頁面和原型圖過多這里就不一一展示,詳情可見壓縮文件

    二、數(shù)據(jù)庫設(shè)計

    因為聊天系統(tǒng)的所有功能基本上都是圍繞著用戶進行的。聊天是用戶和用戶聊天,添加好友也是用戶添加用戶,印象管理也是用戶給用戶添加印象。所以主要的聯(lián)表操作都和 user 表有關(guān)。這里就先給出整個項目的 ER 圖

    根據(jù) ER 圖可以構(gòu)建數(shù)據(jù)庫的物理設(shè)計如下

  • 好友關(guān)系表 friendship
  • 好友印象表 impression
  • 聊天記錄表 record
  • 好友驗證表 validation
  • 用戶表 user
  • Redis 中的存儲結(jié)構(gòu)的說明,因為 Redis 的 Nosql 的性質(zhì)很適合用于存儲未讀的聊天記錄,我是用 Redis 中的哈希結(jié)構(gòu)進行存儲未讀消息。每一條記錄規(guī)則如下鍵為 unread+userId,值為一個 Java 中的 Map<String, Map<String, String>> 的類型。Map<String, Map<String, String>> 的類型數(shù)據(jù)格式如下:

    senderId: {nickname:xxx,content:xxx,pic:xxx }

    除此之外在 Redis 中單獨存儲一個哈希結(jié)構(gòu),鍵為 unreadNum+userId,值為未讀消息數(shù)量。

    三、架構(gòu)設(shè)計

    1.技術(shù)棧

    (1)前端

    ①Vue 作為前端框架

    ②vue-router 進行前端路由管理

    ③webpack 開發(fā) SPA(單頁面應(yīng)用)

    ④mint-UI 作為 UI 框架

    ⑤STOMP 實現(xiàn) Socket 通信的框架

    ⑥axios 發(fā)送請求

    ⑦sass(css 預(yù)處理器,進行 CSS 代碼的編寫)

    前端架構(gòu)說明:

    Webpack 搭建項目前端框架,打包生成 SPA(單頁面)的移動端應(yīng)用。

    1、 Webpack 配置文件

    webpack.base.conf.js 為基礎(chǔ)配置 一些最基本的 loader 與 plugin 都在這里面 webpack.dev.conf.js 為開發(fā)環(huán)境配置,配置了適合開發(fā)環(huán)境的 sourceMap,能快速的定位開發(fā)環(huán)境代碼報錯位置 webpack.prod.conf.js 為生產(chǎn)環(huán)境下配置,關(guān)閉了 sourceMap,極大的減小了線上環(huán)境代碼包的大小,啟用了 UglifyJsPlugin 進行代碼壓縮,使首屏加載速度低于 500ms。

    2、 Src 目錄下為主要代碼, assets 文件夾下存儲著圖片,iconfont 等靜態(tài)資源。Router 文件夾下為 vue 的路由,控制著頁面的跳轉(zhuǎn)。 Views 文件夾下為視圖組件,我們開發(fā)的代碼主要在這里。每個人負(fù)責(zé)不同的模塊,采用 gitflow 工作流,幾乎沒有產(chǎn)生沖突。

    (2)后端

    ①Spring Boot 作為后端框架

    ②Shiro 作為安全驗證框架

    ③STOMP 協(xié)議作為通信協(xié)議

    ④Redis 存儲未讀消息

    ⑤MySQL 作為用戶信息等的存儲

    (3)部署

    ①Docker 部署

    (4)測試

    ①Selemiun

    ②textNG

    (5)項目管理平臺

    ①github+git

    后端架構(gòu)說明:

    (1) 后端 package 說明如下:

    ① config 表示配置文件,里面存放諸如 Shiro,STOMP 的配置

    ② controller 放置 API 接口

    ③ dao 放置數(shù)據(jù)庫操作類和接口,其下的 impl 這個 package 表示 JAP 擴展實現(xiàn)類

    ④ dto 存放數(shù)據(jù)庫表的交互數(shù)據(jù)結(jié)構(gòu)

    ⑤ entity 存放數(shù)據(jù)庫表中的映射

    ⑥ pojo:存放普通 Java 類,一般是存放工具類

    ⑦ processor:存放過濾器,攔截器和監(jiān)聽器

    ⑧ service:供其他模塊調(diào)用的服務(wù)類,采用接口和實現(xiàn)的方式。impl 為其實現(xiàn)類

    ⑨ vo 和前端交互的數(shù)據(jù)類(主要用于將數(shù)據(jù)格式轉(zhuǎn)化為和前端約定的格式)

    (2) 使用如此結(jié)構(gòu)的原因

    ① 這些 package 中需要實現(xiàn) config 中的文件和 entity 中的文件,只要這兩個 package 的文件配置了,整個項目就可以運行。之后的編寫只需要在其他包中添加自己的代碼即可

    ② 在完成了 config 和 entity 的文件之后,其他功能的編寫只需要注重代碼邏輯的實現(xiàn)即可

    ③ 當(dāng)多個人同時寫后端的時候,在寫自己的功能的時候只需要在對應(yīng)的 package 中編寫對應(yīng)的代碼即可互不干擾。

    四、功能實現(xiàn)

    后端實現(xiàn):

    1) 基礎(chǔ)配置:

    1. RedisConfig

    主要是注入 RedisTemplate<Object, Object> 這個 bean

    2. Shiro

    首先建立自己的匹配器 TokenCredentialsMatcher 用于驗證比較,此比較器實現(xiàn)了 CredentialsMatcher 接口,實現(xiàn)類 doCredentialsMatch 方法。我們通過 token 比較進行驗證用戶登錄狀態(tài)是否合法。

    String token = (String) authenticationToken.getCredentials(); return TokenManager.verify(token); ```http://www.biyezuopin.vip然后再建立自己的 Reanlm:TokenRealm,自己配置的 TokenRealm 繼承自 AuthorizingRealm,并且再構(gòu)造函數(shù)的時候就將我們自己的 TokenCredentialsMatcher 作為此類的匹配器加入。這樣的話進行比較的時候就不會調(diào)用默認(rèn)的比較匹配器而是調(diào)用我們自定義的比較器。我們重寫 supports 方法,只有當(dāng) support 方法返回 true 的時候才會之后的操作。

    return token instanceof MyToken

    然后再重寫 doGetAuthenticationInfo 方法和 doGetAuthorizationInfo 方法,但是 doGetAuthorizationInfo 為授權(quán)方法,在我們本系統(tǒng)中并沒有使用,所以只給出前者核心代碼

    String token = (String)authenticationToken.getCredentials();
    Integer userId = TokenManager.getId(token);
    if(userId==null)
    userId=0;
    return new SimpleAuthenticationInfo(userId, token, getName());

    MyToken 是我自己建立的 Token 最主要的方式是重寫 getPrincipal 和 getCredentials,前者表示標(biāo)識,后者表示驗證的憑證

    @Override
    public Object getPrincipal() {
    Integer userId = -1;
    if(TokenManager.verify(this.token)){
    userId = TokenManager.getId(token);
    }
    return userId;
    }

    @Override
    public Object getCredentials() {
    return token;
    }

    做好基礎(chǔ)文件之后開始編寫 ShiroConfig,主要完成兩個函數(shù)一個是注冊 ShiroFilterFactoryBean 的函數(shù),另一個是注冊 SessionsSecurityManager 的函數(shù),在前中我們配置我們的過濾器,以及過濾器攔截的 url,在后這種我們進行的工作主要是關(guān)閉 Shiro 自帶的 session 功能。

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

    shiroFilterFactoryBean.setSecurityManager(securityManager);

    Map<String, Filter> filterMap = new HashMap<>();
    filterMap.put(“verification”, new AuthFilter());
    shiroFilterFactoryBean.setFilters(filterMap);
    Map<String, String> filterRuleMap = new LinkedHashMap<>();
    filterRuleMap.put(“/verification/”, “verification”);
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterRuleMap);
    return shiroFilterFactoryBean;
    }

    @Bean
    public SessionsSecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

    securityManager.setRealm(tokenRealm);
    //關(guān)閉Shiro自帶的Session。
    DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
    DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
    defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
    subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
    securityManager.setSubjectDAO(subjectDAO);
    return securityManager;
    }

    #### 3. 然后再來看過濾器的編寫,這里主要是做登錄驗證的其主要思路如下:編寫的過濾器繼承自 BasicHttpAuthenticationFilter 類,然后重寫了 isLoginAttempt 方法用于判斷是否是進行需要登錄驗證的操作,如果判斷返回 true 則才會進行之后的操作。然后重寫 executeLogin 方法即這個方法進行 token 的驗證,通過我們自定義的 TokenReanlm 進行驗證。如果驗證成功則返回 true 即繼續(xù)執(zhí)行原來的請求,如果驗證失敗則進入 onAccessDenied 方法。我們通過沖洗 onAccessDenied 方法將降入此方法后就返回 401 異常說明,token 驗證失敗。

    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
    return ((HttpServletRequest) request).getHeader(“Authorization”) != null;
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response){
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    String authorization = httpServletRequest.getHeader(“Authorization”);
    MyToken token = new MyToken(authorization.substring(7));
    getSubject(request, response).login(token);
    return true;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    try {
    if (isLoginAttempt(request, response)) {
    executeLogin(request, response);
    } else {
    throw new Exception();
    }
    } catch (Exception e){
    return false;
    }
    return true;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response){
    HttpServletResponse httpServletResponse = (HttpServletResponse)response;
    AuthMessage authMessage = AuthMessage.getAuthMessage(
    “請先登錄”, “”, “l(fā)ogin /auth/login”,
    “/auth/login”, “l(fā)ogin”, “application/json”);
    httpServletResponse.setCharacterEncoding(“UTF-8”);
    httpServletResponse.setHeader(“Content-Type”, “application/json;charset=UTF-8”);
    String ret = JSON.toJSONString(authMessage);
    httpServletResponse.setStatus(401);
    try {
    response.getWriter().write(ret);
    response.getWriter().close();
    } catch (IOException ex) {
    ex.printStackTrace();
    }
    return false;
    }

    #### 4. WebSocket 配置文件:通過重寫 registerStompEndpoints 方法規(guī)定 socket 建立連接時候的接口。然后再 configureMessageBroker 方法中設(shè)置訂閱 url 和發(fā)送消息的前綴

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint(“/chat”).setAllowedOrigins(“*”).withSockJS(); //設(shè)置連接url并且設(shè)置跨域
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker(“/subscription”); //設(shè)置訂閱的url
    config.setApplicationDestinationPrefixes(“/socket”); //設(shè)置訪問url前綴
    }

    2) 數(shù)據(jù)庫實體 entity 的類在這里就不展示,詳情可見代碼壓縮包,因為這個 package 中的文件和數(shù)據(jù)庫中的表緊密聯(lián)系,而數(shù)據(jù)庫中的表在之前已經(jīng)詳細(xì)說明過了 3) dto 作為 entity 中的對外交互類這里也不給出具體代碼,具體代碼可以見代碼壓縮包#### 5.controller1. ###### 登錄首先通過前端傳送上來的數(shù)據(jù)去數(shù)據(jù)庫中尋找數(shù)據(jù)庫中的 User,這里調(diào)用了 UserService 的 setUser 方法,此方法將 user 放置到 UserSerivce 對象中的時候同時會去數(shù)據(jù)庫查找賬號對應(yīng)的 user,且放入到此類的 userInDataBase 中,調(diào)用 UserService 的 encode 方法將當(dāng)前收到的密碼進行加密,然后進行判斷,如果 userInDataBase 為空也就是 UserService 方法中的 isRegistered()返回了 false,則表示賬號還沒被注冊,如果 checkPassword 方法返回 false 也就是 encode 之后的密碼和 userInDataBase 中的密碼不一樣,說明密碼錯誤。如果二者都不滿足則登錄成功,這里如果發(fā)送錯誤的話就會拋出異常然后有一個全局的異常捕捉函數(shù)進行處理

    userService.setUser(user);

    userService.encode();

    if(!userService.isRegistered()){
    throw new HttpException(HttpStatus.UNAUTHORIZED, AuthMessage.getAuthMessage(
    “賬號未注冊”,“”, “registration /auth/registration”,
    “/auth/registration”, “registration”, “application/json”));

    } else if(!userService.checkPassword()){
    throw new HttpException(HttpStatus.UNAUTHORIZED, AuthMessage.getAuthMessage(
    “賬號密碼不匹配”,“”, “l(fā)ogin /auth/login”,
    “/auth/login”, “l(fā)ogin”, “application/json”));

    }else{
    response.setStatus(200);
    return AuthMessage.getAuthMessage(
    “登錄成功”,TokenManager.sign(userService.getUserInDataBase().getId()), “messages /management/messages”,
    “/management/messages”, “messages”, “application/json”);
    }

    ###### 2.注冊和登錄邏輯類似,通過 UserService 的 setUser 方法將注冊的用戶放置到 UserService 類中,然后進行是否注冊過賬號密碼是否小于 8 的判斷,如果都沒有的話,隨機分配一個頭像,年齡默認(rèn)為 1,性別默認(rèn)為女,然后進行注冊

    userService.setUser(user);
    if(userService.isRegistered()){
    throw new HttpException(HttpStatus.FORBIDDEN, AuthMessage.getAuthMessage(
    “賬號已注冊”,“”, “registration /auth/registration”,
    “/auth/registration”, “registration”, “application/json”));
    }else if(user.getPassword().length()<8){
    throw new HttpException(HttpStatus.FORBIDDEN, AuthMessage.getAuthMessage(
    “密碼長度小于八位”,“”, “registration /auth/registration”,
    “/auth/registration”, “registration”, “application/json”));
    }else{
    userService.encode();
    userService.initPic();
    userService.setAge(1);
    userService.setGender(“男”);
    userService.save();
    response.setStatus(201);
    return AuthMessage.getAuthMessage(
    “注冊成功”, TokenManager.sign(userService.getUser().getId()), “messages /management/messages”,
    “/management/messages”, “messages”, “application/json”);
    }

    ###### 3. Socket 實現(xiàn)實時聊天1. 規(guī)定如下,一個用戶之間里一個 socket 連接,當(dāng)點擊進入聊天框的時候建立連接,離開聊天框的時候斷開連接。每個用戶訂閱的評到就是/subscription/userId。然后每一個用戶要發(fā)送消息的時候消息格式如下:

    {
    senderId:xxx,
    recipientId:xxx,
    content:{“me”:”xxx”}
    }

    后端接收到消息之后將 content 轉(zhuǎn)化為{“he”,”xxx”}這是因為前端渲染需要,然后通過 recipientId 可以知道要發(fā)送得了路徑,可以通過 MessageingTemplate.converAndSend 來主動推送消息。除此之外還要將消息送入 Reids 和 MySQL。

    @MessageMapping(“/chat”)
    public void sayHello(ChatMessage message){
    User sender = userService.findById(message.getSenderId());
    User recipient = userService.findById(message.getRecipientId());
    if(sendernull||recipientnull)
    return;
    Map<String, String> content = message.getContent();;
    String destination = “/subscription/” + recipient.getId();
    String origin = “/subscription/” + sender.getId();
    if(relationshipService.isFriend(recipient, sender)){
    Record record = new Record(content.get(“my”), new Date(), sender, recipient);
    userMessage.save(record);
    userMessage.addMessage(recipient.getId(), sender.getId(), sender.getNickname(), content.get(“my”), sender.getPic());
    messagingTemplate.convertAndSend(destination, new HashMap<String, Object>(){{
    put(“senderId”, String.valueOf(sender.getId()));
    put(“senderPic”, sender.getPic());
    put(“senderName”, sender.getNickname());
    put(“content”, new HashMap<String, String>(){{
    put(“he”, content.get(“my”));
    }}
    );
    }});
    messagingTemplate.convertAndSend(origin, new HashMap<String, String>(){{
    put(“content”,“發(fā)送成功”);
    }});http://www.biyezuopin.vip
    }else{
    messagingTemplate.convertAndSend(origin, new HashMap<String, String>(){{
    put(“content”,“對方不是你好友”);
    }});
    }
    }

    ###### 4. 消息盒子的消息獲取因為消息盒子是采用輪詢方式獲取,我們通過 UserService 的無參數(shù)的 setUser 方法可以將 Shiro 框架中通過驗證的用戶放入到 UserService 的 user 中去,然后我們獲取到 Redis 的所有存儲消息并且返回給前端,返回給前端的數(shù)據(jù)包括 Redis 中的未讀消息(發(fā)送方頭像,姓名,Id,內(nèi)容),以及對于每一個發(fā)送方的未讀消息數(shù)量,以及自己的頭像,和基礎(chǔ)信息。

    @RequestMapping(value = “/index”, method = {RequestMethod.GET})
    public InfoMessage getIndex(@RequestParam Integer customer){
    User user = userService.setUser();
    if(!(customernull||customer-1)){
    userMessage.readMessage(user.getId(),customer);
    }
    Map<Object, Object> unreadMessages = userMessage.getAllUnread(user.getId());
    Map<Object, Object> unreadNum = userMessage.getAllUnreadNum(user.getId());
    LinkMessage linkMessage = new LinkMessage(
    “”,
    “”,
    “”,
    “”);
    return new InfoMessage(“獲取成功”, unreadMessages, unreadNum, new UserInformation(
    user.getId(), user.getNickname(), user.getPic(), user.getGender(), user.getAge()
    ), linkMessage);
    }

    ###### 5. 獲取所有的歷史記錄這個邏輯也比較簡單,也是獲取到當(dāng)前登錄的用戶之后,去查找當(dāng)前登錄的用戶和對方的所有聊天記錄,然后將消息轉(zhuǎn)化為前端要求的格式返回即可。這里不給出代碼,具體可見代碼壓縮包

    @RequestMapping(value = “/history/download”, method = {RequestMethod.GET})
    public ResponseEntity download(@RequestParam Integer customer){
    User user = userService.setUser();
    List records = recordRepository.getChatRecord(user.getId(), customer, -1);
    HttpHeaders headers = new HttpHeaders();
    headers.add(“Cache-Control”, “no-cache, no-store, must-revalidate”);
    headers.add(“Content-Disposition”, “attachment; filename=” + System.currentTimeMillis() + “.xls”);
    headers.add(“Pragma”, “no-cache”);
    headers.add(“Expires”, “0”);
    headers.add(“Last-Modified”, new Date().toString());
    headers.add(“ETag”, String.valueOf(System.currentTimeMillis()));
    try {
    File file = new File(“history/”+user.getId()+“with”+customer+“.txt”); // 相對路徑,如果沒有則要建立一個新的output.txt文件
    if(!file.exists()) {
    file.createNewFile(); // 創(chuàng)建新文件,有同名的文件的話直接覆蓋
    }
    FileWriter writer = new FileWriter(file);
    BufferedWriter out = new BufferedWriter(writer);
    for(RecordDto record: records){
    out.write(“at “+ record.getTime()+”|”+record.getSenderId()+“–>”+record.getRecipientId()+“=”+record.getContent()+“rn”);
    }
    out.flush(); // 把緩存區(qū)內(nèi)容壓入文件
    out.close();
    return ResponseEntity.ok().headers(headers) .contentLength(file.length()).contentType(MediaType.parseMediaType(“application/octet-stream”)) .body(new FileSystemResource(file));
    } catch (IOException e) {
    e.printStackTrace();
    }
    File file = new File(“history/”+user.getId()+“with”+customer+“.txt”);
    return ResponseEntity.ok().headers(headers) .contentLength(file.length()).contentType(MediaType.parseMediaType(“application/octet-stream”)) .body(new FileSystemResource(file));
    }

    ###### 6. 歷史記錄下載下載分為兩部分,第一步為查詢聊天記錄,查詢的流程在上面已經(jīng)說過了,這里不再贅述,主要是第二部。我們更改 httpheader,加入文件流的控制。然后將我們查詢到的聊天記錄根據(jù)規(guī)定格式寫入到 Java 的 File 類中,然后將這個 File 類返回給前端即可。###### 7. 刪除歷史記錄刪除歷史記錄的邏輯很簡單,知道知道獲取到對方 Id 和自己的 Id 然后將數(shù)據(jù)庫中的所有這兩個人的記錄都刪除即可(sender=a,customer=b||sender=b,customer=a)這里就不給出代碼,具體見壓縮包### 前端實現(xiàn):#### Home 主界面的編寫使用 mint-ui 的 mt-tabbar 標(biāo)簽來實現(xiàn)消息盒子組件與好友列表組件之間的切換,同時添加了圖片實現(xiàn)點擊可以切換狀態(tài)。 消息 好友列表 ```

    好友列表的渲染

    根據(jù)后端傳來的雙層數(shù)組進行列表渲染,使用 Vue 中 value 和 key 的列表渲染方式,將不同分組的人員分開渲染,同時使用 URL 的傳值方式,使點擊不同的用戶,好友展示中展示不同的好友信息

    <div v-for="(value,key) in friends" :key="key"><mt-cell :title="key"></mt-cell><mt-cellv-for="(value2,key2) in value":key="key2":to="'/home/showfriend?group='+key+'&id='+key2"><span>{{value2.nickname}}</span><img slot="icon" v-bind:src="value2.pic" width="28" height="28" /></mt-cell></div>

    添加好友模塊

    使用 mint-ui 的搜索框組件進行昵稱的輸入,同時使用兩個下拉框?qū)崿F(xiàn)年齡和性別的篩選,使用 Vue 的原生回車事件進行提交

    <mt-searchv-model="searchContent"cancel-text="取消"placeholder="輸入昵稱搜索"@keyup.enter.native="search"></mt-search>

    發(fā)送好友請求模塊

    模塊初始化時,使用 LocalStorage 取出存儲的用戶信息,使用 axios 的錯誤捕獲來判斷是否成功發(fā)送好友請求

    .catch(err => {MessageBox.alert("不能重復(fù)添加,或者添加自己").then(action => {});});

    個人信息修改模塊

    個人修改的信息通過表單獲取,同時使用正則表達式判斷是否符合要求。圖片文件則進行后綴以及大小的合法性判斷,不符合要求則不進行修改

    var regPos = /^\d+$/;if (this.info.nickname == "" ||this.info.account == "" ||this.info.pic == "" ||this.info.gender == "") {MessageBox.alert("所填項不能為空").then(action => {});return false;}else if(this.info.gender!="男" && this.info.gender !="女"){MessageBox.alert("性別只能為男或女").then(action => {});return false;}else if(!regPos.test(this.info.age)){MessageBox.alert("年齡只能為非負(fù)整數(shù)").then(action => {});return false;}var img = e.target.files[0];if (img.type !== "image/jpeg" &&img.type !== "image/png" &&img.type !== "image/gif") {MessageBox.alert("請選擇圖片文件").then(action => {});return false;} else if (img.size > 1024 * 30) {console.log(img.size);MessageBox.alert("選擇小于30kb的圖片").then(action => {});return false;http://www.biyezuopin.vip}

    好友展示模塊

    通過 mint-ui 的上拉框進行功能的選擇,可以選擇刪除好友,添加標(biāo)簽,移動好友的功能,添加標(biāo)簽時與以往的標(biāo)簽進行比較,若有相同的則進行去重操作

    <mt-button icon="more" slot="right" @click.native="actionSheet"></mt-button>var add = {};add.targetId = this.info.id;add.contents = this.info.impressions;const impressions = new Set(add.contents);if(!impressions.has(value)){this.info.impressions.push(value);}console.log(impressions);add.contents = [...impressions.values()];

    好友驗證模塊

    使用兩個列表渲染,展示自己發(fā)送的請求,以及別人發(fā)送給你的請求,同時組件被創(chuàng)建時,自動請求后端,將所有的未讀消息置為已讀消息

    this.axios //標(biāo)記好友請求已讀.post("/verification/user/validation-reading").then(response => {}).catch(err => {console.log(err); //異常});

    聊天模塊

    Connection方法,建立stomp連接,并且為了防止websocket連接中斷,我采用了心跳保活技術(shù),每60s發(fā)送一條無用消息,確保連接正常。 并且推出頁面會斷開socket連接,以減小服務(wù)器壓力。

    發(fā)送消息,進行了內(nèi)容判斷 發(fā)送內(nèi)容不能為空或者內(nèi)容長度不能超過 200

    登錄注冊模塊

    登錄后 將 token 與個人信息相關(guān)的數(shù)據(jù)存入 localstorage 內(nèi),退出登錄就直接清除 localstorage 內(nèi)全部數(shù)據(jù)。

    注冊后,直接跳轉(zhuǎn)到主頁面,有良好的錯誤處理機制。

    測試模塊

    TESTNG 文件配置

    使用 XML 配置要測試的模塊,包括注冊功能,登錄功能,添加好友功能,聊天功能等

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Link聊天自動化測試"><test name="測試"><classes><class name="register.Register"/><class name="login.Login" /><class name="addfriend.Addfriend"/><class name="impression.Impression"/><class name="chat.Chat"/><class name="deletefriend.Deletefriend"/></classes></test> </suite>

    瀏覽器驅(qū)動公共代碼

    公共代碼中實現(xiàn)公共操作,包括啟動瀏覽器驅(qū)動,瀏覽器驅(qū)動關(guān)閉,共享變量 DRIVER,供所有模塊使用

    @BeforeClass public static void setUp() throws Exception {System.out.println("啟動瀏覽器");System.setProperty("webdriver.chrome.driver","./src/chromedriver.exe");driver = new ChromeDriver();driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); }

    注冊模塊自動化測試代碼

    使用 selenium 進行瀏覽器的操作,輸入待注冊的賬號,注冊完成后跳轉(zhuǎn)到主頁面進行登錄,判斷是否注冊成功

    Main.setUp(); Main.driver.get(RegisterURL); Main.driver.findElement(By.name("user_register")).sendKeys(this.email); Main.driver.findElement(By.name("user_name")).sendKeys(this.name); Main.driver.findElement(By.name("user_pass")).sendKeys(this.psw); Main.driver.findElement(By.cssSelector(".btn_register")).click(); Thread.sleep(3000); String current_url = Main.driver.getCurrentUrl(); try {Assert.assertEquals(current_url,this.SuccessURL,"注冊驗證失敗!!"); }catch (Exception e){Main.tearDown(); } Main.tearDown();

    登錄模塊自動化代碼

    在輸入框中輸入賬號與密碼,點擊登錄,頁面加載完成后判斷是否轉(zhuǎn)跳到主頁面

    Main.setUp(); Main.driver.get(Main.BaseURL); WebElement email = Main.driver.findElement(By.name("user_login")); WebElement psw = Main.driver.findElement(By.name("user_pass")); WebElement button = Main.driver.findElement(By.cssSelector(".btn_login")); email.sendKeys(Main.email); psw.sendKeys(Main.psw); button.click();

    印象模塊自動化測試代碼

    根據(jù)提供的賬號登錄到主頁面,點擊好友框,添加印象,然后匹配數(shù)據(jù)庫中的印象表,查看是否添加成功

    Main.driver.findElement(By.xpath("//*[text()='康王']")).click(); Main.driver.findElement(By.className("mintui-more")).click(); Thread.sleep(2000); Main.driver.findElement(By.xpath("//*[text()='添加標(biāo)簽']")).click(); Main.driver.findElement(By.cssSelector("input")).sendKeys(this.impre); Main.driver.findElement(By.xpath("//*[text()='確定']")).click();

    好友刪除自動化測試代碼

    點擊頁面刪除好友按鈕,退回到好友列表界面,查看是否刪除該好友

    Main.driver.findElement(By.className("mintui-more")).click(); Main.driver.findElement(By.xpath("//*[text()='刪除好友']")).click(); Main.driver.findElement(By.xpath("//*[text()='確定']")).click(); Main.driver.findElement(By.xpath("//*[text()='確定']")).click();try{WebElement a = Main.driver.findElement(By.xpath("//*[text()='康王']"));Thread.sleep(4000); }catch (Exception e){Main.tearDown(); } Main.tearDown();

    聊天界面自動化測試代碼

    點擊預(yù)訂的好友,點擊發(fā)送消息,跳轉(zhuǎn)到消息界面,輸入框中輸入消息,點擊發(fā)送,查看頁面中是否渲染出該條消息,并登陸另一賬號查看是否接受到該消息

    Main.driver.findElement(By.className("mint-button--large")).click(); Main.driver.findElement(By.className("footer_inpuit")).sendKeys("呆呆呆呆"); Main.driver.findElement(By.className("footer_button")).click(); try{WebElement a = Main.driver.findElement(By.xpath("//*[text()='確定']")); }catch(Exception e){Assert.fail("發(fā)送消息失敗!!");Main.tearDown(); }

    五、成果展示

    登錄頁面 注冊頁面

    消息盒子頁面(無消息) 消息盒子(有消息)

    好友盒子(無朋友驗證) 好友盒子(有朋友驗證)

    在好友列表中點擊好友之后顯示的頁面 點擊右上角的點之后的頁面

    個人信息 搜索結(jié)果界面

    搜索點擊好友后的界面 可以發(fā)送驗證消息(帶信息)

    聊天界面

    驗證消息

    全套資料下載地址:https://download.csdn.net/download/sheziqiong/85595798

    總結(jié)

    以上是生活随笔為你收集整理的基于Vue+Java实现的在线聊天APP系统设计与实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。