@@ -1,449 +0,0 @@
using System.Net.WebSockets ;
using JNPF.Common.Configuration ;
using JNPF.Common.Const ;
using JNPF.Common.Enums ;
using JNPF.Common.Extension ;
using JNPF.Common.Manager ;
using JNPF.Common.Models.User ;
using JNPF.Common.Options ;
using JNPF.Common.Security ;
using JNPF.DataEncryption ;
using JNPF.Extras.WebSockets.Models ;
using JNPF.Message.Entitys ;
using JNPF.Message.Entitys.Dto.IM ;
using JNPF.Message.Entitys.Entity ;
using JNPF.Message.Entitys.Enums ;
using JNPF.Message.Entitys.Model.IM ;
using JNPF.RemoteRequest.Extensions ;
using JNPF.Systems.Entitys.Permission ;
using JNPF.WebSockets ;
using Mapster ;
using SqlSugar ;
namespace JNPF.Message.Handlers ;
/// <summary>
/// IM 处理程序.
/// </summary>
public class IMHandler : WebSocketHandler
{
/// <summary>
/// SqlSugarClient客户端.
/// </summary>
private static SqlSugarScope ? _sqlSugarClient ;
/// <summary>
/// 缓存管理.
/// </summary>
private readonly ICacheManager _cacheManager ;
private readonly MessageOptions _messageOptions = App . GetConfig < MessageOptions > ( "Message" , true ) ;
/// <summary>
/// 初始化一个<see cref="IMHandler"/>类型的新实例.
/// </summary>
public IMHandler (
WebSocketConnectionManager webSocketConnectionManager ,
ISqlSugarClient sqlSugarClient ,
ICacheManager cacheManager )
: base ( webSocketConnectionManager )
{
_sqlSugarClient = ( SqlSugarScope ) sqlSugarClient ;
_cacheManager = cacheManager ;
}
/// <summary>
/// 消息接收.
/// </summary>
/// <param name="client"></param>
/// <param name="result"></param>
/// <param name="receivedMessage"></param>
/// <returns></returns>
public override async Task ReceiveAsync ( WebSocketClient client , WebSocketReceiveResult result , string receivedMessage )
{
try
{
MessageInput ? message = receivedMessage . ToObject < MessageInput > ( ) ;
var claims = JWTEncryption . ReadJwtToken ( message . token . Replace ( "Bearer " , string . Empty ) . Replace ( "bearer " , string . Empty ) ) ? . Claims ;
client . UserId = claims . FirstOrDefault ( e = > e . Type = = ClaimConst . CLAINMUSERID ) ? . Value ;
client . ConnectionConfig = claims . FirstOrDefault ( e = > e . Type = = ClaimConst . CONNECTIONCONFIG ) ? . Value . ToObject < ConnectionConfigOptions > ( ) ;
client . Account = claims . FirstOrDefault ( e = > e . Type = = ClaimConst . CLAINMACCOUNT ) ? . Value ;
client . UserName = claims . FirstOrDefault ( e = > e . Type = = ClaimConst . CLAINMREALNAME ) ? . Value ;
client . SingleLogin = ( LoginMethod ) Enum . Parse ( typeof ( LoginMethod ) , claims . FirstOrDefault ( e = > e . Type = = ClaimConst . SINGLELOGIN ) ? . Value ) ;
client . LoginTime = string . Format ( "{0:yyyy-MM-dd HH:mm}" , string . Format ( "{0}000" , claims . FirstOrDefault ( e = > e . Type = = "iat" ) ? . Value ) . TimeStampToDateTime ( ) ) ;
client . LoginIpAddress = client . LoginIpAddress ;
client . Token = message . token ;
client . IsMobileDevice = message . mobileDevice ;
if ( client . WebSocket . State ! = WebSocketState . Open ) return ;
await OnConnected ( client . ConnectionId , client ) ;
WebSocketConnectionManager . AddToTenant ( client . ConnectionId , client . ConnectionConfig ? . ConfigId ) ;
WebSocketConnectionManager . AddToUser ( client . ConnectionId , string . Format ( "{0}-{1}" , client . ConnectionConfig ? . ConfigId , client . UserId ) ) ;
message . sendClientId = client . ConnectionId ;
await MessageRoute ( message ) ;
}
catch ( Exception e )
{
}
}
/// <summary>
/// 消息通道.
/// </summary>
/// <param name="message"></param>
private async Task MessageRoute ( MessageInput message )
{
WebSocketClient client = WebSocketConnectionManager . GetSocketById ( message . sendClientId ) ;
if ( string . IsNullOrEmpty ( client . UserId ) )
{
await SendMessageAsync ( client . ConnectionId , new { method = "logout" } . ToJsonString ( ) ) ;
return ;
}
// 判断ORM内是否存在该连接
if ( ! _sqlSugarClient . IsAnyConnection ( client . ConnectionConfig . ConfigId ) )
{
_sqlSugarClient . AddConnection ( JNPFTenantExtensions . GetConfig ( client . ConnectionConfig ) ) ;
_sqlSugarClient . Ado . CommandTimeOut = 10 ;
}
// 当前数据连接ConfigId是否等于目标库ConfigId
if ( _sqlSugarClient . CurrentConnectionConfig . ConfigId ! = client . ConnectionConfig . ConfigId )
{
_sqlSugarClient . ChangeDatabase ( client . ConnectionConfig . ConfigId ) ;
}
// 验证连接是否成功
if ( ! _sqlSugarClient . Ado . IsValidConnection ( ) )
{
await OnDisconnected ( client . WebSocket ) ;
return ;
}
if ( string . IsNullOrEmpty ( client . HeadIcon ) )
{
UserEntity userEntity = await _sqlSugarClient . Queryable < UserEntity > ( ) . SingleAsync ( it = > it . Id = = client . UserId ) ;
if ( userEntity ! = null )
{
client . HeadIcon = "/api/file/Image/userAvatar/" + userEntity . HeadIcon ;
await OnConnected ( client . ConnectionId , client ) ;
}
}
switch ( message . method )
{
// 建立连接
case MothodType . OnConnection :
{
List < UserOnlineModel > list = await GetOnlineUserList ( client . ConnectionConfig . ConfigId ) ;
if ( list = = null )
{
list = new List < UserOnlineModel > ( ) ;
}
switch ( client . SingleLogin )
{
case LoginMethod . Single :
{
UserOnlineModel ? user = list . Find ( it = > it . userId . Equals ( client . UserId ) & & it . isMobileDevice . Equals ( client . IsMobileDevice ) ) ;
if ( user = = null )
{
list . Add ( new UserOnlineModel ( )
{
connectionId = client . ConnectionId ,
userId = client . UserId ,
account = client . Account ,
userName = client . UserName ,
lastTime = DateTime . Now ,
lastLoginIp = client . LoginIpAddress ,
tenantId = client . ConnectionConfig . ConfigId ,
lastLoginPlatForm = client . LoginPlatForm ,
isMobileDevice = client . IsMobileDevice ,
token = message . token
} ) ;
await SetOnlineUserList ( client . ConnectionConfig . ConfigId , list ) ;
}
// 不同浏览器
else if ( user ! = null & & ! user . token . Equals ( message . token ) )
{
var onlineUser = WebSocketConnectionManager . GetSocketById ( user . connectionId ) ;
if ( onlineUser ! = null )
await SendMessageAsync ( onlineUser . ConnectionId , new { method = MessageSendType . logout . ToString ( ) , msg = "此账号已在其他地方登陆" } . ToJsonString ( ) ) ;
list . RemoveAll ( ( x ) = > x . connectionId = = user . connectionId ) ;
list . Add ( new UserOnlineModel ( )
{
connectionId = client . ConnectionId ,
userId = client . UserId ,
account = client . Account ,
userName = client . UserName ,
lastTime = DateTime . Now ,
lastLoginIp = client . LoginIpAddress ,
tenantId = client . ConnectionConfig . ConfigId ,
lastLoginPlatForm = client . LoginPlatForm ,
isMobileDevice = client . IsMobileDevice ,
token = message . token
} ) ;
await SetOnlineUserList ( client . ConnectionConfig . ConfigId , list ) ;
}
}
break ;
case LoginMethod . SameTime :
{
UserOnlineModel ? user = list . Find ( it = > it . token . Equals ( message . token ) ) ;
if ( user ! = null )
{
WebSocketClient ? onlineUser = WebSocketConnectionManager . GetSocketById ( user . connectionId ) ;
if ( onlineUser ! = null )
await SendMessageAsync ( onlineUser . ConnectionId , new { method = MessageSendType . closeSocket . ToString ( ) } . ToJsonString ( ) ) ;
list . RemoveAll ( ( x ) = > x . connectionId = = user . connectionId ) ;
}
list . Add ( new UserOnlineModel ( )
{
connectionId = client . ConnectionId ,
userId = client . UserId ,
account = client . Account ,
userName = client . UserName ,
lastTime = DateTime . Now ,
lastLoginIp = client . LoginIpAddress ,
tenantId = client . ConnectionConfig . ConfigId ,
lastLoginPlatForm = client . LoginPlatForm ,
isMobileDevice = client . IsMobileDevice ,
token = message . token
} ) ;
await SetOnlineUserList ( client . ConnectionConfig . ConfigId , list ) ;
}
break ;
}
var onlineUserList = GetAllUserIdFromTenant ( client . ConnectionConfig . ConfigId ) ;
// 获取接收者为当前用户的聊天且未读的信息
var imContentList = _sqlSugarClient . Queryable < IMContentEntity > ( ) . Where ( x = > x . ReceiveUserId . Equals ( client . UserId ) & & x . State . Equals ( 0 ) & & ( SqlFunc . IsNullOrEmpty ( x . SendDeleteMark ) | | x . SendDeleteMark ! = client . UserId ) ) . GroupBy ( x = > new { x . SendUserId , x . ReceiveUserId } ) . Select ( x = > new IMContentEntity
{
State = SqlFunc . AggregateSum ( SqlFunc . IIF ( x . State = = 0 , 1 , 0 ) ) ,
SendUserId = x . SendUserId ,
ReceiveUserId = x . ReceiveUserId
} ) . ToList ( ) ;
var receiveList = _sqlSugarClient . Queryable < IMContentEntity > ( ) . Where ( x = > x . ReceiveUserId = = client . UserId & & ( SqlFunc . IsNullOrEmpty ( x . SendDeleteMark ) | | x . SendDeleteMark ! = client . UserId ) ) . OrderBy ( x = > x . SendTime , OrderByType . Desc ) . ToList ( ) ;
var unreadNums = imContentList . Adapt < List < IMUnreadNumModel > > ( ) ;
foreach ( var item in unreadNums )
{
var entity = receiveList . FirstOrDefault ( x = > x . SendUserId = = item . sendUserId ) ;
item . defaultMessage = entity ? . Content ;
item . defaultMessageType = entity ? . ContentType ;
item . defaultMessageTime = entity ? . SendTime . ToString ( ) ;
}
var unreadNoticeCount = await _sqlSugarClient . Queryable < MessageEntity , MessageReceiveEntity > ( ( m , mr ) = > new JoinQueryInfos ( JoinType . Left , m . Id = = mr . MessageId ) ) . Where ( ( m , mr ) = > m . Type = = 1 & & m . DeleteMark = = null & & mr . UserId = = client . UserId & & mr . IsRead = = 0 ) . Select ( ( m , mr ) = > new { mr . Id , mr . UserId , mr . IsRead , m . Type , m . DeleteMark } ) . CountAsync ( ) ;
var unreadMessageCount = await _sqlSugarClient . Queryable < MessageEntity , MessageReceiveEntity > ( ( m , mr ) = > new JoinQueryInfos ( JoinType . Left , m . Id = = mr . MessageId ) ) . Select ( ( m , mr ) = > new { mr . Id , mr . UserId , mr . IsRead , m . Type , m . DeleteMark } ) . MergeTable ( ) . Where ( x = > x . Type = = 2 & & x . DeleteMark = = null & & x . UserId = = client . UserId & & x . IsRead = = 0 ) . CountAsync ( ) ;
var messageDefault = await _sqlSugarClient . Queryable < MessageEntity > ( ) . Where ( x = > x . DeleteMark = = null & & x . EnabledMark = = 1 ) . OrderBy ( x = > x . CreatorTime , OrderByType . Desc ) . FirstAsync ( ) ;
var messageDefaultText = messageDefault = = null ? string . Empty : messageDefault . Title ;
var messageDefaultTime = messageDefault = = null ? DateTime . Now : messageDefault . CreatorTime ;
await SendMessageAsync ( client . ConnectionId , new { method = MessageSendType . initMessage . ToString ( ) , onlineUserList , unreadNums , unreadNoticeCount , unreadMessageCount , messageDefaultText , messageDefaultTime } . ToJsonString ( ) ) ;
await SendMessageToTenantAsync ( client . ConnectionConfig . ConfigId , new { method = MessageSendType . online . ToString ( ) , userId = client . UserId } . ToJsonString ( ) , client . ConnectionId ) ;
}
break ;
// 发送消息
case MothodType . SendMessage :
{
string toUserId = message . toUserId ;
MessageReceiveType messageType = message . messageType ;
object messageContent = message . messageContent ;
string fileName = string . Empty ;
var toUserEntity = await _sqlSugarClient . Queryable < UserEntity > ( ) . SingleAsync ( it = > it . Id = = toUserId ) ;
// 将发送消息对象信息补全
var toAccount = toUserEntity . Account ;
var toHeadIcon = toUserEntity . HeadIcon ;
var toRealName = toUserEntity . RealName ;
var entity = new IMContentEntity ( ) ;
var toMessage = new object ( ) ;
switch ( messageType )
{
case MessageReceiveType . text :
entity = CreateIMContent ( client . UserId , toUserId , messageContent . ToString ( ) , messageType . ToString ( ) ) ;
break ;
case MessageReceiveType . image :
{
var directoryPath = FileVariable . IMContentFilePath ;
if ( ! Directory . Exists ( directoryPath ) )
Directory . CreateDirectory ( directoryPath ) ;
var imageInput = messageContent . ToObject < MessagetImageInput > ( ) ;
fileName = fileName = imageInput . name ;
toMessage = new { path = "/api/file/Image/IM/" + fileName , width = imageInput . width , height = imageInput . height } ;
entity = CreateIMContent ( client . UserId , toUserId , toMessage . ToJsonString ( ) , messageType . ToString ( ) ) ;
}
break ;
case MessageReceiveType . voice :
var voiceInput = messageContent . ToObject < MessageVoiceInput > ( ) ;
toMessage = new { path = "/api/file/Image/IM/" + voiceInput . name , length = voiceInput . length } ;
entity = CreateIMContent ( client . UserId , toUserId , toMessage . ToJsonString ( ) , messageType . ToString ( ) ) ;
break ;
}
// 写入到会话表中
if ( await _sqlSugarClient . Queryable < ImReplyEntity > ( ) . AnyAsync ( it = > it . UserId = = client . UserId & & it . ReceiveUserId = = toUserId ) )
{
var imReplyEntity = await _sqlSugarClient . Queryable < ImReplyEntity > ( ) . SingleAsync ( it = > it . UserId = = client . UserId & & it . ReceiveUserId = = toUserId ) ;
imReplyEntity . ReceiveTime = entity . SendTime ;
await _sqlSugarClient . Updateable ( imReplyEntity ) . ExecuteCommandAsync ( ) ;
}
else
{
var imReplyEntity = new ImReplyEntity ( )
{
Id = SnowflakeIdHelper . NextId ( ) ,
UserId = client . UserId ,
ReceiveUserId = toUserId ,
ReceiveTime = entity . SendTime
} ;
await _sqlSugarClient . Insertable ( imReplyEntity ) . ExecuteCommandAsync ( ) ;
}
await _sqlSugarClient . Insertable ( entity ) . ExecuteCommandAsync ( ) ;
switch ( messageType )
{
case MessageReceiveType . text :
await SendMessageAsync ( client . ConnectionId , new { method = MessageSendType . sendMessage . ToString ( ) , client . UserId , account = client . Account , headIcon = client . HeadIcon , realName = client . UserName , toAccount , toHeadIcon , messageType = messageType . ToString ( ) , toUserId , toRealName , toMessage = messageContent , dateTime = DateTime . Now , latestDate = DateTime . Now } . ToJsonString ( ) ) ;
break ;
case MessageReceiveType . image :
var imageInput = messageContent . ToObject < MessagetImageInput > ( ) ;
toMessage = new { path = "/api/file/Image/IM/" + fileName , width = imageInput . width , height = imageInput . height } ;
await SendMessageAsync ( client . ConnectionId , new { method = MessageSendType . sendMessage . ToString ( ) , client . UserId , account = client . Account , headIcon = client . HeadIcon , realName = client . UserName , toAccount , toHeadIcon , messageType = messageType . ToString ( ) , toUserId , toMessage , dateTime = DateTime . Now , latestDate = DateTime . Now } . ToJsonString ( ) ) ;
break ;
case MessageReceiveType . voice :
var voiceInput = messageContent . ToObject < MessageVoiceInput > ( ) ;
toMessage = new { path = "/api/file/Image/IM/" + voiceInput . name , length = voiceInput . length } ;
await SendMessageAsync ( client . ConnectionId , new { method = MessageSendType . sendMessage . ToString ( ) , client . UserId , account = client . Account , headIcon = client . HeadIcon , realName = client . UserName , toAccount , toHeadIcon , messageType = messageType . ToString ( ) , toUserId , toMessage , dateTime = DateTime . Now } . ToJsonString ( ) ) ;
break ;
}
if ( WebSocketConnectionManager . GetSocketClientToUserCount ( string . Format ( "{0}-{1}" , client . ConnectionConfig . ConfigId , toUserId ) ) > 0 )
{
switch ( messageType )
{
case MessageReceiveType . text :
await SendMessageToUserAsync ( string . Format ( "{0}-{1}" , client . ConnectionConfig . ConfigId , toUserId ) , new { method = MessageSendType . receiveMessage . ToString ( ) , messageType = messageType . ToString ( ) , formUserId = client . UserId , formMessage = messageContent , dateTime = DateTime . Now , latestDate = DateTime . Now , headIcon = client . HeadIcon , realName = client . UserName , account = client . Account } . ToJsonString ( ) ) ;
break ;
case MessageReceiveType . image :
var imageInput = messageContent . ToObject < MessagetImageInput > ( ) ;
var formMessage = new { path = "/api/file/Image/IM/" + fileName , width = imageInput . width , height = imageInput . height } ;
await SendMessageToUserAsync ( string . Format ( "{0}-{1}" , client . ConnectionConfig . ConfigId , toUserId ) , new { method = MessageSendType . receiveMessage . ToString ( ) , messageType = messageType . ToString ( ) , formUserId = client . UserId , formMessage , dateTime = DateTime . Now , latestDate = DateTime . Now , headIcon = client . HeadIcon , realName = client . UserName , account = client . Account } . ToJsonString ( ) ) ;
break ;
case MessageReceiveType . voice :
var voiceInput = messageContent . ToObject < MessageVoiceInput > ( ) ;
toMessage = new { path = "/api/file/Image/IM/" + voiceInput . name , length = voiceInput . length } ;
await SendMessageToUserAsync ( string . Format ( "{0}-{1}" , client . ConnectionConfig . ConfigId , toUserId ) , new { method = MessageSendType . receiveMessage . ToString ( ) , messageType = messageType . ToString ( ) , formUserId = client . UserId , formMessage = toMessage , dateTime = DateTime . Now , latestDate = DateTime . Now , headIcon = client . HeadIcon , realName = client . UserName , account = client . Account } . ToJsonString ( ) ) ;
break ;
}
}
await GeTuiMessage ( toUserId , client ) ;
}
break ;
case MothodType . UpdateReadMessage :
var fromUserId = message . formUserId ;
await _sqlSugarClient . Updateable < IMContentEntity > ( )
. SetColumns ( x = > new IMContentEntity ( )
{
State = 1 ,
ReceiveTime = DateTime . Now
} ) . Where ( x = > x . State = = 0 & & x . SendUserId = = fromUserId & & x . ReceiveUserId = = client . UserId ) . ExecuteCommandAsync ( ) ;
break ;
case MothodType . MessageList :
var sendUserId = message . toUserId ; // 发送者
var receiveUserId = message . formUserId ; // 接收者
var data = await _sqlSugarClient . Queryable < IMContentEntity > ( ) . WhereIF ( ! string . IsNullOrEmpty ( message . keyword ) , it = > it . Content . Contains ( message . keyword ) )
. Where ( i = > ( i . SendUserId = = message . toUserId & & i . ReceiveUserId = = message . formUserId ) | | ( i . SendUserId = = message . formUserId & & i . ReceiveUserId = = message . toUserId ) & & ( SqlFunc . IsNullOrEmpty ( i . SendDeleteMark ) | | i . SendDeleteMark ! = client . UserId ) ) . OrderBy ( it = > it . SendTime , message . sord = = "asc" ? OrderByType . Asc : OrderByType . Desc )
. Select ( it = > new IMContentListOutput
{
id = it . Id ,
sendUserId = it . SendUserId ,
sendTime = it . SendTime ,
receiveUserId = it . ReceiveUserId ,
receiveTime = it . ReceiveTime ,
content = it . Content ,
contentType = it . ContentType ,
state = it . State
} ) . ToPagedListAsync ( message . currentPage , message . pageSize ) ;
await SendMessageAsync ( client . ConnectionId , new { method = MessageSendType . messageList . ToString ( ) , list = data . list . OrderBy ( x = > x . sendTime ) , pagination = data . pagination } . ToJsonString ( ) ) ;
break ;
case MothodType . HeartCheck : break ;
}
}
/// <summary>
/// 获取在线用户列表.
/// </summary>
/// <param name="tenantId">租户ID.</param>
/// <returns></returns>
public async Task < List < UserOnlineModel > > GetOnlineUserList ( string tenantId )
{
string cacheKey = string . Format ( "{0}{1}" , CommonConst . CACHEKEYONLINEUSER , tenantId ) ;
return await _cacheManager . GetAsync < List < UserOnlineModel > > ( cacheKey ) ;
}
/// <summary>
/// 保存在线用户列表.
/// </summary>
/// <param name="tenantId">租户ID.</param>
/// <param name="onlineList">在线用户列表.</param>
/// <returns></returns>
public async Task < bool > SetOnlineUserList ( string tenantId , List < UserOnlineModel > onlineList )
{
return await _cacheManager . SetAsync ( string . Format ( "{0}{1}" , CommonConst . CACHEKEYONLINEUSER , tenantId ) , onlineList ) ;
}
/// <summary>
/// 创建IM内容.
/// </summary>
/// <returns></returns>
private IMContentEntity CreateIMContent ( string sendUserId , string receiveUserId , string message , string messageType )
{
return new IMContentEntity ( )
{
Id = SnowflakeIdHelper . NextId ( ) ,
SendUserId = sendUserId ,
SendTime = DateTime . Parse ( DateTime . Now . ToString ( "yyyy-MM-dd HH:mm:ss" ) ) ,
ReceiveUserId = receiveUserId ,
State = 0 ,
Content = message ,
ContentType = messageType
} ;
}
private async Task GeTuiMessage ( string toUserIds , WebSocketClient client )
{
var getuiUrl = "{0}?clientId={1}&title={2}&content={3}1&text={4}&create=true" ;
if ( toUserIds . Any ( ) )
{
var clientIdList = await _sqlSugarClient . Queryable < UserDeviceEntity > ( ) . Where ( x = > toUserIds = = x . UserId & & x . DeleteMark = = null ) . Select ( x = > x . ClientId ) . ToListAsync ( ) ;
if ( clientIdList . Any ( ) )
{
var clientId = string . Join ( "," , clientIdList ) ;
var textDic = new Dictionary < string , string > ( ) ;
textDic . Add ( "type" , "3" ) ;
textDic . Add ( "name" , client . UserName + "/" + client . Account ) ;
textDic . Add ( "formUserId" , client . UserId ) ;
textDic . Add ( "headIcon" , "/api/File/Image/userAvatar/" + client . HeadIcon ) ;
getuiUrl = string . Format ( getuiUrl , _messageOptions . AppPushUrl , clientId , client . UserName + "/" + client . Account , "您有一条新消息" , textDic . ToJsonString ( ) ) ;
await getuiUrl . GetAsStringAsync ( ) ;
}
}
}
}