using MAX.Plugins; using Hangfire; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using MySql.Data.MySqlClient; using Store.Modules.CloudProduct.Services; namespace Store.Modules.CloudProduct { public sealed class CloudProductPlugin : PluginBase { public override string Id => "CloudProduct"; protected override void RegisterServices(IServiceCollection services) { RequireConfiguration("Search:Url"); RequireConfiguration("Search:IndexName"); RequireConfiguration("Search:FavoritesIndexName"); RequireConfiguration("RawData:Path"); // 业务连接独立于宿主的 Hangfire 存储。 var connection = RequireConfiguration("CloudProductConnection"); var connectionStringBuilder = new MySqlConnectionStringBuilder(connection) { AllowUserVariables = true }; services.AddTransient(); services.AddTransient(); services.AddAuthorization(options => options.AddPolicy("CloudGoodsViolationWrite", policy => policy.RequireAuthenticatedUser().RequireClaim("permission", "cloudgoods:violation:write"))); services.AddKeyedSingleton("CloudProductConnection", (_, _) => new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, connectionStringBuilder.ConnectionString) .UseAdoConnectionPool(true).Build()); services.AddHostedService(); // 服务自行创建客户端,单例复用连接池并由容器负责释放。 services.AddSingleton(); var maximum = Context.Configuration.GetValue("ProductInspection:MaxConcurrentAnalysis") ?? 5; if (maximum is < 1 or > 100) throw new InvalidOperationException("识别并发数必须在 1 至 100 之间。"); services.AddKeyedSingleton("ProductInspectionAnalysisGate", (_, _) => new SemaphoreSlim(maximum, maximum)); } /// /// 管道装配阶段(builder.Build() 之后、app.Run() 之前):这里可以挂中间件、解析容器里的服务 /// protected override void ConfigureApplication(IApplicationBuilder app) { try { var Configuration = Context.Configuration; // 注册定时任务:把未同步(IndexStatus=0)的商品写入 OpenSearch 分词索引 var cron = Configuration.GetValue("Indexing:Cron") ?? "*/5 * * * *"; // 用容器里的 IRecurringJobManager,而不是静态 RecurringJob: // AddHangfire 只把 JobStorage 注册进容器(没有设置 JobStorage.Current),静态 API 在 Build() 之后调用会直接抛异常。 app.ApplicationServices.GetRequiredService().AddOrUpdate( "goods-index-pending", job => job.ProcessPendingAsync(), cron); app.ApplicationServices.GetRequiredService().AddOrUpdate( "goods-violation-delete-pending", job => job.ProcessPendingAsync(), "* * * * *"); } catch (Exception ex) { // 定时任务注册失败时记录错误,数据库恢复后重启以重新注册。 Console.WriteLine("[Hangfire] CloudProduct 定时任务注册失败: " + ex.Message); } } } }