写在前面
个人博客网址:https://jiong952.github.io/
个人github网址:https://github.com/jiong952
源码地址:https://github.com/jiong952/QQ2.0
课程题目
实现一个带图形用户界面的 Java 版即时聊天程序。
【功能提示】
- 用户登录及登录验证:用户能够使用固定帐号(帐号程序内置即可,无需完成额外的注册功能)登录系统,系统能对预定的帐号、密码进行验证;
- 聊天功能:能够在两台以上的机器上登录运行程序,能使用不同帐号完成在线的即时消息发送(聊天);
- 文件传输;
- 好友管理:能够显示好友列表,并能够添加、修改、删除好友;
- 在线、离线状态显示:能够显示好友的在线状态或离线状态。
- 聊天记录管理:能够以文件或数据库形式将聊天记录进行存储,并能打开、显示、删除所存储的聊天记录。
题目分析与设计
开发环境
编程语言:Java
开发环境:IntelliJ IDEA 2021.1.1
要求完成度:100%
需求分析
本题要求实现的功能如下登录验证、局域网实时聊天、离线留言、文件发送接收、好友管理(增删改查)、好友列表实时显示在线离线状态、聊天记录管理(显示、删除、添加、持久化到数据库和本地);除此之外,还添加了群发功能,服务器日志显示,好友验证,好友单删与双删,服务器推送功能。以下为软件功能架构分析图。
软件功能架构图
界面设计
登录界面
仿qq登录界面设计,包含头像显示和背景框,还原度较高,图标也使用qq图标,读取显示屏大小进行设置窗体自动显示在屏幕正中央。
登录界面图
使用BorderLayout布局管理器进行布局,图片进行了自适应设置,可以按原比例显示,不会被裁剪,账号输入框以及密码输入框加入输入监听,提醒用户进行输入,登录按钮增加信息校验功能。登录成功弹窗有提示
登录成功提示
主界面
主界面同样按照qq界面进行还原,上方显示用户信息,包括用户昵称以及个性签名和图片,图片采取适配设置,中间面板采用选项卡组建JTabbedPane加文件树组件Jtree实现切换界面以及好友列表管理和好友分类。
单击我的好友和星标好友可对好友列表进行展开和收起,双击好友可跳转到聊天界面,聊天界面进行唯一性处理保证只会出现一个聊天框。单击群聊列表可进行切换。星标好友可对好友进行特殊颜色显示。好友列表可对头像进行在线离线状态显示,一旦有用户上线,立刻会刷新好友列表进行在线显示。
下方实现了群发消息按钮,添加好友按钮以及好友验证按钮。群发消息可跳转到群发界面一键发送消息给全部好友,添加好友可跳转到搜索好友界面,好友验证可进行跳转到好友申请界面进行查看。
QQ主界面
聊天界面
聊天界面也按照东南西北中布局模式,上方显示聊天对象,可显示头像、昵称以及对好友的备注信息。中部聊天窗口采用JtextArea放进滚动面板,设置为不可编辑。底部发送窗口也采用JtextArea+滚动面板设计,可自动换行。底部聊天记录按钮点击则会将历史聊天记录显示在聊天窗口;在发送窗口输入信息后点击“发送消息”按钮可进行消息发送,该按钮自动进行消息判空验证,单击底部“发送文件”按钮可调出文件选择器,可发送任意格式文件,选择后单击“发送”按钮即可发送。东部是文件接收区域,收到的文件会以新增文件选项卡的形式出现。
文件区域选项卡是一个个独立的文件接收面板,上方显示包括文件类型预览图,文件名,文件大小,文件接收进度条,中间显示文件即将存储的路径,下方显示接收,拒绝以及更改存储路径的“另存”按钮。单击下方选项卡可进行文件切换。窗口最上方放置不经常使用的功能:聊天记录管理菜单以及好友管理菜单。单击聊天记录菜单出现:删除聊天记录以及备份聊天记录到本地的选项;单击好友管理菜单可出现修改好友信息以及删除好友选项。
聊天界面
文件选择器
接收文件
修改好友信息界面
该界面使用的是Idea自带的Swing UI Designer的GUI Form进行设计,采用的是表单布局方式,该界面显示好友的全部信息包括头像、Id、昵称、性别、年龄、个性签名以及成为好友的日期以及发起好友请求的人是谁。可对好友进行备注修改以及是否设置为星标好友,单击下方修改按钮即可进行修改,单击取消按钮即可进行取消退出修改。
修改好友信息界面
群发消息窗口
群发消息窗口采用的是轻便化设计,只有输入窗口以及发送按钮,输入进行即可进行群发。
群发窗口
搜索好友界面
搜索好友界面采用的是南北布局样式,上方位搜索输入框,搜索按钮,以及添加按钮。用户输入要添加的用户id后点击搜索即可出现符合要求的用户,该过程自动过滤用户本人,搜索界面在下方Jtable表格输入,可清晰的显示搜索的人数以及用户的ID、昵称、性别、年龄、个性签名信息,表格设置为不可编辑且只可以单选,单击表项点击添加按钮即可发送好友申请,该过程进行好友验证,如果已经是好友则会进行提示。
搜索好友界面
好友验证界面
这里采用的也是轻便式卡片式窗口布局,采用JtabbedPane选项卡进行验证切换,面板可显示申请用户的头像,ID,昵称,性别,年龄以及个性签名,点击同意即可成为好友,并立刻刷新好友界面。
好友验证界面
数据库表设计
本系统总共包括四张表,user表,message表,friend表,del_friend表,下面是每张表的设计详情。
user表
user表字段属性如下
其中昵称user_name设置为默认是user_id,头像在数据库仅存储路径,将图片文件存储在服务器本地文件夹,这样可以减轻数据库压力,加快存储速度。
服务器头像文件
性别设置为更小的tinyint类型,注册时间设置为date类型且不可更改,修改时间则设置为datetime类型且默认值为CURRENT_TIMESTAMP。
字段名 | 类型 | 中文名 | 备注 |
---|---|---|---|
user_id | varchar | id | 【*】 |
password | varchar | 密码 | 【*】 |
user_name | varchar | 昵称 | 默认为user_id |
avatar_path | varchar | 头像 | 设置默认头像 |
gender | tinyint | 性别 | 0女 1男 2未填 |
age | int | 年龄 | |
signature | varchar | 电话 | 个性签名 |
create_time | Date | 注册时间 | 注册时间 |
update_time | datetime | 修改时间 | 每次修改信息都自动更新 默认值CURRENT_TIMESTAMP |
message表
groupId是预留字段,为拓展群聊成功准备,sender_del和getter_del是消息记录删除的重要字段,用户可删除任意记录,若该记录已被删除则置为1,用户不再可读;content字段存放消息内容或者文件的存储路径,如果文件离线发送,会将文件以用户名+时间戳+文件名暂时存储在服务器端本地,在数据库表仅存储文件路径,增快读取速率。type对应消息常量类,服务器根据消息种类进行转发和响应。file_name字段用于保存文件名,因为存储到服务器的文件名和发送给用户的文件名是不一样的,所以要单独存储。Success字段是实现离线发送的重要字段,当用户离线时,该字段置为0,当用户上线,读取getter_id为用户且离线的消息进行发送,这样就实现了离线留言功能。
服务器用户文件暂存
字段名 | 类型 | 中文名 | 备注 |
---|---|---|---|
msg_id | varchar | 消息id | 主键 非空 自增 |
sender_id | varchar | 发送者id | 非空 |
getter_id | varchar | 接收者id | 非空 |
group_id | varchar | 群号 | 默认为空,则是普通消息 查询群聊记录要查这个字段 |
sender_del | int | 发送者删除 | 1删除 0正常【默认】 |
getter_del | int | 接收者删除 | 1删除 0正常【默认】 |
content | varchar | 内容 | 可以是普通消息,也可以是文件存放的路径 |
type | int | 消息类型 | 对应消息类型常量类 |
send_time | datetime | 发送时间 | |
file_name | varchar | 文件名 | 因为存储到服务器的文件名和发送给用户的文件名是不一样的,所以要单独存储 |
success | tintint | 发送成功 | 1成功 0好友离线 先不发送 |
friend好友表
好友关系记录表,成为好友会插入一条好友记录,is_ask可以判定是谁发起的好友申请,create_time可以表明成为好友的时间,可拓展做好友周年纪念等功能,remark和satr即可设置好友备注以及星标。
字段名 | 类型 | 中文名 | 备注 |
---|---|---|---|
id | int | 主键 | 自增 |
myid | varchar | 用户id | |
friend_id | varchar | 好友id | |
is_ask | tinyint | 是否请求 | 这个好友是否是发送好友申请的人 |
create_time | date | 成为好友的时间 | |
remark | varchar | 备注 | |
star | tinyint | 星标 |
del_friend删除好友关系表
该表项用于好友单删和双删区分,当单方面删除时,另一方是可以保留单方面好友关系即体现为:好友列表仍存在该好友,消息记录仍可见,只是不可以发消息,仍然可以对好友进行修改。当单删时,会删除friend表的一条记录,同时插入del_friend表一条记录。
字段名 | 类型 | 中文名 | 备注 |
---|---|---|---|
id | int | 主键 | 自增 |
ask_del_id | varchar | 请求删除的用户id | 请求删除的用户id |
del_id | varchar | 被删除的用户id | 被删除的用户id |
模块与分层设计
本系统分为两个项目程序,即服务端和客户端。
代码编写上采用了经典的MVC分层设计,即Model、View、Controller三层。Model层主要负责处理业务代码和数据操作代码;View层主要展示数据和供用户使用;Controller层负责模型和视图的协调。以此达到代码解耦合便于维护。
分包架构方面:
客户端分为manage包、request包、service包、thread包、view包以及通用的common包和utils包;
- manage包:管理窗口唯一类
- request包:客户端主动创建请求线程向服务端发送请求,请求响应数据
- service包:客户端业务逻辑层,通过通信线程发送消息到服务端
- thread包:存放与服务端通信线程类
- view包:存放swing视图类
- common包:存放实体类
- utils包:存放工具类
服务端分为manage包、dao包、service包、thread包、start包以及通用的common包和utils包;
- dao包:存放数据访问层类
- thread包:相当于controller层,一方面保持与单个用户的通信进程,负责转发消息,另一方面接收所有用户请求响应回数据
- start包:存放服务端启动类
C/S架构通信设计
服务端与客户端的通信设计方面,我模拟web端HTTP通信请求响应模型设计了监听线程与请求线程两种,如下图所示
C/S通信架构
- **请求线程(RequestThread):**使用了HTTP请求相应模型request和response模式,加以简化,客户端主动向服务端发起请求,等待响应数据。
- **监听线程(ConnectThread):**客户端登录后,与服务端建立一条通信线程,便于服务端实时动态感知在线用户人数,用户进行消息发送,文件传输,好友验证等不需要立刻接收响应的动作时,都是通过该线程进行通信。服务端通信端接收到客户端数据,承担转发或暂存功能;
- **管理线程(ManageThread):**该类用户服务端管理在线用户线程,客户端建立全局唯一的通信监听线程;
请求响应模型
Request请求类,客户端装包,发送,服务端拆包,解析。
属性名 | 类型 | 中文名 | 备注 |
---|---|---|---|
requesterId | String | 请求者ID | |
content | String | 请求内容 | 请求内容填写后端方法名,相当于路径映射 |
params | Object[] | 请求参数 | 可以传任意类型多种参数 |
Response响应类,服务端装包,发送,客户端拆包,解析。
属性名 | 类型 | 中文名 | 备注 |
---|---|---|---|
stateCode | String | 响应码 | 响应码逻辑模拟HTTP响应码 |
returnValue | Object | 返回值 | 响应内容就是客户端请求的值 |
StateCode响应类 interface类
属性名 | 类型 | 中文名 |
---|---|---|
SUCCEED | String | 操作成功 |
FAIL | String | 操作失败 |
HAS_LOGIN | String | 已经登录 |
NOT_FOUND | String | 找不到对应url |
响应状态码可规定不同的响应状态,在用户view进行区分显示。
在web设计时,浏览器会自动解析response状态码,显示成功页面或者404,502
本系统也可以设计一个拦截器类,拿到响应后做状态码判断进行不同页面显示,为避免冗杂,就没有添加。
整个请求响应流程如下所示:
请求响应流程图
用户点击界面进行请求,客户端请求消息包封装后,进行序列化,用户通过socket创建字节输入流输入请求包给服务端,服务端控制器进行解析拆包,通过content路径找到对应的方法调用不同的业务逻辑进行数据库访问,将消息结果进行封装为response包,发送响应回客户端,客户端拆包后将数据响应到展示层界面。
优点:每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
监听线程
监听线程主要作用于单向消息,在这里服务器主要作为消息转发的角色
客户端只能单向写入或者单向读取【或者理解为被动读取,即用户不知道什么时候就会收到消息】
消息类型MessageType类 interface类
属性名 | 类型 | 中文名 |
---|---|---|
MESSAGE_COMMON_MSG | String | 普通信息包 |
MESSAGE_GET_ONLINE_FRIEND | String | 客户端请求拉取在线用户列表 |
MESSAGE_RETURN_ONLINE_FRIEND | String | 服务端返回在线用户列表 |
MESSAGE_CLIENT_EXIT | String | 客户端请求退出 |
MESSAGE_TO_ALL_MSG | String | 群发信息包 |
MESSAGE_GROUP_CHAT | String | 群聊信息包 |
MESSAGE_FILE | String | 文件信息包 |
MESSAGE_NEWS | String | 服务端推送消息 |
NEW_ONLINE | String | 有新的好友上线了 |
ASK_MAKE_FRIEND | String | 好友请求 |
SUCCESS_MAKE_FRIEND_TO_ASK | String | 好友申请成功 |
SUCCESS_MAKE_FRIEND_TO_PERMIT | String | 同意成功 |
SEND_SUCCESS | String | 消息发送成功 |
NEW_OFFLINE | String | 有好友下线了 |
SEND_SUCCESS_TO_ALL | String | 群发成功 |
SEND_FILE_SUCCESS | String | 发送文件成功 |
线程管理
线程管理思想如下图所示:
管理线程模型
服务端:
在服务端将所有在线的与客户端建立连接的通讯线程做统一管理
ManageServerConnectClientThread类,也就是这样,服务器后台才可以随时从管理库中拿到目标getter进行消息转发。
客户端:
对于客户端,也创建一个管理类,ManageClientConnectServerThread类,这个类的作用在于代码的分割,把用户的线程提升到全局作用域,这样在service层想要往通讯线程中写入东西,只需要从管理类中取出即可。
复杂功能设计
多用户登录 单点登录 安全退出
多用户登录:
使用服务端多线程开发实现多用户登录,每当有一个用户发起登录请求,立即产生一条新的通信线程,有多少个用户在线,服务端就有多少个通信线程。
单点登录:
单点登录,即不可重复登录,用户登录之后,在服务端的在线线程管理类加入用户,每当有用户进行登录请求,先在在线用户线程池查看是否存在,若存在则已经登录,返回HAS_LOGIN状态码,禁止用户重复登录。
安全退出:
客户端退出不是仅仅结束客户端进程就可以,应当发送exit消息包给服务端,将用户从在线线程池中去除。
上线离线提醒
好友列表可实时刷新好友在线状态,方便用户判断使用。原理在于当用户登录之后,将用户查询的好友列表与在线线程池进行对比,一方面处理用户自身好友在线状态,另一方面遍历在线好友列表通知该用户上线,发送更新列表提醒。
文件传输功能
文件传输实际上只是上传文件和下载文件的过程,具体流程如下图所示。
这部分主要是序列化文件以及从本地读取文件和保存文件
离线留言 离线文件
离线留言和离线文件都要依靠message的success字段,当服务器转发消息时,目标对象在线时,success字段置为true存入数据库,而目标对象离线时,success字段置为false存入数据库。随后,当getter登录时,先到数据库查看是否有接收对象是自己,但是未发送成功的消息,有就取出,由服务器再次转发。对于文件消息,存入数据库时,只存储文件路径,真实文件存储在服务器本地,以用户id+时间戳+文件名方式存储。
双向好友删除
这个功能是根据现实优化的,删除好友功能是双向的,存在单删和回删功能。
单删时,主动删除的用户A将看不到被删除的用户B的一切消息;而用户B是可以看到A的在线状态,还可以查看历史聊天记录的,仅仅只是不能发送消息;当用户B回删之后,才真正是断绝了好友关系。
为了实现这个功能,我增设了一张del_friend表,设置有ask_del_id和del_id,单删时,删除friend表中my_id为A,friend_id为B的记录,同时往del_friend表增加一条ask_del_id为A和del_id为B的记录。
好友验证
这个功能也是根据现实增加的,添加好友是个双向的过程,一方发起申请,另一方同意才可以成为好友;因此我将好友验证设计为如下双向响应的过程:
好友验证过程
测试分析
jar包使用说明
form-1.2.1.jar
使用Swing UI Designer的form LayOut需要用到的jar
mysql-connector-java-8.0.21.jar
数据库连接需要的驱动包
junit-4.13.1.jar
单元测试包
数据库配置说明
数据库名称为qq,采用utf8mb4字符集,排序规则为utf8mb4_0900_ai_ci,创建数据库后导入qq.sql文件执行,即可生成全部数据。
典型测试数据构建与预期对比
登录退出测试
登录功能
- 测试数据:用户id:a、用户密码:a
- 预期结果:成功登录
- 服务端日志:
用户端Socket[addr=/127.0.0.1,port=14098,localport=9998] 服务端通讯线程等待客户端发送的消息… 【2022-05-27 15:06:30】a登录成功 当前在线人数:1人 |
---|
单点登录
- 测试数据:用户id:a、用户密码:a (a已经登录)
- 预期结果:登录失败
- 服务端日志:
用户端Socket[addr=/127.0.0.1,port=14175,localport=9998] 【2022-05-27 15:08:43】a已登录过 |
---|
多用户登录
- 测试数据:用户id:zjh、用户密码:a (a已经登录)
- 预期结果:a和zjh同时在线
- 服务端日志:
用户端Socket[addr=/127.0.0.1,port=14266,localport=9998] 服务端通讯线程等待客户端发送的消息… 【2022-05-27 15:13:23】zjh登录成功 当前在线人数:2人 |
---|
退出测试
- 测试数据:用户a和zjh 退出a
- 预期结果: zjh正常在线 a退出
- 服务端日志:
【2022-05-27 15:17:28】用户a退出 当前在线人数:1人 |
---|
聊天测试
在线聊天
- 测试数据:用户a和zjh (在线状态)
- 预期结果: 实时接收消息
- 服务端日志:
【2022-05-27 15:22:25】用户a查看和zjh是否是好友 【Fri May 27 15:22:25 CST 2022】a 向zjh发送了: 你好啊 服务端通讯线程等待客户端发送的消息… 用户端Socket[addr=/127.0.0.1,port=14370,localport=9998] 【2022-05-27 15:22:30】用户zjh查看和a是否是好友 【Fri May 27 15:22:30 CST 2022】zjh 向a发送了: enen 服务端通讯线程等待客户端发送的消息… 用户端Socket[addr=/127.0.0.1,port=14375,localport=9998] 【2022-05-27 15:22:36】用户zjh查看和a是否是好友 【Fri May 27 15:22:36 CST 2022】zjh 向a发送了: 吃饭了吗 服务端通讯线程等待客户端发送的消息… 用户端Socket[addr=/127.0.0.1,port=14376,localport=9998] 【2022-05-27 15:22:40】用户a查看和zjh是否是好友 【Fri May 27 15:22:40 CST 2022】a 向zjh发送了: 吃了吃了 服务端通讯线程等待客户端发送的消息… |
---|
离线留言
- 测试数据:用户a和zjh (a在线 zjh不在线)
- 预期结果:a成功发送 zjh上线后收到消息 时间同步
头像灰色不在线
时间同步
- 服务端日志:
【2022-05-27 15:26:24】a登录成功 当前在线人数:1人 用户端Socket[addr=/127.0.0.1,port=14405,localport=9998] 【2022-05-27 15:26:24】用户a请求好友列表 用户端Socket[addr=/127.0.0.1,port=14406,localport=9998] 【2022-05-27 15:26:28】用户a请求好友列表 用户端Socket[addr=/127.0.0.1,port=14408,localport=9998] 【2022-05-27 15:26:35】用户a查看和zjh是否是好友 【Fri May 27 15:26:35 CST 2022】a 向zjh发送了: 你快上线 服务端通讯线程等待客户端发送的消息… 用户端Socket[addr=/127.0.0.1,port=14409,localport=9998] 【2022-05-27 15:26:38】用户a查看和zjh是否是好友 【Fri May 27 15:26:38 CST 2022】a 向zjh发送了: 我在等你 服务端通讯线程等待客户端发送的消息… 用户端Socket[addr=/127.0.0.1,port=14445,localport=9998] 服务端通讯线程等待客户端发送的消息… 离线消息发送Message{msgId=118, senderId=‘a’, getterId=‘zjh’, groupId=‘null’, content=‘你快上线’, sendTime=2022-05-27 15:26:35.0, msgType=‘4’, fileBytes=null, fileName=‘null’} 离线消息发送Message{msgId=119, senderId=‘a’, getterId=‘zjh’, groupId=‘null’, content=‘我在等你’, sendTime=2022-05-27 15:26:38.0, msgType=‘4’, fileBytes=null, fileName=‘null’} 【2022-05-27 15:27:21】zjh登录成功 当前在线人数:2人 |
---|
文件传输测试
在线文件传输
- 测试数据:用户a和zjh (在线状态)
- 预期结果: 实时接收文件 a发送照片文件pic.png,zjh接收后另存在桌面
a点击发送文件
a成功发送 zjh成功收到
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3EoBl4XC-1653643145056)(https://myblogimgbed.oss-cn-shenzhen.aliyuncs.com/img/202205271718830.png)]
修改路径
接收文件
文件创建时间与接收时间一致
- 服务端日志:
【Fri May 27 15:32:28 CST 2022】a 向zjh发送了: pic.png |
---|
离线文件传输
- 测试数据:用户a和zjh (zjh在线状态 a离线)
- 预期结果:离线发送接收文件 zjh发送文档文件《Java大作业封面及格式.doc》,zjh接收后另存在D盘
离线发送成功
a上线成功收到
成功保存
文件保存实时
- 服务端日志:
【Fri May 27 15:32:28 CST 2022】zjh 向a发送了: Java大作业封面及格式.doc |
---|
好友验证测试
搜索好友
- 测试数据:搜索id为a
- 预期结果:查询到所有id带a的用户(除用户外)不可添加已是好友用户
模糊搜索好友
好友预提醒
- 服务端日志:
【2022-05-27 15:45:31】用户a搜索用户 用户端Socket[addr=/127.0.0.1,port=14794,localport=9998] 【2022-05-27 15:45:33】用户a查看和zjh是否是好友 用户端Socket[addr=/127.0.0.1,port=14807,localport=9998] |
---|
好友申请
- 测试数据:a向bac发出好友申请
- 预期结果:发送好友申请成功
向用户bac发出好友申请
- 服务端日志:
【2022-05-27 15:48:01】用户a向bac发送好友申请 用户端Socket[addr=/127.0.0.1,port=14839,localport=9998] |
---|
好友同意
- 测试数据:bac同意a的好友申请
- 预期结果:刷新双方好友列表
好友验证界面
点击同意 立即刷新双方好友列表
- 服务端日志:
【2022-05-27 15:48:55】用户bac同意a的好友请求 用户端Socket[addr=/127.0.0.1,port=14856,localport=9998] 【2022-05-27 15:48:56】用户bac请求好友列表 用户端Socket[addr=/127.0.0.1,port=14859,localport=9998] 【2022-05-27 15:49:03】用户a请求好友列表 |
---|
好友管理测试
单删好友
- 测试数据:a单删bac
- 预期结果:a列表不存在bac
点击删除
确认删除
删除成功
bac仍然可以看到聊天记录和a的在线状态
回删功能
- 测试数据:bac回删
- 预期结果:bac与a的好友关系彻底结束 聊天记录彻底删除
删除提醒
彻底删除
删除前聊天记录被彻底删除
修改好友信息
- 测试数据:bac修改好友zjh备注为“红色胖子” 设置为星标好友
- 预期结果:好友列表与聊天窗口备注改变 星标好友列表新增zjh
修改测试
修改成功
聊天记录测试
显示聊天记录
- 测试数据:a点击与admin(库里)的聊天窗口 点击聊天记录
- 预期结果:历史聊天记录显示在聊天窗口
备份聊天记录到本地
- 测试数据:a点击与admin(库里)的聊天窗口 点击菜单栏聊天记录 ——》备份到本地
- 预期结果:本地D:\用户聊天记录备份\a\admin\1653639736003出现聊天文档以及聊天文件
备份本地成功
删除聊天记录
- 测试数据:a点击与admin(库里)的聊天窗口 点击菜单栏聊天记录 ——》删除聊天记录
- 预期结果:a本地聊天记录删除,数据库相应字段改为del = 1状态
删除前本地备份
点击删除
删除提示
成功删除
群发测试
- 测试数据:a向所有好友群发“你好”
- 预期结果:a的所有好友同时收到消息
测试成功