using BuildFlow.Host.Configuration; using BuildFlow.Host.Hubs; using BuildFlow.Host.Services.BackgroundServices; using BuildFlow.Host.Services.Integration; using BuildFlow.Host.Services.Notifications; using BuildFlow.Host.Services.Users; using FastEndpoints.Swagger; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using BuildFlow.Shared.Api; using BuildFlow.Shared.Infrastructure.Serialization; using File.Application; using File.Infrastructure; using IM.Application; using IM.Domain.Common; using IM.Infrastructure; using BuildFlow.Platform.Application; using BuildFlow.Platform.Infrastructure.Persistence; using Volo.Abp; using Volo.Abp.Modularity; using Workflow.Application; using Workflow.Application.Engine; using Workflow.Application.Integration; using Workflow.Application.Notifications; using Workflow.Infrastructure; namespace BuildFlow.Host; [DependsOn( typeof(BuildFlow.Application.RAGApplicationModule), typeof(BuildFlow.Infrastructure.RAGInfrastructureModule), typeof(PlatformApplicationModule), typeof(FileApplicationModule), typeof(FileInfrastructureModule), typeof(IMApplicationModule), typeof(IMInfrastructureModule), typeof(WorkflowApplicationModule), typeof(WorkflowInfrastructureModule))] /// /// BuildFlow 主 Host 的 ABP 模块。通过 [DependsOn] 聚合 platform/rag/file/im/workflow 五个业务模块的 /// Application + Infrastructure 层,统一注册跨切面基础设施(OTel/FastEndpoints/SignalR/JWT/CORS/ /// 跨模块集成接口)与中间件管道。HTTP 层(Endpoints/Hubs)已全部并入本 Host 程序集。 /// platform 模块承载用户/角色/权限/菜单/通知(原属 rag,已独立成库)。 /// public class BuildFlowHostModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { var services = context.Services; var config = services.GetConfiguration(); // ===== 跨切面基础设施(仅注册一次)===== // OpenTelemetry:统一导出 Traces + Metrics var otlpEndpoint = config["OpenTelemetry:OtlpEndpoint"] ?? "http://localhost:4316"; services.AddOpenTelemetry() .ConfigureResource(r => r .AddService(serviceName: "buildflow", serviceVersion: "1.0.0")) .WithTracing(tracing => tracing .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddEntityFrameworkCoreInstrumentation() .AddOtlpExporter(o => o.Endpoint = new Uri(otlpEndpoint))) .WithMetrics(metrics => metrics .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddRuntimeInstrumentation() .AddOtlpExporter((exporter, reader) => exporter.Endpoint = new Uri(otlpEndpoint))); // FastEndpoints + Swagger(Endpoints 已并入本程序集,默认扫描当前程序集) services.AddFastEndpoints(); services.SwaggerDocument(); // SignalR(rag 通知 + im 聊天共用) services.AddSignalR(); // im 的 DomainExceptionHubFilter 注册为全局 hub filter(统一异常信封) services.AddSingleton(); // 当前用户上下文:各模块的 CurrentUserContext 实现 services.AddHttpContextAccessor(); services.AddScoped(); services.AddScoped(); // 串联:IM @AI → RAG 问答(跨模块集成,RAG 实现 IAiBotHandler 供 IM 调用) services.AddScoped(); // 串联:AI 对话 → 触发工作流(跨模块集成,Workflow 实现 IWorkflowTriggerService 供 RAG 调用) services.AddScoped(); // workflow 后台服务:超时调度 + Webhook 投递 services.AddScoped(); services.Configure(config.GetSection("Workflow:Notifications")); services.AddNotifications(config); services.AddHostedService(); services.AddHostedService(); // 消息中心通知分发器(仅 rag 用) services.AddScoped(); // 进程内文件存储:覆盖 rag 原有的 HTTP 版 FileStorageClient(后注册者胜出) services.AddScoped(); // JWT 认证 + CORS:使用共享内核扩展方法(消除重复的认证/跨域配置) services.AddSharedJwtAuthentication(config); services.AddSharedCors(); // 响应包裹中间件配置:文件下载为二进制流式响应,必须绕过 JSON 包裹与 MemoryStream 缓冲 services.Configure(opts => { opts.PassthroughPrefixes.Add("/api/files/download"); // Swagger/OpenAPI 文档必须保持原始结构,不能包裹成 { code, data, message },否则 Swagger UI 无法识别 openapi 字段。 opts.PassthroughPrefixes.Add("/swagger"); }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); // 中间件管道(只装配一次,避免双重包裹): // CORS → Routing → GlobalException → gRPC ×2 → ApiResponse → Auth → AuthZ → FastEndpoints → Swagger app.UseCors(); app.UseRouting(); // 全局异常中间件(rag 版本是超集:含流式 HasStarted 守卫) app.UseMiddleware(); // 响应包裹中间件(rag 版本是超集:含 /api/auth/refresh 排除 + SSE 流式路径排除) app.UseMiddleware(); app.UseAuthentication(); app.UseAuthorization(); app.UseFastEndpoints(config => { config.Endpoints.RoutePrefix = "api"; // 统一数据规范:时间字段输出 UTC 毫秒时间戳 config.Serializer.Options.Converters.Add(new TimestampDateTimeConverter()); config.Serializer.Options.Converters.Add(new TimestampDateTimeOffsetConverter()); // 校验失败统一 { code:400, message, data:null }(使用内核共享信封类型) config.Errors.StatusCode = 400; config.Errors.ResponseBuilder = (failures, ctx, statusCode) => { var message = string.Join("; ", failures.Select(f => f.ErrorMessage)); return new BuildFlow.Shared.Api.ValidationErrorResponse(400, message); }; // 全局异常过滤器:扩展为同时识别 rag 与 file-system 两套领域异常 config.Endpoints.Configurator = ep => { ep.PostProcessors(Order.After, typeof(AggregateExceptionHandler<,>)); }; }); app.UseSwaggerGen(); // SignalR Hub(rag 通知 + im 聊天,不同路径共存) context.GetEndpointRouteBuilder().MapHub("/hubs/notifications"); context.GetEndpointRouteBuilder().MapHub("/hubs/chat"); } } /// /// 聚合异常过滤器(FastEndpoints post-processor),同时映射 rag 与 file-system 两套领域异常。 /// 使用内核共享的 信封类型(替代之前的私有 ExceptionEnvelope record)。 ///