第四章 系统设计
系统设计是把用户需求转化为系统的最重要开发环节,解决了“应该怎么做系统”的问题。在本章节中,主要是在系统需求分析的基础上,对系统架构、系统功能模块设计、系统工作流程设计和系统数据库设计进行阐述。
4.1 系统架构
实时聊天系统采用SpringBoot+Free Marker+Jpa框架开发,是标准的MVC模式,将整个系统划分为View层,Controller层,Service层,DAO层四层。其中,Free Marker拿取数据进行展示数据,SpringBoot实现业务对象管理,Jpa作为数据对象的持久化引擎。整个系统架构运行流程如图4-1所示:
在这里插入图片描述

图4-1 系统架构运行图
View层:与Controller层结合比较紧密,需要二者结合起来协同工发,主要负责前台ftl页面的表示。
Controller层:控制器,导入service层,因为service中的方法是我们使用到的,controller通过接收前端传过来的参数进行业务操作,在返回一个指定的路径或者数据表。
Service层:存放业务逻辑处理,也是一些关于数据库处理的操作,但不是直接和数据库打交道,它有接口还有接口的实现方法,在接口的实现方法中需要导入Dao层,Dao层是直接跟数据库打交道的,它也是个接口,只有方法名字,具体实现在mapper.xml文件里,service是供我们使用的方法。
Dao层:负责对数据向数据库增删改查的操作。在该注册的框架中,如果不使用SpringBoot的话,每个层之间的数据传递都需要new一个调用该层数据的类的实例。而使用了SpringBoot的话,需要做的就是把DAO层和BIZ层的每个类都写一个接口类,接口类里写实现类的方法,在调用的时候不new对象,直接用对象点(.)方法就可以,还需要对每个对象加上set/get方法。
持久层:使用了Jpa来将实体对象持久化到数据库中。不用再进行繁杂的Jdbc和sql语句。在Dao层使用Jpa语法可以直接使用想要进行的sql,或者可以直接加上@Query注解后面写要进行的sql语句

4.2 系统功能模块设计
实质上,实时聊天系统的综合性相对较强,复杂程度相对较高,可对现有软件进行充分利用,进行系统设计与规划。构建完善成熟的实时聊天系统,其中涉及到以下内容,即前台网页界面、处理程序、MySQL 后台数据库系统等,在网站页面中显示出以下内容,例如用户头像、群组名称、群组成员、群组信息等。处理程序其实也就是对用户提交表单与相关操作进行处理,存储在后台数据库的信息有账户数据、聊天记录数据、群组数据和新闻数据等。
因此,在线系统需要具备前台功能和后台功能,其中,前台功能实现以下功能,用户注册、用户登录、添加好友、加入群聊、创建群聊、查看聊天记录、发送消息、删除好友、退出群聊、解散群聊和个人设置。系统前台功能如图4-2所示:
在这里插入图片描述

图4-2 系统前台功能模块结构图
系统后台功能实现以下功能,聊天管理和系统设置。系统后台功能如图4-3所示:
在这里插入图片描述

