更新Hangfire提取到 框架

This commit is contained in:
chfei committed 2026-11-03 12:17:49 +08:00
1 parent 33ef90a5cc
commit 7c29e57623
11 files changed
+151 -48

No files matched your search

+9 -34
View File
@@ -1,7 +1,5 @@
using MAX.Plugins;
using Hangfire;
using Hangfire.MySql;
using HangfireBasicAuthenticationFilter;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -16,28 +14,17 @@ namespace Store.Modules.CloudProduct
public override string Id => "CloudProduct";
protected override void RegisterServices(IServiceCollection services)
{
var configuration = Context.Configuration;
RequireConfiguration("Search:Url");
RequireConfiguration("Search:IndexName");
RequireConfiguration("Search:FavoritesIndexName");
RequireConfiguration("RawData:Path");
// ===== Hangfire 后台定时任务(商品分词同步) =====
var hangfireConnection = configuration.GetValue<string>("CloudProductConnection")
?? throw new InvalidOperationException("缺少配置 CloudProductConnection,CloudProduct 模块的 Hangfire 无法启动。");
// 存储中的建表/查询语句依赖用户变量;与业务仓储共用 FreeSql。
var connectionStringBuilder = new MySqlConnectionStringBuilder(hangfireConnection)
{
AllowUserVariables = true
};
services.AddHangfire((provider, config) => config
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseStorage(new MySqlStorage(provider.GetRequiredKeyedService<IFreeSql>("CloudProductConnection"), new MySqlStorageOptions() { TablesPrefix = "Hangfire_" })));
services.AddHangfireServer();
// 业务连接独立于宿主的 Hangfire 存储。
var connection = RequireConfiguration("CloudProductConnection");
var connectionStringBuilder = new MySqlConnectionStringBuilder(connection)
{
AllowUserVariables = true
};
services.AddTransient<GoodsIndexJob>();
services.AddTransient<GoodsViolationIndexJob>();
services.AddAuthorization(options => options.AddPolicy("CloudGoodsViolationWrite",
@@ -57,18 +44,6 @@ namespace Store.Modules.CloudProduct
try
{
var Configuration = Context.Configuration;
// Hangfire 后台任务看板(默认地址 /hangfire)
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[]
{
new HangfireCustomBasicAuthenticationFilter(){
User = Configuration.GetValue<string>("Hangfire:Dashboard:User") ?? "admin",
Pass = Configuration.GetValue<string>("Hangfire:Dashboard:Pass") ?? "1234560"
}
}
});
// 注册定时任务:把未同步(IndexStatus=0)的商品写入 OpenSearch 分词索引
var cron = Configuration.GetValue<string>("Indexing:Cron") ?? "*/5 * * * *";
@@ -83,11 +58,11 @@ namespace Store.Modules.CloudProduct
}
catch (Exception ex)
{
// 数据库不可用/表未初始化时避免阻断应用启动(看板与定时任务都会连库),恢复后重启即可重新装配
Console.WriteLine("[Hangfire] 看板/定时任务装配失败: " + ex.Message);
// 定时任务注册失败时记录错误,数据库恢复后重启以重新注册。
Console.WriteLine("[Hangfire] CloudProduct 定时任务注册失败: " + ex.Message);
}
}
}
}
}
@@ -2,10 +2,19 @@ using Microsoft.Extensions.DependencyInjection;
namespace Store.Modules.CloudProduct.Services;
/// <summary>
/// 违规商品搜索索引清理任务,由 CloudProductPlugin 注册为每分钟执行一次。
/// 每批处理最多 1000 个尚未完成索引删除的违规商品,循环处理至无待处理记录。
/// </summary>
public class GoodsViolationIndexJob(IServiceScopeFactory scopeFactory)
{
/// <summary>
/// 删除待处理商品的 OpenSearch 索引,成功后将违规记录的 IsIndexDeleted 标记为 true。
/// 失败时异常交由 Hangfire 处理,尚未标记完成的记录留待后续执行继续处理。
/// </summary>
public async Task ProcessPendingAsync()
{
// 为本次任务创建独立作用域,执行结束后释放业务服务及其作用域依赖。
using var scope = scopeFactory.CreateScope();
await scope.ServiceProvider.GetRequiredService<GoodsViolationService>()
.DeleteViolationIndexesAsync();
@@ -23,12 +23,7 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.25" />
<PackageReference Include="Hangfire.Dashboard.Basic.Authentication" Version="8.0.0" />
<PackageReference Include="Hangfire.MySql" Version="1.0.0" />
<PackageReference Include="OpenSearch.Client" Version="2.1.0" />
<!-- 覆盖 Hangfire 的旧传递依赖,修复 CVE-2024-21907。 -->
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>
</Project>
-6
View File
@@ -15,11 +15,5 @@
},
"Delete": {
"SoftDelete": true
},
"Hangfire": {
"Dashboard" :{
"User": "admin",
"Pass": "1234560"
}
}
}
+11 -2
View File
@@ -66,8 +66,16 @@ public ExampleService([FromKeyedServices("CloudProduct")] PluginContext context)
}
```
数据库、Hangfire、OpenSearch 保留在各自插件内;认证、日志等仍由宿主管理。
CloudProduct 的搜索启动检查使用 BackgroundService,网络错误记录日志,不阻断 HTTP 启动;Hangfire 仍按既有配置运行。
业务数据库和 OpenSearch 保留在各自插件内;Hangfire、认证、日志由宿主管理。
CloudProduct 的搜索启动检查使用 BackgroundService,网络错误记录日志,不阻断 HTTP 启动。
### 宿主 Hangfire 配置
宿主通过 MAX.Framework 的 AddStoreHangfire 注册共享存储和 Worker,在认证、授权中间件之后调用 UseStoreHangfire 挂载 `/hangfire`。插件只注册自己的 Job 和定时任务。
宿主 appsettings.json 必须提供 `Hangfire:ConnectionString`、`Hangfire:Dashboard:User` 和 `Hangfire:Dashboard:Pass`。可用环境变量 `Hangfire__ConnectionString`、`Hangfire__Dashboard__User`、`Hangfire__Dashboard__Pass` 覆盖。原 `Plugins__CloudProduct__Hangfire__Dashboard__*` 配置需迁移至宿主。
初始连接指向原 CloudProduct 任务库,继续使用 `Hangfire_` 表和已有任务。部署时将实际 CloudProductConnection 对应的任务库连接填入宿主配置,保留原 Job 程序集、类型、方法和定时任务 ID。Hangfire 使用独立 FreeSql 单例,云商品业务仍使用插件的 CloudProductConnection。数据库恢复后,定时任务注册失败的实例需重启以重新注册。
## 新增插件
@@ -81,6 +89,7 @@ CloudProduct 的搜索启动检查使用 BackgroundService,网络错误记录
```powershell
dotnet run --project Tests/PluginLoadingSmoke -- configuration
dotnet run --project Tests/PluginLoadingSmoke -- hangfire
dotnet run --project Tests/PluginLoadingSmoke -- "$PWD/Bin/Plugins"
dotnet run --project Tests/PluginLoadingSmoke -- "$PWD/Bin/Plugins/Store.ImageCache" image
dotnet run --project Tests/PluginLoadingSmoke -- <空目录或不存在目录> empty