Files
blazor-swm/Infrastructure/AutoNumberGenerator/AutoNumberGeneratorService.cs
T
2026-07-21 14:35:37 +07:00

94 lines
3.1 KiB
C#

using Indotalent.Data.Entities;
using Indotalent.Infrastructure.Database;
using Microsoft.EntityFrameworkCore;
using System.Collections.Concurrent;
namespace Indotalent.Infrastructure.AutoNumberGenerator;
public class AutoNumberGeneratorService
{
private readonly AppDbContext _context;
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new();
public AutoNumberGeneratorService(AppDbContext context)
{
_context = context;
}
public async Task<string> GenerateNextNumberAsync(
string entityName,
string prefixTemplate,
string? suffixTemplate = null,
int paddingLength = 4,
bool useYear = true,
bool useMonth = false,
CancellationToken cancellationToken = default)
{
var now = DateTime.UtcNow;
int? year = useYear ? now.Year : null;
int? month = useMonth ? now.Month : null;
var lockKey = $"{entityName}_{year}_{month}";
var semaphore = _locks.GetOrAdd(lockKey, _ => new SemaphoreSlim(1, 1));
await semaphore.WaitAsync(cancellationToken);
try
{
var sequence = await _context.AutoNumberSequence
.FirstOrDefaultAsync(ns =>
ns.EntityName == entityName &&
ns.Year == year &&
ns.Month == month,
cancellationToken);
if (sequence == null)
{
sequence = new AutoNumberSequence
{
EntityName = entityName,
Year = year,
Month = month,
CurrentSequence = 0,
PrefixTemplate = prefixTemplate,
SuffixTemplate = suffixTemplate,
PaddingLength = paddingLength
};
_context.AutoNumberSequence.Add(sequence);
}
sequence.CurrentSequence = (sequence.CurrentSequence ?? 0) + 1;
var formattedNumber = BuildFormattedNumber(sequence, now);
await _context.SaveChangesAsync(cancellationToken);
return formattedNumber;
}
finally
{
semaphore.Release();
if (semaphore.CurrentCount == 1) _locks.TryRemove(lockKey, out _);
}
}
private string BuildFormattedNumber(AutoNumberSequence sequence, DateTime now)
{
string ProcessTemplate(string? template)
{
if (string.IsNullOrEmpty(template)) return "";
return template
.Replace("{EntityName}", sequence.EntityName)
.Replace("{Year}", now.Year.ToString())
.Replace("{Year2}", now.ToString("yy"))
.Replace("{Month}", now.Month.ToString("D2"));
}
var prefix = ProcessTemplate(sequence.PrefixTemplate);
var suffix = ProcessTemplate(sequence.SuffixTemplate);
var padding = sequence.PaddingLength ?? 4;
var paddedSequence = (sequence.CurrentSequence ?? 1).ToString($"D{padding}");
return $"{prefix}{paddedSequence}{suffix}";
}
}