添加项目文件。
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
namespace IM.Commons
|
||||
{
|
||||
public class BaseEvent
|
||||
{
|
||||
// --- 标准元数据 ---
|
||||
public Guid EventId { get; init; } = Guid.NewGuid();
|
||||
public DateTime OccurredOn { get; init; } = DateTime.Now;
|
||||
public Guid CorrelationId { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public abstract class BaseSpecification<T, TResult> : ISpecification<T, TResult>
|
||||
{
|
||||
public Expression<Func<T, bool>> Criteria { get; private set; }
|
||||
|
||||
public List<Expression<Func<T, object>>> Includes { get; private set; }
|
||||
|
||||
|
||||
public Expression<Func<T, TResult>> Select { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public static class EnumHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取枚举的 Description 描述信息
|
||||
/// </summary>
|
||||
public static string GetDescription(this Enum value)
|
||||
{
|
||||
FieldInfo? field = value.GetType().GetField(value.ToString());
|
||||
if (field == null) return value.ToString();
|
||||
|
||||
DescriptionAttribute? attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
|
||||
|
||||
return attribute == null ? value.ToString() : attribute.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace IM.Commons
|
||||
{
|
||||
public class EventHandlerException : Exception
|
||||
{
|
||||
public EventHandlerException()
|
||||
{
|
||||
}
|
||||
|
||||
public EventHandlerException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public class GrpcOptions
|
||||
{
|
||||
public string MessageServiceUrl { get; set; }
|
||||
public string IdentityServiceUrl { get; set; }
|
||||
public string ContactServiceUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AsmResolver.DotNet" Version="5.5.1" />
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.12.14" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
/// <summary>
|
||||
/// 所有项目中的实现了IModuleInitializer接口都会被调用,请在Initialize中编写注册本模块需要的服务。
|
||||
/// 一个项目中可以放多个实现了IModuleInitializer的类。不过为了集中管理,还是建议一个项目中只放一个实现了IModuleInitializer的类
|
||||
/// </summary>
|
||||
public interface IModuleInitializer
|
||||
{
|
||||
public void Initialize(IServiceCollection services);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public interface IRedisService
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T">缓存对象类型</typeparam>
|
||||
/// <param name="key">缓存索引值</param>
|
||||
/// <param name="value">要缓存的对象</param>
|
||||
/// <param name="expiration">过期时间</param>
|
||||
/// <returns></returns>
|
||||
Task SetAsync<T>(string key, T value, TimeSpan? expiration = null);
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key">缓存索引</param>
|
||||
/// <returns></returns>
|
||||
Task<T?> GetAsync<T>(string key);
|
||||
/// <summary>
|
||||
/// 删除缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存索引</param>
|
||||
/// <returns></returns>
|
||||
Task RemoveAsync(string key);
|
||||
}
|
||||
|
||||
public static class RedisServiceExtension
|
||||
{
|
||||
public static IServiceCollection AddRedisCache(this IServiceCollection services)
|
||||
{
|
||||
return services.AddScoped<IRedisService, RedisCacheService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public interface ISpecification<T, TResut> : ISpecification<T>
|
||||
{
|
||||
public Expression<Func<T, TResut>> Select { get; }
|
||||
}
|
||||
public interface ISpecification<T>
|
||||
{
|
||||
Expression<Func<T, bool>> Criteria { get; }
|
||||
List<Expression<Func<T, object>>> Includes { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class FriendAddedEvent : BaseEvent
|
||||
{
|
||||
public Guid OwnerId { get; set; }
|
||||
public string OwnerNickName { get; set; }
|
||||
public string? OwnerAvatar { get; set; }
|
||||
public Guid TargetId { get; set; }
|
||||
public string TargetNickName { get; set; }
|
||||
public string? TargetAvatar { get; set; }
|
||||
public string? RemarkName { get; set; }
|
||||
public string Status { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class FriendRequestStateUpdateEvent : BaseEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 申请人
|
||||
/// </summary>
|
||||
public Guid OwnerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 被申请人
|
||||
/// </summary>
|
||||
public Guid TargetId { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 申请附言
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 申请状态(0:待通过,1:拒绝,2:同意,3:拉黑)
|
||||
/// </summary>
|
||||
public string State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string? RemarkName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class GroupBlockEvent : BaseEvent
|
||||
{
|
||||
public Guid GroupId { get; private set; }
|
||||
public string GroupName { get; private set; }
|
||||
public Guid GroupMaster { get; private set; }
|
||||
public string Status { get; private set; }
|
||||
public string? Avatar { get; private set; }
|
||||
|
||||
public GroupBlockEvent(Guid groupId, string groupName, Guid groupMaster, string status, string? avatar)
|
||||
{
|
||||
GroupId = groupId;
|
||||
GroupName = groupName;
|
||||
GroupMaster = groupMaster;
|
||||
Status = status;
|
||||
Avatar = avatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class GroupCreateEvent
|
||||
{
|
||||
public Guid GroupId { get; private set; }
|
||||
public string GroupName { get; private set; }
|
||||
public Guid GroupMaster { get; private set; }
|
||||
public string? Avatar { get; private set; }
|
||||
|
||||
public GroupCreateEvent(Guid groupId, string groupName, Guid groupMaster, string? avatar)
|
||||
{
|
||||
GroupId = groupId;
|
||||
GroupName = groupName;
|
||||
GroupMaster = groupMaster;
|
||||
Avatar = avatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class GroupInvitationAcceptEvent : GroupInvitationCreateEvent
|
||||
{
|
||||
public GroupInvitationAcceptEvent(Guid invitationId, Guid userId, string userNickName, string? userAvatar, Guid groupId, string groupName, string groupAvatar, Guid operatorId, string operatorName, string operatorAvatar) : base(invitationId, userId, userNickName, userAvatar, groupId, groupName, groupAvatar, operatorId, operatorName, operatorAvatar)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class GroupInvitationCreateEvent : BaseEvent
|
||||
{
|
||||
public Guid InvitationId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public string UserNickName { get; private set; }
|
||||
public string? UserAvatar { get; private set; }
|
||||
public Guid GroupId { get; private set; }
|
||||
public string GroupName { get; private set; }
|
||||
public string GroupAvatar { get; private set; }
|
||||
public Guid OperatorId { get; private set; }
|
||||
public string OperatorName { get; private set; }
|
||||
public string OperatorAvatar { get; private set; }
|
||||
|
||||
public GroupInvitationCreateEvent(Guid invitationId, Guid userId, string userNickName, string? userAvatar, Guid groupId, string groupName, string groupAvatar, Guid operatorId, string operatorName, string operatorAvatar)
|
||||
{
|
||||
InvitationId = invitationId;
|
||||
UserId = userId;
|
||||
UserNickName = userNickName;
|
||||
UserAvatar = userAvatar;
|
||||
GroupId = groupId;
|
||||
GroupName = groupName;
|
||||
GroupAvatar = groupAvatar;
|
||||
OperatorId = operatorId;
|
||||
OperatorName = operatorName;
|
||||
OperatorAvatar = operatorAvatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class GroupMemberJoinedEvent
|
||||
{
|
||||
public Guid Id { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public Guid GroupId { get; private set; }
|
||||
public string GroupNickName { get; private set; }
|
||||
public string? Avatar { get; private set; }
|
||||
public string Role { get; private set; }
|
||||
|
||||
public GroupMemberJoinedEvent(Guid id, Guid userId, Guid groupId, string groupNickName, string? avatar, string role)
|
||||
{
|
||||
Id = id;
|
||||
UserId = userId;
|
||||
GroupId = groupId;
|
||||
GroupNickName = groupNickName;
|
||||
Avatar = avatar;
|
||||
Role = role;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class GroupRequestDeclinedEvent : GroupRequestPassedEvent
|
||||
{
|
||||
public GroupRequestDeclinedEvent(Guid requestId, Guid groupId, string groupName, string groupAvatar, Guid userId, string userNickName, string? userAvatar, Guid operatorId, string operatorName, string operatorAvatar, string description, DateTimeOffset created, DateTimeOffset updated) : base(requestId, groupId, groupName, groupAvatar, userId, userNickName, userAvatar, operatorId, operatorName, operatorAvatar, description, created, updated)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class GroupRequestPassedEvent
|
||||
{
|
||||
public Guid RequestId { get; private set; }
|
||||
public Guid GroupId { get; private set; }
|
||||
public string GroupName { get; private set; }
|
||||
public string GroupAvatar { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public string UserNickName { get; private set; }
|
||||
public string? UserAvatar { get; private set; }
|
||||
public Guid OperatorId { get; private set; }
|
||||
public string OperatorName { get; private set; }
|
||||
public string OperatorAvatar { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public DateTimeOffset Created { get; private set; }
|
||||
public DateTimeOffset Updated { get; private set; }
|
||||
|
||||
public GroupRequestPassedEvent(Guid requestId, Guid groupId, string groupName, string groupAvatar, Guid userId, string userNickName, string? userAvatar, Guid operatorId, string operatorName, string operatorAvatar, string description, DateTimeOffset created, DateTimeOffset updated)
|
||||
{
|
||||
RequestId = requestId;
|
||||
GroupId = groupId;
|
||||
GroupName = groupName;
|
||||
GroupAvatar = groupAvatar;
|
||||
UserId = userId;
|
||||
UserNickName = userNickName;
|
||||
UserAvatar = userAvatar;
|
||||
OperatorId = operatorId;
|
||||
OperatorName = operatorName;
|
||||
OperatorAvatar = operatorAvatar;
|
||||
Description = description;
|
||||
Created = created;
|
||||
Updated = updated;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class MsgCreatedEvent
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ClientId { get; set; }
|
||||
public string ChatType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息类型
|
||||
/// (0:文本,1:图片,2:语音,3:视频,4:文件,5:语音聊天,6:视频聊天)
|
||||
/// </summary>
|
||||
public string MsgType { get; set; }
|
||||
/// <summary>
|
||||
/// 发送者
|
||||
/// </summary>
|
||||
public Guid SenderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 接收者(私聊为用户ID,群聊为群聊ID)
|
||||
/// </summary>
|
||||
public Guid TargetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息状态(0:已发送,1:已撤回)
|
||||
/// </summary>
|
||||
public string State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息推送唯一标识符
|
||||
/// </summary>
|
||||
public string StreamKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息排序标识
|
||||
/// </summary>
|
||||
|
||||
public long SequenceId { get; set; }
|
||||
public MsgContent Content { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public record MsgContent(string Fallback, object Body, Dictionary<string, string> Ext, QuoteInfoDto Quote);
|
||||
public record QuoteInfoDto(Guid MessageId, Guid SenderId, string SenderName, string MessageType, string Preview);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class MsgWithdrawEvent
|
||||
{
|
||||
public Guid MsgId { get; private set; }
|
||||
public string State { get; private set; }
|
||||
public string StreamKey { get; private set; }
|
||||
|
||||
public MsgWithdrawEvent(Guid msgId, string state, string streamKey)
|
||||
{
|
||||
MsgId = msgId;
|
||||
State = state;
|
||||
StreamKey = streamKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace IM.Commons.IntegrationEvents
|
||||
{
|
||||
public class UserProfileUpdateEvent : BaseEvent
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public string NickName { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string Status { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public class RedisCacheService : IRedisService
|
||||
{
|
||||
private readonly IDistributedCache _cache;
|
||||
public RedisCacheService(IDistributedCache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public async Task<T?> GetAsync<T>(string key)
|
||||
{
|
||||
var valueBytes = await _cache.GetAsync(key);
|
||||
if (valueBytes is null || valueBytes.Length == 0) return default;
|
||||
return JsonSerializer.Deserialize<T>(valueBytes);
|
||||
}
|
||||
|
||||
public async Task RemoveAsync(string key) => await _cache.RemoveAsync(key);
|
||||
|
||||
public async Task SetAsync<T>(string key, T value, TimeSpan? expiration = null)
|
||||
{
|
||||
var options = new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = expiration ?? TimeSpan.FromHours(1)
|
||||
};
|
||||
var valueBytes = JsonSerializer.SerializeToUtf8Bytes(value);
|
||||
await _cache.SetAsync(key, valueBytes, options);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace IM.Commons
|
||||
{
|
||||
public static class RedisHelper
|
||||
{
|
||||
public static string GetRefreshTokenKey(string token) => $"sys:refreshtoken:{token}";
|
||||
public static string GetUserinfoKey(string userId) => $"user:uinfo:{userId}";
|
||||
public static string GetUserinfoKeyByUsername(string username) => $"user:uinfobyid:{username}";
|
||||
public static string GetSequenceIdKey(string streamKey) => $"chat:seq:{streamKey}";
|
||||
public static string GetSequenceIdLockKey(string streamKey) => $"lock:seq:{streamKey}";
|
||||
public static string GetConnectionIdKey(string userId) => $"signalr:user:con:{userId}";
|
||||
|
||||
public static string GetUploadPartKey(Guid taskId) => $"upload:task:{taskId}:parts";
|
||||
public static string MergeStatus(Guid taskId) => $"upload:task:{taskId}:merge";
|
||||
public static string GetUploadInfoKey(string sessionId) => $"upload:task:{sessionId}:info";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public static class ReflectionHelper
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 据产品名称获取程序集
|
||||
/// </summary>
|
||||
/// <param name="productName"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Assembly> GetAssembliesByProductName(string productName)
|
||||
{
|
||||
var asms = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var asm in asms)
|
||||
{
|
||||
var asmCompanyAttr = asm.GetCustomAttribute<AssemblyProductAttribute>();
|
||||
if (asmCompanyAttr != null && asmCompanyAttr.Product == productName)
|
||||
{
|
||||
yield return asm;
|
||||
}
|
||||
}
|
||||
}
|
||||
//是否是微软等的官方Assembly
|
||||
private static bool IsSystemAssembly(Assembly asm)
|
||||
{
|
||||
var asmCompanyAttr = asm.GetCustomAttribute<AssemblyCompanyAttribute>();
|
||||
if (asmCompanyAttr == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
string companyName = asmCompanyAttr.Company;
|
||||
return companyName.Contains("Microsoft");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSystemAssembly(string asmPath)
|
||||
{
|
||||
var moduleDef = AsmResolver.DotNet.ModuleDefinition.FromFile(asmPath);
|
||||
var assembly = moduleDef.Assembly;
|
||||
if (assembly == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var asmCompanyAttr = assembly.CustomAttributes.FirstOrDefault(c => c.Constructor?.DeclaringType?.FullName == typeof(AssemblyCompanyAttribute).FullName);
|
||||
if (asmCompanyAttr == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var companyName = ((AsmResolver.Utf8String?)asmCompanyAttr.Signature?.FixedArguments[0]?.Element)?.Value;
|
||||
if (companyName == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return companyName.Contains("Microsoft");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断file这个文件是否是程序集
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsManagedAssembly(string file)
|
||||
{
|
||||
using var fs = File.OpenRead(file);
|
||||
using PEReader peReader = new PEReader(fs);
|
||||
return peReader.HasMetadata && peReader.GetMetadataReader().IsAssembly;
|
||||
}
|
||||
|
||||
private static Assembly? TryLoadAssembly(string asmPath)
|
||||
{
|
||||
AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
|
||||
Assembly? asm = null;
|
||||
try
|
||||
{
|
||||
asm = Assembly.Load(asmName);
|
||||
}
|
||||
catch (BadImageFormatException ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
catch (FileLoadException ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
if (asm == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
asm = Assembly.LoadFile(asmPath);
|
||||
}
|
||||
catch (BadImageFormatException ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
catch (FileLoadException ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
return asm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// loop through all assemblies
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Assembly> GetAllReferencedAssemblies(bool skipSystemAssemblies = true)
|
||||
{
|
||||
Assembly? rootAssembly = Assembly.GetEntryAssembly();
|
||||
if (rootAssembly == null)
|
||||
{
|
||||
rootAssembly = Assembly.GetCallingAssembly();
|
||||
}
|
||||
var returnAssemblies = new HashSet<Assembly>(new AssemblyEquality());
|
||||
var loadedAssemblies = new HashSet<string>();
|
||||
var assembliesToCheck = new Queue<Assembly>();
|
||||
assembliesToCheck.Enqueue(rootAssembly);
|
||||
if (skipSystemAssemblies && IsSystemAssembly(rootAssembly) != false)
|
||||
{
|
||||
if (IsValid(rootAssembly))
|
||||
{
|
||||
returnAssemblies.Add(rootAssembly);
|
||||
}
|
||||
}
|
||||
while (assembliesToCheck.Any())
|
||||
{
|
||||
var assemblyToCheck = assembliesToCheck.Dequeue();
|
||||
foreach (var reference in assemblyToCheck.GetReferencedAssemblies())
|
||||
{
|
||||
if (!loadedAssemblies.Contains(reference.FullName))
|
||||
{
|
||||
var assembly = Assembly.Load(reference);
|
||||
if (skipSystemAssemblies && IsSystemAssembly(assembly))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
assembliesToCheck.Enqueue(assembly);
|
||||
loadedAssemblies.Add(reference.FullName);
|
||||
if (IsValid(assembly))
|
||||
{
|
||||
returnAssemblies.Add(assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var asmsInBaseDir = Directory.EnumerateFiles(AppContext.BaseDirectory,
|
||||
"*.dll", new EnumerationOptions { RecurseSubdirectories = true });
|
||||
foreach (var asmPath in asmsInBaseDir)
|
||||
{
|
||||
if (!IsManagedAssembly(asmPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
|
||||
//如果程序集已经加载过了就不再加载
|
||||
if (returnAssemblies.Any(x => AssemblyName.ReferenceMatchesDefinition(x.GetName(), asmName)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (skipSystemAssemblies && IsSystemAssembly(asmPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Assembly? asm = TryLoadAssembly(asmPath);
|
||||
if (asm == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//Assembly asm = Assembly.Load(asmName);
|
||||
if (!IsValid(asm))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (skipSystemAssemblies && IsSystemAssembly(asm))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
returnAssemblies.Add(asm);
|
||||
}
|
||||
return returnAssemblies.ToArray();
|
||||
}
|
||||
|
||||
private static bool IsValid(Assembly asm)
|
||||
{
|
||||
try
|
||||
{
|
||||
asm.GetTypes();
|
||||
asm.DefinedTypes.ToList();
|
||||
return true;
|
||||
}
|
||||
catch (ReflectionTypeLoadException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class AssemblyEquality : EqualityComparer<Assembly>
|
||||
{
|
||||
public override bool Equals(Assembly? x, Assembly? y)
|
||||
{
|
||||
if (x == null && y == null) return true;
|
||||
if (x == null || y == null) return false;
|
||||
return AssemblyName.ReferenceMatchesDefinition(x.GetName(), y.GetName());
|
||||
}
|
||||
|
||||
public override int GetHashCode([DisallowNull] Assembly obj)
|
||||
{
|
||||
return obj.GetName().FullName.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public class Result<T>(int code, string message, T? data = default)
|
||||
{
|
||||
public int Code { get; private set; } = code;
|
||||
public string Message { get; private set; } = message;
|
||||
public T? Data { get; private set; } = data;
|
||||
[JsonIgnore]
|
||||
public bool Succeeded
|
||||
{
|
||||
get => Code == (int)ResultCode.SUCCESS;
|
||||
}
|
||||
|
||||
public static Result<T> Success(T? data = default) => new((int)ResultCode.SUCCESS, ResultCode.SUCCESS.GetDescription(), data);
|
||||
|
||||
public static Result<T> Fail(ResultCode code) => new((int)code, code.GetDescription());
|
||||
public static Result<T> Fail(ResultCode code, string errorMsg) => new((int)code, errorMsg);
|
||||
public static Result<T> Fail<FuncT>(Result<FuncT> result) => new(result.Code, result.Message);
|
||||
}
|
||||
|
||||
public class Result
|
||||
{
|
||||
public static Result<T> Success<T>(T? data = default) => new((int)ResultCode.SUCCESS, ResultCode.SUCCESS.GetDescription(), data);
|
||||
|
||||
public static Result<object> Success() => new((int)ResultCode.SUCCESS, ResultCode.SUCCESS.GetDescription(), null);
|
||||
|
||||
public static Result<T> Fail<T>(ResultCode code) => new((int)code, code.GetDescription());
|
||||
public static Result<object> Fail(ResultCode code) => new((int)code, code.GetDescription());
|
||||
public static Result<T> Fail<T>(ResultCode code, string errorMsg) => new((int)code, errorMsg);
|
||||
public static Result<T> Fail<T, FuncT>(Result<FuncT> result) => new(result.Code, result.Message);
|
||||
public static Result<object> Fail<FuncT>(Result<FuncT> result) => new(result.Code, result.Message);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace IM.Commons
|
||||
{
|
||||
public enum ResultCode
|
||||
{
|
||||
// 3.1 成功类
|
||||
/// <summary>成功响应</summary>
|
||||
[Description("成功")]
|
||||
SUCCESS = 0,
|
||||
|
||||
// 3.2 系统级错误(1000 ~ 1999)
|
||||
/// <summary>未知异常</summary>
|
||||
[Description("系统错误")]
|
||||
SYSTEM_ERROR = 1000,
|
||||
/// <summary>服务器维护中或宕机</summary>
|
||||
[Description("服务不可用")]
|
||||
SERVICE_UNAVAILABLE = 1001,
|
||||
/// <summary>后端超时</summary>
|
||||
[Description("请求超时")]
|
||||
REQUEST_TIMEOUT = 1002,
|
||||
/// <summary>缺少或参数不合法</summary>
|
||||
[Description("参数错误")]
|
||||
PARAMETER_ERROR = 1003,
|
||||
/// <summary>数据库读写失败</summary>
|
||||
[Description("数据库错误")]
|
||||
DATABASE_ERROR = 1004,
|
||||
/// <summary>无权限访问</summary>
|
||||
[Description("权限不足")]
|
||||
PERMISSION_DENIED = 1005,
|
||||
/// <summary>Token 无效/过期</summary>
|
||||
[Description("认证失败")]
|
||||
AUTH_FAILED = 1006,
|
||||
|
||||
// 3.3 用户相关错误(2000 ~ 2099)
|
||||
/// <summary>查询不到用户</summary>
|
||||
[Description("用户不存在")]
|
||||
USER_NOT_FOUND = 2000,
|
||||
/// <summary>注册时用户已存在</summary>
|
||||
[Description("用户已存在")]
|
||||
USER_ALREADY_EXISTS = 2001,
|
||||
/// <summary>登录密码错误</summary>
|
||||
[Description("密码错误")]
|
||||
PASSWORD_ERROR = 2002,
|
||||
/// <summary>被管理员封禁</summary>
|
||||
[Description("用户被禁用")]
|
||||
USER_DISABLED = 2003,
|
||||
/// <summary>需重新登录</summary>
|
||||
[Description("登录过期")]
|
||||
LOGIN_EXPIRED = 2004,
|
||||
|
||||
// 3.4 好友相关错误(2100 ~ 2199)
|
||||
/// <summary>重复申请</summary>
|
||||
[Description("好友申请已存在")]
|
||||
FRIEND_REQUEST_EXISTS = 2100,
|
||||
/// <summary>不是好友</summary>
|
||||
[Description("好友关系不存在")]
|
||||
FRIEND_RELATION_NOT_FOUND = 2101,
|
||||
/// <summary>重复添加</summary>
|
||||
[Description("已经是好友")]
|
||||
ALREADY_FRIENDS = 2102,
|
||||
/// <summary>被对方拒绝</summary>
|
||||
[Description("好友请求被拒绝")]
|
||||
FRIEND_REQUEST_REJECTED = 2103,
|
||||
/// <summary>被对方拉黑</summary>
|
||||
[Description("无法申请加好友")]
|
||||
CANNOT_ADD_FRIEND = 2104,
|
||||
/// <summary>好友请求不存在</summary>
|
||||
[Description("好友请求不存在")]
|
||||
FRIEND_REQUEST_NOT_FOUND = 2105,
|
||||
/// <summary>处理好友请求操作无效</summary>
|
||||
[Description("处理好友请求操作无效")]
|
||||
INVALID_ACTION = 2106,
|
||||
/// <summary>注册错误</summary>
|
||||
[Description("注册错误")]
|
||||
REGISTER_ERROR = 2107,
|
||||
|
||||
// 3.5 群聊相关错误(2200 ~ 2299)
|
||||
/// <summary>查询不到群</summary>
|
||||
[Description("群不存在")]
|
||||
GROUP_NOT_FOUND = 2200,
|
||||
/// <summary>不能重复加入</summary>
|
||||
[Description("已在群中")]
|
||||
ALREADY_IN_GROUP = 2201,
|
||||
/// <summary>超出限制</summary>
|
||||
[Description("群成员已满")]
|
||||
GROUP_FULL = 2202,
|
||||
/// <summary>需要邀请/验证</summary>
|
||||
[Description("无加群权限")]
|
||||
NO_GROUP_PERMISSION = 2203,
|
||||
/// <summary>邀请链接过期</summary>
|
||||
[Description("群邀请已过期")]
|
||||
GROUP_INVITE_EXPIRED = 2204,
|
||||
/// <summary>群聊请求不存在</summary>
|
||||
[Description("群聊请求不存在")]
|
||||
GROUP_REQUEST_NOT_FOUND = 2205,
|
||||
/// <summary>群聊成员成员不存在</summary>
|
||||
[Description("群聊成员不存在")]
|
||||
GROUP_MEMBER_NOT_FOUNT = 2206,
|
||||
|
||||
// 3.6 消息相关错误(2300 ~ 2399)
|
||||
/// <summary>发送时异常</summary>
|
||||
[Description("消息发送失败")]
|
||||
MESSAGE_SEND_FAILED = 2300,
|
||||
/// <summary>查询不到消息</summary>
|
||||
[Description("消息不存在")]
|
||||
MESSAGE_NOT_FOUND = 2301,
|
||||
/// <summary>超过时间限制</summary>
|
||||
[Description("消息撤回失败")]
|
||||
MESSAGE_RECALL_FAILED = 2302,
|
||||
/// <summary>message_type 不合法</summary>
|
||||
[Description("不支持的消息类型")]
|
||||
UNSUPPORTED_MESSAGE_TYPE = 2303,
|
||||
|
||||
// 3.7 文件相关错误(2400 ~ 2499)
|
||||
/// <summary>存储服务错误</summary>
|
||||
[Description("文件上传失败")]
|
||||
FILE_UPLOAD_FAILED = 2400,
|
||||
/// <summary>下载时未找到</summary>
|
||||
[Description("文件不存在")]
|
||||
FILE_NOT_FOUND = 2401,
|
||||
/// <summary>超过配置限制</summary>
|
||||
[Description("文件大小超限")]
|
||||
FILE_TOO_LARGE = 2402,
|
||||
/// <summary>格式不允许</summary>
|
||||
[Description("文件类型不支持")]
|
||||
FILE_TYPE_NOT_SUPPORTED = 2403,
|
||||
|
||||
// 3.8 管理后台相关错误(3000 ~ 3099)
|
||||
/// <summary>账号错误</summary>
|
||||
[Description("管理员不存在")]
|
||||
ADMIN_NOT_FOUND = 3000,
|
||||
/// <summary>后台登录失败</summary>
|
||||
[Description("密码错误")]
|
||||
ADMIN_PASSWORD_ERROR = 3001,
|
||||
/// <summary>角色未找到</summary>
|
||||
[Description("角色不存在")]
|
||||
ROLE_NOT_FOUND = 3002,
|
||||
/// <summary>无操作权限</summary>
|
||||
[Description("权限不足")]
|
||||
ADMIN_PERMISSION_DENIED = 3003,
|
||||
/// <summary>后台日志写入失败</summary>
|
||||
[Description("操作记录失败")]
|
||||
OPERATION_LOG_FAILED = 3004,
|
||||
|
||||
// 3.9 会话相关错误(3100 ~ 3199)
|
||||
/// <summary>发送时异常</summary>
|
||||
[Description("会话不存在")]
|
||||
CONVERSATION_NOT_FOUND = 3100,
|
||||
|
||||
// 3.10 分片相关错误(3200 ~ 3299)
|
||||
/// <summary>分片不存在异常</summary>
|
||||
[Description("分片不存在")]
|
||||
CHUNK_NOT_FOUND = 3201,
|
||||
/// <summary>分片合并异常</summary>
|
||||
[Description("分片合并失败")]
|
||||
CHUNK_COMBINE_FAIL = 3202
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user