80 lines
3.8 KiB
C#
80 lines
3.8 KiB
C#
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<GoodsIndexJob>();
|
||
services.AddTransient<GoodsViolationIndexJob>();
|
||
services.AddAuthorization(options => options.AddPolicy("CloudGoodsViolationWrite",
|
||
policy => policy.RequireAuthenticatedUser().RequireClaim("permission", "cloudgoods:violation:write")));
|
||
services.AddKeyedSingleton<IFreeSql>("CloudProductConnection", (_, _) =>
|
||
new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, connectionStringBuilder.ConnectionString)
|
||
.UseAdoConnectionPool(true).Build());
|
||
services.AddHostedService<CloudProductSearchStartup>();
|
||
// 服务自行创建客户端,单例复用连接池并由容器负责释放。
|
||
services.AddSingleton<ImageAnalysisQuotaService>();
|
||
|
||
// 图片识别沿用专用缓存库,所有请求共享并发上限。
|
||
var inspectionConnection = RequireConfiguration("ProductInspection:ConnectionString");
|
||
services.AddKeyedSingleton<IFreeSql>("ProductInspectionConnection", (_, _) =>
|
||
new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, inspectionConnection)
|
||
.UseAdoConnectionPool(true).Build());
|
||
var maximum = Context.Configuration.GetValue<int?>("ProductInspection:MaxConcurrentAnalysis") ?? 5;
|
||
if (maximum is < 1 or > 100) throw new InvalidOperationException("识别并发数必须在 1 至 100 之间。");
|
||
services.AddKeyedSingleton<SemaphoreSlim>("ProductInspectionAnalysisGate", (_, _) => new SemaphoreSlim(maximum, maximum));
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 管道装配阶段(builder.Build() 之后、app.Run() 之前):这里可以挂中间件、解析容器里的服务
|
||
/// </summary>
|
||
protected override void ConfigureApplication(IApplicationBuilder app)
|
||
{
|
||
try
|
||
{
|
||
var Configuration = Context.Configuration;
|
||
// 注册定时任务:把未同步(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);
|
||
app.ApplicationServices.GetRequiredService<IRecurringJobManager>().AddOrUpdate<GoodsViolationIndexJob>(
|
||
"goods-violation-delete-pending", job => job.ProcessPendingAsync(), "* * * * *");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 定时任务注册失败时记录错误,数据库恢复后重启以重新注册。
|
||
Console.WriteLine("[Hangfire] CloudProduct 定时任务注册失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
}
|