136 lines
4.1 KiB
C#
136 lines
4.1 KiB
C#
using System.Collections.Concurrent;
|
|
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 ConcurrentDictionary<SeqConfig, long> _cachedSequence;
|
|
|
|
public IReadOnlyDictionary<SeqConfig, long> CachedSequence => _cachedSequence;
|
|
|
|
public SeqService(IOptions<DatabaseOutputOptions> options)
|
|
{
|
|
var connStr = options.Value.ConnectionString ?? throw new ApplicationException("未配置输出数据库连接字符串");
|
|
var builder = new MySqlConnectionStringBuilder(connStr)
|
|
{
|
|
Database = "mes_global"
|
|
};
|
|
_connectionString = builder.ConnectionString;
|
|
|
|
_cachedSequence = new ConcurrentDictionary<SeqConfig, long>();
|
|
}
|
|
|
|
private async Task<long> 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<long> 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<long[]> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 添加并取得一个缓存的流水号
|
|
/// </summary>
|
|
/// <param name="config"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 移除一个缓存的流水号
|
|
/// </summary>
|
|
/// <param name="config"></param>
|
|
public bool RemoveCachedSeq(SeqConfig config)
|
|
{
|
|
return _cachedSequence.Remove(config, out _);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清空所有缓存的流水号
|
|
/// </summary>
|
|
public void ClearCache()
|
|
{
|
|
_cachedSequence.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将缓存的流水号应用至数据库
|
|
/// </summary>
|
|
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();
|
|
}
|
|
} |