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; /// 跨实例串行化违规上报和商品索引写入,防止下架后被旧批次写回。 public class GoodsIndexLock([FromKeyedServices("CloudProduct")] PluginContext context) : ISingletonDependency { public Task AcquireAsync() => AcquireAsync("goods-index:"); /// 收藏重建、整条文档写入和安全状态更新共用独立锁,防止旧快照覆盖新结论。 public virtual Task AcquireFavoritesAsync() => AcquireAsync("favorites-index:"); private async Task 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; } } }