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();
|
|||
|
|
}
|
|||
|
|
}
|