using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; using MesETL.App.Options; using MesETL.Shared.Helper; using Microsoft.Extensions.Options; using MySqlConnector; namespace MesETL.App.Services.Seq; public class SeqService { private readonly string _connectionString; private readonly Dictionary _cachedSequence; public IReadOnlyDictionary CachedSequence => _cachedSequence; public SeqService(IOptions options) { var connStr = options.Value.ConnectionString ?? throw new ApplicationException("未配置输出数据库连接字符串"); var builder = new MySqlConnectionStringBuilder(connStr) { Database = "mes_global" }; _connectionString = builder.ConnectionString; _cachedSequence = new Dictionary(); } private async Task UpdateSequenceID(string name,int step,long max,bool recycle, int add) { var sql = new StringBuilder( $""" INSERT INTO seq (SeqName,CurrentVal,Increment,MinVal,MaxVal,UpdateTime) VALUES ({name},{add},{step},1,{max},NOW()) ON DUPLICATE KEY UPDATE UpdateTime = NOW(), """); if (recycle) { sql.Append($"CurrentVal = (@updatedVal := IF(CurrentVal + {add} >= MaxVal, {add}, CurrentVal + {add}));"); } else { sql.Append($"CurrentVal = (@updatedVal := CurrentVal + {add});"); } sql.Append("SELECT @updatedVal;"); var result = await DatabaseHelper.QueryScalarAsync(_connectionString, sql.ToString()); return Convert.ToInt64(result); } public async Task PeekKey(SeqConfig config) { var sql = $"SELECT CurrentVal FROM seq WHERE SeqName = '{config.Name}' LIMIT 1;"; return Convert.ToInt64(await DatabaseHelper.QueryScalarAsync(_connectionString, sql)); } public async Task GetKeys(SeqConfig config, int count) { if (count < 1) return []; var list = new long[count]; var add = config.Step * count; var lastId = await UpdateSequenceID(config.Name, config.Step, config.Max, config.Recycle, add); var step = Convert.ToInt64(config.Step); for (var i = count - 1; i > -1; i--) { list[i] = lastId; lastId -= step; } return list; } /// /// 添加并取得一个缓存的流水号 /// /// /// public long AddCachedSeq(SeqConfig config) { if (!_cachedSequence.TryGetValue(config, out var val)) { var seq = PeekKey(config).GetAwaiter().GetResult(); val = seq; _cachedSequence[config] = val; } var step = config.Step; if (config.Recycle) { val = val + step >= config.Max ? val : val + step; } else val += step; _cachedSequence[config] = val; return val; } /// /// 移除一个缓存的流水号 /// /// public void RemoveCachedSeq(SeqConfig config) { _cachedSequence.Remove(config); } /// /// 清空所有缓存的流水号 /// public void ClearCache() { _cachedSequence.Clear(); } /// /// 将缓存的流水号应用至数据库 /// public async Task ApplyToDatabaseAsync() { if (_cachedSequence.Count == 0) return; var sql = GenerateCachedSeqSql(); await DatabaseHelper.NonQueryAsync(_connectionString, sql); } private string GenerateCachedSeqSql() { var sb = new StringBuilder(); foreach (var kv in _cachedSequence) { sb.AppendLine($"UPDATE seq SET CurrentVal = {kv.Value} WHERE SeqName = '{kv.Key.Name}';"); } return sb.ToString(); } }