图4-3 系统后台功能模块结构图
目录
摘 要 I
ABSTRACT II
第一章 绪论 1
1.1 课题背景、目的及意义 1
1.1.1 课题背景 1
1.1.2 课题目的和意义 2
第二章 相关技术介绍 3
2.1 Javascript 3
2.2 Ajax 3
2.3 MySQL 3
2.4 SpringBoot框架 3
2.5 Free Marker模板引擎 4
2.6 B/S模式 4
2.8 系统开发平台及运行环境 5
2.7.1 系统开发平台 5
2.7.2 运行环境 6
第三章 系统需求分析 8
3.1 功能需求分析 8
3.2 非功能需求分析 11
3.3 可行性分析 12
3.3.1 经济可行性 12
3.3.2 技术可行性 12
3.3.3 操作可行性 13
第四章 系统设计 14
4.1 系统架构 14
4.2 系统功能模块设计 15
4.3 系统工作流程设计 16
4.4 数据库设计 16
4.4.1 数据库概念设计 17
4.4.2 数据库逻辑设计 24
第五章 系统实现 30
5.1 关键代码 30
5.2 用户模块 49
5.2.1 好友申请 49
5.2.2 好友设置 52
5.2.3 注册登录 54
5.2.4 发送消息 55
5.2.5 个人中心 57
5.2.6 设置 59
5.2.7 群组管理 59
5.3 管理员模块 62
5.3.1 管理员登录 62
5.3.2 聊天管理 62
5.3.3 系统设置 64
5.3.4 个人信息 66
第六章 系统测试 67
6.1 测试的目的与目标 67
6.2 测试方法 67
6.3 测试用例 68
6.4 测试结论 68
结论与展望 70
致 谢 71
参考文献 72
部分代码参考:

package com.yuanlrc.base.server.home;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.yuanlrc.base.bean.WebSocketMsg;
import com.yuanlrc.base.entity.common.Account;
import com.yuanlrc.base.entity.common.AccountGroupMember;
import com.yuanlrc.base.entity.common.Friend;
import com.yuanlrc.base.entity.common.MsgContent;
import com.yuanlrc.base.entity.common.MsgLog;
import com.yuanlrc.base.service.common.AccountGroupMemberService;
import com.yuanlrc.base.service.common.AccountService;
import com.yuanlrc.base.service.common.FriendService;
import com.yuanlrc.base.service.common.MsgContentService;
import com.yuanlrc.base.service.common.MsgLogService;

@Service
@ServerEndpoint("/webSocket/{userid}")
public class WebSocket {

	public static Map<Long, WebSocket> clients = new ConcurrentHashMap<Long, WebSocket>(); 
	
	public static int onlineCount = 0;//在线人数
    
	private Long userid;//用户id
	
	private Session session;
	
	private List<Friend> friendList;
	
	private static AccountService accountService;
	
	private static FriendService friendService;
	
	private static MsgContentService msgContentService;
	
	private static MsgLogService msgLogService;
	
	private static AccountGroupMemberService accountGroupMemberService;
	
	/**
	 * 建立连接
	 * @param userid
	 * @param session
	 * @throws IOException
	 */
    @OnOpen 
    public void onOpen(@PathParam("userid") Long userid, Session session) throws IOException { 
        this.userid = userid;
        this.session = session;
        this.friendList = friendService.findMyFriendList(userid);
    	addOnlineCount(); 
        clients.put(userid, this);
        System.out.println("成功建立连接,用户ID = 【" + userid + "】,当前在线用户数:" + onlineCount);
        //检查该用户是否有未读消息
        loadUnReadMsg(userid);
        //给所有好友发送上线消息
        onlineNotice(userid);
    } 
   
	/**
     * 连接关闭
     * @throws IOException
     */
    @OnClose 
    public void onClose() throws IOException { 
        clients.remove(userid); 
        subOnlineCount();
        //给所有好友发送下线消息
        offlineNotice(userid);
        System.out.println("已断开连接,用户ID = 【" + userid + "】,当前在线用户数:" + onlineCount);
    } 
   
    /**
     * 当收到信息
     * @param message
     * @throws IOException
     */
    @OnMessage 
    public void onMessage(String message) throws IOException { 
    	WebSocketMsg webSocketMsg = JSONObject.parseObject(message, WebSocketMsg.class);
    	System.out.println(webSocketMsg);
    	sendMsg(webSocketMsg);
    	
    } 
   
    /**
     * 发生错误
     * @param session
     * @param error
     */
    @OnError 
    public void onError(Session session, Throwable error) { 
        error.printStackTrace(); 
    } 
    
       
    public void sendMessageAll(String message) throws IOException { 
        for (WebSocket item : clients.values()) { 
            item.session.getAsyncRemote().sendText(message); 
        } 
    } 
   
