基于.Net Core3.1 与signalR实现一个即时通讯工具(四)——功能实现
- 打开上一步创建的控制器:IMController,代码如下
using Microsoft.AspNetCore.SignalR;
using MySql.Data.MySqlClient.Memcached;
using SpoonRapidCore.DataBaseHelper;
using SRFEntity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SRF.IM.Hubs
{
public class IMHub : Hub
{
#region 数据库操作类
private ISqlSugarHelper<Base_User> userHelper = new SqlSugarHelper<Base_User>();
private ISqlSugarHelper<Base_HistoryMeeting> meetingHelper = new SqlSugarHelper<Base_HistoryMeeting>();
private SqlSugarHelper<Base_Msg> msgHelper = new SqlSugarHelper<Base_Msg>();
#region
#region 发送至客户端
/// <summary>
/// 像所有在线通讯广播
/// </summary>
/// <param name="Message"></param>
/// <param name="Name"></param>
/// <returns></returns>
public async Task SendNotice(string Message, string Name = "系统提示")
{
await Clients.All.SendAsync("RNotice", Name, Message);
}
/// <summary>
/// 通过连接ID向指定通讯发送消息
/// </summary>
/// <param name="ConnectionID"></param>
/// <param name="SendUser"></param>
/// <param name="Message"></param>
/// <returns></returns>
public async Task SendMessage(string ConnectionId,string SendUser, string Message)
{
await Clients.User(ConnectionId).SendAsync("RMessage",SendUser, Message);
}
/// <summary>
/// 通过多个连接ID向指定通讯发送消息
/// </summary>
/// <param name="ConnectionIDs"></param>
/// <param name="SendUser"></param>
/// <param name="Message"></param>
/// <returns></returns>
public async Task SendMessages(List<string> ConnectionIds, string SendUser, string Message)
{
await Clients.Users(ConnectionIds).SendAsync("RMessages",SendUser, Message);
}
public async Task SendLog(string ConnectionId, string SendUser, string Message)
{
await Clients.User(ConnectionId).SendAsync("SendLog", SendUser, Message);
}
#endregion
#region 服务端接受
/// <summary>
/// 客户端上线
/// </summary>
/// <param name="UserId"></param>
/// <param name="LoginStatus"></param>
/// <param name="UserName"></param>
/// <returns></returns>
public async Task ConnectedOn(string UserId, string LoginStatus,string UserName)
{
string connectionId = Context.ConnectionId;
try
{
var isExist = userHelper.GetEntity(UserId);
Base_User base_User = new Base_User
{
ConnectId = connectionId,
IsOnline = 1,
LoginDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
UserId = UserId,
};
if (isExist == null)
{
userHelper.SaveForm(base_User);
}
else
{
isExist.ConnectId = connectionId;
isExist.IsOnline = 1;
isExist.LoginDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
isExist.UserId = UserId;
userHelper.UpdateEntity(isExist);
}
await SendLog(connectionId, UserId, "您已连接到IM,您的连接ID为:" + connectionId + ",您的登录状态为:" + LoginStatus);
}
catch (Exception ex)
{
await SendLog(connectionId, UserId, "连接失败!失败原因为:"+ex.Message);
}
}
/// <summary>
/// 会话上线
/// </summary>
/// <param name="UserId"></param>
/// <param name="ToUserId"></param>
/// <returns></returns>
public async Task MeetingConnectOn(string UserId, string ToUserId)
{
try
{
string connectionId = Context.ConnectionId;
/*判断该会话是存在*/
StringBuilder H_SqlWhere=new StringBuilder();
H_SqlWhere.AppendFormat(" AND MeetingOwn='{0}' AND UserId='{1}'",UserId,ToUserId);
var HMeeting = meetingHelper.GetEntityByWhereSql(H_SqlWhere.ToString());
if (HMeeting == null)
{
HMeeting = new Base_HistoryMeeting
{
HistoryDate = DateTime.Now,
HistoryId = Guid.NewGuid().ToString(),
MeetingOwn = UserId,
UserId = ToUserId,
OwnCID = Context.ConnectionId,
};
meetingHelper.SaveForm(HMeeting);
}
else
{
HMeeting.OwnCID = Context.ConnectionId;
HMeeting.HistoryDate = DateTime.Now;
meetingHelper.UpdateEntity(HMeeting);
}
/*所有会话设置已读*/
StringBuilder MsgRead_StrSql = new StringBuilder();
MsgRead_StrSql.AppendFormat("UPDATE dbo.Base_Msg SET MsgStatus=0 WHERE ToUser='{0}' AND FromUser='{1}' AND MsgStatus=1", UserId, ToUserId);
msgHelper.ExecuteCommand(MsgRead_StrSql.ToString());
await SendLog(Context.ConnectionId, UserId, "会话建立成功!");
}
catch (Exception ex)
{
await SendLog(Context.ConnectionId, UserId, "会话建立失败!失败原因为:" + ex.Message);
}
}
/// <summary>
/// 会话关闭
/// </summary>
/// <param name="UserId"></param>
/// <param name="ToUserId"></param>
/// <returns></returns>
public async Task MeetingConnectOff(string UserId, string ToUserId)
{
try
{
StringBuilder GetConnectionIdStrSql = new StringBuilder();
GetConnectionIdStrSql.AppendFormat(" AND MeetingOwn='{0}' AND UserId='{1}'", ToUserId, UserId);
Base_HistoryMeeting ToUserMeeting = meetingHelper.GetEntityByWhereSql(GetConnectionIdStrSql.ToString());
StringBuilder H_SqlWhere = new StringBuilder();
H_SqlWhere.AppendFormat(" AND MeetingOwn='{0}' AND UserId='{1}'", UserId, ToUserId);
var HMeeting = meetingHelper.GetEntityByWhereSql(H_SqlWhere.ToString());
HMeeting.OwnCID = "";
meetingHelper.UpdateEntity(HMeeting);
//meetingHelper.DeleteByKey(HMeeting);
await SendLog(Context.ConnectionId, UserId, "会话关闭成功");
}
catch (Exception ex)
{
await SendLog(Context.ConnectionId, UserId, "会话关闭失败!失败原因为:" + ex.Message);
}
}
#endregion
#region 服务端对连接管理
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnDisconnectedAsync(exception);
}
#endregion
}
}
#endregion
- 打开Index.cshtml
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" href="~/lib/layui-v2.5.6/layui/css/layui.css" />
<script src="~/lib/layer-v3.1.1/layer/layer.js"></script>
<script src="~/lib/layui-v2.5.6/layui/layui.js"></script>
<script src="~/js/requst.js"></script>
<script src="~/js/SRFIM.js"></script>
<link rel="stylesheet" href="~/css/SRFIM.css" />
<script src="~/lib/jquery-cookies/jquery.cookie-v1.4.1.js"></script>
</head>
<body>
<div id="LoginForm" style="display:none">
<form class="layui-form" style="padding:10px;">
<div class="layui-form-item">
<label class="layui-form-label" style="display:block">用户名</label>
<div class="layui-input-block" style="width:100%;">
<input id="LoginName" type="text" name="title" required lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码框</label>
<div class="layui-input-block">
<input id="Password" type="password" name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
</div>
</div>
</form>
<button class="layui-btn layui-btn-normal layui-btn-sm" style="width:90%;margin-left:5%" onclick="GoLogin()">即刻登录</button>
</div>
<button class="layui-btn" onclick="isLogin()" style="margin:40px;">Ring</button>
</body>
</html>
<style>
.layui-input-block {
margin-left: 0px;
}
.layui-form-label {
width: auto;
}
</style>
- 打开ChatIndex.cshtml
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>chatIndex</title>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-cookies/jquery.cookie-v1.4.1.js"></script>
<script src="~/lib/microsoft/signalr/dist/browser/signalr.min.js"></script>
<script src="~/js/IM.js"></script>
<link rel="stylesheet" href="~/lib/layui-v2.5.6/layui/css/layui.css" />
<script src="~/lib/layer-v3.1.1/layer/layer.js"></script>
<script src="~/lib/layui-v2.5.6/layui/layui.js"></script>
<script src="~/js/SRFIM.js"></script>
<link rel="stylesheet" href="~/lib/optiscroll/dist/optiscroll.css" />
<script src="~/lib/optiscroll/dist/jquery.optiscroll.min.js"></script>
<link rel="stylesheet" href="~/css/SRFIM.css" />
<script src="~/js/requst.js"></script>
</head>
<body>
<div class="top">
<div class="top_headpicBox">
<img src="~/images/u=1529359824,2671467657&fm=26&gp=0.jpg" class="top_headpic" />
</div>
<div class="demepart_info">
<c>UI绘制部门员工:日近长安远</c>
</div>
</div>
<div>
<div class="layui-tab layui-tab-brief" lay-filter="mainBox">
<ul class="layui-tab-title">
<li class="layui-this" style="width:75px"><i class="layui-icon layui-icon-reply-fill" style="font-size: 20px;color:#555555"></i></li>
<li style="width:75px"><i class="layui-icon layui-icon-group " style="font-size: 20px;color:#555555"></i> </li>
<li style="width:75px"><i class="layui-icon layui-icon-template-1" style="font-size: 20px;color:#555555"></i> </li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<div class="historyBox optiscroll" id="historyBox" style="height: 320px;overflow:hidden;">
<ul>
</ul>
<div style="width:315px;margin-top: 40px;">
<hr class="layui-bg-gray" style="width: 80%;margin-left: 10%;">
<div class="history_bottom">没有更多消息了</div>
</div>
</div>
</div>
<div class="layui-tab-item ">
<div class="groupBox optiscroll" id="groupBox" style="height: 320px;overflow:hidden;width: 315px;">
<ul>
<li id="8277e0910d75015645465616e091ad" class=" ">
<div style="width: 315px;">
<div class="groupBox_headpicBox">
<img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3114504376,226392045&fm=26&gp=0.jpg"
class="groupBox_headpic">
</div>
<div class="groupBox_msgBox">
<div class="groupBox_msgName">测试小组001</div>
<div class="groupBox_msgDetail">张三:?[图片]上次说的方案你看了吗</div>
</div>
<div style="width:20px;float: left;">
<span class="layui-badge layui-bg-gray groupBox_msgCount">20</span>
<i rel="8277e0910d750195b448797616e091ad" class="layui-icon layui-icon-close-fill groupBox_closeBtn"></i>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="layui-tab-item">内容3</div>
</div>
</div>
</div>
</body>
</html>
<script>
$(function () {
connectOn();
InitPage();
$('#historyBox').optiscroll();
$('#groupBox').optiscroll();
})
//上线
function connectOn() {
var IMUID = $.cookie('IMUID')
var IMUName = $.cookie('IMUName')
$(".demepart_info").html(IMUName);
connection.start().then(function () {
connection.invoke("ConnectedOn", IMUID, "隐身", IMUName).catch(function (err) {
});
}).catch(function () {
alert("连接失败!请尝试刷新后重试!");
});
}
//接收到新的消息
connection.on("RNMessage", function (SendUserId, Message, base_User, unReadNums) {
var ele = $("#" + SendUserId);
if (ele.length == 0) {
var fillHtml = '';
fillHtml += '<li id="' + base_User.userId + '">';
fillHtml += ' <div style="width: 315px;">';
fillHtml += ' <div class="historyBox_headpicBox">';
fillHtml += ' <img src="' + base_User.userHeadpic + '" class="historyBox_headpic">';
fillHtml += ' </div>';
fillHtml += ' <div class="historyBox_msgBox">';
fillHtml += ' <div class="historyBox_msgName">' + base_User.userName + '</div>';
fillHtml += ' <div class="historyBox_msgDetail">' + Message + '</div>';
fillHtml += ' </div>';
fillHtml += ' <div style="width: 20px;float: left;">';
fillHtml += ' <span class="layui-badge historyBox_msgCount" style="display:none;line-height:62px">0</span>';
fillHtml += ' <i rel="' + base_User.userId + '" class="layui-icon layui-icon-close-fill historyBox_closeBtn"></i>';
fillHtml += ' </div>';
fillHtml += ' </div>';
fillHtml += '</li>';
var isHasChild = $("#historyBox ul li");
if (isHasChild.length == 0) {
$("#historyBox ul ").html(fillHtml);
}
else {
$("#historyBox ul li").before(fillHtml);
}
}
else {
var firstEleId = $("#historyBox ul li:first").attr('id');
if (firstEleId != SendUserId) {
$("#" + SendUserId).remove();
$("#historyBox ul li").before(ele);
}
}
BindHistoryEvent();
var childrenCountNode = $("#" + SendUserId).children().children().children(".historyBox_msgCount");
var childrenMsgNode = $("#" + SendUserId).children().children().children(".historyBox_msgDetail");
var childrenCloseBtnNode = $("#" + SendUserId).children().children().children(".historyBox_closeBtn");
var childrenImgNode = $("#" + SendUserId).children().children(".historyBox_headpicBox").children(".historyBox_headpic");
childrenImgNode.addClass("layui-anim layui-anim-fadein layui-anim-loop");
childrenMsgNode.html(Message);
var oldCount = childrenCountNode.html();
var msgCount = parseInt(oldCount) + 1;
childrenCountNode.html(unReadNums).show();
childrenCloseBtnNode.css("line-height", "15px");
});
//发送消息时 更新自己的主页面会话记录最后一条消息
connection.on("RSIndexMessage", function (SendUserId, Message) {
$("#" + SendUserId).children().children().children('.historyBox_msgDetail').html(Message);
});
connection.on("LoginOut", function () {
parent.layer.closeAll();
clearAllCookie();
setTimeout(function () {
parent.layer.msg('您的账号已在别处登录,本地强制下线!', { icon: 3, time: 5000 });
},2000)
});
function InitPage() {
GetMeeting();
}
</script>
- 打开ChatBox.cshtml
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ChatBox</title>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" href="~/lib/layui-v2.5.6/layui/css/layui.css" />
<script src="~/lib/layer-v3.1.1/layer/layer.js"></script>
<script src="~/lib/layui-v2.5.6/layui/layui.js"></script>
<script src="~/js/SRFIM.js"></script>
<link rel="stylesheet" href="~/lib/optiscroll/dist/optiscroll.css" />
<script src="~/lib/optiscroll/dist/jquery.optiscroll.min.js"></script>
<link rel="stylesheet" href="~/css/SRFIM.css" />
<script src="~/lib/jquery-cookies/jquery.cookie-v1.4.1.js"></script>
<script src="~/js/requst.js"></script>
<script src="~/lib/microsoft/signalr/dist/browser/signalr.min.js"></script>
<script src="~/js/IM.js"></script>
<script src="~/js/web.js"></script>
</head>
<body>
<div style="height: 300px;">
<div style="height: 300px;" id="historyMsgBox" class="optiscroll">
<ul style="padding-top: 10px;padding-bottom: 20px;" id="msgContentBox">
</ul>
</div>
</div>
<div style="" id="toolBar">
<blockquote class="layui-elem-quote" style="margin-bottom: 0px;border-left-color: #1e9fff;padding: 10px;">
功能区
<button type="button" class="layui-btn layui-btn-sm layui-btn-primary">
<i class="layui-icon layui-icon-time" style=""></i>
</button>
</blockquote>
</div>
<div style="padding: 2px;">
<textarea id="sendBox" style="display: none;height: 100px;"></textarea>
</div>
</body>
</html>
<script>
var connection_main = null;
var editEle = null;
var editEleIndex = null;
var myOptiscrollInstance = null;
var ToUserId = GetQuery('ToUserId');
var IMUID = $.cookie('IMUID');
layui.use('layedit', function () {
editEle = layui.layedit;
editEle.set({
uploadImage: {
url: '/IM/SendMsgBoxImgUpload?UserId=' + IMUID + "&ToUserId=" + ToUserId, //接口url,
type: ''
}
});
editEleIndex = editEle.build('sendBox', {
});
});
var IMUID = $.cookie('IMUID')
var IMUName = $.cookie('IMUName')
connection.start().then(function () {
connection.invoke("MeetingConnectOn", IMUID, ToUserId).catch(function (err) {
});
}).catch(function () {
alert("连接失败!请尝试刷新后重试!");
});
connection.on("RMessage", function (Message) {
receiveMsg(Message);
});
var mockReceiveId = null;
$(function () {
$(document).keyup(function (event) {
if (event.keyCode == 13) {
alert(1);
}
});
GetMsg();
var element = document.querySelector('#historyMsgBox')
myOptiscrollInstance = new Optiscroll(element);
myOptiscrollInstance.scrollTo(false, 5000);
})
//获得消息
function GetMsg() {
$.getData({
url: '/IM/GetMyHistoryMsg?UserId=' + IMUID + '&ToUserId=' + ToUserId,
success: function (data) {
var fillHtml = '';
var msgList = data.msgHistoryList;
for (var i = data.count - 1; i >= 0; i--) {
if (i % 7 == 0)
fillHtml += '<li class="bubble_center">' + msgList[i].MsgSendDate + '</li>'
var isOwn = msgList[i].own;
if (isOwn == 0) {
fillHtml += '<li class="bubble_right" id="' + msgList[i].MsgId + '"><span>' + msgList[i].MsgContent + '</span></li>';
}
else
fillHtml += '<li class="bubble_left" id="' + msgList[i].MsgId + '"><span>' + msgList[i].MsgContent + '</span></li>';
}
$("#msgContentBox").html(fillHtml);
BindHistoryMsgEvent();
},
fail: function () {
}
});
}
var sendCount = 0;
//发送消息
function sendMsg(connection, toUserId) {
sendCount++;
var sendContent = editEle.getContent(editEleIndex);
if (sendContent == "" || sendContent == "<br>")
return false;
var postData = {
Message: sendContent,
UserId: IMUID,
ToUserId: toUserId
};
$.postData({
//url: '/IM/SendMessage?UserId=' + IMUID + '&Message=' + sendContent + '&ToUserId=' + toUserId,
url: '/IM/SendMessage',
async: false,
data: postData,
success: function (data) {
if (data.isSuccess) {
var fillHtml = '';
if (sendCount % 7 == 0) {
fillHtml += '<li class="bubble_center">' + data.msgSendDate + '</li>'
}
fillHtml += '<li id="' + data.msgId + '" class="bubble_right layui-anim layui-anim-upbit">';
fillHtml += '<i class="' + data.msgId + '" style="font-size:10px;color:#e4e4e4">正在发送中... </i>';
fillHtml += '<span>' + sendContent + '</span>';
fillHtml += '</li>';
$("#msgContentBox").append(fillHtml);
setTimeout(function () {
fillHtml += '';
$("#" + data.msgId).children('.' + data.msgId).html('<i style="font-size: 10px; color:#e4e4e4">已送达 </i> ');
$("#" + data.msgId).children('.' + data.msgId).fadeOut(300);
}, 600)
myOptiscrollInstance.scrollTo(false, 5000);
editEle.clearContent(editEleIndex);
}
else {
var fillHtml = '';
if (sendCount % 7 == 0) {
fillHtml += '<li class="bubble_center">' + msgList[i].MsgSendDate + '</li>'
}
fillHtml += '<li id="' + data.msgId + '" class="bubble_right layui-anim layui-anim-upbit">';
fillHtml += '<i class="' + data.msgId + '" style="font-size:10px;color:#e4e4e4">正在发送中... </i>';
fillHtml += '<span style="background-color:red">' + sendContent + '</span>';
fillHtml += '</li>';
$("#msgContentBox").append(fillHtml);
myOptiscrollInstance.scrollTo(false, 5000);
editEle.clearContent(editEleIndex);
setTimeout(function () {
$("#" + data.msgId).children('.' + data.msgId).html('<b style="color:red">!</b> ');
}, 600)
}
BindHistoryMsgEvent();
},
fail: function () {
}
})
}
//接收消息
function receiveMsg(receiveContent) {
if (receiveContent == "" || receiveContent == "<br>")
return false;
var fillHtml = '';
fillHtml += '<li class="bubble_left layui-anim layui-anim-upbit"><span>' + receiveContent + '</span></li>';
$("#msgContentBox").append(fillHtml);
myOptiscrollInstance.scrollTo(false, 5000);
}
//关闭窗口
function closeChatBox(ToUserId) {
var UserId = IMUID;
connection.invoke("MeetingConnectOff", UserId, ToUserId).catch(function (err) {
});
}
</script>
<style>
</style>
- 引用的js/css文件如下
1.IM.js
2.requst.js 封装的ajax请求类
3.SRFIM.js
4.web.js web常用方法
5.SRFIM.css 页面样式
资源下载地址:
引用文件资源类