wanshenmean
2026-03-09 2df3b287a5c868f987b99ff00dc1d2339747b8f1
Merge branch 'feature/multi-outbound-address-roundrobin' - 实现多出库口轮询功能
已添加1个文件
已修改5个文件
114 ■■■■ 文件已修改
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/AutoOutboundTaskOptions.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/RoundRobinService.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-09-multi-outbound-address-roundrobin-design.md 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/AutoOutboundTaskOptions.cs
@@ -18,13 +18,13 @@
        public int CheckIntervalSeconds { get; set; } = 300;
        /// <summary>
        /// æŒ‰å··é“前缀配置目标地址
        /// æŒ‰å··é“前缀配置目标地址(支持多出库口)
        /// </summary>
        public Dictionary<string, string> TargetAddresses { get; set; }
        public Dictionary<string, List<string>> TargetAddresses { get; set; }
            = new()
            {
                { "GW", "10081" },
                { "CW", "10080" }
                { "GW", new List<string> { "10081" } },
                { "CW", new List<string> { "10080" } }
            };
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/RoundRobinService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
using System.Collections.Concurrent;
namespace WIDESEA_Core.Core
{
    /// <summary>
    /// è½®è¯¢æœåŠ¡ - çº¿ç¨‹å®‰å…¨çš„地址轮询选择
    /// </summary>
    public class RoundRobinService
    {
        /// <summary>
        /// è½®è¯¢è®¡æ•°å™¨ - key: å··é“前缀, value: å½“前索引
        /// </summary>
        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, oldValue) => (oldValue + 1) % addresses.Count  // è½®è¯¢ï¼šé€’增后取模
            );
            return addresses[index];
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -8,6 +8,7 @@
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Core;
using WIDESEA_DTO;
using WIDESEA_DTO.Task;
using WIDESEA_IBasicService;
@@ -24,6 +25,7 @@
        private readonly ILocationInfoService _locationInfoService;
        private readonly HttpClientHelper _httpClientHelper;
        private readonly IConfiguration _configuration;
        private readonly RoundRobinService _roundRobinService;
        public IRepository<Dt_Task> Repository => BaseDal;
@@ -42,13 +44,15 @@
            IStockInfoService stockInfoService,
            ILocationInfoService locationInfoService,
            HttpClientHelper httpClientHelper,
            IConfiguration configuration) : base(BaseDal)
            IConfiguration configuration,
            RoundRobinService roundRobinService) : base(BaseDal)
        {
            _mapper = mapper;
            _stockInfoService = stockInfoService;
            _locationInfoService = locationInfoService;
            _httpClientHelper = httpClientHelper;
            _configuration = configuration;
            _roundRobinService = roundRobinService;
        }
        #region WCS逻辑处理
@@ -381,20 +385,37 @@
        }
        /// <summary>
        /// æ ¹æ®å··é“确定目标地址
        /// æ ¹æ®å··é“确定目标地址(支持多出库口轮询)
        /// </summary>
        private string DetermineTargetAddress(string roadway, Dictionary<string, string> addressMap)
        private string DetermineTargetAddress(string roadway, Dictionary<string, List<string>> addressMap)
        {
            if (string.IsNullOrWhiteSpace(roadway))
                return "10080"; // é»˜è®¤åœ°å€
                return "10080";
            // æŸ¥æ‰¾åŒ¹é…çš„巷道前缀
            string matchedPrefix = null;
            foreach (var kvp in addressMap)
            {
                if (roadway.Contains(kvp.Key))
                    return kvp.Value;
                {
                    matchedPrefix = kvp.Key;
                    break;
                }
            }
            return "10080"; // é»˜è®¤åœ°å€
            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);
        }
        /// <summary>
@@ -473,8 +494,8 @@
                // 4. èŽ·å–é…ç½®çš„ç›®æ ‡åœ°å€æ˜ å°„
                var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses")
                    .Get<Dictionary<string, string>>()
                    ?? new Dictionary<string, string>();
                    .Get<Dictionary<string, List<string>>>()
                    ?? new Dictionary<string, List<string>>();
                // 5. æ‰¹é‡åˆ›å»ºä»»åŠ¡
                var taskList = new List<Dt_Task>();
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs
@@ -42,6 +42,7 @@
// 2�����÷���
builder.Services.AddSingleton(new AppSettings(builder.Configuration));//ע��
builder.Services.AddAllOptionRegister();//��ȡ�����ļ�
builder.Services.AddSingleton<RoundRobinService>();
builder.Services.Configure<AutoOutboundTaskOptions>(
    builder.Configuration.GetSection("AutoOutboundTask"));
builder.Services.AddMemoryCacheSetup();//����
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
@@ -34,9 +34,9 @@
  "AutoOutboundTask": {
    "Enable": true, /// æ˜¯å¦å¯ç”¨è‡ªåŠ¨å‡ºåº“ä»»åŠ¡
    "CheckIntervalSeconds": 300, /// æ£€æŸ¥é—´éš”(秒)
    "TargetAddresses": {    /// æŒ‰å··é“前缀配置目标地址
      "GW": "10081",
      "CW": "10080"
        "TargetAddresses": {    /// æŒ‰å··é“前缀配置目标地址(支持多出库口)
            "GW": ["10081"],
            "CW": ["10080"]
    }
  }
}
Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-09-multi-outbound-address-roundrobin-design.md
@@ -313,11 +313,15 @@
## å®žæ–½æ¸…单
- [ ] ä¿®æ”¹ `AutoOutboundTaskOptions.TargetAddresses` ç±»åž‹
- [ ] åˆ›å»º `RoundRobinService` ç±»
- [ ] ä¿®æ”¹ `TaskService.DetermineTargetAddress` æ–¹æ³•
- [ ] åœ¨ `Program.cs` æ³¨å†Œ `RoundRobinService`
- [ ] æ›´æ–° `appsettings.json` é…ç½®ç¤ºä¾‹
- [ ] ç¼–译验证
- [ ] æ‰‹åŠ¨æµ‹è¯•
- [ ] æ›´æ–°è®¾è®¡æ–‡æ¡£
- [x] ä¿®æ”¹ `AutoOutboundTaskOptions.TargetAddresses` ç±»åž‹
- [x] åˆ›å»º `RoundRobinService` ç±»
- [x] ä¿®æ”¹ `TaskService.DetermineTargetAddress` æ–¹æ³•
- [x] åœ¨ `Program.cs` æ³¨å†Œ `RoundRobinService`
- [x] æ›´æ–° `appsettings.json` é…ç½®ç¤ºä¾‹
- [x] ç¼–译验证
- [ ] æ‰‹åŠ¨æµ‹è¯•ï¼ˆéœ€è¦è¿è¡ŒçŽ¯å¢ƒå’Œæ•°æ®åº“ï¼‰
- [x] æ›´æ–°è®¾è®¡æ–‡æ¡£
**实施日期**: 2026-03-09
**实施人**: Claude Code
**状态**: ä»£ç å®žçŽ°å®Œæˆï¼Œå¾…æ‰‹åŠ¨æµ‹è¯•