diff --git a/Extensions/ApiExtensions.cs b/Extensions/ApiExtensions.cs
new file mode 100644
index 0000000..0c3ce6b
--- /dev/null
+++ b/Extensions/ApiExtensions.cs
@@ -0,0 +1,28 @@
+using Scalar.AspNetCore;
+
+namespace Foxel.Extensions;
+
+///
+/// API相关的扩展方法
+///
+public static class ApiExtensions
+{
+ ///
+ /// 添加应用程序OpenAPI
+ ///
+ public static IServiceCollection AddApplicationOpenApi(this IServiceCollection services)
+ {
+ services.AddOpenApi(opt => { opt.AddDocumentTransformer(); });
+ return services;
+ }
+
+ ///
+ /// 使用应用程序OpenAPI
+ ///
+ public static WebApplication UseApplicationOpenApi(this WebApplication app)
+ {
+ app.MapOpenApi();
+ app.MapScalarApiReference();
+ return app;
+ }
+}
diff --git a/Extensions/ApplicationBuilderExtensions.cs b/Extensions/ApplicationBuilderExtensions.cs
deleted file mode 100644
index 841fe38..0000000
--- a/Extensions/ApplicationBuilderExtensions.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Microsoft.Extensions.FileProviders;
-using Scalar.AspNetCore;
-
-namespace Foxel.Extensions;
-
-public static class ApplicationBuilderExtensions
-{
- public static void UseApplicationStaticFiles(this WebApplication app)
- {
- var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "Uploads");
- if (!Directory.Exists(uploadsPath))
- {
- Directory.CreateDirectory(uploadsPath);
- }
-
- app.UseStaticFiles(new StaticFileOptions
- {
- FileProvider = new PhysicalFileProvider(uploadsPath),
- RequestPath = "/Uploads"
- });
- }
-
- public static void UseApplicationOpenApi(this WebApplication app)
- {
- app.MapOpenApi();
- app.MapScalarApiReference();
- }
-}
\ No newline at end of file
diff --git a/Extensions/ApplicationConfigurationExtensions.cs b/Extensions/ApplicationConfigurationExtensions.cs
new file mode 100644
index 0000000..bd5a87e
--- /dev/null
+++ b/Extensions/ApplicationConfigurationExtensions.cs
@@ -0,0 +1,56 @@
+using Foxel.Services.Initializer;
+
+namespace Foxel.Extensions;
+
+///
+/// 应用程序配置扩展方法
+///
+public static class ApplicationConfigurationExtensions
+{
+ ///
+ /// 配置应用程序中间件管道
+ ///
+ public static WebApplication ConfigureApplicationPipeline(this WebApplication app)
+ {
+ // 转发头处理
+ app.UseForwardedHeaders();
+
+ // 静态文件
+ app.UseApplicationStaticFiles();
+
+ // 开发环境特定配置
+ if (!app.Environment.IsDevelopment())
+ {
+ app.UseExceptionHandler("/Error", createScopeForErrors: true);
+ app.UseHsts();
+ }
+
+ // API文档
+ app.UseApplicationOpenApi();
+
+ // CORS
+ app.UseCors("MyAllowSpecificOrigins");
+
+ // 身份验证和授权
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ // 路由
+ app.MapControllers();
+ app.UseHttpsRedirection();
+
+ return app;
+ }
+
+ ///
+ /// 初始化应用程序
+ ///
+ public static async Task InitializeApplicationAsync(this WebApplication app)
+ {
+ using var scope = app.Services.CreateScope();
+ var initializer = scope.ServiceProvider.GetRequiredService();
+ await initializer.InitializeAsync();
+
+ return app;
+ }
+}
diff --git a/Extensions/ApplicationServiceExtensions.cs b/Extensions/ApplicationServiceExtensions.cs
new file mode 100644
index 0000000..f52b7fa
--- /dev/null
+++ b/Extensions/ApplicationServiceExtensions.cs
@@ -0,0 +1,40 @@
+namespace Foxel.Extensions;
+
+///
+/// 应用程序服务配置扩展方法
+///
+public static class ApplicationServiceExtensions
+{
+ ///
+ /// 添加所有应用程序服务
+ ///
+ public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration)
+ {
+ // 基础服务
+ services.AddControllers();
+ services.AddHttpServices();
+
+ // 数据库
+ services.AddApplicationDbContext(configuration);
+
+ // 核心业务服务
+ services.AddCoreServices();
+ services.AddManagementServices();
+ services.AddBackgroundServices();
+ services.AddAiServices();
+ services.AddInitializationServices();
+
+ // 身份验证和授权
+ services.AddApplicationAuthentication();
+ services.AddApplicationAuthorization();
+ services.AddApplicationCors();
+
+ // API文档
+ services.AddApplicationOpenApi();
+
+ // HTTP相关
+ services.AddForwardedHeaders();
+
+ return services;
+ }
+}
diff --git a/Extensions/AuthenticationExtensions.cs b/Extensions/AuthenticationExtensions.cs
new file mode 100644
index 0000000..3ab7dc7
--- /dev/null
+++ b/Extensions/AuthenticationExtensions.cs
@@ -0,0 +1,75 @@
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.IdentityModel.Tokens;
+using System.Text;
+using Foxel.Services.Configuration;
+
+namespace Foxel.Extensions;
+
+///
+/// 身份验证和授权相关的扩展方法
+///
+public static class AuthenticationExtensions
+{
+ ///
+ /// 添加应用程序身份验证
+ ///
+ public static IServiceCollection AddApplicationAuthentication(this IServiceCollection services)
+ {
+ // 构建临时服务提供者来获取配置服务
+ using var serviceProvider = services.BuildServiceProvider();
+ var configuration = serviceProvider.GetRequiredService();
+
+ services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(options =>
+ {
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ValidateIssuerSigningKey = true,
+ ValidIssuer = configuration["Jwt:Issuer"],
+ ValidAudience = configuration["Jwt:Audience"],
+ IssuerSigningKey = new SymmetricSecurityKey(
+ Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"]))
+ };
+ });
+
+ return services;
+ }
+
+ ///
+ /// 添加应用程序授权
+ ///
+ public static IServiceCollection AddApplicationAuthorization(this IServiceCollection services)
+ {
+ services.AddAuthorization(options =>
+ {
+ options.DefaultPolicy = new Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder()
+ .RequireAuthenticatedUser()
+ .Build();
+ });
+
+ return services;
+ }
+
+ ///
+ /// 添加应用程序CORS配置
+ ///
+ public static IServiceCollection AddApplicationCors(this IServiceCollection services)
+ {
+ services.AddCors(options =>
+ {
+ options.AddPolicy(name: "MyAllowSpecificOrigins",
+ policy => { policy.WithOrigins().AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); });
+ });
+
+ return services;
+ }
+}
diff --git a/Extensions/CoreServiceExtensions.cs b/Extensions/CoreServiceExtensions.cs
new file mode 100644
index 0000000..f1a70b7
--- /dev/null
+++ b/Extensions/CoreServiceExtensions.cs
@@ -0,0 +1,94 @@
+using Foxel.Services;
+using Foxel.Services.AI;
+using Foxel.Services.Auth;
+using Foxel.Services.Background;
+using Foxel.Services.Background.Processors;
+using Foxel.Services.Configuration;
+using Foxel.Services.Initializer;
+using Foxel.Services.Management;
+using Foxel.Services.Mapping;
+using Foxel.Services.Media;
+using Foxel.Services.Storage;
+using Foxel.Services.VectorDb;
+
+namespace Foxel.Extensions;
+
+///
+/// 核心业务服务相关的扩展方法
+///
+public static class CoreServiceExtensions
+{
+ ///
+ /// 添加核心业务服务
+ ///
+ public static IServiceCollection AddCoreServices(this IServiceCollection services)
+ {
+ // 配置服务
+ services.AddSingleton();
+
+ // 核心业务服务
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ return services;
+ }
+
+ ///
+ /// 添加管理服务
+ ///
+ public static IServiceCollection AddManagementServices(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ return services;
+ }
+
+ ///
+ /// 添加后台任务服务
+ ///
+ public static IServiceCollection AddBackgroundServices(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddHostedService();
+
+ // 任务处理器
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ return services;
+ }
+
+ ///
+ /// 添加AI和向量数据库服务
+ ///
+ public static IServiceCollection AddAiServices(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton(provider =>
+ provider.GetRequiredService());
+ services.AddHostedService();
+
+ return services;
+ }
+
+ ///
+ /// 添加初始化服务
+ ///
+ public static IServiceCollection AddInitializationServices(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ return services;
+ }
+}
diff --git a/Extensions/DatabaseExtensions.cs b/Extensions/DatabaseExtensions.cs
new file mode 100644
index 0000000..3cdf113
--- /dev/null
+++ b/Extensions/DatabaseExtensions.cs
@@ -0,0 +1,27 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace Foxel.Extensions;
+
+///
+/// 数据库相关的扩展方法
+///
+public static class DatabaseExtensions
+{
+ ///
+ /// 添加应用程序数据库上下文
+ ///
+ public static IServiceCollection AddApplicationDbContext(this IServiceCollection services, IConfiguration configuration)
+ {
+ var connectionString = configuration.GetConnectionString("DefaultConnection");
+ if (string.IsNullOrEmpty(connectionString))
+ {
+ connectionString = Environment.GetEnvironmentVariable("DEFAULT_CONNECTION");
+ }
+
+ Console.WriteLine($"数据库连接: {connectionString}");
+ services.AddDbContextFactory(options =>
+ options.UseNpgsql(connectionString));
+
+ return services;
+ }
+}
diff --git a/Extensions/HttpExtensions.cs b/Extensions/HttpExtensions.cs
new file mode 100644
index 0000000..3327fa9
--- /dev/null
+++ b/Extensions/HttpExtensions.cs
@@ -0,0 +1,56 @@
+using Microsoft.AspNetCore.HttpOverrides;
+using Microsoft.Extensions.FileProviders;
+
+namespace Foxel.Extensions;
+
+///
+/// HTTP相关的扩展方法
+///
+public static class HttpExtensions
+{
+ ///
+ /// 添加HTTP相关服务
+ ///
+ public static IServiceCollection AddHttpServices(this IServiceCollection services)
+ {
+ services.AddHttpClient();
+ services.AddHttpContextAccessor();
+ services.AddMemoryCache();
+ return services;
+ }
+
+ ///
+ /// 配置转发头信息
+ ///
+ public static IServiceCollection AddForwardedHeaders(this IServiceCollection services)
+ {
+ services.Configure(options =>
+ {
+ options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
+ options.KnownNetworks.Clear();
+ options.KnownProxies.Clear();
+ });
+
+ return services;
+ }
+
+ ///
+ /// 使用应用程序静态文件
+ ///
+ public static WebApplication UseApplicationStaticFiles(this WebApplication app)
+ {
+ var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "Uploads");
+ if (!Directory.Exists(uploadsPath))
+ {
+ Directory.CreateDirectory(uploadsPath);
+ }
+
+ app.UseStaticFiles(new StaticFileOptions
+ {
+ FileProvider = new PhysicalFileProvider(uploadsPath),
+ RequestPath = "/Uploads"
+ });
+
+ return app;
+ }
+}
diff --git a/Extensions/ServiceCollectionExtensions.cs b/Extensions/ServiceCollectionExtensions.cs
deleted file mode 100644
index a841026..0000000
--- a/Extensions/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using Foxel.Services;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
-using Microsoft.AspNetCore.Authentication.Cookies;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.IdentityModel.Tokens;
-using System.Text;
-using Foxel.Services.AI;
-using Foxel.Services.Auth;
-using Foxel.Services.Background;
-using Foxel.Services.Configuration;
-using Foxel.Services.Initializer;
-using Foxel.Services.Management;
-using Foxel.Services.Media;
-using Foxel.Services.Storage;
-using Foxel.Services.Background.Processors;
-using Foxel.Services.Mapping;
-using Foxel.Services.VectorDb;
-
-namespace Foxel.Extensions;
-
-public static class ServiceCollectionExtensions
-{
- public static void AddCoreServices(this IServiceCollection services)
- {
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddHostedService();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
-
- }
-
- public static void AddApplicationDbContext(this IServiceCollection services, IConfiguration configuration)
- {
- var connectionString = configuration.GetConnectionString("DefaultConnection");
- if (string.IsNullOrEmpty(connectionString))
- {
- connectionString = Environment.GetEnvironmentVariable("DEFAULT_CONNECTION");
- }
-
- Console.WriteLine($"数据库连接: {connectionString}");
- services.AddDbContextFactory(options =>
- options.UseNpgsql(connectionString));
- }
-
- public static void AddApplicationOpenApi(this IServiceCollection services)
- {
- services.AddOpenApi(opt => { opt.AddDocumentTransformer(); });
- }
-
- public static void AddApplicationAuthentication(this IServiceCollection services)
- {
- IConfigService configuration = services.BuildServiceProvider().GetRequiredService();
- services.AddAuthentication(options =>
- {
- options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
- options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
- options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
- })
- .AddJwtBearer(options =>
- {
- options.TokenValidationParameters = new TokenValidationParameters
- {
- ValidateIssuer = true,
- ValidateAudience = true,
- ValidateLifetime = true,
- ValidateIssuerSigningKey = true,
- ValidIssuer = configuration["Jwt:Issuer"],
- ValidAudience = configuration["Jwt:Audience"],
- IssuerSigningKey = new SymmetricSecurityKey(
- Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"]))
- };
- });
- }
-
- public static void AddApplicationAuthorization(this IServiceCollection services)
- {
- services.AddAuthorization(options =>
- {
- options.DefaultPolicy = new Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder()
- .RequireAuthenticatedUser()
- .Build();
- });
- }
-
- public static void AddApplicationCors(this IServiceCollection services)
- {
- services.AddCors(options =>
- {
- options.AddPolicy(name: "MyAllowSpecificOrigins",
- policy => { policy.WithOrigins().AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); });
- });
- }
-
- public static void AddVectorDbServices(this IServiceCollection services)
- {
- services.AddSingleton();
- services.AddSingleton(provider =>
- provider.GetRequiredService());
- }
-}
\ No newline at end of file
diff --git a/Models/BatchDeleteResult.cs b/Models/BatchDeleteResult.cs
new file mode 100644
index 0000000..ef87a2a
--- /dev/null
+++ b/Models/BatchDeleteResult.cs
@@ -0,0 +1,8 @@
+namespace Foxel.Models;
+
+public class BatchDeleteResult
+{
+ public int SuccessCount { get; set; }
+ public int FailedCount { get; set; }
+ public List FailedIds { get; set; } = new();
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
index 59b16ef..23a1207 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,52 +1,27 @@
using Foxel.Extensions;
-using Foxel.Services.Initializer;
-using Foxel.Services.VectorDb;
-using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
var environment = builder.Environment;
+
Console.WriteLine($"当前环境: {environment.EnvironmentName}");
+
+// 配置日志记录
builder.Logging.AddDatabaseLogging(config =>
{
config.MinLevel = LogLevel.Information;
config.Enabled = true;
});
-builder.Services.AddMemoryCache();
-builder.Services.AddApplicationDbContext(builder.Configuration);
-builder.Services.AddApplicationOpenApi();
-builder.Services.AddControllers();
-builder.Services.AddHttpClient();
-builder.Services.AddCoreServices();
-builder.Services.AddHttpContextAccessor();
-builder.Services.AddApplicationAuthentication();
-builder.Services.AddApplicationAuthorization();
-builder.Services.AddApplicationCors();
-builder.Services.AddVectorDbServices();
-builder.Services.AddHostedService();
-builder.Services.Configure(options =>
-{
- options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
- options.KnownNetworks.Clear();
- options.KnownProxies.Clear();
-});
+
+// 添加所有应用程序服务
+builder.Services.AddApplicationServices(builder.Configuration);
+
var app = builder.Build();
-using (var scope = app.Services.CreateScope())
-{
- var initializer = scope.ServiceProvider.GetRequiredService();
- await initializer.InitializeAsync();
-}
-app.UseForwardedHeaders();
-app.UseApplicationStaticFiles();
-if (!app.Environment.IsDevelopment())
-{
- app.UseExceptionHandler("/Error", createScopeForErrors: true);
- app.UseHsts();
-}
-app.UseApplicationOpenApi();
-app.UseCors("MyAllowSpecificOrigins");
-app.UseAuthentication();
-app.UseAuthorization();
-app.MapControllers();
-app.UseHttpsRedirection();
+// 初始化应用程序
+await app.InitializeApplicationAsync();
+
+// 配置中间件管道
+app.ConfigureApplicationPipeline();
+
+// 启动应用程序
app.Run();
\ No newline at end of file
diff --git a/Services/AI/FaceClusteringService.cs b/Services/Face/FaceClusteringService.cs
similarity index 100%
rename from Services/AI/FaceClusteringService.cs
rename to Services/Face/FaceClusteringService.cs
diff --git a/Services/AI/IFaceClusteringService.cs b/Services/Face/IFaceClusteringService.cs
similarity index 100%
rename from Services/AI/IFaceClusteringService.cs
rename to Services/Face/IFaceClusteringService.cs
diff --git a/Services/Management/IUserManagementService.cs b/Services/Management/IUserManagementService.cs
index d78ac6e..c33b51c 100644
--- a/Services/Management/IUserManagementService.cs
+++ b/Services/Management/IUserManagementService.cs
@@ -12,11 +12,4 @@ public interface IUserManagementService
Task UpdateUserAsync(int id, string userName, string email, string role);
Task DeleteUserAsync(int id);
Task BatchDeleteUsersAsync(List ids);
-}
-
-public class BatchDeleteResult
-{
- public int SuccessCount { get; set; }
- public int FailedCount { get; set; }
- public List FailedIds { get; set; } = new();
-}
+}
\ No newline at end of file