From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点

---
 Aging.Infrastructure/Core/ResourceManager.cs |  258 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 258 insertions(+), 0 deletions(-)

diff --git a/Aging.Infrastructure/Core/ResourceManager.cs b/Aging.Infrastructure/Core/ResourceManager.cs
new file mode 100644
index 0000000..68f4c2b
--- /dev/null
+++ b/Aging.Infrastructure/Core/ResourceManager.cs
@@ -0,0 +1,258 @@
+using System.Collections.Concurrent;
+using Aging.Infrastructure.Contracts;
+using log4net;
+
+namespace Aging.Infrastructure.Core
+{
+    /// <summary>
+    /// 璧勬簮绠$悊瀹炵幇銆�
+    /// 鎻愪緵绾跨▼瀹夊叏鐨勮祫婧愰绾︺�佺绾﹁幏鍙栧拰閲婃斁鍔熻兘銆�
+    /// 浣跨敤 ConcurrentDictionary 瀛樺偍娲昏穬棰勭害锛屼娇鐢� SemaphoreSlim 瀹炵幇 per-resource 閿併��
+    /// </summary>
+    public sealed class ResourceManager : IResourceManager
+    {
+        private static readonly ILog Log = LogManager.GetLogger(typeof(ResourceManager));
+
+        /// <summary>
+        /// 娲昏穬棰勭害瀛楀吀锛坘ey = ResourceKey.ToString()锛�
+        /// </summary>
+        private readonly ConcurrentDictionary<string, ResourceReservation> _reservations = new();
+
+        /// <summary>
+        /// 姣忚祫婧愪俊鍙烽噺瀛楀吀锛岀敤浜庡疄鐜扮粏绮掑害閿�
+        /// </summary>
+        private readonly ConcurrentDictionary<string, SemaphoreSlim> _resourceLocks = new();
+
+        /// <summary>
+        /// 鍏ㄥ眬涓栦唬鍙凤紝姣忔鏂伴绾﹂�掑
+        /// </summary>
+        private long _generation;
+
+        /// <summary>
+        /// 榛樿绉熺害鏈夋晥鏈燂紙绉掞級
+        /// </summary>
+        private const int DefaultLeaseDurationSeconds = 30;
+
+        /// <inheritdoc/>
+        public async Task<ResourceReservation> ReserveAsync(
+            ResourceOwnerToken owner, ResourceKey resource, CancellationToken ct)
+        {
+            if (owner == null) throw new ArgumentNullException(nameof(owner));
+            if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+            var resourceKey = resource.ToString();
+            var semaphore = _resourceLocks.GetOrAdd(resourceKey, _ => new SemaphoreSlim(1, 1));
+
+            try
+            {
+                await semaphore.WaitAsync(ct).ConfigureAwait(false);
+
+                try
+                {
+                    // 妫�鏌ユ槸鍚﹀凡鏈� Active 棰勭害
+                    if (_reservations.TryGetValue(resourceKey, out var existingReservation))
+                    {
+                        if (existingReservation.State == ReservationState.Active)
+                        {
+                            // 妫�鏌ユ槸鍚︿负鍚屼竴 Owner锛堢画鏈熷満鏅級
+                            if (existingReservation.Owner.TokenId == owner.TokenId)
+                            {
+                                // 鍚屼竴鎵�鏈夎�呯画鏈燂細鏇存柊涓栦唬鍙�
+                                var renewedGeneration = Interlocked.Increment(ref _generation);
+                                owner.Generation = renewedGeneration;
+                                existingReservation.Owner.Generation = renewedGeneration;
+
+                                Log.Info($"[ResourceManager] 璧勬簮 {resourceKey} 缁湡鎴愬姛锛屾柊涓栦唬鍙�: {renewedGeneration}");
+                                return existingReservation;
+                            }
+
+                            // 涓嶅悓鎵�鏈夎�呭啿绐�
+                            var conflictMsg = $"璧勬簮 {resourceKey} 宸茶鎵�鏈夎�� {existingReservation.Owner.OwnerId} 棰勭害锛�" +
+                                             $"褰撳墠璇锋眰鑰�: {owner.OwnerId}";
+                            Log.Warn($"[ResourceManager] 璧勬簮鍐茬獊: {conflictMsg}");
+                            throw new ResourceConflictException(conflictMsg);
+                        }
+                    }
+
+                    // 鍒涘缓鏂伴绾�
+                    var newGeneration = Interlocked.Increment(ref _generation);
+                    owner.Generation = newGeneration;
+
+                    var reservation = new ResourceReservation(resource, owner)
+                    {
+                        State = ReservationState.Active
+                    };
+
+                    _reservations[resourceKey] = reservation;
+
+                    Log.Info($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害鎴愬姛锛屾墍鏈夎��: {owner.OwnerId}锛屼笘浠e彿: {newGeneration}");
+                    return reservation;
+                }
+                finally
+                {
+                    semaphore.Release();
+                }
+            }
+            catch (OperationCanceledException)
+            {
+                Log.Warn($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害琚彇娑�");
+                throw;
+            }
+            catch (ResourceConflictException)
+            {
+                throw;
+            }
+            catch (Exception ex)
+            {
+                Log.Error($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害澶辫触: {ex.Message}", ex);
+                throw;
+            }
+        }
+
+        /// <inheritdoc/>
+        public async Task<IOperationLease> AcquireOperationAsync(
+            ResourceOwnerToken owner, ResourceKey resource, CancellationToken ct)
+        {
+            if (owner == null) throw new ArgumentNullException(nameof(owner));
+            if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+            var resourceKey = resource.ToString();
+
+            try
+            {
+                // 妫�鏌ヨ祫婧愭槸鍚︽湁鏈夋晥棰勭害
+                if (!_reservations.TryGetValue(resourceKey, out var reservation))
+                {
+                    throw new InvalidOperationException($"璧勬簮 {resourceKey} 鏈棰勭害锛屾棤娉曡幏鍙栨搷浣滅绾�");
+                }
+
+                if (reservation.State != ReservationState.Active)
+                {
+                    throw new InvalidOperationException($"璧勬簮 {resourceKey} 棰勭害鐘舵�佷负 {reservation.State}锛屾棤娉曡幏鍙栨搷浣滅绾�");
+                }
+
+                // 楠岃瘉 Owner 鍖归厤锛圱okenId + Generation锛�
+                if (reservation.Owner.TokenId != owner.TokenId || reservation.Owner.Generation != owner.Generation)
+                {
+                    throw new InvalidOperationException(
+                        $"璧勬簮 {resourceKey} 鎵�鏈夎�呬笉鍖归厤锛屾湡鏈� TokenId: {reservation.Owner.TokenId}锛孏eneration: {reservation.Owner.Generation}");
+                }
+
+                // 鍒涘缓 OperationLease锛堢煭鏈燂紝榛樿30绉掕繃鏈燂級
+                var lease = new OperationLease(
+                    resource,
+                    owner,
+                    onRelease: () => Log.Debug($"[ResourceManager] 绉熺害閲婃斁: {resourceKey}"),
+                    leaseDurationSeconds: DefaultLeaseDurationSeconds);
+
+                Log.Info($"[ResourceManager] 鑾峰彇鎿嶄綔绉熺害: {resourceKey}锛屾墍鏈夎��: {owner.OwnerId}锛屾湁鏁堟湡: {DefaultLeaseDurationSeconds}绉�");
+
+                await Task.CompletedTask; // 淇濇寔寮傛绛惧悕
+                return lease;
+            }
+            catch (InvalidOperationException)
+            {
+                throw;
+            }
+            catch (Exception ex)
+            {
+                Log.Error($"[ResourceManager] 鑾峰彇鎿嶄綔绉熺害澶辫触: {ex.Message}", ex);
+                throw;
+            }
+        }
+
+        /// <inheritdoc/>
+        public async Task ReleaseReservationAsync(
+            ResourceOwnerToken owner, ResourceKey resource, CancellationToken ct)
+        {
+            if (owner == null) throw new ArgumentNullException(nameof(owner));
+            if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+            var resourceKey = resource.ToString();
+            var semaphore = _resourceLocks.GetOrAdd(resourceKey, _ => new SemaphoreSlim(1, 1));
+
+            try
+            {
+                await semaphore.WaitAsync(ct).ConfigureAwait(false);
+
+                try
+                {
+                    if (!_reservations.TryGetValue(resourceKey, out var reservation))
+                    {
+                        Log.Warn($"[ResourceManager] 灏濊瘯閲婃斁涓嶅瓨鍦ㄧ殑璧勬簮棰勭害: {resourceKey}");
+                        return;
+                    }
+
+                    // 楠岃瘉 Owner 鍖归厤
+                    if (reservation.Owner.TokenId != owner.TokenId)
+                    {
+                        throw new InvalidOperationException(
+                            $"璧勬簮 {resourceKey} 閲婃斁澶辫触锛氭墍鏈夎�呬笉鍖归厤锛屾湡鏈� TokenId: {reservation.Owner.TokenId}");
+                    }
+
+                    // 鏍囪棰勭害涓� Released
+                    reservation.State = ReservationState.Released;
+                    _reservations.TryRemove(resourceKey, out _);
+
+                    Log.Info($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害宸查噴鏀撅紝鎵�鏈夎��: {owner.OwnerId}");
+                }
+                finally
+                {
+                    semaphore.Release();
+                }
+
+                await Task.CompletedTask; // 淇濇寔寮傛绛惧悕
+            }
+            catch (InvalidOperationException)
+            {
+                throw;
+            }
+            catch (OperationCanceledException)
+            {
+                Log.Warn($"[ResourceManager] 璧勬簮 {resourceKey} 閲婃斁鎿嶄綔琚彇娑�");
+                throw;
+            }
+            catch (Exception ex)
+            {
+                Log.Error($"[ResourceManager] 閲婃斁璧勬簮 {resourceKey} 澶辫触: {ex.Message}", ex);
+                throw;
+            }
+        }
+
+        /// <inheritdoc/>
+        public bool IsReserved(ResourceKey resource, out ResourceReservation? reservation)
+        {
+            if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+            var resourceKey = resource.ToString();
+            var result = _reservations.TryGetValue(resourceKey, out var res);
+
+            if (result && res != null && res.State == ReservationState.Active)
+            {
+                reservation = res;
+                return true;
+            }
+
+            reservation = null;
+            return false;
+        }
+
+        /// <inheritdoc/>
+        public bool ValidateOwner(ResourceOwnerToken owner, ResourceKey resource)
+        {
+            if (owner == null) throw new ArgumentNullException(nameof(owner));
+            if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+            var resourceKey = resource.ToString();
+
+            if (!_reservations.TryGetValue(resourceKey, out var reservation))
+                return false;
+
+            if (reservation.State != ReservationState.Active)
+                return false;
+
+            return reservation.Owner.TokenId == owner.TokenId &&
+                   reservation.Owner.Generation == owner.Generation;
+        }
+    }
+}

--
Gitblit v1.9.1