From 4c1f6449a2dd28bdfb76dae2bac127c2081f9f54 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期一, 09 三月 2026 09:22:28 +0800
Subject: [PATCH] 添加多出库口轮询功能设计文档

---
 Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-09-multi-outbound-address-roundrobin-design.md |  323 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 323 insertions(+), 0 deletions(-)

diff --git a/Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-09-multi-outbound-address-roundrobin-design.md b/Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-09-multi-outbound-address-roundrobin-design.md
new file mode 100644
index 0000000..d0dfc6c
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-09-multi-outbound-address-roundrobin-design.md
@@ -0,0 +1,323 @@
+# 澶氬嚭搴撳彛杞鍔熻兘璁捐鏂囨。
+
+**鏃ユ湡**: 2026-03-09
+**浣滆��**: Claude Code
+**鐘舵��**: 寰呭疄鏂�
+
+## 姒傝堪
+
+鏈璁℃棬鍦ㄦ敼杩涜嚜鍔ㄥ嚭搴撲换鍔$殑鐩爣鍦板潃閰嶇疆鍔熻兘锛屾敮鎸佷竴涓贩閬撻厤缃涓嚭搴撳彛锛屽苟閫氳繃杞绠楁硶瀹炵幇璐熻浇鍧囪 銆�
+
+## 闇�姹傝儗鏅�
+
+褰撳墠绯荤粺涓紝`TargetAddresses` 閰嶇疆閲囩敤涓�瀵逛竴鏄犲皠锛堝贩閬撳墠缂� 鈫� 鍑哄簱鍙e湴鍧�锛夈�備絾瀹為檯涓氬姟涓紝涓�涓贩閬撳彲鑳芥湁澶氫釜鍑哄簱鍙o紝闇�瑕佹敮鎸侊細
+
+1. **涓�瀵瑰鍏崇郴**锛氫竴涓贩閬撳彲浠ラ厤缃涓嚭搴撳彛
+2. **璐熻浇鍧囪 **锛氶�氳繃杞绠楁硶閫夋嫨鍑哄簱鍙o紝閬垮厤鍗曠偣鍘嬪姏
+3. **鍚戝悗鍏煎**锛氫繚鎸佸鍗曞嚭鍙i厤缃殑鏀寔
+
+## 鎶�鏈柟妗�
+
+### 鏋舵瀯閫夋嫨
+
+閲囩敤 **鍐呭瓨杞璁℃暟鍣� + 鐙珛鏈嶅姟绫�** 鐨勬ā寮忥細
+
+1. **RoundRobinService**锛氱嫭绔嬬殑杞鏈嶅姟绫伙紝绠$悊杞璁℃暟鍣�
+2. **閰嶇疆妯″瀷鍙樻洿**锛歚Dictionary<string, string>` 鈫� `Dictionary<string, List<string>>`
+3. **绾跨▼瀹夊叏**锛氫娇鐢� `ConcurrentDictionary` 淇濊瘉澶氱嚎绋嬪畨鍏�
+
+### 鏍稿績璁捐
+
+#### 1. 閰嶇疆鏍煎紡鍙樻洿
+
+**appsettings.json:**
+```json
+{
+  "AutoOutboundTask": {
+    "TargetAddresses": {
+      "GW": ["10081", "10082", "10083"],
+      "CW": ["10080"]
+    }
+  }
+}
+```
+
+**璇存槑锛�**
+- 鍊肩被鍨嬩粠 `string` 鏀逛负 `string[]`锛圝SON 鏁扮粍锛�
+- 鏀寔涓�涓贩閬撻厤缃涓嚭搴撳彛
+- 鍗曚釜鍑哄簱鍙d篃鍙敤鏁扮粍鏍煎紡锛堝彧鏈変竴涓厓绱狅級
+
+#### 2. 閰嶇疆妯″瀷绫�
+
+**AutoOutboundTaskOptions.cs:**
+```csharp
+public class AutoOutboundTaskOptions
+{
+    public bool Enable { get; set; } = true;
+    public int CheckIntervalSeconds { get; set; } = 300;
+
+    public Dictionary<string, List<string>> TargetAddresses { get; set; }
+        = new()
+        {
+            { "GW", new List<string> { "10081" } },
+            { "CW", new List<string> { "10080" } }
+        };
+}
+```
+
+#### 3. 杞鏈嶅姟绫�
+
+**RoundRobinService.cs** (鏂板缓):
+```csharp
+using System.Collections.Concurrent;
+
+namespace WIDESEA_Core.Core
+{
+    /// <summary>
+    /// 杞鏈嶅姟 - 绾跨▼瀹夊叏鐨勫湴鍧�杞閫夋嫨
+    /// </summary>
+    public class RoundRobinService
+    {
+        private readonly ConcurrentDictionary<string, int> _counters = new();
+
+        /// <summary>
+        /// 鑾峰彇涓嬩竴涓湴鍧�锛堣疆璇級
+        /// </summary>
+        /// <param name="key">宸烽亾鍓嶇紑</param>
+        /// <param name="addresses">鍦板潃鍒楄〃</param>
+        /// <returns>閫変腑鐨勫湴鍧�</returns>
+        public string GetNextAddress(string key, List<string> addresses)
+        {
+            if (addresses == null || addresses.Count == 0)
+                return "10080";
+
+            if (addresses.Count == 1)
+                return addresses[0];
+
+            // AddOrUpdate 鏄師瀛愭搷浣滐紝绾跨▼瀹夊叏
+            int index = _counters.AddOrUpdate(
+                key,
+                0,                              // 棣栨浣跨敤锛屼粠 0 寮�濮�
+                (k, old) => (old + 1) % addresses.Count  // 杞锛氶�掑鍚庡彇妯�
+            );
+
+            return addresses[index];
+        }
+    }
+}
+```
+
+**鍏抽敭鐗规�э細**
+- 浣跨敤 `ConcurrentDictionary<string, int>` 瀛樺偍姣忎釜宸烽亾鐨勫綋鍓嶇储寮�
+- `AddOrUpdate` 鏂规硶淇濊瘉鍘熷瓙鎬э紝鏃犻渶棰濆閿�
+- 妯¤繍绠楀疄鐜板惊鐜疆璇�
+- 绾跨▼瀹夊叏锛屾敮鎸佸悗鍙版湇鍔$殑骞跺彂璋冪敤
+
+#### 4. TaskService 鏂规硶淇敼
+
+**DetermineTargetAddress 鏂规硶:**
+```csharp
+private string DetermineTargetAddress(
+    string roadway,
+    Dictionary<string, List<string>> addressMap)
+{
+    if (string.IsNullOrWhiteSpace(roadway))
+        return "10080";
+
+    // 鏌ユ壘鍖归厤鐨勫贩閬撳墠缂�
+    string matchedPrefix = null;
+    foreach (var kvp in addressMap)
+    {
+        if (roadway.Contains(kvp.Key))
+        {
+            matchedPrefix = kvp.Key;
+            break;
+        }
+    }
+
+    if (matchedPrefix == null)
+        return "10080";
+
+    var addresses = addressMap[matchedPrefix];
+    if (addresses == null || addresses.Count == 0)
+        return "10080";
+
+    // 鍗曚釜鍦板潃锛岀洿鎺ヨ繑鍥�
+    if (addresses.Count == 1)
+        return addresses[0];
+
+    // 澶氫釜鍦板潃锛屼娇鐢ㄨ疆璇�
+    return _roundRobinService.GetNextAddress(matchedPrefix, addresses);
+}
+```
+
+**渚濊禆娉ㄥ叆:**
+```csharp
+public class TaskService
+{
+    private readonly RoundRobinService _roundRobinService;
+
+    public TaskService(
+        // ... 鍏朵粬渚濊禆
+        RoundRobinService roundRobinService)
+    {
+        // ... 鍏朵粬璧嬪��
+        _roundRobinService = roundRobinService;
+    }
+}
+```
+
+#### 5. 鏈嶅姟娉ㄥ唽
+
+**Program.cs:**
+```csharp
+// 娉ㄥ唽涓哄崟渚嬶紝淇濊瘉鍏ㄥ眬鍏变韩璁℃暟鍣�
+builder.Services.AddSingleton<RoundRobinService>();
+```
+
+## 鏁版嵁娴佺▼
+
+```mermaid
+graph TD
+    A[鍒涘缓鍑哄簱浠诲姟] --> B[鑾峰彇搴撳瓨宸烽亾淇℃伅]
+    B --> C[鏌ユ壘鍖归厤鐨勫贩閬撳墠缂�]
+    C --> D{鍦板潃鍒楄〃鏁伴噺}
+    D -->|0涓獆 E[杩斿洖榛樿鍦板潃 10080]
+    D -->|1涓獆 F[鐩存帴杩斿洖璇ュ湴鍧�]
+    D -->|澶氫釜| G[璋冪敤 RoundRobinService]
+    G --> H[鏌ヨ/鏇存柊璁℃暟鍣╙
+    H --> I[杩斿洖 addresses counter % count]
+    I --> J[鍒涘缓浠诲姟]
+    F --> J
+    E --> J
+```
+
+## 杞绀轰緥
+
+**閰嶇疆:**
+```json
+{
+  "TargetAddresses": {
+    "GW": ["10081", "10082", "10083"]
+  }
+}
+```
+
+**璋冪敤搴忓垪:**
+| 娆℃暟 | 璁℃暟鍣ㄥ�� | 杩斿洖鍦板潃 |
+|------|---------|---------|
+| 1    | 0       | 10081   |
+| 2    | 1       | 10082   |
+| 3    | 2       | 10083   |
+| 4    | 0       | 10081   |
+| 5    | 1       | 10082   |
+| ...  | ...     | ...     |
+
+## 閿欒澶勭悊
+
+1. **閰嶇疆閿欒**锛�
+   - 鍦板潃鍒楄〃涓虹┖ 鈫� 杩斿洖榛樿鍦板潃 "10080"
+   - 宸烽亾涓嶅尮閰嶄换浣曞墠缂� 鈫� 杩斿洖榛樿鍦板潃 "10080"
+
+2. **骞跺彂瀹夊叏**锛�
+   - 浣跨敤 `ConcurrentDictionary` 淇濊瘉绾跨▼瀹夊叏
+   - `AddOrUpdate` 鏄師瀛愭搷浣�
+
+3. **搴旂敤閲嶅惎**锛�
+   - 杞浣嶇疆閲嶇疆涓� 0
+   - 鍙帴鍙楃殑鏉冭 锛堢畝鍖栧疄鐜帮級
+
+## 鍚戝悗鍏煎鎬�
+
+### 鏃ч厤缃牸寮�
+```json
+{
+  "TargetAddresses": {
+    "GW": "10081",
+    "CW": "10080"
+  }
+}
+```
+
+### 鏂伴厤缃牸寮�
+```json
+{
+  "TargetAddresses": {
+    "GW": ["10081"],
+    "CW": ["10080"]
+  }
+}
+```
+
+**鍏煎鎬у鐞嗭細**
+- JSON 閰嶇疆缁戝畾浼氳嚜鍔ㄥ鐞嗕袱绉嶆牸寮�
+- 鍗曚釜瀛楃涓蹭細琚В鏋愪负鍗曞厓绱犳暟缁�
+- 鐜版湁閰嶇疆鏃犻渶淇敼鍗冲彲宸ヤ綔
+
+## 鎬ц兘褰卞搷
+
+1. **鍐呭瓨寮�閿�**锛氭瘡涓贩閬撳墠缂�涓�涓暣鏁拌鏁板櫒锛堢害 50 瀛楄妭锛�
+2. **CPU 寮�閿�**锛氭ā杩愮畻鍜屽瓧鍏告煡鎵撅紝O(1) 澶嶆潅搴�
+3. **绾跨▼瀹夊叏**锛氭棤閿佽璁★紝`ConcurrentDictionary` 浣跨敤浼樺寲鐨勫悓姝ユ満鍒�
+
+## 娴嬭瘯璁″垝
+
+### 鍗曞厓娴嬭瘯
+1. `RoundRobinService.GetNextAddress` 杞閫昏緫
+2. 鍗曚釜鍦板潃鐩存帴杩斿洖
+3. 澶氫釜鍦板潃杞杩斿洖
+4. 骞跺彂璋冪敤娴嬭瘯
+
+### 闆嗘垚娴嬭瘯
+1. 淇敼 appsettings.json 涓哄鍑哄彛閰嶇疆
+2. 鍒涘缓澶氫釜鍑哄簱浠诲姟
+3. 楠岃瘉鐩爣鍦板潃杞鍒嗛厤
+4. 楠岃瘉鏃ュ織璁板綍
+
+### 鎵嬪姩娴嬭瘯
+```sql
+-- 鍑嗗娴嬭瘯鏁版嵁
+UPDATE Dt_StockInfo
+SET OutboundDate = DATEADD(MINUTE, -5, GETDATE())
+WHERE PalletCode IN ('TEST001', 'TEST002', 'TEST003')
+  AND StockStatus = 1;
+```
+
+鍚姩搴旂敤锛岃瀵熸棩蹇楋細
+```
+info: 鍒涘缓浠诲姟 TEST001锛岀洰鏍囧湴鍧�: 10081
+info: 鍒涘缓浠诲姟 TEST002锛岀洰鏍囧湴鍧�: 10082
+info: 鍒涘缓浠诲姟 TEST003锛岀洰鏍囧湴鍧�: 10083
+```
+
+## 閮ㄧ讲娉ㄦ剰浜嬮」
+
+1. **閰嶇疆鏇存柊**锛�
+   - 灏� `TargetAddresses` 鐨勫�间粠瀛楃涓叉敼涓烘暟缁勬牸寮�
+   - 纭繚姣忎釜鏁扮粍鑷冲皯鏈変竴涓厓绱�
+
+2. **渚濊禆娉ㄥ叆**锛�
+   - 纭繚 `RoundRobinService` 宸叉敞鍐屼负鍗曚緥
+   - `TaskService` 鏋勯�犲嚱鏁伴渶瑕佹敞鍏ヨ鏈嶅姟
+
+3. **鍥炴粴鏂规**锛�
+   - 淇濈暀 `appsettings.json` 澶囦唤
+   - 濡傞亣闂鍙仮澶嶄负鍗曞嚭鍙i厤缃�
+
+## 鏈潵鏀硅繘
+
+1. **鏉冮噸杞**锛氭敮鎸佷负姣忎釜鍦板潃閰嶇疆鏉冮噸
+2. **鍋ュ悍妫�鏌�**锛氭帓闄や笉鍙敤鐨勫嚭搴撳彛
+3. **鎸佷箙鍖�**锛氬皢杞浣嶇疆淇濆瓨鍒� Redis/鏁版嵁搴�
+4. **鐩戞帶缁熻**锛氳褰曟瘡涓湴鍧�鐨勪换鍔″垎閰嶆暟閲�
+
+## 瀹炴柦娓呭崟
+
+- [ ] 淇敼 `AutoOutboundTaskOptions.TargetAddresses` 绫诲瀷
+- [ ] 鍒涘缓 `RoundRobinService` 绫�
+- [ ] 淇敼 `TaskService.DetermineTargetAddress` 鏂规硶
+- [ ] 鍦� `Program.cs` 娉ㄥ唽 `RoundRobinService`
+- [ ] 鏇存柊 `appsettings.json` 閰嶇疆绀轰緥
+- [ ] 缂栬瘧楠岃瘉
+- [ ] 鎵嬪姩娴嬭瘯
+- [ ] 鏇存柊璁捐鏂囨。

--
Gitblit v1.9.3