web版聊天功能简单实现

web版聊天功能简单实现一、问题核心点:如何找到要发送的人?要完成一个功能我觉得首先要分析该功能的逻辑及技术难点,而不是盲目的直接就撸代码,这样非常浪费时间。个人觉得web版聊天功能没什么实际应用场景,以前看过中国移动好

大家好,又见面了,我是你们的朋友全栈君。

一、问题

核心点:如何找到要发送的人?

要完成一个功能我觉得首先要分析该功能的逻辑及技术难点,而不是盲目的直接就撸代码,这样非常浪费时间。个人觉得web版聊天功能没什么实际应用场景,以前看过中国移动好像有过这种东西,所以就简单实现了下

解决:使用缓存存储当前聊天状态

    public class SignalRMessageGroups
    {
        public string ConnectionId { get; set; }
        public long UserId { get; set; }
        public string GroupName { get; set; }
        public static List<SignalRMessageGroups> UserGroups = new List<SignalRMessageGroups>();
    }

  将当前聊天信息存储在内存中,当然你也可以持久化到其它地方,思路是一样的

二、具体实现代码

 使用SignalR进行通讯,具体逻辑不描述(注释都有),因为是在自己的项目实现的,所以只显示部分代码,非常简单的东西,可能js和css写起来麻烦些

Hub代码:

[Authorize]
    public class ChatHub: Hub
    {
        private readonly IOaChatService _chatService;

        public ChatHub(IOaChatService chatService)
        {
            this._chatService = chatService;
        }

        public override async Task OnConnectedAsync()
        {
            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception ex)
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, "ChatHubGroup");
            var curUser = SignalRMessageGroups.UserGroups.FirstOrDefault(m => m.ConnectionId == Context.ConnectionId && m.GroupName == "ChatHubGroup");
            if (curUser != null)
            {
                SignalRMessageGroups.UserGroups.Remove(curUser);
            }
            await base.OnDisconnectedAsync(ex);
        }

        /// <summary>
        /// 信息发送
        /// </summary>
        /// <param name="receiver">接收人</param>
        /// <param name="sender">发送人</param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendMessage(long receiver,long sender, string message)
        {
            //判断接收的人是否在线
            var receiveUser = SignalRMessageGroups.UserGroups.FirstOrDefault(m => m.UserId == receiver && m.GroupName == "ChatHubGroup");
            if (receiveUser != null)
            {
                await Clients.Client(receiveUser.ConnectionId).SendAsync("ReceiveChater", new
                {
                    sender,
                    message,
                    time = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")
                });
                await _chatService.InsertAsync(new Model.OaChat
                {
                    Receiver = receiver,
                    Sender = sender,
                    Message = message
                });
            }
            else
            {
                //发送邮件/短信提醒
            }
        }

        public async Task InitMessage(long userid)
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, "ChatHubGroup");
            var curUser = SignalRMessageGroups.UserGroups.FirstOrDefault(m => m.UserId == userid && m.GroupName == "ChatHubGroup");
            if (curUser != null)
            {
                SignalRMessageGroups.UserGroups.Remove(curUser);
            }
            SignalRMessageGroups.UserGroups.Add(new SignalRMessageGroups
            {
                ConnectionId = Context.ConnectionId,
                GroupName = "ChatHubGroup",
                UserId = userid
            });
            //刷新在线用户列表

            await Clients.All.SendAsync("RefreshOnliner", userid);
        }
    }

  接口(获取在线人员)

[Authorize]
    [Route("api/Chat/[action]")]
    [Produces("application/json")]
    [ApiController]
    public class ChatController : ControllerBase
    {
        private ISystemService _systemService;
        private readonly IOaChatService _chatService;
        private IHubContext<ChatHub> _hubContext;

        public ChatController(IServiceProvider serviceProvider, ISystemService systemService,IOaChatService chatService)
        {
            _hubContext = serviceProvider.GetService<IHubContext<ChatHub>>();
            this._systemService = systemService;
            this._chatService = chatService;
        }

        /// <summary>
        /// 获取全部聊天用户
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<List<ChatUserViewModel>> GetChatUserAsync([FromBody]List<long> chattinguserids)
        {
            //获取用户
            var allUsers = await _systemService.GetAllUserAsync();
            List<ChatUserViewModel> chatUsers = allUsers.Select(m => new ChatUserViewModel
            {
                UserId = m.UserId,
                UserName = m.UserName,
                HeadImg = m.HeadImg,
                CreateTime = m.CreateTime,
                IsChatting = 0,
                IsOnline = 0
            }).ToList();
            var userids = SignalRMessageGroups.UserGroups.Where(m => m.GroupName == "ChatHubGroup").Select(m => m.UserId);

            foreach (var item in chatUsers)
            {
                if (userids.Contains(item.UserId))
                {
                    item.IsOnline = 1;
                }
                if (chattinguserids.HasItems() && chattinguserids.Contains(item.UserId))
                {
                    item.IsChatting = 1;
                }
            }
            return chatUsers.OrderByDescending(m => m.IsChatting).ThenByDescending(m => m.IsOnline).ThenBy(m => m.CreateTime).ToList(); ;
        }


        [HttpGet]
        public async Task<List<ChatUserListDto>> GetChatListAsync([FromQuery]ChatUserListSearchDto model)
        {
            return await _chatService.GetChatListAsync(model);
        }
    }

  页面代码(css、js代码较多)

