YitIdHelper.cs 1.82 KB
namespace FoodLabeling.Th.Domain.Shared.Helpers;

/// <summary>
/// 雪花 Id 生成器(项目规范:实体 string Id 使用 NextId().ToString())
/// </summary>
public static class YitIdHelper
{
    private const long Epoch = 1_609_459_200_000L;
    private const int WorkerIdBits = 5;
    private const int SequenceBits = 12;
    private const long MaxSequence = (1L << SequenceBits) - 1;
    private const int WorkerIdShift = SequenceBits;
    private const int TimestampShift = SequenceBits + WorkerIdBits;
    private const long WorkerId = 1;

    private static long _lastTimestamp = -1L;
    private static long _sequence;
    private static readonly object SyncRoot = new();

    /// <summary>
    /// 生成下一个雪花 Id
    /// </summary>
    public static long NextId()
    {
        lock (SyncRoot)
        {
            var timestamp = CurrentTimestamp();
            if (timestamp < _lastTimestamp)
            {
                timestamp = WaitNextMillis(_lastTimestamp);
            }

            if (_lastTimestamp == timestamp)
            {
                _sequence = (_sequence + 1) & MaxSequence;
                if (_sequence == 0)
                {
                    timestamp = WaitNextMillis(_lastTimestamp);
                }
            }
            else
            {
                _sequence = 0;
            }

            _lastTimestamp = timestamp;
            return ((timestamp - Epoch) << TimestampShift) | (WorkerId << WorkerIdShift) | _sequence;
        }
    }

    private static long CurrentTimestamp() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

    private static long WaitNextMillis(long lastTimestamp)
    {
        var timestamp = CurrentTimestamp();
        while (timestamp <= lastTimestamp)
        {
            timestamp = CurrentTimestamp();
        }

        return timestamp;
    }
}