    public static synchronized int getOnlineCount() { 
        return onlineCount; 
    } 
   
    public static synchronized void addOnlineCount() { 
        WebSocket.onlineCount++; 
    } 
   
    public static synchronized void subOnlineCount() { 
        WebSocket.onlineCount--; 
    } 
   
    public static synchronized Map<Long, WebSocket> getClients() { 
        return clients;
    }
    
    /**
     * 发送消息
     * @param webSocketMsg
     */
    public void sendMsg(WebSocketMsg webSocketMsg){
    	if(WebSocketMsg.CHAT_TYPE_SINGLE.equals(webSocketMsg.getChatType())){
    		//单人聊天
    		sendSingleMsg(webSocketMsg);
    		return;
    	}
    	if(WebSocketMsg.CHAT_TYPE_GROUP.equals(webSocketMsg.getChatType())){
    		//群聊天
    		sendGroupMsg(webSocketMsg);
    		return;
    	}
    	if(WebSocketMsg.CHAT_TYPE_EVENT.equals(webSocketMsg.getChatType())){
    		//事件
    		if(WebSocketMsg.MSG_TYPE_REFRESH_FRIEND_LIST.equals(webSocketMsg.getMsgType())){
    			//刷新好友列表
    			refreshFriendList(webSocketMsg);
    		}
    		return;
    	}
    }
	

	/**
     * 发送单人一对一聊天消息
     * @param webSocket
     * @param webSocketMsg
     */
    public void sendSingleMsg(WebSocketMsg webSocketMsg){
    	WebSocket webSocket = clients.get(webSocketMsg.getToId());
    	Friend friend = null;
    	if(webSocket != null){
    		friend = webSocket.getFriend(webSocketMsg.getFromId());
    	}else{
    		List<Friend> myFriendList = friendService.findMyFriendList(webSocketMsg.getToId());
    		for(Friend f: myFriendList){
    			if(f.getFriendAccount().getId().longValue() == webSocketMsg.getFromId().longValue()){
    				friend = f;
    				break;
    			}
    		}
    	}
    	if(friend == null){
    		//表示对方删除了你
			webSocketMsg.setMsgType(WebSocketMsg.MSG_TYPE_NOTICE);
			webSocketMsg.setToId(userid);
			webSocketMsg.setMsg("你还不是对方的好友,请先加好友!");
			session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
			return;
    	}
		if(friend.getStatus() == Friend.FRIEND_STATUS_BLOCK){
    		//表示对方拉黑了你
    		webSocketMsg.setMsgType(WebSocketMsg.MSG_TYPE_NOTICE);
    		webSocketMsg.setToId(userid);
    		webSocketMsg.setMsg("你已被对方拉黑,无法发送消息!");
    		session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
    		return;
    	}

    	webSocketMsg.setFromId(friend.getId());
    	String extAttr = friend.getRemark() + ";" + friend.getFriendAccount().getHeadPic();
    	//将成员的头像和昵称放在附加字段中
    	webSocketMsg.setExtAttr(extAttr);
    	if(webSocketMsg.getMsgType() == WebSocketMsg.MSG_TYPE_ONLINE){
    		webSocketMsg.setMsg("您的好友【" + friend.getRemark() + "】上线啦!");
    	}
    	if(webSocketMsg.getMsgType() == WebSocketMsg.MSG_TYPE_OFFLINE){
    		webSocketMsg.setMsg("您的好友【" + friend.getRemark() + "】下线啦!");
    	}
    	if(webSocket != null){
    		webSocket.session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
    	}
    	
    	if(webSocketMsg.getMsgType() == WebSocketMsg.MSG_TYPE_NOTICE || webSocketMsg.getMsgType() == WebSocketMsg.MSG_TYPE_ONLINE || webSocketMsg.getMsgType() == WebSocketMsg.MSG_TYPE_OFFLINE){
    		//通知信息不需要保存到数据库
    		return;
    	}
    	//消息保存到数据库
    	saveMsgLog(accountService.find(webSocketMsg.getToId()),saveMsgContent(webSocketMsg));
    }
    
