diff --git a/aspnet-core/LINGYUN.MicroService.sln b/aspnet-core/LINGYUN.MicroService.sln index 358dab774..8c07c2fe9 100644 --- a/aspnet-core/LINGYUN.MicroService.sln +++ b/aspnet-core/LINGYUN.MicroService.sln @@ -251,7 +251,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.PermissionManag EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Account.Web", "modules\account\LINGYUN.Abp.Account.Web\LINGYUN.Abp.Account.Web.csproj", "{5F43141B-6C63-4547-B9D6-D69EC3D7CA7E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AspNetCore.SignalR.JwtToken", "modules\common\LINGYUN.Abp.AspNetCore.SignalR\LINGYUN.Abp.AspNetCore.SignalR.JwtToken.csproj", "{A66D48C9-F141-4111-9169-CEB64AFFF61D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.AspNetCore.SignalR.JwtToken", "modules\common\LINGYUN.Abp.AspNetCore.SignalR\LINGYUN.Abp.AspNetCore.SignalR.JwtToken.csproj", "{A66D48C9-F141-4111-9169-CEB64AFFF61D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.RealTime.SignalR", "modules\common\LINGYUN.Abp.RealTime.SignalR\LINGYUN.Abp.RealTime.SignalR.csproj", "{524276E1-053D-4191-ABF7-4CDA01BFFBC3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -671,6 +673,10 @@ Global {A66D48C9-F141-4111-9169-CEB64AFFF61D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A66D48C9-F141-4111-9169-CEB64AFFF61D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A66D48C9-F141-4111-9169-CEB64AFFF61D}.Release|Any CPU.Build.0 = Release|Any CPU + {524276E1-053D-4191-ABF7-4CDA01BFFBC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {524276E1-053D-4191-ABF7-4CDA01BFFBC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {524276E1-053D-4191-ABF7-4CDA01BFFBC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {524276E1-053D-4191-ABF7-4CDA01BFFBC3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -798,6 +804,7 @@ Global {2D377D3A-70EC-4BB3-9F4C-6C933693DA98} = {52B5D4F7-237B-4E0A-A167-68442164F70A} {5F43141B-6C63-4547-B9D6-D69EC3D7CA7E} = {9E72FEB9-A626-4312-892B-CDD043879758} {A66D48C9-F141-4111-9169-CEB64AFFF61D} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E} + {524276E1-053D-4191-ABF7-4CDA01BFFBC3} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/AbpIMSignalRModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/AbpIMSignalRModule.cs index 1708965fd..4eaf55790 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/AbpIMSignalRModule.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/AbpIMSignalRModule.cs @@ -1,12 +1,12 @@ using LINGYUN.Abp.AspNetCore.SignalR.JwtToken; -using LINGYUN.Abp.RealTime; +using LINGYUN.Abp.RealTime.SignalR; using Volo.Abp.AspNetCore.SignalR; using Volo.Abp.Modularity; namespace LINGYUN.Abp.IM.SignalR { [DependsOn( - typeof(AbpRealTimeModule), + typeof(AbpRealTimeSignalRModule), typeof(AbpAspNetCoreSignalRModule), typeof(AbpAspNetCoreSignalRJwtTokenModule))] public class AbpIMSignalRModule : AbpModule diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Hubs/MessagesHub.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Hubs/MessagesHub.cs index 3d1d255b8..014a9032e 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Hubs/MessagesHub.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Hubs/MessagesHub.cs @@ -1,11 +1,16 @@ using LINGYUN.Abp.IM.Contract; +using LINGYUN.Abp.IM.Group; using LINGYUN.Abp.IM.Messages; using LINGYUN.Abp.RealTime.Client; +using LINGYUN.Abp.RealTime.SignalR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using System; +using System.Collections.Immutable; +using System.Linq; using System.Threading.Tasks; +using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Users; @@ -16,13 +21,72 @@ namespace LINGYUN.Abp.IM.SignalR.Hubs { protected IFriendStore FriendStore { get; } protected IMessageStore MessageStore { get; } + protected IUserGroupStore UserGroupStore { get; } public MessagesHub( IFriendStore friendStore, - IMessageStore messageStore) + IMessageStore messageStore, + IUserGroupStore userGroupStore) { FriendStore = friendStore; MessageStore = messageStore; + UserGroupStore = userGroupStore; + } + + protected override async Task OnClientConnectedAsync(IOnlineClient client) + { + // 加入通讯组 + var userGroups = await UserGroupStore.GetUserGroupsAsync(client.TenantId, client.UserId.Value); + foreach (var group in userGroups) + { + await Groups.AddToGroupAsync(client.ConnectionId, group.Name); + var groupClient = Clients.Group(group.Name); + if (groupClient != null) + { + // 发送用户上线通知 + await groupClient.SendAsync("onUserOnlined", client.TenantId, client.UserId.Value); + } + } + + // 发送好友上线通知 + var userFriends = await FriendStore.GetListAsync(client.TenantId, client.UserId.Value); + if (userFriends.Count > 0) + { + var friendClientIds = userFriends.Select(friend => friend.FriendId.ToString()).ToImmutableArray(); + var userClients = Clients.Users(friendClientIds); + if (userClients != null) + { + await userClients.SendAsync("onUserOnlined", client.TenantId, client.UserId.Value); + } + } + } + + protected override async Task OnClientDisconnectedAsync(IOnlineClient client) + { + // 从通讯组断开会话 + var userGroups = await UserGroupStore.GetUserGroupsAsync(client.TenantId, client.UserId.Value); + foreach (var group in userGroups) + { + await Groups.RemoveFromGroupAsync(client.ConnectionId, group.Name); + var groupClient = Clients.Group(group.Name); + if (groupClient != null) + { + // 发送用户下线指令 + await groupClient.SendAsync("onUserOfflined", client.TenantId, client.UserId.Value); + } + } + + // 发送好友下线通知 + var userFriends = await FriendStore.GetListAsync(client.TenantId, client.UserId.Value); + if (userFriends.Count > 0) + { + var friendClientIds = userFriends.Select(friend => friend.FriendId.ToString()).ToImmutableArray(); + var userClients = Clients.Users(friendClientIds); + if (userClients != null) + { + await userClients.SendAsync("onUserOfflined", client.TenantId, client.UserId.Value); + } + } } [HubMethodName("LastContactFriends")] @@ -116,23 +180,13 @@ namespace LINGYUN.Abp.IM.SignalR.Hubs // 持久化 await MessageStore.StoreMessageAsync(chatMessage); - try + if (!chatMessage.GroupId.IsNullOrWhiteSpace()) { - if (!chatMessage.GroupId.IsNullOrWhiteSpace()) - { - await SendMessageToGroupAsync(chatMessage); - } - else - { - await SendMessageToUserAsync(chatMessage); - } + await SendMessageToGroupAsync(chatMessage); } - catch (Exception ex) + else { - Logger.LogWarning("Could not send message, group: {0}, formUser: {1}, toUser: {2}", - chatMessage.GroupId, chatMessage.FormUserName, - chatMessage.ToUserId.HasValue ? chatMessage.ToUserId.ToString() : "None"); - Logger.LogWarning("Send group message error: {0}", ex.Message); + await SendMessageToUserAsync(chatMessage); } } @@ -153,11 +207,6 @@ namespace LINGYUN.Abp.IM.SignalR.Hubs var onlineClientContext = new OnlineClientContext(chatMessage.TenantId, chatMessage.ToUserId.GetValueOrDefault()); var onlineClients = OnlineClientManager.GetAllByContext(onlineClientContext); - // 需要捕捉每一个发送任务的异常吗? - // var onlineClientConnections = onlineClients.Select(c => c.ConnectionId).ToImmutableList(); - // var signalRClient = Clients.Clients(onlineClientConnections); - // await signalRClient.SendAsync("getChatMessage", chatMessage); - foreach (var onlineClient in onlineClients) { try @@ -172,6 +221,7 @@ namespace LINGYUN.Abp.IM.SignalR.Hubs } catch (Exception ex) { + // 发送异常记录就行了,因为消息已经持久化 Logger.LogWarning("Could not send message to user: {0}", chatMessage.ToUserId); Logger.LogWarning("Send to user message error: {0}", ex.Message); } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Messages/SignalRMessageSender.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Messages/SignalRMessageSender.cs index dc804e5c6..705649860 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Messages/SignalRMessageSender.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Messages/SignalRMessageSender.cs @@ -2,66 +2,36 @@ using LINGYUN.Abp.IM.SignalR.Hubs; using LINGYUN.Abp.RealTime.Client; using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using System; +using System.Collections.Immutable; +using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.IM.SignalR.Messages { - public class SignalRMessageSender : IMessageSender, ITransientDependency + [Dependency(ServiceLifetime.Transient, ReplaceServices = true)] + [ExposeServices(typeof(IMessageSender))] + public class SignalRMessageSender : MessageSenderBase { - public ILogger Logger { protected get; set; } - private readonly IOnlineClientManager _onlineClientManager; private readonly IHubContext _hubContext; - private readonly IMessageStore _messageStore; - public SignalRMessageSender( IOnlineClientManager onlineClientManager, IHubContext hubContext, - IMessageStore messageStore) + IMessageStore messageStore, + ILogger logger) + : base(messageStore, logger) { _hubContext = hubContext; - _messageStore = messageStore; _onlineClientManager = onlineClientManager; - - Logger = NullLogger.Instance; } - /// - /// 服务端调用发送消息方法 - /// - /// - /// - public async Task SendMessageAsync(ChatMessage chatMessage) - { - // 持久化 - await _messageStore.StoreMessageAsync(chatMessage); - try - { - if (!chatMessage.GroupId.IsNullOrWhiteSpace()) - { - await SendMessageToGroupAsync(chatMessage); - } - else - { - await SendMessageToUserAsync(chatMessage); - } - } - catch (Exception ex) - { - Logger.LogWarning("Could not send message, group: {0}, formUser: {1}, toUser: {2}", - chatMessage.GroupId, chatMessage.FormUserName, - chatMessage.ToUserId.HasValue ? chatMessage.ToUserId.ToString() : "None"); - Logger.LogWarning("Send group message error: {0}", ex.Message); - } - } - - protected virtual async Task SendMessageToGroupAsync(ChatMessage chatMessage) + protected override async Task SendMessageToGroupAsync(ChatMessage chatMessage) { var signalRClient = _hubContext.Clients.Group(chatMessage.GroupId); if (signalRClient == null) @@ -73,28 +43,25 @@ namespace LINGYUN.Abp.IM.SignalR.Messages await signalRClient.SendAsync("getChatMessage", chatMessage); } - protected virtual async Task SendMessageToUserAsync(ChatMessage chatMessage) + protected override async Task SendMessageToUserAsync(ChatMessage chatMessage) { var onlineClientContext = new OnlineClientContext(chatMessage.TenantId, chatMessage.ToUserId.Value); var onlineClients = _onlineClientManager.GetAllByContext(onlineClientContext); - foreach (var onlineClient in onlineClients) + try { - try + var onlineClientConnectionIds = onlineClients.Select(client => client.ConnectionId).ToImmutableArray(); + var signalRClients = _hubContext.Clients.Clients(onlineClientConnectionIds); + if (signalRClients == null) { - var signalRClient = _hubContext.Clients.Client(onlineClient.ConnectionId); - if (signalRClient == null) - { - Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " with connectionId " + onlineClient.ConnectionId + " from SignalR hub!"); - continue; - } - - await signalRClient.SendAsync("getChatMessage", chatMessage); - } - catch (Exception ex) - { - Logger.LogWarning("Could not send message to user: {0}", chatMessage.ToUserId); - Logger.LogWarning("Send to user message error: {0}", ex.Message); + Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " connection from SignalR hub!"); + return; } + await signalRClients.SendAsync("getChatMessage", chatMessage); + } + catch (Exception ex) + { + Logger.LogWarning("Could not send message to user: {0}", chatMessage.ToUserId); + Logger.LogWarning("Send to user message error: {0}", ex.Message); } } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/IFriendStore.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/IFriendStore.cs index 98722ee80..8f0aae9ef 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/IFriendStore.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/IFriendStore.cs @@ -7,6 +7,18 @@ namespace LINGYUN.Abp.IM.Contract public interface IFriendStore { /// + /// 是否是好友关系 + /// + /// + /// + /// + /// + Task IsFriendAsync( + Guid? tenantId, + Guid userId, + Guid friendId + ); + /// /// 查询好友列表 /// /// @@ -42,7 +54,7 @@ namespace LINGYUN.Abp.IM.Contract /// /// /// - Task> GetListAsync( + Task> GetPagedListAsync( Guid? tenantId, Guid userId, string filter = "", @@ -82,7 +94,7 @@ namespace LINGYUN.Abp.IM.Contract /// /// /// - Task AddMemberAsync( + Task AddMemberAsync( Guid? tenantId, Guid userId, Guid friendId, diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/UserAddFriendResult.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/UserAddFriendResult.cs new file mode 100644 index 000000000..70d1c00ba --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/UserAddFriendResult.cs @@ -0,0 +1,12 @@ +namespace LINGYUN.Abp.IM.Contract +{ + public class UserAddFriendResult + { + public bool Successed => Status == UserFriendStatus.Added; + public UserFriendStatus Status { get; } + public UserAddFriendResult(UserFriendStatus status) + { + Status = status; + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/UserFriendStatus.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/UserFriendStatus.cs new file mode 100644 index 000000000..b9b7843d5 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Contract/UserFriendStatus.cs @@ -0,0 +1,14 @@ +namespace LINGYUN.Abp.IM.Contract +{ + public enum UserFriendStatus : byte + { + /// + /// 需要验证 + /// + NeedValidation, + /// + /// 已添加 + /// + Added + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Group/IUserGroupStore.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Group/IUserGroupStore.cs index 670f55eba..d46dbf9ef 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Group/IUserGroupStore.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Group/IUserGroupStore.cs @@ -6,6 +6,14 @@ namespace LINGYUN.Abp.IM.Group { public interface IUserGroupStore { + /// + /// 成员是否在群组 + /// + /// + /// + /// + /// + Task MemberHasInGroupAsync(Guid? tenantId, long groupId, Guid userId); /// /// 获取群组用户身份 /// diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/ChatMessage.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/ChatMessage.cs index e5b51f37e..28a4aa492 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/ChatMessage.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/ChatMessage.cs @@ -1,29 +1,79 @@ using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; +using Volo.Abp.ObjectExtending; namespace LINGYUN.Abp.IM.Messages { - public class ChatMessage + public class ChatMessage : ExtensibleObject { + /// + /// 租户 + /// public Guid? TenantId { get; set; } - + /// + /// 群组标识 + /// public string GroupId { get; set; } - + /// + /// 消息标识 + /// + /// + /// 调用者无需关注此字段,将由服务自动生成 + /// public string MessageId { get; set; } - + /// + /// 发送者标识 + /// public Guid FormUserId { get; set; } - + /// + /// 发送者名称 + /// public string FormUserName { get; set; } - + /// + /// 接收用户标识 + /// + /// + /// 设计为可空是为了兼容群聊消息 + /// /remarks> public Guid? ToUserId { get; set; } - + /// + /// 消息内容 + /// [DisableAuditing] public string Content { get; set; } - + /// + /// 发送时间 + /// public DateTime SendTime { get; set; } + /// + /// 是否匿名发送 + /// + public bool IsAnonymous { get; set; } + /// + /// 消息类型 + /// + public MessageType MessageType { get; set; } = MessageType.Text; - public bool IsAnonymous { get; set; } = false; + public override IEnumerable Validate(ValidationContext validationContext) + { + var results = ExtensibleObjectValidator.GetValidationErrors(this, validationContext); - public MessageType MessageType { get; set; } = MessageType.Text; + foreach (var result in ValidateReceiver(validationContext)) + { + results.Add(result); + } + + return results; + } + + protected virtual IEnumerable ValidateReceiver(ValidationContext validationContext) + { + if (GroupId.IsNullOrWhiteSpace() && !ToUserId.HasValue) + { + yield return new ValidationResult(""); + } + } } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/MessageSenderBase.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/MessageSenderBase.cs new file mode 100644 index 000000000..f39a4c428 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/MessageSenderBase.cs @@ -0,0 +1,47 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.IM.Messages +{ + public abstract class MessageSenderBase : IMessageSender + { + protected IMessageStore Store { get; } + protected ILogger Logger { get; } + protected MessageSenderBase( + IMessageStore store, + ILogger logger) + { + Store = store; + Logger = logger; + } + + public virtual async Task SendMessageAsync(ChatMessage chatMessage) + { + // 持久化 + await Store.StoreMessageAsync(chatMessage); + + try + { + if (!chatMessage.GroupId.IsNullOrWhiteSpace()) + { + await SendMessageToGroupAsync(chatMessage); + } + else + { + await SendMessageToUserAsync(chatMessage); + } + } + catch (Exception ex) + { + Logger.LogWarning("Could not send message, group: {0}, formUser: {1}, toUser: {2}", + chatMessage.GroupId, chatMessage.FormUserName, + chatMessage.ToUserId.HasValue ? chatMessage.ToUserId.ToString() : "None"); + Logger.LogWarning("Send group message error: {0}", ex.Message); + } + } + + protected abstract Task SendMessageToGroupAsync(ChatMessage chatMessage); + protected abstract Task SendMessageToUserAsync(ChatMessage chatMessage); + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/MessageType.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/MessageType.cs index eb9e6fb9f..f247d2d57 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/MessageType.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/MessageType.cs @@ -2,9 +2,29 @@ { public enum MessageType { + /// + /// 文本消息 + /// Text = 0, + /// + /// 图片消息 + /// Image = 10, + /// + /// 链接 + /// Link = 20, - Video = 30 + /// + /// 视频 + /// + Video = 30, + /// + /// 音频 + /// + Voice = 40, + /// + /// 文件 + /// + File = 50 } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/NullMessageSender.cs b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/NullMessageSender.cs new file mode 100644 index 000000000..dd1469d1f --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.IM/LINGYUN/Abp/IM/Messages/NullMessageSender.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.IM.Messages +{ + public class NullMessageSender : MessageSenderBase, ITransientDependency + { + public NullMessageSender(IMessageStore store, ILogger logger) + : base(store, logger) + { + } + + protected override Task SendMessageToGroupAsync(ChatMessage chatMessage) + { + Logger.LogWarning("No IMessageSender Interface implementation!"); + return Task.CompletedTask; + } + + protected override Task SendMessageToUserAsync(ChatMessage chatMessage) + { + Logger.LogWarning("No IMessageSender Interface implementation!"); + return Task.CompletedTask; + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/AbpNotificationsSignalRModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/AbpNotificationsSignalRModule.cs index 130a9fd30..b75e6d564 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/AbpNotificationsSignalRModule.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/AbpNotificationsSignalRModule.cs @@ -1,12 +1,12 @@ using LINGYUN.Abp.AspNetCore.SignalR.JwtToken; -using LINGYUN.Abp.RealTime; +using LINGYUN.Abp.RealTime.SignalR; using Volo.Abp.AspNetCore.SignalR; using Volo.Abp.Modularity; namespace LINGYUN.Abp.Notifications.SignalR { [DependsOn( - typeof(AbpRealTimeModule), + typeof(AbpRealTimeSignalRModule), typeof(AbpNotificationModule), typeof(AbpAspNetCoreSignalRModule), typeof(AbpAspNetCoreSignalRJwtTokenModule))] diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs index 67f7c60e5..0b89ef241 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.Authorization; +using LINGYUN.Abp.RealTime.Client; +using LINGYUN.Abp.RealTime.SignalR; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; @@ -12,6 +14,24 @@ namespace LINGYUN.Abp.Notifications.SignalR.Hubs private INotificationStore _notificationStore; protected INotificationStore NotificationStore => LazyGetRequiredService(ref _notificationStore); + protected override async Task OnClientConnectedAsync(IOnlineClient client) + { + if (client.TenantId.HasValue) + { + // 以租户为分组,将用户加入租户通讯组 + await Groups.AddToGroupAsync(client.ConnectionId, client.TenantId.Value.ToString()); + } + } + + protected override async Task OnClientDisconnectedAsync(IOnlineClient client) + { + if (client.TenantId.HasValue) + { + // 以租户为分组,将移除租户通讯组 + await Groups.RemoveFromGroupAsync(client.ConnectionId, client.TenantId.Value.ToString()); + } + } + [HubMethodName("GetNotification")] public virtual async Task> GetNotificationAsync( NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10) @@ -26,7 +46,5 @@ namespace LINGYUN.Abp.Notifications.SignalR.Hubs { await NotificationStore.ChangeUserNotificationReadStateAsync(CurrentTenant.Id, CurrentUser.GetId(), long.Parse(id), readState); } - - } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/OnlineClientHubBase.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/OnlineClientHubBase.cs deleted file mode 100644 index f091ecf06..000000000 --- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/OnlineClientHubBase.cs +++ /dev/null @@ -1,82 +0,0 @@ -using LINGYUN.Abp.RealTime.Client; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using System; -using System.Threading.Tasks; -using Volo.Abp.AspNetCore.SignalR; -using Volo.Abp.Security.Claims; - -namespace LINGYUN.Abp.Notifications.SignalR -{ - public abstract class OnlineClientHubBase : AbpHub - { - private ICurrentPrincipalAccessor _currentPrincipalAccessor; - protected ICurrentPrincipalAccessor CurrentPrincipalAccessor => LazyGetRequiredService(ref _currentPrincipalAccessor); - - private IOnlineClientManager _onlineClientManager; - protected IOnlineClientManager OnlineClientManager => LazyGetRequiredService(ref _onlineClientManager); - - private IHttpContextAccessor _httpContextAccessor; - protected IHttpContextAccessor HttpContextAccessor => LazyGetRequiredService(ref _httpContextAccessor); - - public override async Task OnConnectedAsync() - { - await base.OnConnectedAsync(); - IOnlineClient onlineClient = CreateClientForCurrentConnection(); - Logger.LogDebug("A client is connected: " + onlineClient.ToString()); - OnlineClientManager.Add(onlineClient); - if (onlineClient.TenantId.HasValue) - { - // 以租户为分组,将用户加入租户通讯组 - await Groups.AddToGroupAsync(onlineClient.ConnectionId, onlineClient.TenantId.Value.ToString()); - } - } - - public override async Task OnDisconnectedAsync(Exception exception) - { - await base.OnDisconnectedAsync(exception); - Logger.LogDebug("A client is disconnected: " + Context.ConnectionId); - try - { - // 从通讯组移除 - var onlineClient = OnlineClientManager.GetByConnectionIdOrNull(Context.ConnectionId); - if(onlineClient != null) - { - // 移除在线客户端 - OnlineClientManager.Remove(Context.ConnectionId); - } - } - catch (Exception ex) - { - Logger.LogWarning(ex.ToString(), ex); - } - } - - protected virtual IOnlineClient CreateClientForCurrentConnection() - { - // abp框架没有处理,需要切换一下用户身份令牌.否则无法获取用户信息 - using (CurrentPrincipalAccessor.Change(Context.User)) - { - return new OnlineClient(Context.ConnectionId, GetClientIpAddress(), - CurrentTenant.Id, CurrentUser.Id) - { - ConnectTime = Clock.Now, - UserName = CurrentUser.UserName - }; - } - } - - protected virtual string GetClientIpAddress() - { - try - { - return HttpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString(); - } - catch (Exception ex) - { - Logger.LogException(ex, LogLevel.Warning); - return null; - } - } - } -} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs index 0bfa29e70..505af781c 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; @@ -53,26 +54,44 @@ namespace LINGYUN.Abp.Notifications.SignalR Logger.LogDebug($"Find online client with user {identifier.UserId} - {identifier.UserName}"); var onlineClientContext = new OnlineClientContext(notification.TenantId, identifier.UserId); var onlineClients = _onlineClientManager.GetAllByContext(onlineClientContext); - foreach (var onlineClient in onlineClients) + var onlineClientConnectionIds = onlineClients.Select(client => client.ConnectionId).ToImmutableArray(); + try { - try + var signalRClients = _hubContext.Clients.Clients(onlineClientConnectionIds); + if (signalRClients == null) { - Logger.LogDebug($"Find online client {onlineClient.UserId} - {onlineClient.ConnectionId}"); - var signalRClient = _hubContext.Clients.Client(onlineClient.ConnectionId); - if (signalRClient == null) - { - Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " with connectionId " + onlineClient.ConnectionId + " from SignalR hub!"); - continue; - } - Logger.LogDebug($"Found a singalr client, begin senging notifications"); - await signalRClient.SendAsync("getNotification", notification); - } - catch (Exception ex) - { - Logger.LogWarning("Could not send notifications to user: {0}", identifier.UserId); - Logger.LogWarning("Send to user notifications error: {0}", ex.Message); + Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " connection from SignalR hub!"); + return; } + Logger.LogDebug($"Found a singalr client, begin senging notifications"); + await signalRClients.SendAsync("getNotification", notification); } + catch (Exception ex) + { + Logger.LogWarning("Could not send notifications to user: {0}", identifier.UserId); + Logger.LogWarning("Send to user notifications error: {0}", ex.Message); + } + + //foreach (var onlineClient in onlineClients) + //{ + // try + // { + // Logger.LogDebug($"Find online client {onlineClient.UserId} - {onlineClient.ConnectionId}"); + // var signalRClient = _hubContext.Clients.Client(onlineClient.ConnectionId); + // if (signalRClient == null) + // { + // Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " with connectionId " + onlineClient.ConnectionId + " from SignalR hub!"); + // continue; + // } + // Logger.LogDebug($"Found a singalr client, begin senging notifications"); + // await signalRClient.SendAsync("getNotification", notification); + // } + // catch (Exception ex) + // { + // Logger.LogWarning("Could not send notifications to user: {0}", identifier.UserId); + // Logger.LogWarning("Send to user notifications error: {0}", ex.Message); + // } + //} } } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationData.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationData.cs index c9955adba..7548d8d0b 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationData.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationData.cs @@ -3,6 +3,13 @@ using System.Collections.Generic; namespace LINGYUN.Abp.Notifications { + /// + /// 通知数据 + /// + /// + /// TODO: 2020-10-29 针对不同语言的用户,如果在发布时期就本地化语言是错误的设计 + /// 把通知的标题和内容设计为 让客户端自行本地化 + /// public class NotificationData { public const string NotificationKey = "N:G"; @@ -153,14 +160,14 @@ namespace LINGYUN.Abp.Notifications public bool HasUserNotification(out Guid userId, out string userName) { + userName = ""; if (Properties.TryGetValue(UserIdNotificationKey, out object userKey)) { userId = (Guid)userKey; var name = TryGetData(UserNameNotificationKey); - userName = name != null ? name.ToString() : ""; + userName = name?.ToString() ?? userName; return true; } - userName = ""; return false; } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/Class1.cs b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/Class1.cs new file mode 100644 index 000000000..06fc047ee --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace LINGYUN.Abp.RealTime.SignalR +{ + public class Class1 + { + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN.Abp.RealTime.SignalR.csproj b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN.Abp.RealTime.SignalR.csproj new file mode 100644 index 000000000..a9ebaa946 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN.Abp.RealTime.SignalR.csproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + + diff --git a/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN/Abp/RealTime/SignalR/AbpRealTimeSignalRModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN/Abp/RealTime/SignalR/AbpRealTimeSignalRModule.cs new file mode 100644 index 000000000..3e8995e8d --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN/Abp/RealTime/SignalR/AbpRealTimeSignalRModule.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.SignalR; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.RealTime.SignalR +{ + [DependsOn( + typeof(AbpAspNetCoreSignalRModule))] + public class AbpRealTimeSignalRModule : AbpModule + { + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/OnlineClientHubBase.cs b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN/Abp/RealTime/SignalR/Hubs/OnlineClientHubBase.cs similarity index 72% rename from aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/OnlineClientHubBase.cs rename to aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN/Abp/RealTime/SignalR/Hubs/OnlineClientHubBase.cs index 0d9d2aac9..c8d6f3307 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/OnlineClientHubBase.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.RealTime.SignalR/LINGYUN/Abp/RealTime/SignalR/Hubs/OnlineClientHubBase.cs @@ -1,18 +1,14 @@ -using LINGYUN.Abp.IM.Group; -using LINGYUN.Abp.RealTime.Client; +using LINGYUN.Abp.RealTime.Client; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System; using System.Threading.Tasks; using Volo.Abp.AspNetCore.SignalR; -namespace LINGYUN.Abp.IM.SignalR +namespace LINGYUN.Abp.RealTime.SignalR { public abstract class OnlineClientHubBase : AbpHub { - private IUserGroupStore _userGroupStore; - protected IUserGroupStore UserGroupStore => LazyGetRequiredService(ref _userGroupStore); - private IOnlineClientManager _onlineClientManager; protected IOnlineClientManager OnlineClientManager => LazyGetRequiredService(ref _onlineClientManager); @@ -25,12 +21,7 @@ namespace LINGYUN.Abp.IM.SignalR IOnlineClient onlineClient = CreateClientForCurrentConnection(); Logger.LogDebug("A client is connected: " + onlineClient.ToString()); OnlineClientManager.Add(onlineClient); - // 加入通讯组 - var userGroups = await UserGroupStore.GetUserGroupsAsync(onlineClient.TenantId, onlineClient.UserId.Value); - foreach(var group in userGroups) - { - await Groups.AddToGroupAsync(onlineClient.ConnectionId, group.Name); - } + await OnClientConnectedAsync(onlineClient); } public override async Task OnDisconnectedAsync(Exception exception) @@ -43,13 +34,9 @@ namespace LINGYUN.Abp.IM.SignalR var onlineClient = OnlineClientManager.GetByConnectionIdOrNull(Context.ConnectionId); if(onlineClient != null) { - var userGroups = await UserGroupStore.GetUserGroupsAsync(onlineClient.TenantId, onlineClient.UserId.Value); - foreach (var group in userGroups) - { - await Groups.RemoveFromGroupAsync(onlineClient.ConnectionId, group.Name); - } // 移除在线客户端 OnlineClientManager.Remove(Context.ConnectionId); + await OnClientDisconnectedAsync(onlineClient); } } catch (Exception ex) @@ -79,5 +66,15 @@ namespace LINGYUN.Abp.IM.SignalR return null; } } + + protected virtual Task OnClientConnectedAsync(IOnlineClient client) + { + return Task.CompletedTask; + } + + protected virtual Task OnClientDisconnectedAsync(IOnlineClient client) + { + return Task.CompletedTask; + } } } diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN.Abp.MessageService.Application.Contracts.csproj b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN.Abp.MessageService.Application.Contracts.csproj index c6e46ad83..553cb00b1 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN.Abp.MessageService.Application.Contracts.csproj +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN.Abp.MessageService.Application.Contracts.csproj @@ -28,4 +28,8 @@ + + + + diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN/Abp/MessageService/Chat/IChatAppService.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN/Abp/MessageService/Chat/IChatAppService.cs index f49837302..29425cf45 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN/Abp/MessageService/Chat/IChatAppService.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application.Contracts/LINGYUN/Abp/MessageService/Chat/IChatAppService.cs @@ -1,5 +1,4 @@ -using LINGYUN.Abp.IM.Group; -using LINGYUN.Abp.IM.Messages; +using LINGYUN.Abp.IM.Messages; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; @@ -14,41 +13,41 @@ namespace LINGYUN.Abp.MessageService.Chat /// /// Task SendMessageAsync(ChatMessage input); - /// - /// 申请加入群组 - /// - /// - /// - Task ApplyJoinGroupAsync(UserJoinGroupDto input); - /// - /// 获取我的群组 - /// - /// - Task> GetMyGroupsAsync(); - /// - /// 获取群组用户 - /// - /// - /// - Task> GetGroupUsersAsync(GroupUserGetByPagedDto input); - /// - /// 处理用户群组申请 - /// - /// - /// - Task GroupAcceptUserAsync(GroupAcceptUserDto input); - /// - /// 群组移除用户 - /// - /// - /// - Task GroupRemoveUserAsync(GroupRemoveUserDto input); + ///// + ///// 申请加入群组 + ///// + ///// + ///// + //Task ApplyJoinGroupAsync(UserJoinGroupDto input); + ///// + ///// 获取我的群组 + ///// + ///// + //Task> GetMyGroupsAsync(); + ///// + ///// 获取群组用户 + ///// + ///// + ///// + //Task> GetGroupUsersAsync(GroupUserGetByPagedDto input); + ///// + ///// 处理用户群组申请 + ///// + ///// + ///// + //Task GroupAcceptUserAsync(GroupAcceptUserDto input); + ///// + ///// 群组移除用户 + ///// + ///// + ///// + //Task GroupRemoveUserAsync(GroupRemoveUserDto input); /// /// 获取群组消息 /// /// /// - Task> GetGroupMessageAsync(GroupMessageGetByPagedDto input); + Task> GetMyGroupMessageAsync(GroupMessageGetByPagedDto input); /// /// 获取我的消息 /// diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/ChatAppService.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/ChatAppService.cs index f3434c2f0..8883332ec 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/ChatAppService.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/ChatAppService.cs @@ -1,7 +1,6 @@ using LINGYUN.Abp.IM.Group; using LINGYUN.Abp.IM.Messages; using Microsoft.AspNetCore.Authorization; -using System.Collections.Immutable; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Application.Dtos; @@ -50,9 +49,12 @@ namespace LINGYUN.Abp.MessageService.Chat return new ListResultDto(chatMessages); } - public virtual async Task> GetGroupMessageAsync(GroupMessageGetByPagedDto input) + public virtual async Task> GetMyGroupMessageAsync(GroupMessageGetByPagedDto input) { - // TODO: 增加验证,用户不在群组却来查询这个群组消息,是非法客户端操作 + if (! await _userGroupStore.MemberHasInGroupAsync(CurrentTenant.Id, input.GroupId, CurrentUser.GetId())) + { + throw new BusinessException(MessageServiceErrorCodes.YouHaveNotJoinedGroup); + } var groupMessageCount = await _messageStore .GetGroupMessageCountAsync(CurrentTenant.Id, input.GroupId, @@ -66,61 +68,61 @@ namespace LINGYUN.Abp.MessageService.Chat return new PagedResultDto(groupMessageCount, groupMessages); } - public virtual async Task> GetGroupUsersAsync(GroupUserGetByPagedDto input) - { - var groupUserCardCount = await _userGroupStore - .GetMembersCountAsync(CurrentTenant.Id, input.GroupId); - - var groupUserCards = await _userGroupStore.GetMembersAsync(CurrentTenant.Id, - input.GroupId, input.Sorting, input.Reverse, - input.SkipCount, input.MaxResultCount); - - return new PagedResultDto(groupUserCardCount, groupUserCards); - } - - [Authorize] - public virtual async Task> GetMyGroupsAsync() - { - var myGroups = await _userGroupStore.GetUserGroupsAsync(CurrentTenant.Id, CurrentUser.GetId()); - - return new ListResultDto(myGroups.ToImmutableList()); - } - - public virtual async Task GroupAcceptUserAsync(GroupAcceptUserDto input) - { - var myGroupCard = await _userGroupStore - .GetUserGroupCardAsync(CurrentTenant.Id, input.GroupId, CurrentUser.GetId()); - if (myGroupCard == null) - { - // 当前登录用户不再用户组 - throw new UserFriendlyException(""); - } - if (!myGroupCard.IsAdmin) - { - // 当前登录用户没有加人权限 - throw new UserFriendlyException(""); - } - await _userGroupStore - .AddUserToGroupAsync(CurrentTenant.Id, input.UserId, input.GroupId, CurrentUser.GetId()); - } - - public virtual async Task GroupRemoveUserAsync(GroupRemoveUserDto input) - { - var myGroupCard = await _userGroupStore - .GetUserGroupCardAsync(CurrentTenant.Id, input.GroupId, CurrentUser.GetId()); - if (myGroupCard == null) - { - // 当前登录用户不再用户组 - throw new UserFriendlyException(""); - } - if (!myGroupCard.IsAdmin) - { - // 当前登录用户没有踢人权限 - throw new UserFriendlyException(""); - } - await _userGroupStore - .RemoveUserFormGroupAsync(CurrentTenant.Id, input.UserId, input.GroupId); - } + //public virtual async Task> GetGroupUsersAsync(GroupUserGetByPagedDto input) + //{ + // var groupUserCardCount = await _userGroupStore + // .GetMembersCountAsync(CurrentTenant.Id, input.GroupId); + + // var groupUserCards = await _userGroupStore.GetMembersAsync(CurrentTenant.Id, + // input.GroupId, input.Sorting, input.Reverse, + // input.SkipCount, input.MaxResultCount); + + // return new PagedResultDto(groupUserCardCount, groupUserCards); + //} + + //[Authorize] + //public virtual async Task> GetMyGroupsAsync() + //{ + // var myGroups = await _userGroupStore.GetUserGroupsAsync(CurrentTenant.Id, CurrentUser.GetId()); + + // return new ListResultDto(myGroups.ToImmutableList()); + //} + + //public virtual async Task GroupAcceptUserAsync(GroupAcceptUserDto input) + //{ + // var myGroupCard = await _userGroupStore + // .GetUserGroupCardAsync(CurrentTenant.Id, input.GroupId, CurrentUser.GetId()); + // if (myGroupCard == null) + // { + // // 当前登录用户不再用户组 + // throw new UserFriendlyException(""); + // } + // if (!myGroupCard.IsAdmin) + // { + // // 当前登录用户没有加人权限 + // throw new UserFriendlyException(""); + // } + // await _userGroupStore + // .AddUserToGroupAsync(CurrentTenant.Id, input.UserId, input.GroupId, CurrentUser.GetId()); + //} + + //public virtual async Task GroupRemoveUserAsync(GroupRemoveUserDto input) + //{ + // var myGroupCard = await _userGroupStore + // .GetUserGroupCardAsync(CurrentTenant.Id, input.GroupId, CurrentUser.GetId()); + // if (myGroupCard == null) + // { + // // 当前登录用户不再用户组 + // throw new UserFriendlyException(""); + // } + // if (!myGroupCard.IsAdmin) + // { + // // 当前登录用户没有踢人权限 + // throw new UserFriendlyException(""); + // } + // await _userGroupStore + // .RemoveUserFormGroupAsync(CurrentTenant.Id, input.UserId, input.GroupId); + //} public virtual async Task SendMessageAsync(ChatMessage input) { @@ -131,11 +133,5 @@ namespace LINGYUN.Abp.MessageService.Chat return new ChatMessageSendResultDto(input.MessageId); } - - public virtual Task ApplyJoinGroupAsync(UserJoinGroupDto input) - { - // TOTO 发送通知? - return Task.CompletedTask; - } } } diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/MyFriendAppService.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/MyFriendAppService.cs index d76bb4be7..d8254233a 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/MyFriendAppService.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Application/LINGYUN/Abp/MessageService/Chat/MyFriendAppService.cs @@ -41,7 +41,7 @@ namespace LINGYUN.Abp.MessageService.Chat var myFrientCount = await FriendStore.GetCountAsync(CurrentTenant.Id, CurrentUser.GetId()); var myFriends = await FriendStore - .GetListAsync(CurrentTenant.Id, CurrentUser.GetId(), + .GetPagedListAsync(CurrentTenant.Id, CurrentUser.GetId(), input.Filter, input.Sorting, input.Reverse, input.SkipCount, input.MaxResultCount); diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Chat/UserChatFriendEto.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Chat/UserChatFriendEto.cs new file mode 100644 index 000000000..795336f55 --- /dev/null +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Chat/UserChatFriendEto.cs @@ -0,0 +1,23 @@ +using LINGYUN.Abp.IM.Contract; +using System; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.MessageService.Chat +{ + public class UserChatFriendEto : IMultiTenant + { + public Guid? TenantId { get; set; } + /// + /// 用户标识 + /// + public Guid UserId { get; set; } + /// + /// 好友标识 + /// + public Guid FrientId { get; set; } + /// + /// 状态 + /// + public UserFriendStatus Status { get; set; } + } +} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Chat/ChatGroupConsts.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Group/ChatGroupConsts.cs similarity index 86% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Chat/ChatGroupConsts.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Group/ChatGroupConsts.cs index c84bcd637..1cb2174a4 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Chat/ChatGroupConsts.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/Group/ChatGroupConsts.cs @@ -1,4 +1,4 @@ -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { public class ChatGroupConsts { diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/MessageServiceErrorCodes.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/MessageServiceErrorCodes.cs index 574b5cd52..f804cc102 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/MessageServiceErrorCodes.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain.Shared/LINGYUN/Abp/MessageService/MessageServiceErrorCodes.cs @@ -1,34 +1,99 @@ namespace LINGYUN.Abp.MessageService { + /// + /// 消息系统错误码设计 + /// 状态码分为两部分 前2位领域 后3位状态 + /// + /// + /// 领域部分: + /// 01 输入 + /// 02 群组 + /// 03 用户 + /// 04 应用 + /// 05 内部 + /// 10 输出 + /// + /// + /// + /// 状态部分: + /// 200-299 成功 + /// 300-399 成功但有后续操作 + /// 400-499 业务异常 + /// 500-599 内部异常 + /// 900-999 输入输出异常 + /// + /// + /// public class MessageServiceErrorCodes { + public const string Namespace = "LINGYUN.Abp.Message"; /// - /// 管理员已开启全员禁言 + /// 消息不完整 /// - public const string GroupNotAllowedToSpeak = "Messages.Group:1001"; + public const string MessageIncomplete = Namespace + ":01400"; /// - /// 管理员不允许匿名发言 + /// 您还未加入群组,不能进行操作 + /// + public const string YouHaveNotJoinedGroup = Namespace + ":01401"; + /// + /// 已发送群组申请,等待管理员同意 /// - public const string GroupNotAllowedToSpeakAnonymously = "Messages.Group:1002"; + public const string YouHaveAddingToGroup = Namespace + ":02301"; + /// + /// 你需要验证问题才能加入群聊 + /// + public const string YouNeedValidationQuestingByAddGroup = Namespace + ":02302"; + /// + /// 管理员已开启全员禁言 + /// + public const string GroupNotAllowedToSpeak = Namespace + ":02400"; /// /// 管理员已禁止用户发言 /// - public const string GroupUserHasBlack = "Messages.Group:1003"; + public const string GroupUserHasBlack = Namespace + ":02403"; /// - /// 用户已将发信人拉黑 + /// 管理员不允许匿名发言 + /// + public const string GroupNotAllowedToSpeakAnonymously = Namespace + ":02401"; + /// + /// 群组不存在或已解散 /// - public const string UserHasBlack = "Messages.User:1003"; + public const string GroupNotFount = Namespace + ":02404"; /// /// 用户已拒接所有消息 /// - public const string UserHasRejectAllMessage = "Messages.User:1001"; + public const string UserHasRejectAllMessage = Namespace + ":03400"; + /// + /// 用户已将发信人拉黑 + /// + public const string UserHasBlack = Namespace + ":03401"; /// /// 用户不允许匿名发言 /// - public const string UserNotAllowedToSpeakAnonymously = "Messages.User:1002"; + public const string UserNotAllowedToSpeakAnonymously = Namespace + ":03402"; + /// + /// 用户不接收非好友发言 + /// + public const string UserHasRejectNotFriendMessage = Namespace + ":03403"; + /// + /// 接收消息用户不存在或已注销 + /// + public const string UseNotFount = Namespace + ":03404"; + /// + /// 用户拒绝添加好友 + /// + public const string UseRefuseToAddFriend = Namespace + ":03410"; + /// + /// 对方已是您的好友或已发送验证请求,不能重复操作 + /// + public const string UseHasBeenAddedTheFriendOrSendAuthorization = Namespace + ":03411"; + /// + /// 已发送好友申请,等待对方同意 + /// + public const string YouHaveAddingTheUserToFriend = Namespace + ":03301"; /// - /// 已经添加对方为好友 + /// 你需要验证问题才能添加好友 /// - public const string YouHaveAddedTheUserToFriend = "Messages.UserFriend:1001"; + public const string YouNeedValidationQuestingByAddFriend = Namespace + ":03302"; } } diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN.Abp.MessageService.Domain.csproj b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN.Abp.MessageService.Domain.csproj index 6877ef0ce..1e5430533 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN.Abp.MessageService.Domain.csproj +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN.Abp.MessageService.Domain.csproj @@ -18,6 +18,7 @@ + diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/AbpMessageServiceDomainModule.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/AbpMessageServiceDomainModule.cs index e5006dd94..bf0a3429c 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/AbpMessageServiceDomainModule.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/AbpMessageServiceDomainModule.cs @@ -1,6 +1,7 @@ using LINGYUN.Abp.MessageService.Localization; using LINGYUN.Abp.MessageService.Mapper; using Volo.Abp.AutoMapper; +using Volo.Abp.Caching; using Volo.Abp.Localization; using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; @@ -10,6 +11,7 @@ namespace LINGYUN.Abp.MessageService { [DependsOn( typeof(AbpAutoMapperModule), + typeof(AbpCachingModule), typeof(AbpMessageServiceDomainSharedModule))] public class AbpMessageServiceDomainModule : AbpModule { diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/ChatNotificationNames.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/ChatNotificationNames.cs new file mode 100644 index 000000000..c1518c28f --- /dev/null +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/ChatNotificationNames.cs @@ -0,0 +1,14 @@ +namespace LINGYUN.Abp.MessageService.Chat +{ + public static class ChatNotificationNames + { + public const string GroupName = "LINGYUN.Abp.IM.Chat"; + + public static class UserFriend + { + public const string Default = GroupName + ".UserFriend"; + + public const string NeedValidation = Default + ".NeedValidation"; + } + } +} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/FriendStore.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/FriendStore.cs index aee590089..8690a36b1 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/FriendStore.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/FriendStore.cs @@ -1,8 +1,10 @@ using LINGYUN.Abp.IM.Contract; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp; +using Volo.Abp.Caching; using Volo.Abp.Domain.Services; using Volo.Abp.Uow; @@ -10,31 +12,65 @@ namespace LINGYUN.Abp.MessageService.Chat { public class FriendStore : DomainService, IFriendStore { + protected IDistributedCache Cache { get; } protected IUserChatFriendRepository UserChatFriendRepository { get; } + protected IUserChatSettingRepository UserChatSettingRepository { get; } public FriendStore( - IUserChatFriendRepository userChatFriendRepository) + IDistributedCache cache, + IUserChatFriendRepository userChatFriendRepository, + IUserChatSettingRepository userChatSettingRepository) { + Cache = cache; UserChatFriendRepository = userChatFriendRepository; + UserChatSettingRepository = userChatSettingRepository; + } + + public virtual async Task IsFriendAsync( + Guid? tenantId, + Guid userId, + Guid friendId + ) + { + using (CurrentTenant.Change(tenantId)) + { + return await UserChatFriendRepository.IsAddedAsync(userId, friendId); + } } [UnitOfWork] - public virtual async Task AddMemberAsync(Guid? tenantId, Guid userId, Guid friendId, string remarkName = "") + public virtual async Task AddMemberAsync(Guid? tenantId, Guid userId, Guid friendId, string remarkName = "") { using (CurrentTenant.Change(tenantId)) { if (await UserChatFriendRepository.IsAddedAsync(userId, friendId)) { - throw new BusinessException(MessageServiceErrorCodes.YouHaveAddedTheUserToFriend); - + throw new BusinessException(MessageServiceErrorCodes.UseHasBeenAddedTheFriendOrSendAuthorization); } - var userChatFriend = new UserChatFriend(userId, friendId, remarkName, tenantId) + + var status = UserFriendStatus.NeedValidation; + var userChatSetting = await UserChatSettingRepository.FindByUserIdAsync(friendId); + if (userChatSetting != null) + { + if (!userChatSetting.AllowAddFriend) + { + throw new BusinessException(MessageServiceErrorCodes.UseRefuseToAddFriend); + } + + status = userChatSetting.RequireAddFriendValition + ? UserFriendStatus.NeedValidation + : UserFriendStatus.Added; + } + + var userChatFriend = new UserChatFriend(userId, friendId, remarkName, status, tenantId) { CreationTime = Clock.Now, CreatorId = userId }; await UserChatFriendRepository.InsertAsync(userChatFriend); + + return new UserAddFriendResult(status); } } @@ -51,11 +87,7 @@ namespace LINGYUN.Abp.MessageService.Chat bool reverse = false ) { - using (CurrentTenant.Change(tenantId)) - { - return await UserChatFriendRepository - .GetAllMembersAsync(userId, sorting, reverse); - } + return await GetAllFriendByCacheItemAsync(tenantId, userId, sorting, reverse); } public virtual async Task GetCountAsync(Guid? tenantId, Guid userId, string filter = "") @@ -67,7 +99,7 @@ namespace LINGYUN.Abp.MessageService.Chat } } - public virtual async Task> GetListAsync(Guid? tenantId, Guid userId, string filter = "", string sorting = nameof(UserFriend.UserId), bool reverse = false, int skipCount = 0, int maxResultCount = 10) + public virtual async Task> GetPagedListAsync(Guid? tenantId, Guid userId, string filter = "", string sorting = nameof(UserFriend.UserId), bool reverse = false, int skipCount = 0, int maxResultCount = 10) { using (CurrentTenant.Change(tenantId)) { @@ -132,5 +164,34 @@ namespace LINGYUN.Abp.MessageService.Chat } } } + + protected virtual async Task> GetAllFriendByCacheItemAsync( + Guid? tenantId, + Guid userId, + string sorting = nameof(UserFriend.UserId), + bool reverse = false + ) + { + var cacheKey = UserFriendCacheItem.CalculateCacheKey(userId.ToString()); + Logger.LogDebug($"FriendStore.GetCacheItemAsync: {cacheKey}"); + + var cacheItem = await Cache.GetAsync(cacheKey); + if (cacheItem != null) + { + Logger.LogDebug($"Found in the cache: {cacheKey}"); + return cacheItem.Friends; + } + + Logger.LogDebug($"Not found in the cache: {cacheKey}"); + using (CurrentTenant.Change(tenantId)) + { + var friends = await UserChatFriendRepository + .GetAllMembersAsync(userId, sorting, reverse); + cacheItem = new UserFriendCacheItem(friends); + Logger.LogDebug($"Set item in the cache: {cacheKey}"); + await Cache.SetAsync(cacheKey, cacheItem); + return friends; + } + } } } diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IMessageRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IMessageRepository.cs index 095124290..43b45d938 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IMessageRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IMessageRepository.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.IM.Messages; +using LINGYUN.Abp.MessageService.Group; using System; using System.Collections.Generic; using System.Threading; diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IUserChatSettingRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IUserChatSettingRepository.cs index 7f7cdbaa7..6ad18ae70 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IUserChatSettingRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IUserChatSettingRepository.cs @@ -7,14 +7,6 @@ namespace LINGYUN.Abp.MessageService.Chat public interface IUserChatSettingRepository : IBasicRepository { Task UserHasOpendImAsync(Guid userId); - /// - /// 用户是否已被拉黑 - /// - /// - /// - /// - Task UserHasBlackedAsync(Guid formUserId, Guid toUserId); - - Task GetByUserIdAsync(Guid userId); + Task FindByUserIdAsync(Guid userId); } } diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/MessageStore.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/MessageStore.cs index 870cfe537..42a64f200 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/MessageStore.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/MessageStore.cs @@ -1,4 +1,6 @@ -using LINGYUN.Abp.IM.Messages; +using LINGYUN.Abp.IM.Contract; +using LINGYUN.Abp.IM.Messages; +using LINGYUN.Abp.MessageService.Group; using LINGYUN.Abp.MessageService.Utils; using System; using System.Collections.Generic; @@ -12,6 +14,9 @@ namespace LINGYUN.Abp.MessageService.Chat { public class MessageStore : DomainService, IMessageStore { + private IFriendStore _friendStore; + protected IFriendStore FriendStore => LazyGetRequiredService(ref _friendStore); + private IObjectMapper _objectMapper; protected IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper); @@ -137,14 +142,15 @@ namespace LINGYUN.Abp.MessageService.Chat protected virtual async Task StoreUserMessageAsync(ChatMessage chatMessage) { - var userHasBlacked = await UserChatSettingRepository - .UserHasBlackedAsync(chatMessage.ToUserId.Value, chatMessage.FormUserId); - if (userHasBlacked) + // 检查接收用户 + if (!chatMessage.ToUserId.HasValue) { - // 当前发送的用户已被拉黑 - throw new BusinessException(MessageServiceErrorCodes.UserHasBlack); + throw new BusinessException(MessageServiceErrorCodes.UseNotFount); } - var userChatSetting = await UserChatSettingRepository.GetByUserIdAsync(chatMessage.ToUserId.Value); + + var myFriend = await FriendStore.GetMemberAsync(chatMessage.TenantId, chatMessage.ToUserId.Value, chatMessage.FormUserId); + + var userChatSetting = await UserChatSettingRepository.FindByUserIdAsync(chatMessage.ToUserId.Value); if (userChatSetting != null) { if (!userChatSetting.AllowReceiveMessage) @@ -152,12 +158,29 @@ namespace LINGYUN.Abp.MessageService.Chat // 当前发送的用户不接收消息 throw new BusinessException(MessageServiceErrorCodes.UserHasRejectAllMessage); } + + if (myFriend == null && !chatMessage.IsAnonymous) + { + throw new BusinessException(MessageServiceErrorCodes.UserHasRejectNotFriendMessage); + } + if (chatMessage.IsAnonymous && !userChatSetting.AllowAnonymous) { // 当前用户不允许匿名发言 throw new BusinessException(MessageServiceErrorCodes.UserNotAllowedToSpeakAnonymously); } } + else + { + if (myFriend == null) + { + throw new BusinessException(MessageServiceErrorCodes.UserHasRejectNotFriendMessage); + } + } + if (myFriend?.Black == true) + { + throw new BusinessException(MessageServiceErrorCodes.UserHasBlack); + } var messageId = SnowflakeIdGenerator.Create(); var message = new UserMessage(messageId, chatMessage.FormUserId, chatMessage.FormUserName, chatMessage.Content, chatMessage.MessageType); message.SendToUser(chatMessage.ToUserId.Value); @@ -188,7 +211,7 @@ namespace LINGYUN.Abp.MessageService.Chat } var messageId = SnowflakeIdGenerator.Create(); var message = new GroupMessage(messageId, chatMessage.FormUserId, chatMessage.FormUserName, chatMessage.Content, chatMessage.MessageType); - // TODO: 需要压测 高并发场景下的装箱性能影响 + message.SendToGroup(groupId); await MessageRepository.InsertGroupMessageAsync(message); diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatBlack.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatBlack.cs deleted file mode 100644 index 0d3e07975..000000000 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatBlack.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Volo.Abp.Domain.Entities.Auditing; -using Volo.Abp.MultiTenancy; - -namespace LINGYUN.Abp.MessageService.Chat -{ - /// - /// 用户黑名单 - /// - public class UserChatBlack : CreationAuditedEntity, IMultiTenant - { - /// - /// 租户 - /// - public virtual Guid? TenantId { get; protected set; } - /// - /// 用户标识 - /// - public virtual Guid UserId { get; protected set; } - /// - /// 拉黑的用户 - /// - public virtual Guid ShieldUserId { get; protected set; } - protected UserChatBlack() { } - public UserChatBlack(Guid userId, Guid shieldUserId, Guid? tenantId) - { - UserId = userId; - ShieldUserId = shieldUserId; - TenantId = tenantId; - } - } -} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriend.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriend.cs index 1392955d3..d964aa0cc 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriend.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriend.cs @@ -1,10 +1,11 @@ -using System; +using LINGYUN.Abp.IM.Contract; +using System; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; namespace LINGYUN.Abp.MessageService.Chat { - public class UserChatFriend : CreationAuditedEntity, IMultiTenant + public class UserChatFriend : CreationAuditedAggregateRoot, IMultiTenant { /// /// 租户 @@ -35,6 +36,8 @@ namespace LINGYUN.Abp.MessageService.Chat /// public virtual string RemarkName { get; set; } + public virtual UserFriendStatus Status { get; protected set; } + protected UserChatFriend() { } @@ -43,12 +46,30 @@ namespace LINGYUN.Abp.MessageService.Chat Guid userId, Guid friendId, string remarkName = "", + UserFriendStatus status = UserFriendStatus.NeedValidation, Guid? tenantId = null) { UserId = userId; FrientId = friendId; RemarkName = remarkName; + Status = status; TenantId = tenantId; } + + public void SetStatus(UserFriendStatus status = UserFriendStatus.NeedValidation) + { + if (Status == UserFriendStatus.NeedValidation && status == UserFriendStatus.NeedValidation) + { + // 如果是后续验证通过的需要单独的事件 + AddLocalEvent(new UserChatFriendEto + { + TenantId = TenantId, + UserId = UserId, + FrientId = FrientId, + Status = UserFriendStatus.Added + }); + } + Status = status; + } } } diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriendGroup.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriendGroup.cs index 622758140..f6d811721 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriendGroup.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatFriendGroup.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserFriendCacheItem.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserFriendCacheItem.cs new file mode 100644 index 000000000..97082ace3 --- /dev/null +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserFriendCacheItem.cs @@ -0,0 +1,27 @@ +using LINGYUN.Abp.IM.Contract; +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.MessageService.Chat +{ + [Serializable] + public class UserFriendCacheItem + { + public List Friends { get; set; } + + public UserFriendCacheItem() + { + Friends = new List(); + } + + public UserFriendCacheItem(List friends) + { + Friends = friends; + } + + public static string CalculateCacheKey(string userId) + { + return "uid:" + userId; + } + } +} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserSpecialFocus.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserSpecialFocus.cs deleted file mode 100644 index fbf538d53..000000000 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserSpecialFocus.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Volo.Abp.Domain.Entities.Auditing; -using Volo.Abp.MultiTenancy; - -namespace LINGYUN.Abp.MessageService.Chat -{ - /// - /// 用户特别关注 - /// - public class UserSpecialFocus : CreationAuditedEntity, IMultiTenant - { - /// - /// 租户 - /// - public virtual Guid? TenantId { get; protected set; } - /// - /// 用户标识 - /// - public virtual Guid UserId { get; protected set; } - /// - /// 关注的用户 - /// - public virtual Guid FocusUserId { get; protected set; } - protected UserSpecialFocus() { } - public UserSpecialFocus(Guid userId, Guid focusUserId, Guid? tenantId) - { - UserId = userId; - FocusUserId = focusUserId; - TenantId = tenantId; - } - } -} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/EventBus/Local/UserChatFriendEventHandler.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/EventBus/Local/UserChatFriendEventHandler.cs new file mode 100644 index 000000000..5af42b75a --- /dev/null +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/EventBus/Local/UserChatFriendEventHandler.cs @@ -0,0 +1,84 @@ +using LINGYUN.Abp.IM.Messages; +using LINGYUN.Abp.MessageService.Chat; +using LINGYUN.Abp.Notifications; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.MessageService.EventBus.Local +{ + public class UserChatFriendEventHandler : + ILocalEventHandler>, + ILocalEventHandler>, + ILocalEventHandler>, + ILocalEventHandler, + ITransientDependency + { + private ILogger _logger; + private IMessageSender _messageSender; + private INotificationDispatcher _dispatcher; + private IDistributedCache _cache; + + public UserChatFriendEventHandler( + IMessageSender messageSender, + INotificationDispatcher dispatcher, + ILogger logger) + { + _logger = logger; + _dispatcher = dispatcher; + _messageSender = messageSender; + } + + public virtual async Task HandleEventAsync(EntityCreatedEventData eventData) + { + switch (eventData.Entity.Status) + { + case IM.Contract.UserFriendStatus.Added: + await SendFriendAddedMessageAsync(eventData.Entity.UserId, eventData.Entity.FrientId, eventData.Entity.TenantId); + break; + case IM.Contract.UserFriendStatus.NeedValidation: + await SendFriendValidationNotiferAsync(eventData.Entity.UserId, eventData.Entity.FrientId, eventData.Entity.TenantId); + break; + } + await RemoveUserFriendCacheItemAsync(eventData.Entity.UserId); + } + + public virtual async Task HandleEventAsync(EntityDeletedEventData eventData) + { + await RemoveUserFriendCacheItemAsync(eventData.Entity.UserId); + } + + public virtual async Task HandleEventAsync(EntityUpdatedEventData eventData) + { + await RemoveUserFriendCacheItemAsync(eventData.Entity.UserId); + } + + public virtual async Task HandleEventAsync(UserChatFriendEto eventData) + { + if (eventData.Status == IM.Contract.UserFriendStatus.Added) + { + await SendFriendAddedMessageAsync(eventData.UserId, eventData.FrientId, eventData.TenantId); + } + } + + protected virtual async Task SendFriendAddedMessageAsync(Guid userId, Guid friendId, Guid? tenantId = null) + { + // 发送添加好友的第一条消息 + } + + protected virtual async Task SendFriendValidationNotiferAsync(Guid userId, Guid friendId, Guid? tenantId = null) + { + // 发送好友验证通知 + } + + protected virtual async Task RemoveUserFriendCacheItemAsync(Guid userId) + { + // 移除好友缓存 + await _cache.RemoveAsync(UserFriendCacheItem.CalculateCacheKey(userId.ToString())); + } + } +} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/ChatGroup.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/ChatGroup.cs similarity index 97% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/ChatGroup.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/ChatGroup.cs index ee22667ba..66f9382e4 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/ChatGroup.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/ChatGroup.cs @@ -2,7 +2,7 @@ using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { /// /// 聊天群组 diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/GroupChatBlack.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/GroupChatBlack.cs similarity index 95% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/GroupChatBlack.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/GroupChatBlack.cs index 22cfaef08..8d07295c8 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/GroupChatBlack.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/GroupChatBlack.cs @@ -2,7 +2,7 @@ using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { /// /// 用户黑名单 diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/GroupMessage.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/GroupMessage.cs similarity index 87% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/GroupMessage.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/GroupMessage.cs index 14fe37678..b38cd50a3 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/GroupMessage.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/GroupMessage.cs @@ -1,7 +1,8 @@ using LINGYUN.Abp.IM.Messages; +using LINGYUN.Abp.MessageService.Chat; using System; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { public class GroupMessage : Message { diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IGroupRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IGroupRepository.cs similarity index 90% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IGroupRepository.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IGroupRepository.cs index 568845459..58fa823e6 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IGroupRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IGroupRepository.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { public interface IGroupRepository : IBasicRepository { @@ -19,7 +19,7 @@ namespace LINGYUN.Abp.MessageService.Chat Guid formUserId, CancellationToken cancellationToken = default); - Task GetByIdAsync( + Task FindByIdAsync( long id, CancellationToken cancellationToken = default); diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IGroupRepositoryExtensions.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IGroupRepositoryExtensions.cs new file mode 100644 index 000000000..b33056b57 --- /dev/null +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IGroupRepositoryExtensions.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; + +namespace LINGYUN.Abp.MessageService.Group +{ + public static class IGroupRepositoryExtensions + { + public static async Task GetByIdAsync( + this IGroupRepository repository, + long id, + CancellationToken cancellationToken = default) + { + var group = await repository.FindByIdAsync(id, cancellationToken); + + return group ?? throw new BusinessException(MessageServiceErrorCodes.GroupNotFount); + } + } +} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IUserChatGroupRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IUserChatGroupRepository.cs similarity index 94% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IUserChatGroupRepository.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IUserChatGroupRepository.cs index f7f04df57..7f687e70b 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/IUserChatGroupRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/IUserChatGroupRepository.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { public interface IUserChatGroupRepository : IBasicRepository { @@ -51,7 +51,7 @@ namespace LINGYUN.Abp.MessageService.Chat /// Task> GetMembersAsync( long groupId, - string sorting = nameof(UserChatCard.UserId), + string sorting = nameof(GroupUserCard.UserId), bool reverse = false, int skipCount = 0, int maxResultCount = 10, @@ -62,7 +62,7 @@ namespace LINGYUN.Abp.MessageService.Chat /// /// /// - Task> GetMemberGroupsAsync( + Task> GetMemberGroupsAsync( Guid userId, CancellationToken cancellationToken = default); /// diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatGroup.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserChatGroup.cs similarity index 94% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatGroup.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserChatGroup.cs index 9cf6b06b4..1208e03c7 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserChatGroup.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserChatGroup.cs @@ -2,7 +2,7 @@ using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { /// /// 用户群组 diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserGroupCard.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserGroupCard.cs similarity index 97% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserGroupCard.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserGroupCard.cs index a1732631f..63ec38b92 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserGroupCard.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserGroupCard.cs @@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; using Volo.Abp.Timing; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { /// /// 用户群组卡片 diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserGroupStore.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserGroupStore.cs similarity index 86% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserGroupStore.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserGroupStore.cs index 1a7f935b9..af94512e6 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Chat/UserGroupStore.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Group/UserGroupStore.cs @@ -6,7 +6,7 @@ using Volo.Abp.Domain.Services; using Volo.Abp.ObjectMapping; using Volo.Abp.Uow; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { public class UserGroupStore : DomainService, IUserGroupStore { @@ -24,8 +24,16 @@ namespace LINGYUN.Abp.MessageService.Chat UserChatGroupRepository = userChatGroupRepository; } + public virtual async Task MemberHasInGroupAsync(Guid? tenantId, long groupId, Guid userId) + { + using (CurrentTenant.Change(tenantId)) + { + return await UserChatGroupRepository.MemberHasInGroupAsync(groupId, userId); + } + } + [UnitOfWork] - public async Task AddUserToGroupAsync(Guid? tenantId, Guid userId, long groupId, Guid acceptUserId) + public virtual async Task AddUserToGroupAsync(Guid? tenantId, Guid userId, long groupId, Guid acceptUserId) { using (var unitOfWork = UnitOfWorkManager.Begin()) { @@ -62,7 +70,7 @@ namespace LINGYUN.Abp.MessageService.Chat } } - public async Task> GetUserGroupsAsync(Guid? tenantId, Guid userId) + public async Task> GetUserGroupsAsync(Guid? tenantId, Guid userId) { using (CurrentTenant.Change(tenantId)) { diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/en.json b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/en.json index 5ff282bdb..3bb80c584 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/en.json +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/en.json @@ -1,13 +1,23 @@ { "culture": "en", "texts": { - "Messages.Group:1001": "The current group is not allowed to speak", - "Messages.Group:1002": "The current group is not allowed to speak anonymously", - "Messages.Group:1003": "The administrator has banned you from speaking!", - "Messages.User:1001": "Users do not receive anonymous comments!", - "Messages.User:1002": "The user has rejected all messages!", - "Messages.User:1003": "The user rejects the message you sent!", - "Messages.UserFriend:1001": "You have added each other as friends, can not be repeated!", + "LINGYUN.Abp.Message:01400": "Sending the message failed: The message is incomplete!", + "LINGYUN.Abp.Message:01401": "You have not joined the group and cannot operate!", + "LINGYUN.Abp.Message:02301": "Group application has been sent, waiting for administrator's approval", + "LINGYUN.Abp.Message:02302": "You need to validate the questions to join the group chat", + "LINGYUN.Abp.Message:02400": "The administrator has turned on silence mode!", + "LINGYUN.Abp.Message:02403": "The administrator has banned you from speaking!", + "LINGYUN.Abp.Message:02401": "The administrator does not allow anonymous speaking!", + "LINGYUN.Abp.Message:02404": "Sending the message failed: the group does not exist or is disbanded!", + "LINGYUN.Abp.Message:03301": "Friend request has been sent, waiting for the other party's approval", + "LINGYUN.Abp.Message:03302": "You need to verify the problem to add friends", + "LINGYUN.Abp.Message:03400": "The user has rejected all messages!", + "LINGYUN.Abp.Message:03401": "The user rejects the message you sent!", + "LINGYUN.Abp.Message:03402": "Users do not receive anonymous comments!", + "LINGYUN.Abp.Message:03403": "Sending the message failed: the person needs to agree to add a friend!", + "LINGYUN.Abp.Message:03404": "Sending the message failed: the user does not exist or is deactivated!", + "LINGYUN.Abp.Message:03410": "Users refuse to add friends", + "LINGYUN.Abp.Message:03411": "The other party is already your friend or has sent an authentication request. The operation cannot be repeated!", "WelcomeToApplicationNotification": "User Welcome Notice", "NewTenantRegisterdNotification": "Tenants create notification", "WelcomeToApplicationFormUser": "User :{0} welcome to join us!" diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/zh-Hans.json b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/zh-Hans.json index 76b653478..b6f29bbcb 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Localization/Resources/zh-Hans.json @@ -1,13 +1,23 @@ { "culture": "zh-Hans", "texts": { - "Messages.Group:1001": "管理员已开启全员禁言!", - "Messages.Group:1002": "管理员不允许匿名发言!", - "Messages.Group:1003": "管理员已禁止您发言!", - "Messages.User:1001": "用户不接收匿名发言!", - "Messages.User:1002": "用户已拒接所有消息!", - "Messages.User:1003": "用户拒绝您发送的消息!", - "Messages.UserFriend:1001": "您已经添加对方为好友,不能重复添加!", + "LINGYUN.Abp.Message:01400": "发送消息失败: 消息不完整!", + "LINGYUN.Abp.Message:01401": "您还未加入群组,不能进行操作!", + "LINGYUN.Abp.Message:02301": "已发送群组申请,等待管理员同意", + "LINGYUN.Abp.Message:02302": "你需要验证问题才能加入群聊", + "LINGYUN.Abp.Message:02400": "管理员已开启全员禁言!", + "LINGYUN.Abp.Message:02403": "管理员已禁止您发言!", + "LINGYUN.Abp.Message:02401": "管理员不允许匿名发言!", + "LINGYUN.Abp.Message:02404": "发送消息失败: 群组不存在或已解散!", + "LINGYUN.Abp.Message:03301": "已发送好友申请,等待对方同意", + "LINGYUN.Abp.Message:03302": "你需要验证问题才能添加好友", + "LINGYUN.Abp.Message:03400": "用户已拒接所有消息!", + "LINGYUN.Abp.Message:03401": "用户拒绝您发送的消息!", + "LINGYUN.Abp.Message:03402": "用户不接收匿名发言!", + "LINGYUN.Abp.Message:03403": "需要对方同意添加好友才能发送消息!", + "LINGYUN.Abp.Message:03404": "发送消息失败: 用户不存在或已注销账号!", + "LINGYUN.Abp.Message:03410": "用户拒绝添加好友", + "LINGYUN.Abp.Message:03411": "对方已是您的好友或已发送验证请求,不能重复操作!", "WelcomeToApplicationNotification": "用户欢迎通知", "NewTenantRegisterdNotification": "租户创建通知", "WelcomeToApplicationFormUser": "用户:{0} 欢迎您的加入!" diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreMessageRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreMessageRepository.cs index f57f907fa..1a8a69d3b 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreMessageRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreMessageRepository.cs @@ -1,5 +1,6 @@ using LINGYUN.Abp.IM.Messages; using LINGYUN.Abp.MessageService.EntityFrameworkCore; +using LINGYUN.Abp.MessageService.Group; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; @@ -161,10 +162,11 @@ namespace LINGYUN.Abp.MessageService.Chat var groupMsgQuery = DbContext.Set() .Where(msg => msg.ReceiveUserId == userId || msg.CreatorId == userId) - .GroupBy(msg => msg.CreatorId) + .GroupBy(msg => new { msg.CreatorId, msg.ReceiveUserId }) .Select(msg => new { - msg.Key, + msg.Key.CreatorId, + msg.Key.ReceiveUserId, MessageId = msg.Max(x => x.MessageId) }); diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatFriendRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatFriendRepository.cs index b3da8b1f7..4a7dabe34 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatFriendRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatFriendRepository.cs @@ -23,7 +23,7 @@ namespace LINGYUN.Abp.MessageService.Chat public virtual async Task FindByUserFriendIdAsync(Guid userId, Guid friendId, CancellationToken cancellationToken = default) { return await DbSet - .Where(ucf => ucf.UserId == userId && ucf.FrientId == friendId) + .Where(ucf => ucf.UserId == userId && ucf.FrientId == friendId && ucf.Status == UserFriendStatus.Added) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } @@ -34,10 +34,12 @@ namespace LINGYUN.Abp.MessageService.Chat CancellationToken cancellationToken = default) { sorting = reverse ? sorting + " DESC" : sorting; + var userFriendQuery = from ucf in DbContext.Set() join ucc in DbContext.Set() - on ucf.FrientId equals ucc.UserId - where ucf.UserId == userId + // on ucf.FrientId equals ucc.UserId // 查询双向好友的 + on ucf.UserId equals ucc.UserId + where ucf.UserId == userId && ucf.Status == UserFriendStatus.Added select new UserFriend { Age = ucc.Age, @@ -67,7 +69,7 @@ namespace LINGYUN.Abp.MessageService.Chat var userFriendQuery = from ucf in DbContext.Set() join ucc in DbContext.Set() on ucf.FrientId equals ucc.UserId - where ucf.UserId == userId && ucf.FrientId == friendId + where ucf.UserId == userId && ucf.FrientId == friendId && ucf.Status == UserFriendStatus.Added select new UserFriend { Age = ucc.Age, @@ -100,6 +102,7 @@ namespace LINGYUN.Abp.MessageService.Chat // 过滤好友资料 var userChatFriendQuery = DbContext.Set() + .Where(ucf => ucf.Status == UserFriendStatus.Added) .WhereIf(!filter.IsNullOrWhiteSpace(), ucf => ucf.RemarkName.Contains(filter)); // 组合查询 @@ -146,7 +149,7 @@ namespace LINGYUN.Abp.MessageService.Chat on ucf.FrientId equals ucc.UserId join um in userReceiveMsgQuery on ucc.UserId equals um.CreatorId - where ucf.UserId == userId + where ucf.UserId == userId && ucf.Status == UserFriendStatus.Added orderby um.CreationTime descending // 消息创建时间倒序 select new UserFriend { @@ -178,6 +181,7 @@ namespace LINGYUN.Abp.MessageService.Chat .WhereIf(!filter.IsNullOrWhiteSpace(), ucc => ucc.UserName.Contains(filter) || ucc.NickName.Contains(filter)); var userChatFriendQuery = DbContext.Set() + .Where(ucf => ucf.Status == UserFriendStatus.Added) .WhereIf(!filter.IsNullOrWhiteSpace(), ucf => ucf.RemarkName.Contains(filter)); var userFriendQuery = from ucf in userChatFriendQuery diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatGroupRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatGroupRepository.cs deleted file mode 100644 index a234e3058..000000000 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatGroupRepository.cs +++ /dev/null @@ -1,305 +0,0 @@ -using LINGYUN.Abp.IM.Group; -using LINGYUN.Abp.MessageService.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Dynamic.Core; -using System.Threading; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Domain.Repositories.EntityFrameworkCore; -using Volo.Abp.EntityFrameworkCore; - -namespace LINGYUN.Abp.MessageService.Chat -{ - public class EfCoreUserChatGroupRepository : EfCoreRepository, - IUserChatGroupRepository, ITransientDependency - { - public EfCoreUserChatGroupRepository( - IDbContextProvider dbContextProvider) : base(dbContextProvider) - { - } - - public virtual async Task GetMemberAsync( - long groupId, - Guid userId, - CancellationToken cancellationToken = default) - { - var cardQuery = from gp in DbContext.Set() - join ucg in DbContext.Set() - on gp.GroupId equals ucg.GroupId - join ugc in DbContext.Set() - on ucg.UserId equals ugc.UserId - join uc in DbContext.Set() - on ugc.UserId equals uc.UserId - where gp.GroupId == groupId && ugc.UserId == userId - select new GroupUserCard - { - TenantId = uc.TenantId, - UserId = uc.UserId, - UserName = uc.UserName, - Age = uc.Age, - AvatarUrl = uc.AvatarUrl, - IsAdmin = ugc.IsAdmin, - IsSuperAdmin = gp.AdminUserId == uc.UserId, - GroupId = gp.GroupId, - Birthday = uc.Birthday, - Description = uc.Description, - NickName = ugc.NickName ?? uc.NickName, - Sex = uc.Sex, - Sign = uc.Sign - }; - - return await cardQuery - .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); - } - - public virtual async Task> GetMembersAsync( - long groupId, - string sorting = nameof(UserChatCard.UserId), - bool reverse = false, - int skipCount = 0, - int maxResultCount = 10, - CancellationToken cancellationToken = default) - { - sorting ??= nameof(UserChatCard.UserId); - sorting = reverse ? sorting + " desc" : sorting; - var cardQuery = from gp in DbContext.Set() - join ucg in DbContext.Set() - on gp.GroupId equals ucg.GroupId - join ugc in DbContext.Set() - on ucg.UserId equals ugc.UserId - join uc in DbContext.Set() - on ugc.UserId equals uc.UserId - where gp.GroupId == groupId - select new GroupUserCard - { - TenantId = uc.TenantId, - UserId = uc.UserId, - UserName = uc.UserName, - Age = uc.Age, - AvatarUrl = uc.AvatarUrl, - IsAdmin = ugc.IsAdmin, - IsSuperAdmin = gp.AdminUserId == uc.UserId, - GroupId = gp.GroupId, - Birthday = uc.Birthday, - Description = uc.Description, - NickName = ugc.NickName ?? uc.NickName, - Sex = uc.Sex, - Sign = uc.Sign - }; - - return await cardQuery - .OrderBy(sorting ?? nameof(UserChatCard.UserId)) - .PageBy(skipCount, maxResultCount) - .ToListAsync(GetCancellationToken(cancellationToken)); - } - - public virtual async Task GetMembersCountAsync( - long groupId, - CancellationToken cancellationToken = default) - { - var cardQuery = from gp in DbContext.Set() - join ucg in DbContext.Set() - on gp.GroupId equals ucg.GroupId - join ugc in DbContext.Set() - on ucg.UserId equals ugc.UserId - join uc in DbContext.Set() - on ugc.UserId equals uc.UserId - where gp.GroupId == groupId - select ucg; - - return await cardQuery - .CountAsync(GetCancellationToken(cancellationToken)); - } - - public virtual async Task MemberHasInGroupAsync( - long groupId, - Guid userId, - CancellationToken cancellationToken = default) - { - return await DbContext.Set() - .AnyAsync(ucg => ucg.GroupId == groupId && ucg.UserId == userId, - GetCancellationToken(cancellationToken)); - } - - public virtual async Task> GetMemberGroupsAsync( - Guid userId, - CancellationToken cancellationToken = default) - { - var groupQuery = from gp in DbContext.Set() - join ucg in DbContext.Set() - on gp.GroupId equals ucg.GroupId - where ucg.UserId.Equals(userId) - group ucg by new - { - gp.AllowAnonymous, - gp.AllowSendMessage, - gp.MaxUserCount, - gp.Name - } - into cg - select new Group - { - AllowAnonymous = cg.Key.AllowAnonymous, - AllowSendMessage = cg.Key.AllowSendMessage, - MaxUserLength = cg.Key.MaxUserCount, - Name = cg.Key.Name, - GroupUserCount = cg.Count() - }; - - return await groupQuery - .ToListAsync(GetCancellationToken(cancellationToken)); - } - - public virtual async Task RemoveMemberFormGroupAsync( - long groupId, - Guid userId, - CancellationToken cancellationToken = default) - { - await DeleteAsync(ucg => ucg.GroupId == groupId && ucg.UserId == userId); - } - - //public virtual async Task> GetGroupUsersAsync( - // long groupId, - // CancellationToken cancellationToken = default) - //{ - // // TODO: 急需单元测试,对这段代码不是太自信... - // var groupUsers = await (from cg in DbContext.Set() - // join ucg in DbContext.Set() - // on cg.GroupId equals ucg.GroupId - // join ugc in DbContext.Set() - // on ucg.UserId equals ugc.UserId - // where cg.GroupId.Equals(groupId) - // select new GroupUserCard - // { - // GroupId = ucg.GroupId, - // IsSuperAdmin = ugc.UserId == cg.AdminUserId, - // IsAdmin = ugc.IsAdmin, - // TenantId = ucg.TenantId, - // UserId = ucg.UserId - // }) - // .Distinct() - // .AsNoTracking() - // .ToListAsync(GetCancellationToken(cancellationToken)); - // return groupUsers; - //} - - //public virtual async Task GetMemberAsync( - // long groupId, - // Guid userId, - // CancellationToken cancellationToken = default) - //{ - // var groupUserCard = await (from cg in DbContext.Set() - // join ucg in DbContext.Set().DefaultIfEmpty() - // on cg.GroupId equals ucg.GroupId - // join ucc in DbContext.Set().DefaultIfEmpty() - // on ucg.UserId equals ucc.UserId - // join cga in DbContext.Set().DefaultIfEmpty() - // on cg.GroupId equals cga.GroupId - // where ucg.GroupId.Equals(groupId) && cga.UserId.Equals(userId) - // select new GroupUserCard - // { - // IsSuperAdmin = cga != null && cga.IsSuperAdmin, - // IsAdmin = cga != null,//能查到数据就是管理员 - // GroupId = ucg.GroupId, - // UserId = ucg.UserId, - // TenantId = ucg.TenantId, - // Permissions = new Dictionary - // { - // { nameof(ChatGroupAdmin.AllowAddPeople), cga != null && cga.AllowAddPeople }, - // { nameof(ChatGroupAdmin.AllowDissolveGroup), cga != null && cga.AllowDissolveGroup }, - // { nameof(ChatGroupAdmin.AllowKickPeople), cga != null && cga.AllowKickPeople }, - // { nameof(ChatGroupAdmin.AllowSendNotice), cga != null && cga.AllowSendNotice }, - // { nameof(ChatGroupAdmin.AllowSilence), cga != null && cga.AllowSilence }, - // { nameof(ChatGroupAdmin.IsSuperAdmin), cga != null && cga.IsSuperAdmin } - // } - // }) - // .AsNoTracking() - // .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); - - // return groupUserCard; - //} - - //public virtual Task> GetUsersAsync( - // long groupId, - // string filter = "", - // string sorting = nameof(UserGroup.UserId), - // bool reverse = false, - // int skipCount = 0, - // int maxResultCount = 10, - // CancellationToken cancellationToken = default) - //{ - // sorting = reverse ? sorting + " desc" : sorting; - // // TODO: 复杂的实现,暂时无关紧要,后期再说 :) - // throw new NotImplementedException(); - //} - - //public virtual Task GetMembersCountAsync( - // long groupId, - // string filter = "", - // CancellationToken cancellationToken = default) - //{ - // var ss = (from ucg in DbContext.Set() - // join cg in DbContext.Set() on ucg.GroupId equals cg.GroupId - // select cg) - // .WhereIf(!filter.IsNullOrWhiteSpace(),) - - // // TODO: 复杂的实现,暂时无关紧要,后期再说 :) - // //throw new NotImplementedException(); - //} - - //public virtual async Task GetUserGroupAsync( - // long groupId, - // Guid userId, - // CancellationToken cancellationToken = default) - //{ - // return await DbSet.Where(x => x.GroupId.Equals(groupId) && x.UserId.Equals(userId)) - // .AsNoTracking() - // .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); - //} - - //public virtual async Task> GetUserGroupsAsync( - // Guid userId, - // CancellationToken cancellationToken = default) - //{ - // // TODO: 急需单元测试,对这段代码不是太自信... - // var userGroups = await (from ucg in DbSet - // join cg in DbContext.Set() - // on ucg.GroupId equals cg.GroupId - // group cg by new - // { - // cg.GroupId, - // cg.Name, - // cg.AllowAnonymous, - // cg.AllowSendMessage, - // cg.MaxUserCount - // } - // into ug - // orderby ug.Key.GroupId descending - // select new Group - // { - // AllowAnonymous = ug.Key.AllowAnonymous, - // AllowSendMessage = ug.Key.AllowSendMessage, - // GroupUserCount = ug.Count(), - // MaxUserLength = ug.Key.MaxUserCount, - // Name = ug.Key.Name - // }) - // .Distinct() - // .ToListAsync(GetCancellationToken(cancellationToken)); - - // return userGroups; - //} - - //public virtual async Task MemberHasInGroupAsync( - // long groupId, - // Guid userId, - // CancellationToken cancellationToken = default) - //{ - // return await DbSet - // .AnyAsync(x => x.GroupId.Equals(groupId) && x.UserId.Equals(userId), GetCancellationToken(cancellationToken)); - //} - } -} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatSettingRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatSettingRepository.cs index d2784e0af..69b23a8fd 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatSettingRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreUserChatSettingRepository.cs @@ -18,19 +18,13 @@ namespace LINGYUN.Abp.MessageService.Chat { } - public async Task GetByUserIdAsync(Guid userId) + public async Task FindByUserIdAsync(Guid userId) { return await DbSet.Where(x => x.UserId.Equals(userId)) .AsNoTracking() .FirstOrDefaultAsync(); } - public async Task UserHasBlackedAsync(Guid formUserId, Guid toUserId) - { - return await DbContext.Set() - .AnyAsync(x => x.UserId.Equals(toUserId) && x.ShieldUserId.Equals(formUserId)); - } - public async Task UserHasOpendImAsync(Guid userId) { return await DbSet.AnyAsync(x => x.UserId.Equals(userId)); diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/EntityFrameworkCore/MessageServiceDbContextModelCreatingExtensions.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/EntityFrameworkCore/MessageServiceDbContextModelCreatingExtensions.cs index c3307fff8..8fa22cf9b 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/EntityFrameworkCore/MessageServiceDbContextModelCreatingExtensions.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/EntityFrameworkCore/MessageServiceDbContextModelCreatingExtensions.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.MessageService.Chat; +using LINGYUN.Abp.MessageService.Group; using LINGYUN.Abp.MessageService.Notifications; using LINGYUN.Abp.MessageService.Subscriptions; using Microsoft.EntityFrameworkCore; @@ -137,23 +138,23 @@ namespace LINGYUN.Abp.MessageService.EntityFrameworkCore b.HasIndex(p => new { p.TenantId, p.UserId }); }); - builder.Entity(b => - { - b.ToTable(options.TablePrefix + "UserSpecialFocuss", options.Schema); + //builder.Entity(b => + //{ + // b.ToTable(options.TablePrefix + "UserSpecialFocuss", options.Schema); - b.ConfigureMultiTenant(); + // b.ConfigureMultiTenant(); - b.HasIndex(p => new { p.TenantId, p.UserId }); - }); + // b.HasIndex(p => new { p.TenantId, p.UserId }); + //}); - builder.Entity(b => - { - b.ToTable(options.TablePrefix + "UserChatBlacks", options.Schema); + //builder.Entity(b => + //{ + // b.ToTable(options.TablePrefix + "UserChatBlacks", options.Schema); - b.ConfigureMultiTenant(); + // b.ConfigureMultiTenant(); - b.HasIndex(p => new { p.TenantId, p.UserId }); - }); + // b.HasIndex(p => new { p.TenantId, p.UserId }); + //}); builder.Entity(b => { diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreGroupRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Group/EfCoreGroupRepository.cs similarity index 95% rename from aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreGroupRepository.cs rename to aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Group/EfCoreGroupRepository.cs index 5d5bac6b8..0a3a5ec85 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreGroupRepository.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Group/EfCoreGroupRepository.cs @@ -9,7 +9,7 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; -namespace LINGYUN.Abp.MessageService.Chat +namespace LINGYUN.Abp.MessageService.Group { public class EfCoreGroupRepository : EfCoreRepository, IGroupRepository, ITransientDependency @@ -20,7 +20,7 @@ namespace LINGYUN.Abp.MessageService.Chat { } - public virtual async Task GetByIdAsync( + public virtual async Task FindByIdAsync( long id, CancellationToken cancellationToken = default) { diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Group/EfCoreUserChatGroupRepository.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Group/EfCoreUserChatGroupRepository.cs new file mode 100644 index 000000000..6b274be28 --- /dev/null +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Group/EfCoreUserChatGroupRepository.cs @@ -0,0 +1,166 @@ +using LINGYUN.Abp.IM.Group; +using LINGYUN.Abp.MessageService.Chat; +using LINGYUN.Abp.MessageService.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.MessageService.Group +{ + public class EfCoreUserChatGroupRepository : EfCoreRepository, + IUserChatGroupRepository, ITransientDependency + { + public EfCoreUserChatGroupRepository( + IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public virtual async Task GetMemberAsync( + long groupId, + Guid userId, + CancellationToken cancellationToken = default) + { + var cardQuery = from gp in DbContext.Set() + join ucg in DbContext.Set() + on gp.GroupId equals ucg.GroupId + join ugc in DbContext.Set() + on ucg.UserId equals ugc.UserId + join uc in DbContext.Set() + on ugc.UserId equals uc.UserId + where gp.GroupId == groupId && ugc.UserId == userId + select new GroupUserCard + { + TenantId = uc.TenantId, + UserId = uc.UserId, + UserName = uc.UserName, + Age = uc.Age, + AvatarUrl = uc.AvatarUrl, + IsAdmin = ugc.IsAdmin, + IsSuperAdmin = gp.AdminUserId == uc.UserId, + GroupId = gp.GroupId, + Birthday = uc.Birthday, + Description = uc.Description, + NickName = ugc.NickName ?? uc.NickName, + Sex = uc.Sex, + Sign = uc.Sign + }; + + return await cardQuery + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetMembersAsync( + long groupId, + string sorting = nameof(UserChatCard.UserId), + bool reverse = false, + int skipCount = 0, + int maxResultCount = 10, + CancellationToken cancellationToken = default) + { + sorting ??= nameof(UserChatCard.UserId); + sorting = reverse ? sorting + " desc" : sorting; + var cardQuery = from gp in DbContext.Set() + join ucg in DbContext.Set() + on gp.GroupId equals ucg.GroupId + join ugc in DbContext.Set() + on ucg.UserId equals ugc.UserId + join uc in DbContext.Set() + on ugc.UserId equals uc.UserId + where gp.GroupId == groupId + select new GroupUserCard + { + TenantId = uc.TenantId, + UserId = uc.UserId, + UserName = uc.UserName, + Age = uc.Age, + AvatarUrl = uc.AvatarUrl, + IsAdmin = ugc.IsAdmin, + IsSuperAdmin = gp.AdminUserId == uc.UserId, + GroupId = gp.GroupId, + Birthday = uc.Birthday, + Description = uc.Description, + NickName = ugc.NickName ?? uc.NickName, + Sex = uc.Sex, + Sign = uc.Sign + }; + + return await cardQuery + .OrderBy(sorting ?? nameof(UserChatCard.UserId)) + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task GetMembersCountAsync( + long groupId, + CancellationToken cancellationToken = default) + { + var cardQuery = from gp in DbContext.Set() + join ucg in DbContext.Set() + on gp.GroupId equals ucg.GroupId + join ugc in DbContext.Set() + on ucg.UserId equals ugc.UserId + join uc in DbContext.Set() + on ugc.UserId equals uc.UserId + where gp.GroupId == groupId + select ucg; + + return await cardQuery + .CountAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task MemberHasInGroupAsync( + long groupId, + Guid userId, + CancellationToken cancellationToken = default) + { + return await DbContext.Set() + .AnyAsync(ucg => ucg.GroupId == groupId && ucg.UserId == userId, + GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetMemberGroupsAsync( + Guid userId, + CancellationToken cancellationToken = default) + { + var groupQuery = from gp in DbContext.Set() + join ucg in DbContext.Set() + on gp.GroupId equals ucg.GroupId + where ucg.UserId.Equals(userId) + group ucg by new + { + gp.AllowAnonymous, + gp.AllowSendMessage, + gp.MaxUserCount, + gp.Name + } + into cg + select new IM.Group.Group + { + AllowAnonymous = cg.Key.AllowAnonymous, + AllowSendMessage = cg.Key.AllowSendMessage, + MaxUserLength = cg.Key.MaxUserCount, + Name = cg.Key.Name, + GroupUserCount = cg.Count() + }; + + return await groupQuery + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task RemoveMemberFormGroupAsync( + long groupId, + Guid userId, + CancellationToken cancellationToken = default) + { + await DeleteAsync(ucg => ucg.GroupId == groupId && ucg.UserId == userId); + } + } +} diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN/Abp/MessageService/Chat/ChatController.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN/Abp/MessageService/Chat/ChatController.cs index 24c790083..75be01a94 100644 --- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN/Abp/MessageService/Chat/ChatController.cs +++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN/Abp/MessageService/Chat/ChatController.cs @@ -1,5 +1,4 @@ -using LINGYUN.Abp.IM.Group; -using LINGYUN.Abp.IM.Messages; +using LINGYUN.Abp.IM.Messages; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Volo.Abp; @@ -19,25 +18,11 @@ namespace LINGYUN.Abp.MessageService.Chat _chatAppService = chatAppService; } - [HttpPost] - [Route("groups/join")] - public virtual async Task ApplyJoinGroupAsync(UserJoinGroupDto input) - { - await _chatAppService.ApplyJoinGroupAsync(input); - } - [HttpGet] [Route("group/messages")] - public virtual async Task> GetGroupMessageAsync(GroupMessageGetByPagedDto input) + public virtual async Task> GetMyGroupMessageAsync(GroupMessageGetByPagedDto input) { - return await _chatAppService.GetGroupMessageAsync(input); - } - - [HttpGet] - [Route("groups/users")] - public virtual async Task> GetGroupUsersAsync(GroupUserGetByPagedDto input) - { - return await _chatAppService.GetGroupUsersAsync(input); + return await _chatAppService.GetMyGroupMessageAsync(input); } [HttpGet] @@ -54,27 +39,6 @@ namespace LINGYUN.Abp.MessageService.Chat return await _chatAppService.GetMyLastChatMessageAsync(input); } - [HttpGet] - [Route("groups/me")] - public virtual async Task> GetMyGroupsAsync() - { - return await _chatAppService.GetMyGroupsAsync(); - } - - [HttpPost] - [Route("groups/users/accept")] - public virtual async Task GroupAcceptUserAsync(GroupAcceptUserDto input) - { - await _chatAppService.GroupAcceptUserAsync(input); - } - - [HttpDelete] - [Route("groups/users/remove")] - public virtual async Task GroupRemoveUserAsync(GroupRemoveUserDto input) - { - await _chatAppService.GroupRemoveUserAsync(input); - } - [HttpGet] [Route("send-message")] public virtual async Task SendMessageAsync(ChatMessage input) diff --git a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db index 40ba6aaa2..e31aebbf5 100644 Binary files a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db and b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db differ diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests.csproj index 46deef7a3..8823d7b76 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests.csproj +++ b/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests/LINGYUN.Abp.Features.LimitValidation.Redis.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + netcoreapp3.1 false diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Tests/LINGYUN.Abp.Features.LimitValidation.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Tests/LINGYUN.Abp.Features.LimitValidation.Tests.csproj index 3b0dad961..bce9ca44e 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Tests/LINGYUN.Abp.Features.LimitValidation.Tests.csproj +++ b/aspnet-core/tests/LINGYUN.Abp.Features.LimitValidation.Tests/LINGYUN.Abp.Features.LimitValidation.Tests.csproj @@ -1,7 +1,7 @@ - net5.0 + netcoreapp3.1 false diff --git a/vueJs/src/components/InstantMessage/index.vue b/vueJs/src/components/InstantMessage/index.vue index 64f2568fa..e68c4f149 100644 --- a/vueJs/src/components/InstantMessage/index.vue +++ b/vueJs/src/components/InstantMessage/index.vue @@ -39,7 +39,7 @@ import { User } from '@/api/users' class MyContract { id = '' displayName = '' - avatar = '' + avatar = 'http://upload.qqbodys.com/allimg/1710/1035512943-0.jpg' type = '' index = 'A' unread = 0 @@ -92,10 +92,20 @@ class Message { this.content = chatMessage.content this.fromUser.id = chatMessage.formUserId this.fromUser.displayName = chatMessage.formUserName + this.fromUser.avatar = 'http://upload.qqbodys.com/allimg/1710/1035512943-0.jpg' this.toContactId = chatMessage.toUserId this.type = ChatMessage.getType(chatMessage.messageType) this.sendTime = new Date(chatMessage.sendTime).getTime() } + + public toChatMessage() { + const chatMessage = new ChatMessage() + chatMessage.formUserId = this.fromUser.id + chatMessage.formUserName = this.fromUser.displayName + chatMessage.toUserId = this.toContactId + chatMessage.content = this.content + return chatMessage + } } @Component({ @@ -129,6 +139,7 @@ export default class InstantMessage extends mixins(EventBusMiXin) { this.unSubscribeAll() this.subscribe('onShowImDialog', this.onShowImDialog) this.subscribe('onUserFriendAdded', this.onUserFriendAdded) + this.subscribe('onReceivedChatMessage', this.onReceivedChatMessage) this.handleInitDefaultMenus() this.handleStartConnection() } @@ -270,8 +281,7 @@ export default class InstantMessage extends mixins(EventBusMiXin) { next(messages, isEnd) }) .catch(() => { - next(new Array(), true) - imui.messageViewToBottom() + next([], true) }) } @@ -279,38 +289,41 @@ export default class InstantMessage extends mixins(EventBusMiXin) { console.log('Event:menu-avatar-click') } - private handleMessageClick(e: any, key: any, message: any) { + private handleMessageClick(e: any, key: any, message: Message) { console.log(e) console.log(key) console.log(message) - } - - private handleReceiveMessage(chatMessage: ChatMessage) { - const message = new Message() - message.fromChatMessage(chatMessage) const imui = this.$refs.IMUI as any - imui.appendMessage(message, chatMessage.formUserId) - const currentContact = imui.currentContact - if (currentContact && currentContact.id === chatMessage.formUserId) { - currentContact.lastContent = chatMessage.content - currentContact.lastSendTime = new Date(chatMessage.sendTime).getTime() - } else { - imui.updateContact(chatMessage.formUserId, { - unread: '+1', - lastSendTime: new Date(chatMessage.sendTime).getTime(), - lastContent: chatMessage.content + if (key === 'status') { + imui.updateMessage(message.id, message.toContactId, { + status: 'going' }) + const chatMessage = message.toChatMessage() + this.connection + .invoke('SendMessage', chatMessage) + .then(() => { + imui + .updateMessage(message.id, message.toContactId, { + status: 'succeed' + }) + }) + .catch(() => { + imui + .updateMessage(message.id, message.toContactId, { + status: 'failed' + }) + }) } } + private handleReceiveMessage(chatMessage: ChatMessage) { + this.trigger('onReceivedChatMessage', chatMessage) + } + private handleSendMessage(message: Message, next: any, file: any) { console.log(message, next, file) const imui = this.$refs.IMUI as any - const chatMessage = new ChatMessage() - chatMessage.formUserId = message.fromUser.id - chatMessage.formUserName = message.fromUser.displayName - chatMessage.toUserId = message.toContactId - chatMessage.content = message.content + const chatMessage = message.toChatMessage() this.connection .invoke('SendMessage', chatMessage) .then(() => { @@ -326,12 +339,32 @@ export default class InstantMessage extends mixins(EventBusMiXin) { }) } + private onReceivedChatMessage(chatMessage: ChatMessage) { + const message = new Message() + message.fromChatMessage(chatMessage) + const imui = this.$refs.IMUI as any + imui.appendMessage(message, chatMessage.formUserId) + const currentContact = imui.currentContact + if (currentContact && currentContact.id === chatMessage.formUserId) { + currentContact.lastContent = chatMessage.content + currentContact.lastSendTime = new Date(chatMessage.sendTime).getTime() + } else { + imui.updateContact(chatMessage.formUserId, { + unread: '+1', + lastSendTime: new Date(chatMessage.sendTime).getTime(), + lastContent: chatMessage.content + }) + } + } + private onChangeContract(contract: any) { const imui = this.$refs.IMUI as any imui.updateContact(contract.id, { unread: 0 }) imui.closeDrawer() + imui.forceUpdateMessage() + console.log(imui.contacts) } private onUserFriendAdded(user: User) { diff --git a/vueJs/src/components/Notification/index.vue b/vueJs/src/components/Notification/index.vue index 09339c7c9..73a217b4a 100644 --- a/vueJs/src/components/Notification/index.vue +++ b/vueJs/src/components/Notification/index.vue @@ -53,6 +53,9 @@ export default class extends mixins(EventBusMiXin) { this.subscribe('onNotificationReadChanged', () => { this.notificationCount -= 1 }) + this.subscribe('onReceivedChatMessage', () => { + this.notificationCount += 1 + }) } }