@{ ViewData["Title"] = "聊天"; Layout = "~/Views/Shared/_LayoutJQ.cshtml"; UserIdentity user = ViewBag.User; } @inject MsSystem.Web.Infrastructure.TokenClient tokenClient @inject Microsoft.Extensions.Configuration.IConfiguration configuration @section css{ <style> body { background:#fff !important; } .ibox-content{ border:none !important; } .chat-main { display: inline-block; background: #eee; border-radius: 3px; } .chat-main-left { width: 200px; float: left; overflow-y: auto; display: none; border: 1px solid #ddd; box-shadow: 0px 0px 10px #ddd; border-right: none; } .chat-main-right { width: 600px; float: left; height: 550px; border: 1px solid #ddd; box-shadow: 0px 0px 10px #ddd; border-radius: 3px; } .chat-users{ margin-left:0px; height:550px; } .chat-user { cursor: pointer; margin: 10px; border-radius: 3px; } .chat-user.active { background: #fff; border-radius: 4px; } .currentUser { background: #eee; font-size: 12px; text-align: left; line-height: 60px; height: 60px; color: #333; font-weight: bold; opacity: .8; } .currentUser img{ width:45px; height:45px; margin-left:10px; } .message-count { background: red; color: #fff; width: 15px; height: 15px; text-align: center; border-radius: 50%; display: none; margin: 2px; line-height: 15px; } .chatim { position: absolute; right: 0px; background: #fff; width: 260px; border-radius: 3px; bottom: 0px; } .chatim.active { box-shadow: 0px 0px 10px #eee; border: 1px solid #eee; } .chatim-title { height: 55px; line-height: 55px; background: #eee; padding-left: 10px; } .chatim-body { height: 450px; overflow-y: auto; } .chatim-userbox { margin: 10px; } .chatim-userbox-category{ cursor:pointer; } .chatim-userbox-online a{ color:#333; } .chatim-userbox-notonline a{ color:#ddd; } .chatim ul { list-style: none; margin: 0px; padding: 0px; } .chatim ul li{ cursor:pointer; margin:10px; } .chat-user:not(.active):hover { background: #fff; } .chat-user a{ margin-left:10px; } .chat-user-name { text-align: left; } .chat-user-name .chat-close { width: 16px; height: 16px; line-height: 16px; border-radius: 50%; background: #ddd; color: #fff; font-size: 12px; margin-left: 30px; position: relative; text-align: center; float: right; display:none; } .chat-user-name:hover .chat-close { display: inline-block; background: #FF0000; } .chatim-user img { width: 35px; height: 35px; } .message-input{ border-right:none; border-left:none; } #sendMessage { position: relative; top: -35px; float: right; border-radius: 0px; width: 70px; } .chatim-title-username{ font-size:16px; font-weight:bold; margin:0px 10px; } .chatim-title img{ width:40px; height:40px; margin-top:-3px; } .chatim-user span{ margin:0px 10px; } .chatim-userbox-category span { width: 12px; display: inline-block; font-size: 14px; } .chatim-footer{ text-align:center; padding-bottom:10px; } .chatim-footer a { font-size: 18px; color: #fff; cursor: pointer; width: 38px; height: 38px; display: inline-block; line-height: 38px; border-radius: 50%; background: #0e9aef; box-shadow: 0px 0px 5px #0e9aef; text-align: center; } .more-msg { font-size: 12px; color: #4ea9e9; } .more-msg:hover{ text-decoration:underline; color: #4ea9e9; } </style> } @section scripts{ <script src="/lib/signalr/dist/browser/signalr.min.js"></script> <script> $(function () { var chattinguserids = []; var currentUserid = "@user.UserId"; const connection = new signalR.HubConnectionBuilder() .withUrl("http://localhost:5000/hub/oa/chatHub", { transport: signalR.HttpTransportType.LongPolling, accessTokenFactory: () => { return "Authorization", getToken(); } }).build(); connection.start().then(function () { connection.invoke('InitMessage', currentUserid); }).catch(function (err) { return console.error(err.toString()); }); //获取聊天信息 connection.on("ReceiveChater", function (data) { //判断是否已打开 var _target = $('.users-list .chat-user[data-userid=' + data.sender + ']'); var userlength = $('.users-list .chat-user').length; if (_target[0] == undefined) { var issactive = userlength == 0 ? true : false; var username = $('.chatim-body a[data-userid=' + data.sender + ']').text(); var img = $('.chatim-body a[data-userid=' + data.sender + ']').parent().prev().attr('src'); addLeftUser(data.sender, username, img, issactive); resetDiscussionTitle(data.sender, username, img); } var chatuser = $('.users-list .chat-user[data-userid=' + data.sender + ']'); var msgcount = chatuser.find('.message-count').text(); chatuser.find('.message-count').show(); if (msgcount == '') { chatuser.find('.message-count').text(1); } else { chatuser.find('.message-count').text(parseInt(msgcount) + 1); } var headimg = chatuser.find('img').attr('src'); var username = chatuser.find('.chat-user-name a').text(); var html = ''; html += '<div class="chat-message chat-left">'; html += '<img class="message-avatar" src="' + headimg +'" alt="">'; html += '<div class="message">'; html += '<div><a class="message-author">' + username +'</a>'; html += '<span class="message-date">' + data.time+'</span></div>'; html += '<span class="message-content">'; html += data.message; html += '</span>'; html += '</div>'; html += '</div>'; addOrShowDiscussion(data.sender, false); var relBox = $('.chat-discussion[data-touser=' + data.sender + ']'); relBox.append(html); srcollBottom(relBox); }); //获取在线人列表 connection.on("RefreshOnliner", data => { axios.post('/OA/Chat/GetChatUserAsync', chattinguserids).then(function (response) { var data = response.data; $('.chatim-userbox ul').empty(); $.each(data, function (index, item) { var html = '<li>'; html += '<div class="chatim-user">'; html += '<img src="' + item.HeadImg+'" />'; html += '<span><a data-userid="' + item.UserId + '">' + item.UserName + '</a></span>'; html += '</div>'; html += '</li>'; if (item.IsOnline == 1) { $('.chatim-userbox-online ul').append(html); } else { $('.chatim-userbox-notonline ul').append(html); } }); }); }); function getToken() { return '@tokenClient.GetToken().Result'; } document.getElementById('sendMessage').addEventListener('click', function (e) { e.preventDefault(); sendMessage(); }); $('textarea[name=message]').on('keydown', function (e) { if (e.keyCode != 13) { return; } else { event.preventDefault(); e.returnValue = false; sendMessage(); } }); $('.users-list').on('click', '.chat-user', function () { $('.users-list .chat-user').removeClass('active'); $(this).addClass('active'); var currentDom = $(this).find('.chat-user-name a'); var userid = currentDom.attr('data-userid'); var imgsrc = $(this).find('img').attr('src'); resetDiscussionTitle(userid, currentDom.text(), imgsrc); addOrShowDiscussion(userid, true); resetUserMessageCount(userid); addOnlines(userid); $('.chat-message-form').show(); }); function sendMessage() { var userid = $('#ToUserId').val(); var message = $('textarea[name=message]').val(); if (message == '' || message.trim() == '') { return; } connection.invoke('SendMessage', userid, currentUserid, message); $('textarea[name=message]').val(''); var time = $.getCurrentTime(); var html = ''; html += '<div class="chat-message chat-right">'; html += '<img class="message-avatar" src="@user.HeadImg" alt="">'; html += '<div class="message">'; html += '<div><a class="message-author">@user.UserName</a>'; html += '<span class="message-date">' + time + '</span></div>'; html += '<span class="message-content">'; html += message; html += '</span>'; html += '</div>'; html += '</div>'; var chatdis = $('.chat-discussion[data-touser=' + userid + ']'); chatdis.append(html); resetUserMessageCount(userid); srcollBottom(chatdis); } function srcollBottom(container) { var scrollToContainer = container.find('.chat-message :last'); container.animate({ scrollTop: scrollToContainer.offset().top - container.offset().top + container.scrollTop() }, 800); } function resetUserMessageCount(userid) { var chatuser = $('.users-list .chat-user[data-userid=' + userid + ']'); chatuser.find('.message-count').text('').hide(); } function addOnlines(userid) { var flag = false; for (var i = 0; i < chattinguserids.length; i++) { if (chattinguserids[i] == userid) { flag = true; } } if (!flag) { chattinguserids.push(userid); } } function showChatBox(userid) { var length = $('.users-list .chat-user').length; if (length <= 1) { $('.chat-main-left').show(); } addOrShowDiscussion(userid, true); } function addOrShowDiscussion(userid, toTop) { var disc = $('.chat-discussion[data-touser=' + userid + ']'); if (disc.length == 1) { $('.chat-discussion').hide(); disc.show(); } else { var html = ''; if ($('.chat-discussion').length == 1 || toTop == true) { $('.chat-discussion').hide(); html = '<div class="chat-discussion" data-pageindex="0" data-touser="' + userid + '"><a class="more-msg"><i class="fa fa-clock-o"></i>查看更多消息</a></div>'; } else { html = '<div class="chat-discussion" style="display:none" data-pageindex="0" data-touser="' + userid + '"><a class="more-msg"><i class="fa fa-clock-o"></i>查看更多消息</a></div>'; } $('.chat-discussion:last').after(html) } $('.chat-message-form').show(); } function resetDiscussionTitle(userid, username,imgsrc) { $('#ToUserId').val(userid); $('.currentUser span').text(username); $('.currentUser img').attr('src', imgsrc); } function addLeftUser(userid, username, img,isactive) { var html = ''; if (isactive) { $('.chat-user').removeClass('active'); html += '<div class="chat-user active" data-userid="' + userid + '">'; } else { html += '<div class="chat-user" data-userid="' + userid + '">'; } html += '<span class="pull-right message-count"></span>'; html += '<img class="chat-avatar" src="' + img + '" alt="">'; html += '<div class="chat-user-name"><a data-userid="' + userid + '">' + username + '</a><span class="chat-close"><i class="fa fa-close"></i></span>'; html += '</div>'; html += '</div>'; $('.users-list').append(html); $('.chat-main-left').show(); } $('.chatim-userbox-category').on('click', function () { var type = $(this).attr('data-type'); if (type == 1) { $(this).attr('data-type', 0); $(this).find('span i').attr('class', 'fa fa-angle-right'); $(this).next().hide(); } else { $(this).attr('data-type', 1); $(this).find('span i').attr('class', 'fa fa-angle-down'); $(this).next().show(); } }); $('.chatim-userbox-online ul').on('dblclick', 'li', function () { var userid = $(this).find('a').attr('data-userid'); var username = $(this).find('a').text(); var _target = $('.users-list .chat-user[data-userid=' + userid + ']'); if (_target[0] == undefined) { var img = $(this).find('img').attr('src'); addLeftUser(userid, username, img, true); showChatBox(userid); } else { $('.users-list .chat-user').removeClass('active'); _target.addClass('active'); addOrShowDiscussion(userid, true); } var imgsrc = $(this).find('img').attr('src'); resetDiscussionTitle(userid, username, imgsrc); }); $('.users-list').on('click', '.chat-close', function (e) { e.stopPropagation(); var currentUser = $(this).parent().parent(); //当前会话只有一个情况 var uleg = $('.users-list .chat-user').length; if (uleg == 1) { currentUser.remove(); $('.chat-discussion').hide(); $('.chat-discussion:first').show(); resetDiscussionTitle('', '','/uploadfile/342bd59b-edf4-48cf-aa27-d13e5a0b70df.jpeg'); $('.chat-main-left,.chat-message-form').hide(); } else { var prevUser = currentUser.prev(); if (prevUser.length == 1) { var userid = prevUser.find('a').attr('data-userid'); var username = prevUser.find('a').text(); if (currentUser.hasClass('active')) { prevUser.addClass('active'); } addOrShowDiscussion(userid); var imgsrc = prevUser.find('img').attr('src'); resetDiscussionTitle(userid, username, imgsrc) } else { var nextUser = currentUser.next(); if (nextUser.length == 1) { var nextUserId = nextUser.find('a').attr('data-userid'); var nextUsername = nextUser.find('a').text(); if (currentUser.hasClass('active')) { nextUser.addClass('active'); } addOrShowDiscussion(nextUserId); var imgsrc = nextUser.find('img').attr('src'); resetDiscussionTitle(nextUserId, nextUsername, imgsrc) } } currentUser.remove(); } }); $('.chatim-footer').on('click', 'a', function () { var type = $(this).attr('data-type'); if (type == 1) { $(this).attr('data-type', 0); $('.chatim-title,.chatim-body').fadeOut(); $(this).find('i').attr('class', 'fa fa-comments'); $('.chatim').removeClass('active'); } else { $(this).attr('data-type', 1); $('.chatim-title,.chatim-body').fadeIn(); $(this).find('i').attr('class', 'fa fa-close'); $('.chatim').addClass('active'); } }); $('.chat-main-right').on('click', 'a.more-msg', function () { var _this = $(this); var pageindex = _this.parent().attr('data-pageindex'); var receiver = _this.parent().attr('data-touser'); var url = '/OA/Chat/GetChatListAsync?PageIndex=' + pageindex + '&Receiver=' + receiver + ''; axios.get(url).then(function (response) { var data = response.data; if (data.length > 0) { var ToUserId = $('#ToUserId').val(); var currentDis = $('.chat-discussion[data-touser=' + ToUserId + ']'); currentDis.find('a.more-msg').hide(); var html = '<a class="more-msg"><i class="fa fa-clock-o"></i>查看更多消息</a>'; $.each(data, function (index, item) { var headimg = ''; var username = ''; if (item.Sender == '@user.UserId') { headimg = '@user.HeadImg'; username = '@user.UserName'; html += '<div class="chat-message chat-right">'; } else { headimg = $('.currentUser img').attr('src'); username = $('.currentUser span').text(); html += '<div class="chat-message chat-left">'; } var time = $.unixToDate(item.CreateTime, true); html += '<img class="message-avatar" src="' + headimg + '" alt="">'; html += '<div class="message">'; html += '<div><a class="message-author">' + username + '</a>'; html += '<span class="message-date">' + time + '</span></div>'; html += '<span class="message-content">'; html += item.Message; html += '</span>'; html += '</div>'; html += '</div>'; }); var addpageindex = pageindex == 0 ? 1 : pageindex; if (addpageindex == 1) { currentDis.empty(); } currentDis.prepend(html); currentDis.attr('data-pageindex', parseInt(addpageindex) + 1); } else { _this.hide(); } }); }); }); </script> } <div class="wrapper-content"> <div class="row"> <div class="ibox"> <div class="ibox-content"> <div class="row" style="margin:0px auto;text-align: center;"> <div class="chat-main"> <div class="chat-main-left"> <div class="chat-users"> <div class="users-list"></div> </div> </div> <div class="chat-main-right"> <div class="currentUser"> <img src="/uploadfile/342bd59b-edf4-48cf-aa27-d13e5a0b70df.jpeg" /> <span></span> <input type="hidden" id="ToUserId" value="" /> </div> <div class="chat-discussion" data-touser=""> <h3> WEB 在线聊天系统 </h3> </div> <div class="chat-message-form" style="display:none"> <div class="form-group"> <textarea class="form-control message-input" name="message"></textarea> </div> <button class="btn btn-primary" id="sendMessage">发送</button> </div> </div> <div class="clear"></div> </div> </div> <div class="chatim active"> <div class="chatim-title"> <img src="@user.HeadImg" /> <span class="chatim-title-username"> @user.UserName </span> <span class="green">在线</span> </div> <div class="chatim-body"> <div class="chatim-userbox chatim-userbox-online"> <div class="chatim-userbox-category" data-type="1"> <span><i class="fa fa-angle-down"></i></span>在线用户 </div> <ul></ul> </div> <div class="chatim-userbox chatim-userbox-notonline"> <div class="chatim-userbox-category" data-type="0"> <span><i class="fa fa-angle-right"></i></span>未在线用户 </div> <ul style="display:none"></ul> </div> </div> <div class="chatim-footer"> <a data-type="1"><i class="fa fa-close"></i></a> </div> </div> </div> </div> </div> </div> 

 

数据库设计,就一张表

  

SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for oa_chat -- ---------------------------- DROP TABLE IF EXISTS `oa_chat`; CREATE TABLE `oa_chat` ( `Id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `Sender` bigint(20) NOT NULL COMMENT '发送方', `Message` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '消息', `Receiver` bigint(20) NOT NULL COMMENT '接收方', `CreateTime` bigint(20) NOT NULL COMMENT '创建时间', PRIMARY KEY (`Id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 156 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1; 

  

 

效果如下:

<span role="heading" aria-level="2">web版聊天功能简单实现

双击在线用户发送并发送信息

<span role="heading" aria-level="2">web版聊天功能简单实现

用户接收到消息

<span role="heading" aria-level="2">web版聊天功能简单实现

 <span role="heading" aria-level="2">web版聊天功能简单实现

部分代码地址:

https://github.com/wangmaosheng/MsSystem-BPM-ServiceAndWebApps/blob/master/src/Web/MVC/MsSystem.Web/Areas/OA/Views/Chat/Index.cshtml

也可以clone 下项目运行查看效果,docker功能已完成,可直接运行

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/155238.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号