using Fake.AspNetCore; using Fake.Autofac; using Fake.Modularity; using Fake.Rbac.Application; using Fake.Rbac.Infrastructure; using Fake.AspNetCore.Auditing; using Fake.AspNetCore.ExceptionHandling; using Fake.AspNetCore.Newtonsoft; using Fake.AspNetCore.Swagger; using Fake.EventBus.IntegrationEventLog; using Fake.FeiShu; using Fake.FileManagement.Application; using Fake.FileManagement.Infrastructure; using Fake.ObjectStorage; using Fake.Security.Claims; using Fake.Serilog; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Oms.Application; using Oms.SqlSugarCore; using SimpleAdmin.Api.Auth; using SimpleAdmin.Api.Storage; using SimpleAdmin.Api.Swagger; using SimpleAdmin.Api.WeChat; using Swashbuckle.AspNetCore.SwaggerGen; namespace SimpleAdmin.Api; [DependsOn( typeof(FakeAutofacModule), typeof(OmsApplicationModule), typeof(OmsSqlSugarCoreModule), typeof(FakeRbacApplicationModule), typeof(FakeRbacInfrastructureModule), typeof(FakeFileManagementApplicationModule), typeof(FakeFileManagementInfrastructureModule), typeof(FakeObjectStorageTencentCosModule), // 在 RabbitMQ 物理总线之后 Replace IDistributedEventBus → Outbox typeof(FakeEventBusIntegrationEventLogModule), typeof(FakeAspNetCoreModule), typeof(FakeSerilogModule) )] public class SimpleAdminApiModule : FakeModule { private const string DefaultCorsPolicyName = "default"; public override void PreConfigureServices(ServiceConfigurationContext context) { FakeClaimTypes.UserId = "mapId"; } public override void ConfigureServices(ServiceConfigurationContext context) { var services = context.Services; var configuration = context.Services.GetConfiguration(); // 配置动态 API - 扫描 OMS / RBAC / FileManagement 应用服务 services.Configure(options => { options.ApplicationServices2Controller(); options.ApplicationServices2Controller(settings => { settings.RootPath = "rbac"; settings.TypePredicate = type => type == typeof(AuthService) || type == typeof(WeChatCustomerService); }); options.ApplicationServices2Controller(settings => { settings.RootPath = "rbac"; settings.TypePredicate = type => type != typeof(Fake.Rbac.Application.Services.AuthService) && type != typeof(Fake.Rbac.Application.Services.UserService); }); options.ApplicationServices2Controller(settings => { settings.RootPath = "files"; }); }); services.AddOmsWeChat(configuration); services.AddSingleton(); // Outbox / Inbox 与业务同库(EF),通过 DbTransaction 征用 SqlSugar 事务 services.AddDbContext(options => { var connectionString = configuration.GetConnectionString("IntegrationEventLog") ?? configuration.GetConnectionString("Default") ?? configuration["ConnectionStrings:Default"] ?? string.Empty; var serverVersion = ServerVersion.AutoDetect(connectionString); options.UseMySql(connectionString, serverVersion); #if DEBUG options.EnableSensitiveDataLogging(); #endif }); //Json格式化全局处理 context.Services.AddFakeNewtonsoft(); services.AddFakeAspNetCoreAuditing(); // 添加 Fake Swagger 支持 services.AddFakeSwaggerGen(addSecurity: true); // Fake.Rbac 菜单/组织/权限/角色接口无 XML 注释,补全 summary services.Configure(options => { options.OperationFilter(); }); services.AddCors(options => { options.AddPolicy(DefaultCorsPolicyName, builder => { builder.WithOrigins(configuration.GetSection("App:CorsOrigins").Get() ?? []) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); services.Configure(options => { // 反向代理场景下恢复客户端真实来源信息,供 Swagger、鉴权和重定向等中间件使用 options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); services.AddFeiShuExceptionSubscriber(); // 草稿已落库失败:结构化 Details + 清理 msg 机器标记 services.AddTransient(); var exceptionConverterDescriptors = services .Where(d => d.ServiceType == typeof(IException2ResponseConverter)) .ToList(); foreach (var descriptor in exceptionConverterDescriptors) { services.Remove(descriptor); } services.AddTransient(sp => new ExceptionHandling.OmsException2ResponseConverter( sp.GetRequiredService())); // 全局授权配置:所有接口默认需要授权 services.AddMvc(options => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); }); // 添加授权服务 services.AddAuthorization(); services.AddHostedService(); } public override void ConfigureApplication(ApplicationConfigureContext context) { var app = context.GetWebApplication(); // 必须在读取 Scheme/Host/PathBase 的中间件之前执行,否则 Swagger 会生成错误的请求地址 app.UseForwardedHeaders(); // 虚拟路由-网关适配 app.UsePathBase(app.Configuration["ASPNETCORE_PATHBASE"]); // Swagger app.UseFakeSwagger(); // 异常处理 app.UseFakeExceptionHandling(); // CORS app.UseCors(DefaultCorsPolicyName); // 静态文件服务(用于头像等上传文件) app.UseStaticFiles(); // 路由 - 必须在认证和授权之前 app.UseRouting(); // OpenAPI 验签需读取原始 Body,允许二次读流 app.Use(async (ctx, next) => { if (ctx.Request.Path.StartsWithSegments("/openapi")) { ctx.Request.EnableBuffering(); } await next(); }); // 认证和授权 app.UseAuthentication(); app.UseAuthorization(); // 角色/菜单写操作后失效登录用户信息缓存 app.UseMiddleware(); // 端点映射 app.MapControllers(); // FileManagement / Outbox 与业务同库并行(EF) using (var scope = app.Services.CreateScope()) { var fileDb = scope.ServiceProvider.GetRequiredService(); fileDb.Database.Migrate(); var outboxDb = scope.ServiceProvider.GetRequiredService(); IntegrationEventLogSchema.EnsureTablesAsync(outboxDb).GetAwaiter().GetResult(); } app.Services.GetRequiredService().Send("Application started"); } }