Files
StoreWeb/Plugins/CloudProduct/Services/GoodsIndexLock.cs
T
2026-11-06 19:25:36 +08:00

48 lines
2.0 KiB
C#

using MAX.Core.DependencyInjection;
using MAX.Framework.Commons;
using MAX.Plugins;
using Microsoft.Extensions.DependencyInjection;
using MySql.Data.MySqlClient;
using System.Security.Cryptography;
using System.Text;
namespace Store.Modules.CloudProduct.Services;
/// <summary>跨实例串行化违规上报和商品索引写入,防止下架后被旧批次写回。</summary>
public class GoodsIndexLock([FromKeyedServices("CloudProduct")] PluginContext context) : ISingletonDependency
{
public Task<IAsyncDisposable> AcquireAsync() => AcquireAsync("goods-index:");
/// <summary>收藏重建、整条文档写入和安全状态更新共用独立锁,防止旧快照覆盖新结论。</summary>
public virtual Task<IAsyncDisposable> AcquireFavoritesAsync() => AcquireAsync("favorites-index:");
private async Task<IAsyncDisposable> AcquireAsync(string prefix)
{
// 禁用连接池:即使释放锁失败,关闭物理连接也会释放会话锁。
var settings = new MySqlConnectionStringBuilder(context.Configuration["CloudProductConnection"])
{
Pooling = false
};
var connection = new MySqlConnection(settings.ConnectionString);
var name = prefix + Convert.ToHexString(SHA256.HashData(
Encoding.UTF8.GetBytes(settings.Database)))[..40];
try
{
await connection.OpenAsync();
using var command = connection.CreateCommand();
command.CommandText = "SELECT GET_LOCK(@name, 30)";
command.CommandTimeout = 35;
command.Parameters.AddWithValue("@name", name);
var result = await command.ExecuteScalarAsync();
if (result == null || result == DBNull.Value || Convert.ToInt32(result) != 1)
throw new AppException("商品索引正在处理,请稍后重试");
return connection;
}
catch
{
await connection.DisposeAsync();
throw;
}
}
}