using Dpz.Core.Backup;
using Dpz.Core.Infrastructure;
using Dpz.Core.MessageQueue.Extensions;
using Dpz.Core.Public.ViewModel.Messages;
using Dpz.Core.Service.Mcp;
using Dpz.Core.Service.Network;
using Dpz.Core.Service.ObjectStorage;
using Dpz.Core.Service.RepositoryService;
using Dpz.Core.Service.RepositoryServiceImpl;
using Dpz.Core.Web.Jobs.Hangfire;
using Dpz.Core.Web.Jobs.Hangfire.Build;
using Dpz.Core.Web.Jobs.MessageHandlers;
using Dpz.Core.Web.Jobs.Services;
using Dpz.Core.Web.Jobs.Services.FileTransfer;
using Dpz.Core.Web.Jobs.Services.Implements;
namespace Dpz.Core.Web.Jobs;
/// <summary>
/// Web.Jobs 宿主的服务注册扩展方法。
/// </summary>
public static class RegisterDependencyInjectionExtensions
{
/// <summary>
/// 注册 Web.Jobs 运行所需的默认业务服务。
/// </summary>
/// <param name="services">服务集合。</param>
/// <returns>注册后的服务集合。</returns>
public static IServiceCollection AddDefaultInjectServices(this IServiceCollection services)
{
services.AddSingleton<IPushMessage, PushMessage>();
// 文件传输:房间存储(单例,内存存储)
services.AddSingleton<IFileTransferRoomStore, FileTransferRoomStore>();
services.AddSingleton<IFileTransferSessionService, FileTransferSessionService>();
// 文件传输:过期房间后台清理
services.AddHostedService<FileTransferRoomCleanupService>();
services.AddScoped<IBackupRecordService, BackupRecordService>();
services
.AddHttpClient(
"edge",
(_, httpClient) =>
{
httpClient.DefaultRequestHeaders.Add("User-Agent", EnvironmentInfo.UserAgent);
httpClient.Timeout = TimeSpan.FromMinutes(5);
}
)
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
MaxConnectionsPerServer = 10,
});
// 音乐同步下载的大文件耗时较长,使用独立的超时和连接策略
services
.AddHttpClient(
MusicSyncActivator.HttpClientName,
(_, httpClient) =>
{
httpClient.DefaultRequestHeaders.Add("User-Agent", EnvironmentInfo.UserAgent);
httpClient.Timeout = TimeSpan.FromMinutes(30);
}
)
.ConfigurePrimaryHttpMessageHandler(_ => new SocketsHttpHandler
{
ConnectTimeout = TimeSpan.FromSeconds(30),
PooledConnectionLifetime = TimeSpan.FromMinutes(10),
MaxConnectionsPerServer = 4,
});
services.AddNetworkServices();
services.AddInProcessCodeMcp();
services.AddS3ObjectStorage();
services.AddBackupRestoreService();
// 注册邮件发送辅助服务
services.AddScoped<EmailSenderHelper>();
// 获取ssh配置和校验服务
services.AddScoped<SshConfigurationService>();
services.AddHttpClient(
PipelineVersionProbe.HttpClientName,
(_, httpClient) =>
{
httpClient.Timeout = TimeSpan.FromSeconds(5);
httpClient.DefaultRequestHeaders.Add("User-Agent", EnvironmentInfo.UserAgent);
}
);
services.AddScoped<PipelineVersionProbe>();
return services;
}
/// <summary>
/// 注册 Web.Jobs 需要消费的消息队列处理器和 Outbox 后台补发服务
/// </summary>
/// <param name="services">服务集合。</param>
/// <returns>注册后的服务集合。</returns>
public static IServiceCollection AddMessageQueueServices(this IServiceCollection services)
{
// 注册消息消费者
services.AddMessageConsumer<DeleteMarkdownMessage, DeleteMarkdownHandler>();
services.AddMessageConsumer<RemoveImagesMessage, RemoveImagesHandler>();
services.AddMessageConsumer<AddCommentCountMessage, AddCommentCountHandler>();
services.AddMessageConsumer<AddDanmakuCountMessage, AddDanmakuCountHandler>();
services.AddMessageConsumer<AnalyzeCodeMessage, AnalyzeCodeHandler>();
services.AddMessageConsumer<SendMasterEmailMessage, SendMasterEmailHandler>();
services.AddMessageConsumer<SendReplyEmailMessage, SendReplyEmailHandler>();
services.AddMessageConsumer<SendEmailVerifyCodeMessage, SendEmailVerifyCodeHandler>();
services.AddMessageConsumer<GetImageMetadataMessage, GetImageMetadataHandler>();
services.AddMessageOutboxRetryWorker();
return services;
}
}
RegisterDependencyInjectionExtensions.cs 说明
文件定位
该文件位于 Dpz.Core.Web.Jobs 宿主项目根目录,定义了一个静态扩展类 RegisterDependencyInjectionExtensions,为 IServiceCollection 提供两个扩展方法:AddDefaultInjectServices()(默认业务服务)与 AddMessageQueueServices()(消息队列消费服务)。它是整个 Jobs 宿主的 DI 装配中心——所有后台任务、消息消费、文件传输、推送等能力都在这里一次性登记。
该文件本身是纯声明式注册代码,没有复杂执行逻辑,但涉及大量跨项目/跨命名空间符号,下面按已验证的源码关系逐项说明。
宿主如何调用它
在入口 Program.cs 中有如下链式调用:
services.AddBusinessServices(configuration);
services.AddDefaultServices().AddDefaultInjectServices().AddMessageQueueServices();
其中 AddBusinessServices、AddDefaultServices 来自基础服务层(非本文件),本文件的两个方法是 Web.Jobs 自身特有依赖的补充,随后 Program.cs 还会注册 OpenIddict 客户端、Hangfire(AddAppHangfireService)、MVC 与 SignalR Hub 等。
方法一:AddDefaultInjectServices() — 默认业务服务
按生命周期分组,注册内容如下:
1. 单例业务服务
IPushMessage → PushMessage:推送服务。接口定义见 IPushMessage.cs,提供日志消息、CnBeta 消息、代码分析进度、应用通知四类推送(SignalR 对外通知)。- 文件传输(内存态):
IFileTransferRoomStore → FileTransferRoomStore(房间存储)与IFileTransferSessionService → FileTransferSessionService(会话服务),接口/实现均在 Services/FileTransfer 目录下。
2. 后台托管服务
FileTransferRoomCleanupService(HostedService):见 FileTransferRoomCleanupService.cs,每 30 秒用PeriodicTimer扫描过期未配对的传输房间,并通过IHubContext<FileTransferHub>向发起方推送roomExpired通知。
3. Scoped 服务
IBackupRecordService → BackupRecordService(备份记录,来自Dpz.Core.Service.RepositoryService*)。EmailSenderHelper(邮件发送辅助)、SshConfigurationService(读取/校验 SSH 配置,位于 Hangfire/Build)、PipelineVersionProbe(探测各发布目标版本,位于 PipelineVersionProbe.cs)。
4. 三组命名 HttpClient(通过 IHttpClientFactory 注册)
| 客户端名 | 超时/策略 | 用途 |
|---|---|---|
"edge" | 5 分钟超时、固定 User-Agent、跳过证书校验、单服务器最多 10 连接 | 通用外站抓取,在本项目内被 TaskService.cs、ItHomeActivator.cs 通过 CreateClient("edge") 使用 |
MusicSyncActivator.HttpClientName("music-sync") | 30 分钟超时、SocketsHttpHandler 连接池 10 分钟、并发 4 | 音乐同步大文件下载(常量定义见 MusicSyncActivator.cs) |
PipelineVersionProbe.HttpClientName("pipeline-version") | 5 秒超时 + User-Agent | 发布流水线版本探测(常量定义见 PipelineVersionProbe.cs) |
5. 子项目扩展方法聚合
AddNetworkServices()(来自Dpz.Core.Service.Network)、AddInProcessCodeMcp()(Dpz.Core.Service.Mcp)、AddS3ObjectStorage()(Dpz.Core.Service.ObjectStorage)、AddBackupRestoreService()(Dpz.Core.Backup)。这些扩展的实际实现文件未逐一读取,仅通过文件顶部using与其所在的程序集命名空间对应确认。
方法二:AddMessageQueueServices() — 消息队列消费
逐一注册了 9 组「消息类型 → 处理器」,消息类型来自 Dpz.Core.Public.ViewModel.Messages,处理器全部位于 MessageHandlers 目录:
| 消息类型 | 处理器(均已确认存在) |
|---|---|
DeleteMarkdownMessage | DeleteMarkdownHandler.cs |
RemoveImagesMessage | RemoveImagesHandler.cs |
AddCommentCountMessage | AddCommentCountHandler.cs |
AddDanmakuCountMessage | AddDanmakuCountHandler.cs |
AnalyzeCodeMessage | AnalyzeCodeHandler.cs |
SendMasterEmailMessage | SendMasterEmailHandler.cs |
SendReplyEmailMessage | SendReplyEmailHandler.cs |
SendEmailVerifyCodeMessage | SendEmailVerifyCodeHandler.cs |
GetImageMetadataMessage | GetImageMetadataHandler.cs |
这些调用均落到 Dpz.Core.MessageQueue.Extensions 命名空间的基础设施扩展。已通过阅读 MessageQueueServiceExtensions.cs 确认其语义:
AddMessageConsumer<TMessage, THandler>():把处理器注册为 Scoped 的IMessageHandler<TMessage>,同时注册一个RabbitMQConsumerBackgroundService<TMessage>托管后台服务来订阅并消费对应队列。AddMessageOutboxRetryWorker():注册 Singleton 的IMessageOutboxRetryService与MessageOutboxRetryBackgroundService托管后台服务,用于把发布失败的 Outbox 记录定时补发。
说明:RabbitMQ 基础设施的注册(连接工厂、路由约定、发布者、配置节
AddRabbitMQ(...))不在本文件内,由宿主基础服务层完成;本文件只负责「消费端」装配。
整体协作流程
flowchart TD
Start["Program.cs 宿主启动<br/>WebApplication.CreateBuilder"] --> Chain["AddDefaultServices()<br/>AddBusinessServices()(基础服务层,非本文件)"]
Chain --> M1["AddDefaultInjectServices()<br/>(本文件 · 默认业务服务)"]
M1 --> P1["Singleton: PushMessage<br/>对外推送日志/进度/通知"]
M1 --> P2["Singleton: 文件传输房间/会话<br/>FileTransferRoomStore / FileTransferSessionService"]
M1 --> P3["HostedService: FileTransferRoomCleanupService<br/>每30s清理过期房间并通知 FileTransferHub"]
M1 --> P4["Scoped: 备份记录 / 邮件 / SSH配置 / 版本探测"]
M1 --> P5["HttpClientFactory: edge / music-sync / pipeline-version<br/>(差异化超时与连接策略)"]
M1 --> P6["子项目扩展: Network / InProcessCodeMcp / S3 / BackupRestore"]
Chain --> M2["AddMessageQueueServices()<br/>(本文件 · 消息队列消费)"]
M2 --> C1["9 × AddMessageConsumer<TMsg,THandler><br/>处理器注册为 Scoped + RabbitMQ 后台消费服务"]
M2 --> C2["AddMessageOutboxRetryWorker<br/>Outbox 失败消息补发后台服务"]
C1 --> RT["运行时: RabbitMQ 消息到达"]
RT --> H["对应处理器执行(MessageHandlers 目录)"]
H --> RES["处理成功 / 失败记录 Outbox 待补发"]
C2 -.定时补发.-> RT
P5 -. "CreateClient('edge')" .-> FE["TaskService / ItHomeActivator 外站抓取"]
P3 -. SignalR .-> HUB["FileTransferHub 房间过期通知"]
小结
- 本文件把 Jobs 宿主的依赖拆成两个语义清晰、可按需组合的扩展方法:
AddDefaultInjectServices(业务服务装配)与AddMessageQueueServices(消息消费装配),只做声明式注册,逻辑简单直接。 - 覆盖了该宿主的三类支撑能力:后台定时任务(配合 Hangfire 的 Activator/Probe)、RabbitMQ 消息消费 + Outbox 补发、文件传输与实时推送(SignalR),并通过多个命名 HttpClient 工厂区分不同场景的网络策略(抓取、大文件下载、快速版本探测)。
- 符号协作关系均已在源码中验证;
NetworkServiceExtensions、McpServiceExtensions、ObjectStorageExtension、BackupExtensions四个子项目扩展的具体实现文件未逐一读取,仅依据using与命名空间推定其来自相应程序集。