122 lines
3.7 KiB
C#
122 lines
3.7 KiB
C#
using Furion.DependencyInjection;
|
||
using Furion.FriendlyException;
|
||
using Microsoft.Extensions.Logging;
|
||
using SqlSugar;
|
||
using YD_Event.Core.Entities;
|
||
|
||
namespace YD_Event.Core.Domain;
|
||
|
||
/// <summary>
|
||
/// 订单号生成器服务
|
||
/// 提供分布式唯一订单号生成,确保高并发下的唯一性
|
||
/// </summary>
|
||
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<EventRegistration> _registrationRepository;
|
||
private readonly ILogger<OrderNoGeneratorService> _logger;
|
||
|
||
public OrderNoGeneratorService(
|
||
ISqlSugarRepository<EventRegistration> registrationRepository,
|
||
ILogger<OrderNoGeneratorService> logger)
|
||
{
|
||
_registrationRepository = registrationRepository;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成唯一订单号
|
||
/// 格式:yyyyMMddHHmmss + 4位序列号
|
||
/// 保证高并发下的唯一性
|
||
/// </summary>
|
||
/// <returns>唯一订单号</returns>
|
||
public async Task<string> 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("订单号生成失败,请稍后重试");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成退款订单号
|
||
/// 格式:REF + yyyyMMddHHmmss + 4位序列号
|
||
/// </summary>
|
||
/// <returns>退款订单号</returns>
|
||
public async Task<string> GenerateRefundOrderNoAsync()
|
||
{
|
||
var baseOrderNo = await GenerateOrderNoAsync();
|
||
return "REF" + baseOrderNo;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 内部生成订单号(带线程安全的序列号)
|
||
/// </summary>
|
||
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");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取时间戳(用于比较)
|
||
/// </summary>
|
||
private static long GetTimestamp(DateTime dateTime)
|
||
{
|
||
return new DateTimeOffset(dateTime).ToUnixTimeMilliseconds();
|
||
}
|
||
}
|