48 lines
2.0 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|