119 lines
4.6 KiB
C#
119 lines
4.6 KiB
C#
using Hangfire;
|
||
using Hangfire.MySql;
|
||
using HangfireBasicAuthenticationFilter;
|
||
using MAX.Core.Interfaces;
|
||
using MAX.Framework.Interfaces;
|
||
using Microsoft.AspNetCore.Builder;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.Hosting;
|
||
using MySql.Data.MySqlClient;
|
||
using Store.Modules.CloudProduct.Services;
|
||
|
||
namespace Store.Modules.CloudProduct
|
||
{
|
||
|
||
public class CloudProductModule : IModule
|
||
{
|
||
/// <summary>
|
||
/// 本模块自己的配置文件
|
||
/// </summary>
|
||
public const string ConfigurationFileName = "appsettings.CloudProduct.json";
|
||
|
||
public int Priority => IntPriority.Normal;
|
||
|
||
|
||
/// <summary>
|
||
/// 服务注册阶段(builder.Build() 之前):这里只能拿到容器和配置,碰不到 app
|
||
/// </summary>
|
||
public void AddModule(IHostApplicationBuilder host)
|
||
{
|
||
IServiceCollection services = host.Services;
|
||
IConfigurationManager configuration = host.Configuration;
|
||
|
||
if (!string.IsNullOrWhiteSpace(ConfigurationFileName))
|
||
{
|
||
string path = Path.IsPathRooted(ConfigurationFileName)? ConfigurationFileName: Path.Combine(AppContext.BaseDirectory, ConfigurationFileName);
|
||
configuration.AddJsonFile(path, optional: false, reloadOnChange: true);
|
||
}
|
||
|
||
|
||
|
||
|
||
//// 本模块的配置节(Search / RawData / Indexing / Delete)都在模块自己的配置文件里,
|
||
//// 关键项缺失直接报错,避免 OpenSearch 地址为空、原始数据写到意外目录之类的隐性故障
|
||
RequireConfiguration(configuration, "Search:Url");
|
||
RequireConfiguration(configuration, "Search:IndexName");
|
||
RequireConfiguration(configuration, "Search:FavoritesIndexName");
|
||
RequireConfiguration(configuration, "RawData:Path");
|
||
|
||
// ===== Hangfire 后台定时任务(商品分词同步) =====
|
||
var hangfireConnection = configuration.GetValue<string>("CloudProductConnection")
|
||
?? throw new InvalidOperationException("缺少配置 ConnectionStrings:DefaultConnection,CloudProduct 模块的 Hangfire 无法启动。");
|
||
|
||
// AllowUserVariables=true:Hangfire.MySqlStorage 的建表/查询语句依赖用户变量
|
||
var connectionStringBuilder = new MySqlConnectionStringBuilder(hangfireConnection)
|
||
{
|
||
AllowUserVariables = true
|
||
};
|
||
|
||
services.AddHangfire(config => config
|
||
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
|
||
.UseSimpleAssemblyNameTypeSerializer()
|
||
.UseRecommendedSerializerSettings()
|
||
.UseStorage(new MySqlStorage(connectionStringBuilder.ConnectionString, new MySqlStorageOptions() { TablesPrefix = "Hangfire_" })));
|
||
services.AddHangfireServer();
|
||
services.AddTransient<GoodsIndexJob>();
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 管道装配阶段(builder.Build() 之后、app.Run() 之前):这里可以挂中间件、解析容器里的服务
|
||
/// </summary>
|
||
public void UseModule(IApplicationBuilder app)
|
||
{
|
||
try
|
||
{
|
||
var Configuration = app.ApplicationServices.GetService<IConfiguration>()!;
|
||
// 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 * * * *";
|
||
|
||
// 用容器里的 IRecurringJobManager,而不是静态 RecurringJob:
|
||
// AddHangfire 只把 JobStorage 注册进容器(没有设置 JobStorage.Current),静态 API 在 Build() 之后调用会直接抛异常。
|
||
app.ApplicationServices.GetRequiredService<IRecurringJobManager>().AddOrUpdate<GoodsIndexJob>(
|
||
"goods-index-pending",
|
||
job => job.ProcessPendingAsync(),
|
||
cron);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 数据库不可用/表未初始化时避免阻断应用启动(看板与定时任务都会连库),恢复后重启即可重新装配
|
||
Console.WriteLine("[Hangfire] 看板/定时任务装配失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 校验模块配置项存在(值来自模块自己的 <see cref="ConfigurationFileName"/>)
|
||
/// </summary>
|
||
private static void RequireConfiguration(IConfiguration configuration, string key)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(configuration.GetValue<string>(key)))
|
||
{
|
||
throw new InvalidOperationException($"缺少配置 {key},请检查模块配置文件 {ConfigurationFileName}。");
|
||
}
|
||
}
|
||
}
|
||
}
|