diff --git a/YuanXuan.IM.Api.slnx b/YuanXuan.IM.Api.slnx
new file mode 100644
index 0000000..80282f2
--- /dev/null
+++ b/YuanXuan.IM.Api.slnx
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/YuanXuan.IM.Api/CollectionExtensions/ApiVersioningServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/ApiVersioningServiceCollectionExtensions.cs
new file mode 100644
index 0000000..eee0262
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/ApiVersioningServiceCollectionExtensions.cs
@@ -0,0 +1,37 @@
+using Asp.Versioning;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ ///
+ /// Api版本控制服务扩展类
+ ///
+ public static class ApiVersioningServiceCollectionExtensions
+ {
+ ///
+ /// 添加Api版本控制服务
+ ///
+ ///
+ ///
+ public static IServiceCollection AddApiVersion(this IServiceCollection services)
+ {
+ services.AddApiVersioning(config =>
+ {
+ // 默认的Api版号
+ config.DefaultApiVersion = new ApiVersion(1, 0);
+ // 未指定Api版号的时候,使用默认版号
+ config.AssumeDefaultVersionWhenUnspecified = true;
+ // 是否在返回响应头中返回Api版本信息
+ config.ReportApiVersions = true;
+
+ }).AddApiExplorer(options =>
+ {
+ // Api 版本分组名称
+ options.GroupNameFormat = "'v'VVV";
+ // 未指定Api版号的时候,使用默认版号
+ options.SubstituteApiVersionInUrl = true;
+ });
+
+ return services;
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/BatchRegisterServiceCollectionExtension.cs b/YuanXuan.IM.Api/CollectionExtensions/BatchRegisterServiceCollectionExtension.cs
new file mode 100644
index 0000000..0fbe022
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/BatchRegisterServiceCollectionExtension.cs
@@ -0,0 +1,198 @@
+using Masuit.Tools;
+using Microsoft.Extensions.DependencyModel;
+using Serilog;
+using System.Reflection;
+using System.Runtime.Loader;
+using YuanXuan.IM.Common.Attributes;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ ///
+ /// 批量注册服务
+ ///
+ public static class BatchRegisterServiceCollectionExtension
+ {
+ ///
+ /// 批量注册带属性的服务和后台服务
+ ///
+ ///
+ public static void BatchRegisterServices(this IServiceCollection services)
+ {
+ var allAssembly = GetAllAssembly();
+
+ services.RegisterServiceByAttribute(ServiceLifetime.Singleton, allAssembly);
+ services.RegisterServiceByAttribute(ServiceLifetime.Scoped, allAssembly);
+ services.RegisterServiceByAttribute(ServiceLifetime.Transient, allAssembly);
+
+ services.RegisterBackgroundService(allAssembly);
+ }
+ ///
+ /// 通过 InjectAttribute 批量注册服务
+ ///
+ ///
+ ///
+ private static void RegisterServiceByAttribute(this IServiceCollection services, ServiceLifetime serviceLifetime, List allAssembly)
+ {
+ var types = allAssembly.SelectMany(t => t.GetTypes())
+ .Where(t => t.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0 &&
+ t.GetCustomAttribute()?.Lifetime == serviceLifetime &&
+ t.IsClass && !t.IsAbstract).ToList();
+
+ foreach (var type in types)
+ {
+ var baseInterfaces = type.BaseType?.GetInterfaces();
+ Type? typeInterface = null;
+
+ // 获取所有接口,然后过滤掉系统接口和常见的不适合DI的接口
+ var allInterfaces = type.GetInterfaces();
+ var filteredInterfaces = allInterfaces.Where(IsCustomInterface).ToList();
+
+ // 如果没有自定义接口,则使用直接注入
+ if (filteredInterfaces.Count == 0)
+ {
+ //服务非继承自接口的直接注入
+ switch (serviceLifetime)
+ {
+ case ServiceLifetime.Singleton: services.AddSingleton(type); break;
+ case ServiceLifetime.Scoped: services.AddScoped(type); break;
+ case ServiceLifetime.Transient: services.AddTransient(type); break;
+ }
+ Log.Information("直接注入服务: {TypeName} (生命周期: {Lifetime})", type.Name, serviceLifetime);
+ }
+ else
+ {
+ // 如果有多个自定义接口,选择最合适的接口
+ typeInterface = ChooseBestInterface(filteredInterfaces, type);
+
+ //服务继承自接口的和接口一起注入
+ switch (serviceLifetime)
+ {
+ case ServiceLifetime.Singleton: services.AddSingleton(typeInterface, type); break;
+ case ServiceLifetime.Scoped: services.AddScoped(typeInterface, type); break;
+ case ServiceLifetime.Transient: services.AddTransient(typeInterface, type); break;
+ }
+ Log.Information("接口注入服务: {TypeName} -> {InterfaceName} (生命周期: {Lifetime})",
+ type.Name, typeInterface.Name, serviceLifetime);
+ }
+ }
+ }
+
+
+ ///
+ /// 注册后台服务
+ ///
+ ///
+ ///
+ private static void RegisterBackgroundService(this IServiceCollection services, List allAssembly)
+ {
+ List types = allAssembly.SelectMany(t => t.GetTypes()).Where(t => typeof(BackgroundService).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract).ToList();
+
+ foreach (var type in types)
+ {
+ services.AddSingleton(typeof(IHostedService), type);
+ }
+ }
+ ///
+ /// 判断是否为自定义接口(过滤掉系统接口和常见的不适合DI的接口)
+ ///
+ /// 接口类型
+ ///
+ private static bool IsCustomInterface(Type interfaceType)
+ {
+ // 系统命名空间的接口(不适合DI)
+ var systemNamespaces = new[]
+ {
+ "System",
+ "Microsoft",
+ "Windows",
+ "IDisposable",
+ "IComparable",
+ "IEquatable",
+ "IEnumerable",
+ "ICollection",
+ "IList",
+ "IAsyncEnumerable",
+ "IAsyncDisposable"
+ };
+
+ // 如果接口名称包含系统命名空间或常见系统接口名称,则过滤掉
+ if (systemNamespaces.Any(ns => interfaceType.Namespace?.StartsWith(ns) == true ||
+ interfaceType.Name.StartsWith(ns)))
+ {
+ return false;
+ }
+
+ // 检查接口是否有自定义项目的命名空间
+ var customNamespaces = new[]
+ {
+ "LearningOfficer.OA",
+ "LearningOfficer"
+ };
+
+ // 如果接口有自定义项目的命名空间,则认为是自定义接口
+ return customNamespaces.Any(ns => interfaceType.Namespace?.StartsWith(ns) == true);
+ }
+
+ ///
+ /// 从多个自定义接口中选择最合适的接口
+ ///
+ /// 接口列表
+ /// 实现类型
+ ///
+ private static Type ChooseBestInterface(List interfaces, Type implementationType)
+ {
+ // 如果只有一个接口,直接返回
+ if (interfaces.Count == 1)
+ return interfaces[0];
+
+ // 优先选择名称匹配的接口(例如:MyService -> IMyService)
+ var serviceName = implementationType.Name;
+ var preferredInterface = interfaces.FirstOrDefault(i =>
+ i.Name == "I" + serviceName ||
+ i.Name == serviceName.Replace("Service", "").Replace("Impl", "") + "Service");
+
+ if (preferredInterface != null)
+ return preferredInterface;
+
+ // 按接口名称排序,选择最合适的
+ return interfaces
+ .OrderBy(i => i.Name.StartsWith("I") ? 0 : 1) // 优先选择 I 开头的接口
+ .ThenBy(i => i.Name.Length) // 选择名称较短的接口
+ .First();
+ }
+ ///
+ /// 获取全部 Assembly
+ ///
+ ///
+ private static List GetAllAssembly()
+ {
+ var allAssemblies = new List();
+ var loadedAssemblies = new HashSet();
+
+ var dependencyContext = DependencyContext.Default;
+ var libraries = dependencyContext.RuntimeLibraries
+ .Where(lib => !lib.Serviceable && lib.Type != "package")
+ .ToList();
+
+ foreach (var library in libraries)
+ {
+ try
+ {
+ var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(library.Name));
+ if (loadedAssemblies.Add(assembly.FullName))
+ {
+ allAssemblies.Add(assembly);
+ }
+ }
+ catch (Exception)
+ {
+ Log.Fatal("加载程序集失败:{0}", library.Name);
+ // Log or handle the exception if necessary
+ }
+ }
+
+ return allAssemblies;
+ }
+
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/ConfigureOptionServiceCollectionExtension.cs b/YuanXuan.IM.Api/CollectionExtensions/ConfigureOptionServiceCollectionExtension.cs
new file mode 100644
index 0000000..941ef91
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/ConfigureOptionServiceCollectionExtension.cs
@@ -0,0 +1,28 @@
+using YuanXuan.IM.Common.Configs;
+using YuanXuan.IM.Common.Dtos.ALiYun;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ ///
+ /// 强类型配置服务
+ ///
+ public static class ConfigureOptionServiceCollectionExtension
+ {
+
+ ///
+ /// 添加强类型配置服务
+ ///
+ ///
+ public static void AddConfigureOptions(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.Configure(configuration.GetSection("nacos"));
+ services.AddOptions();
+ services.Configure(configuration.GetSection("OSSConfig"));
+ services.Configure(configuration.GetSection("ImConfig"));
+ services.Configure(configuration.GetSection("HangFireSettings"));
+ services.Configure(configuration.GetSection("UpAppVersion"));
+
+ services.Configure(configuration.GetSection("RabbitMQ"));
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/JWTAuthServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/JWTAuthServiceCollectionExtensions.cs
new file mode 100644
index 0000000..1468e56
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/JWTAuthServiceCollectionExtensions.cs
@@ -0,0 +1,72 @@
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+using System.Text;
+using YuanXuan.IM.Common.Configs;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ public static class JWTAuthServiceCollectionExtensions
+ {
+ ///
+ /// Jwt认证服务
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddJwtAuth(this IServiceCollection services, IConfiguration configuration)
+ {
+ //将配置文件中的相关内容反序列化
+ var jwtOption = configuration.GetSection("Jwt").Get();
+
+ services.AddAuthentication(options =>
+ {
+ options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ }).AddJwtBearer(options =>
+ {
+ options.Events = new JwtBearerEvents
+ {
+ //验证失败时的处理
+ OnAuthenticationFailed = context =>
+ {
+ //若失败类型为过期,则返回特定Header,便于客户端判断
+ if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
+ context.Response.Headers.Add("tokenErr", "expired");
+ return Task.CompletedTask;
+ }
+ //// 配置 SignalR 使用 JWT
+ //,OnMessageReceived = context =>
+ //{
+ // var accessToken = context.Request.Query["access_token"];
+ // var path = context.HttpContext.Request.Path;
+ // if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/signalr"))
+ // {
+ // context.Token = accessToken;
+ // }
+ // return Task.CompletedTask;
+ //}
+ };
+
+ options.RequireHttpsMetadata = false;
+ options.SaveToken = true;
+
+ options.TokenValidationParameters = new TokenValidationParameters()
+ {
+ ValidateIssuer = true, //是否验证Issuer
+ ValidIssuer = jwtOption.Issuer, //发行人Issuer
+ ValidateAudience = true, //是否验证Audience
+ ValidAudience = jwtOption.Audience, //订阅人Audience
+ ValidateIssuerSigningKey = true, //是否验证SecurityKey
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOption.AccessSecret)), //SecurityKey
+ ValidateLifetime = true, //是否验证失效时间
+ ClockSkew = TimeSpan.FromSeconds(jwtOption.ClockSkew), //过期时间容错值,解决服务器端时间不同步问题(秒)
+ RequireExpirationTime = true,
+ };
+ });
+
+ services.AddAuthorization();
+
+ return services;
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/NacosServiceCollectionExtension.cs b/YuanXuan.IM.Api/CollectionExtensions/NacosServiceCollectionExtension.cs
new file mode 100644
index 0000000..4838fbe
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/NacosServiceCollectionExtension.cs
@@ -0,0 +1,23 @@
+using Nacos.V2.DependencyInjection;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ ///
+ /// 强类型配置服务
+ ///
+ public static class NacosServiceCollectionExtension
+ {
+
+ ///
+ /// 添加强类型配置服务
+ ///
+ ///
+ public static void AddNacos(this IServiceCollection services, IConfiguration configuration, IHostBuilder host)
+ {
+ services.AddNacosV2Config(configuration);
+ services.AddNacosV2Naming(configuration);
+ host.UseNacosConfig("nacos");
+ //services.AddNacosAspNet(configuration, section: "nacos");
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/PollyServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/PollyServiceCollectionExtensions.cs
new file mode 100644
index 0000000..5f8546f
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/PollyServiceCollectionExtensions.cs
@@ -0,0 +1,21 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ public static class PollyServiceCollectionExtensions
+ {
+ ///
+ /// 添加Polly策略
+ ///
+ ///
+ ///
+ public static IServiceCollection AddPollyPolicies(this IServiceCollection services)
+ {
+ // 添加HTTP客户端工厂
+ services.AddHttpClient("YarpClient")
+ .ConfigurePrimaryHttpMessageHandler(() => new System.Net.Http.HttpClientHandler());
+
+ return services;
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/RedisServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/RedisServiceCollectionExtensions.cs
new file mode 100644
index 0000000..d03f86e
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/RedisServiceCollectionExtensions.cs
@@ -0,0 +1,22 @@
+using YuanXuan.IM.Common.Configs;
+using YuanXuan.IM.Infrastructure.Redis;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ public static class RedisServiceCollectionExtensions
+ {
+ ///
+ /// 添加Redis服务
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddRedis(this IServiceCollection services, IConfiguration configuration)
+ {
+ var connectionStrings = configuration.GetSection("ConnectionStrings").Get();
+
+ RedisHelper.Initialization(connectionStrings.Redis);
+ return services;
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/SerilogServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/SerilogServiceCollectionExtensions.cs
new file mode 100644
index 0000000..6295a09
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/SerilogServiceCollectionExtensions.cs
@@ -0,0 +1,57 @@
+using Serilog;
+using Serilog.Sinks.Grafana.Loki;
+using System;
+using YuanXuan.IM.Api.CollectionExtensions;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ public static class SerilogServiceCollectionExtensions
+ {
+ ///
+ /// 添加Serilog
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddSerilog(this IServiceCollection services, IConfiguration configuration, IHostBuilder hostBuilder, IHostEnvironment environment)
+ {
+ var labels = new List
+ {
+ new LokiLabel
+ {
+ Key="app",
+ Value=environment.ApplicationName
+ },
+ new LokiLabel
+ {
+ Key="env",
+ Value=environment.EnvironmentName
+ },
+ };
+
+ // 配置 Serilog
+ var logger = new LoggerConfiguration()
+ .WriteTo.Console()
+ .Enrich.FromLogContext();
+
+ //if (environment.IsDevelopment())
+ //{
+ logger.MinimumLevel.Information()
+ .WriteTo.GrafanaLoki(configuration["GrafanaLoki:LokiUri"], labels, new List()
+ {
+ //"RequestId","Path"
+ }, tenant: configuration["GrafanaLoki:TenantId"]);
+
+ //}
+ //else
+ //{
+ // logger.MinimumLevel.Information()
+ // .WriteTo.GrafanaLoki(configuration["GrafanaLoki:LokiUri"], labels);
+ //}
+
+ Log.Logger = logger.CreateLogger();
+ services.AddSerilog(Log.Logger);
+ return services;
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/SqlSugarServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/SqlSugarServiceCollectionExtensions.cs
new file mode 100644
index 0000000..fe5abf6
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/SqlSugarServiceCollectionExtensions.cs
@@ -0,0 +1,117 @@
+using Serilog;
+using SqlSugar;
+using System.Text.RegularExpressions;
+using Yitter.IdGenerator;
+using YuanXuan.IM.Common.Configs;
+using YuanXuan.IM.Infrastructure.DBContext;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ public static class SqlSugarServiceCollectionExtensions
+ {
+ ///
+ /// 添加SqlSugar
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddSqlSugar(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment env)
+ {
+ var connectionStrings = configuration.GetSection("ConnectionStrings").Get();
+
+ //YitIdHelper.SetIdGenerator(new IdGeneratorOptions(configuration.GetValue("SnowFlakeWorkId")));
+
+ // 自定义雪花ID算法 程序启动时执行一次就行
+ StaticConfig.CustomSnowFlakeFunc = () =>
+ {
+ return YitIdHelper.NextId();
+ };
+
+ services.AddScoped(provider =>
+ {
+ var serviceProvider = provider; // 获取 IServiceProvider
+ List configs = new()
+ {
+ new ConnectionConfig
+ {
+ DbType = DbType.MySql,
+ ConfigId = nameof(connectionStrings.Db),
+ ConnectionString = connectionStrings?.Db ?? throw new InvalidOperationException("Connection string cannot be null."),
+ IsAutoCloseConnection = true,
+ AopEvents = SetAopEvents(connectionStrings.Db, env.EnvironmentName, serviceProvider),
+ },
+
+ new ConnectionConfig
+ {
+ DbType = DbType.MySql,
+ ConfigId = nameof(connectionStrings.UserCenterDb),
+ ConnectionString = connectionStrings?.UserCenterDb ?? throw new InvalidOperationException("Connection string cannot be null."),
+ IsAutoCloseConnection = true,
+ AopEvents = SetAopEvents(connectionStrings.UserCenterDb, env.EnvironmentName, serviceProvider),
+ }
+ };
+
+ return new SqlSugarClient(configs);
+ });
+
+ services.AddScoped(typeof(SugarRepository<>));
+
+ return services;
+ }
+
+ private static AopEvents SetAopEvents(string connectionString, string environmentName, IServiceProvider serviceProvider)
+ {
+ var aopEvents = new AopEvents();
+
+ if (environmentName == Environments.Development)
+ {
+ // 正则表达式匹配Ip
+ var ipMatch = Regex.Match(connectionString, @"((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))");
+
+ var connections = connectionString.Split(';');
+ string dbNamne = string.Empty;
+ foreach (var item in connections)
+ {
+ if (item.Contains("Database"))
+ {
+ dbNamne = item.Split('=')[1];
+ }
+ }
+
+ aopEvents.OnLogExecuting = (sql, pars) =>
+ {
+ // 打印 Sql 语句
+ Console.WriteLine($"执行Sql:{Environment.NewLine}{UtilMethods.GetNativeSql(sql, pars)}");
+ Log.Logger.Debug($"执行Sql:{Environment.NewLine}{UtilMethods.GetNativeSql(sql, pars)}");
+ };
+ }
+
+ //aopEvents.DataExecuting += (oldValue, entityInfo) =>
+ //{
+ // // 获取 ICurrentUserService
+ // var currentUserService = serviceProvider.GetService();
+
+
+ // //if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityValue is BaseEntity baseEntity)
+ // //{
+ // // if (entityInfo.OperationType == DataFilterType.InsertByObject)
+ // // {
+ // // // 插入时填充创建信息
+ // // baseEntity.CreatedUserId = currentUserService.GetUserId();
+ // // baseEntity.CreatedUserName = currentUserService.GetUserName();
+ // // baseEntity.CreatedUserRealname = currentUserService.GetUserName();
+ // // }
+ // // else if (entityInfo.OperationType == DataFilterType.UpdateByObject)
+ // // {
+ // // // 更新时填充修改信息
+ // // baseEntity.ModifiedUserId = currentUserService.GetUserId();
+ // // baseEntity.ModifiedUserName = currentUserService.GetUserName();
+ // // baseEntity.ModifiedUserRealname = currentUserService.GetUserName();
+ // // }
+ // //}
+ //};
+
+ return aopEvents;
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/SwaggerServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/SwaggerServiceCollectionExtensions.cs
new file mode 100644
index 0000000..0c29dc0
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/SwaggerServiceCollectionExtensions.cs
@@ -0,0 +1,87 @@
+using LearningOfficer.OA.Mobile.Api.Filters;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Microsoft.OpenApi.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ public static class SwaggerServiceCollectionExtensions
+ {
+ ///
+ /// Swagger注入
+ ///
+ ///
+ public static void AddSwagger(this IServiceCollection services)
+ {
+ services.AddSwaggerGen(c =>
+ {
+ c.SwaggerDoc("v1", new OpenApiInfo
+ {
+ Title = "OA移动端Api",
+ Version = "v1"
+
+ });
+ // v2 文档
+ c.SwaggerDoc("v2", new OpenApiInfo
+ {
+ Title = "OA移动端Api",
+ Version = "v2"
+ });
+ // v3 文档
+ c.SwaggerDoc("v3", new OpenApiInfo
+ {
+ Title = "OA移动端Api",
+ Version = "v3"
+ });
+ c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
+ {
+ Description = "在下框中输入请求头中需要添加Jwt授权Token:Bearer {Token},注意中间有空格",
+ Name = "Authorization",
+ In = ParameterLocation.Header,
+ Type = SecuritySchemeType.ApiKey,
+ BearerFormat = "JWT",
+ Scheme = "Bearer"
+ });
+ c.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference {
+ Type = ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ }
+ },
+ new string[] { }
+ }
+ });
+ c.SupportNonNullableReferenceTypes();
+ c.ParameterFilter();
+
+ // 获取主项目生成的 XML 文件路径
+ var mainXmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
+ var mainXmlPath = Path.Combine(AppContext.BaseDirectory, mainXmlFile);
+ c.IncludeXmlComments(mainXmlPath, includeControllerXmlComments: true);
+
+ // 获取所有引用的类库 XML 文件路径
+ var referencedAssemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
+ foreach (var assemblyName in referencedAssemblies)
+ {
+ var libraryXmlPath = Path.Combine(AppContext.BaseDirectory, $"{assemblyName.Name}.xml");
+ if (File.Exists(libraryXmlPath))
+ {
+ c.IncludeXmlComments(libraryXmlPath);
+ }
+ }
+
+
+ });
+ }
+ }
+}
diff --git a/YuanXuan.IM.Api/CollectionExtensions/YarpServiceCollectionExtensions.cs b/YuanXuan.IM.Api/CollectionExtensions/YarpServiceCollectionExtensions.cs
new file mode 100644
index 0000000..9e436ac
--- /dev/null
+++ b/YuanXuan.IM.Api/CollectionExtensions/YarpServiceCollectionExtensions.cs
@@ -0,0 +1,36 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Primitives;
+using Nacos.V2;
+using Nacos.V2.Naming;
+using Nacos.V2.Naming.Dtos;
+using Newtonsoft.Json;
+using System.Threading;
+using Yarp.ReverseProxy.Configuration;
+using YuanXuan.IM.Api.Proxy;
+
+namespace YuanXuan.IM.Api.CollectionExtensions
+{
+ public static class YarpServiceCollectionExtensions
+ {
+ ///
+ /// 添加YARP反向代理服务
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddYarpWithNacos(this IServiceCollection services, IConfiguration configuration)
+ {
+ // 添加YARP服务
+ services.AddReverseProxy();
+
+ // 注册Nacos服务发现提供者
+ services.AddSingleton();
+ services.AddSingleton(provider => provider.GetRequiredService());
+
+ return services;
+ }
+
+ }
+
+}
diff --git a/YuanXuan.IM.Api/Controllers/BaseApiController.cs b/YuanXuan.IM.Api/Controllers/BaseApiController.cs
new file mode 100644
index 0000000..55eed45
--- /dev/null
+++ b/YuanXuan.IM.Api/Controllers/BaseApiController.cs
@@ -0,0 +1,51 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using YuanXuan.IM.Common.Dtos.LoginMobile;
+using YuanXuan.IM.Common.Response;
+
+namespace YuanXuan.IM.Api.Controllers
+{
+ ///
+ /// 自定义基础Api控制器
+ ///
+ [Authorize]
+ [ApiController]
+ public class BaseApiController : ControllerBase
+ {
+ ///
+ /// Api版本前缀
+ ///
+ protected const string RoutePrefix = "api/v{version:apiVersion}";
+ public MobileTokenInfo ZereUser => GetTokenInfo();
+ private MobileTokenInfo GetTokenInfo()
+ {
+ return new MobileTokenInfo
+ {
+ UserName = HttpContext?.User?.FindFirst("UserName")?.Value,
+ UserId = HttpContext?.User?.FindFirst("UserId")?.Value,
+ Jwt_id = HttpContext?.User?.FindFirst("Jwt_id")?.Value,
+ };
+ }
+
+ ///
+ /// 返回成功结果
+ ///
+ /// 数据
+ ///
+ protected IActionResult Success(object data = null)
+ {
+ return Ok(new BaseResponse