    /**
     * 发送群聊天信息
     * @param webSocketMsg
     */
    public void sendGroupMsg(WebSocketMsg webSocketMsg){
    	//首先根据群id获取群成员
    	List<AccountGroupMember> accountGroupMemberList = accountGroupMemberService.findByGroup(webSocketMsg.getToId());
    	if(accountGroupMemberList == null){
    		//表示该群已解散
			webSocketMsg.setMsgType(WebSocketMsg.MSG_TYPE_NOTICE);
			webSocketMsg.setToId(userid);
			webSocketMsg.setMsg("该群已被群主解散!");
			session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
			return;
    	}
    	AccountGroupMember accountGroupMember = getAccountGroupMember(accountGroupMemberList,webSocketMsg.getFromId());
    	if(accountGroupMember == null){
    		webSocketMsg.setMsgType(WebSocketMsg.MSG_TYPE_NOTICE);
			webSocketMsg.setToId(userid);
			webSocketMsg.setMsg("您已被群主移除该群,群成员将不能接受您发送的消息!");
			session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
			return;
    	}
    	//说明这个群成员在线
		String extAttr = accountGroupMember.getNickname() + ";" + accountGroupMember.getMember().getHeadPic();
		//将发送消息成员的头像和昵称放在附加字段中
		webSocketMsg.setExtAttr(extAttr);
		MsgContent msgContent = saveMsgContent(webSocketMsg);
    	//遍历群成员
    	for(AccountGroupMember ag : accountGroupMemberList){
    		//排除给自己发消息
    		if(ag.getMember().getId().longValue() != webSocketMsg.getFromId().longValue()){
    			WebSocket webSocket = clients.get(ag.getMember().getId());
    			if(webSocket != null){
    				webSocket.session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
    			}
    			//消息保存到数据库
    	    	saveMsgLog(ag.getMember(),msgContent);
    		}
    	}
    }
    
    /**
     * 根据账号id获取好友id
     * @param userId
     * @return
     */
    private Friend getFriend(Long userId){
    	for(Friend f : friendList){
    		if(f.getFriendAccount().getId().longValue() == userId.longValue()){
    			return f;
    		}
    	}
    	return null;
    }
    
    /**
     * 加载未读消息
     * @param userId
     */
    private void loadUnReadMsg(Long userId){
    	List<MsgLog> msgLogs = msgLogService.findByAccountIdAndStatus(userId, MsgLog.MSG_STATUS_UNREAD);
    	for(MsgLog msgLog : msgLogs){
    		this.session.getAsyncRemote().sendText(JSONObject.toJSONString(msgLog.getMsgContent()));
    		msgLog.setStatus(MsgLog.MSG_STATUS_READ);
    		//标记为已读
    		msgLogService.save(msgLog);
    	}
    }
    
    /**
     * 从群中获取发送者成员
     * @param accountGroupMemberList
     * @param id
     * @return
     */
    private AccountGroupMember getAccountGroupMember(List<AccountGroupMember> accountGroupMemberList,Long id){
    	for(AccountGroupMember ag : accountGroupMemberList){
    		if(ag.getMember().getId().longValue() == id.longValue())return ag;
    	}
    	return null;		
    }
    
    /**
     * 消息内容保存到数据库
     * @param webSocketMsg
     * @return
     */
    private MsgContent saveMsgContent(WebSocketMsg webSocketMsg){
    	MsgContent msgContent = new MsgContent();
    	msgContent.setAttachSize(webSocketMsg.getAttachSize());
    	msgContent.setAttachUrl(webSocketMsg.getAttachUrl());
    	msgContent.setChatType(webSocketMsg.getChatType());
    	msgContent.setExtAttr(webSocketMsg.getExtAttr());
    	msgContent.setFromId(webSocketMsg.getFromId());
    	msgContent.setMsg(webSocketMsg.getMsg());
    	msgContent.setMsgType(webSocketMsg.getMsgType());
    	msgContent.setToId(webSocketMsg.getToId());
    	return msgContentService.save(msgContent);
    }
    
