using Furion.DependencyInjection;
using Furion.FriendlyException;
using Microsoft.Extensions.Logging;
using SqlSugar;
using YD_Event.Core.Entities;
namespace YD_Event.Core.Domain;
///
/// 订单号生成器服务
/// 提供分布式唯一订单号生成,确保高并发下的唯一性
///
public class OrderNoGeneratorService : ITransient
{
private static readonly object _lock = new();
private static long _lastTimestamp = -1;
private static int _sequence = 0;
private const int MaxSequence = 9999;
private readonly ISqlSugarRepository _registrationRepository;
private readonly ILogger _logger;
public OrderNoGeneratorService(
ISqlSugarRepository registrationRepository,
ILogger logger)
{
_registrationRepository = registrationRepository;
_logger = logger;
}
///
/// 生成唯一订单号
/// 格式:yyyyMMddHHmmss + 4位序列号
/// 保证高并发下的唯一性
///
/// 唯一订单号
public async Task GenerateOrderNoAsync()
{
string orderNo;
int retryCount = 0;
const int maxRetry = 5;
do
{
orderNo = GenerateOrderNoInternal();
retryCount++;
// 检查数据库中是否已存在
var exists = await _registrationRepository.IsAnyAsync(r => r.OrderNo == orderNo);
if (!exists)
{
_logger.LogDebug("生成订单号成功: {OrderNo}", orderNo);
return orderNo;
}
_logger.LogWarning("订单号冲突,重试中 ({RetryCount}/{MaxRetry}): {OrderNo}",
retryCount, maxRetry, orderNo);
// 短暂延迟后重试
await Task.Delay(10);
} while (retryCount < maxRetry);
_logger.LogError("订单号生成失败,已达到最大重试次数: {MaxRetry}", maxRetry);
throw Oops.Bah("订单号生成失败,请稍后重试");
}
///
/// 生成退款订单号
/// 格式:REF + yyyyMMddHHmmss + 4位序列号
///
/// 退款订单号
public async Task GenerateRefundOrderNoAsync()
{
var baseOrderNo = await GenerateOrderNoAsync();
return "REF" + baseOrderNo;
}
///
/// 内部生成订单号(带线程安全的序列号)
///
private string GenerateOrderNoInternal()
{
lock (_lock)
{
var timestamp = DateTime.Now;
var currentTimestamp = GetTimestamp(timestamp);
// 如果是同一毫秒,增加序列号
if (currentTimestamp == _lastTimestamp)
{
_sequence++;
if (_sequence > MaxSequence)
{
// 序列号溢出,等待下一毫秒
Thread.Sleep(1);
timestamp = DateTime.Now;
currentTimestamp = GetTimestamp(timestamp);
_sequence = 0;
}
}
else
{
// 不同毫秒,重置序列号
_sequence = 0;
}
_lastTimestamp = currentTimestamp;
// 格式:yyyyMMddHHmmss + 4位序列号(补零)
return timestamp.ToString("yyyyMMddHHmmss") + _sequence.ToString("D4");
}
}
///
/// 获取时间戳(用于比较)
///
private static long GetTimestamp(DateTime dateTime)
{
return new DateTimeOffset(dateTime).ToUnixTimeMilliseconds();
}
}