    /**
     * 保存消息记录
     * @param userId
     * @param msgContent
     */
    private void saveMsgLog(Account account,MsgContent msgContent){
    	MsgLog msgLog = new MsgLog();
    	msgLog.setAccount(account);
    	msgLog.setMsgContent(msgContent);
    	WebSocket webSocket = clients.get(account.getId());
    	msgLog.setStatus(webSocket == null ? MsgLog.MSG_STATUS_UNREAD : MsgLog.MSG_STATUS_READ);
    	msgLogService.save(msgLog);
    }
    
    /**
     * 群发用户上线通知
     * @param userid
     */
    public void onlineNotice(Long userid) {
		//首先遍历该用户的所有好友
		for(Friend friend : friendList){
			//获取在线的好友
			WebSocket webSocket = clients.get(friend.getFriendAccount().getId());
			if(webSocket != null){
				//表示这个好友在线
				WebSocketMsg webSocketMsg = new WebSocketMsg();
				webSocketMsg.setMsgType(WebSocketMsg.MSG_TYPE_ONLINE);
				webSocketMsg.setToId(friend.getFriendAccount().getId());
				webSocketMsg.setFromId(userid);
				sendSingleMsg(webSocketMsg);
			}
		}
	}
    
    /**
     * 群发用户下线通知
     * @param userid
     */
    public void offlineNotice(Long userid) {
    	//首先遍历该用户的所有好友
    	for(Friend friend : friendList){
    		//获取在线的好友
    		WebSocket webSocket = clients.get(friend.getFriendAccount().getId());
    		if(webSocket != null){
    			//表示这个好友在线,发送消息
    			WebSocketMsg webSocketMsg = new WebSocketMsg();
    			webSocketMsg.setMsgType(WebSocketMsg.MSG_TYPE_OFFLINE);
    			webSocketMsg.setToId(friend.getFriendAccount().getId());
    			webSocketMsg.setFromId(userid);
    			sendSingleMsg(webSocketMsg);
    		}
    	}
    }
    
    /**
     * 刷新好友列表
     * @param webSocketMsg
     */
    public void refreshFriendList(WebSocketMsg webSocketMsg) {
		String msg = webSocketMsg.getMsg();
		if(!StringUtils.isEmpty(msg)){
			String[] split = msg.split(",");
			webSocketMsg.setMsg("您的好友列表有更新啦,快来看看吧!");
			for(String id : split){
				Long uid = Long.valueOf(id);
				WebSocket webSocket = clients.get(uid);
				if(webSocket != null){
					webSocket.friendList = friendService.findMyFriendList(uid);
					clients.put(uid, webSocket);
					webSocket.session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
				}
			}
		}
		//更新自己的好友列表
		friendList = friendService.findMyFriendList(userid);
		session.getAsyncRemote().sendText(JSONObject.toJSONString(webSocketMsg));
	}
    
    @Autowired
    public void setFriendService(FriendService friendService){
    	WebSocket.friendService = friendService;
    }
    
    @Autowired
    public void setMsgContentService(MsgContentService msgContentService){
    	WebSocket.msgContentService = msgContentService;
    }
    
    @Autowired
    public void setMsgLogService(MsgLogService msgLogService){
    	WebSocket.msgLogService = msgLogService;
    }
    
    @Autowired
    public void setAccountGroupMemberService(AccountGroupMemberService accountGroupMemberService){
    	WebSocket.accountGroupMemberService = accountGroupMemberService;
    }
    
    @Autowired
    public void setAccountService(AccountService accountService){
    	WebSocket.accountService = accountService;
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

开源、云原生的融合云平台

更多推荐