From 0005d58f6888dd3e4524784d1b6f103f9b1c588e Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期一, 30 三月 2026 18:33:22 +0800
Subject: [PATCH] 合并
---
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/BackgroundServices/StockMonitorBackgroundService.cs | 199 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 199 insertions(+), 0 deletions(-)
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/BackgroundServices/StockMonitorBackgroundService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/BackgroundServices/StockMonitorBackgroundService.cs
new file mode 100644
index 0000000..074c48e
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/BackgroundServices/StockMonitorBackgroundService.cs
@@ -0,0 +1,199 @@
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using WIDESEA_Core.BaseRepository;
+using WIDESEA_IStockService;
+using WIDESEA_Model.Models;
+using WIDESEA_WMSServer.Hubs;
+
+namespace WIDESEA_WMSServer.BackgroundServices
+{
+ /// <summary>
+ /// 搴撳瓨鐩戞帶鍚庡彴鏈嶅姟
+ /// 瀹氭湡妫�鏌ュ簱瀛樺拰璐т綅鏁版嵁鍙樺寲骞堕�氳繃SignalR鎺ㄩ�佸埌鍓嶇
+ /// </summary>
+ public class StockMonitorBackgroundService : BackgroundService
+ {
+ private readonly ILogger<StockMonitorBackgroundService> _logger;
+ private readonly IHubContext<StockHub> _hubContext;
+ private readonly IServiceProvider _serviceProvider;
+
+ // 璐т綅鐘舵�佸揩鐓э細key = LocationId
+ private ConcurrentDictionary<int, LocationSnapshot> _lastLocationSnapshots = new();
+
+ // 鐩戞帶闂撮殧锛堟绉掞級
+ private const int MonitorIntervalMs = 3000;
+
+ public StockMonitorBackgroundService(
+ ILogger<StockMonitorBackgroundService> logger,
+ IHubContext<StockHub> hubContext,
+ IServiceProvider serviceProvider)
+ {
+ _logger = logger;
+ _hubContext = hubContext;
+ _serviceProvider = serviceProvider;
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogInformation("搴撳瓨鐩戞帶鍚庡彴鏈嶅姟宸插惎鍔�");
+
+ // 绛夊緟搴旂敤瀹屽叏鍚姩
+ await Task.Delay(5000, stoppingToken);
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ try
+ {
+ await CheckChangesAsync();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "妫�鏌ユ暟鎹彉鍖栨椂鍙戠敓閿欒");
+ }
+
+ await Task.Delay(MonitorIntervalMs, stoppingToken);
+ }
+
+ _logger.LogInformation("搴撳瓨鐩戞帶鍚庡彴鏈嶅姟宸插仠姝�");
+ }
+
+ /// <summary>
+ /// 妫�鏌ヨ揣浣嶅拰搴撳瓨鍙樺寲
+ /// </summary>
+ private async Task CheckChangesAsync()
+ {
+ using var scope = _serviceProvider.CreateScope();
+ var stockService = scope.ServiceProvider.GetRequiredService<IStockInfoService>();
+ var locationRepo = scope.ServiceProvider.GetRequiredService<IRepository<Dt_LocationInfo>>();
+
+ // 1. 鑾峰彇鎵�鏈夎揣浣嶆暟鎹�
+ var allLocations = await locationRepo.QueryDataAsync(x => x.LocationStatus != 99); // 鎺掗櫎绂佺敤鐨勮揣浣�
+
+ // 2. 鑾峰彇鎵�鏈夊簱瀛樻暟鎹紙鍖呭惈鏄庣粏锛�
+ var allStockData = await stockService.Repository.Db.Queryable<Dt_StockInfo>()
+ .Includes(x => x.Details)
+ .ToListAsync();
+
+ // 鏋勫缓搴撳瓨瀛楀吀锛歀ocationId -> StockInfo
+ var stockDict = allStockData
+ .Where(s => s.LocationId > 0)
+ .ToDictionary(s => s.LocationId, s => s);
+
+ // 鏋勫缓褰撳墠璐т綅蹇収瀛楀吀
+ var currentSnapshots = new ConcurrentDictionary<int, LocationSnapshot>();
+
+ foreach (var location in allLocations)
+ {
+ // 鑾峰彇璇ヨ揣浣嶇殑搴撳瓨淇℃伅
+ stockDict.TryGetValue(location.Id, out var stock);
+
+ // 璁$畻搴撳瓨鏁伴噺
+ float totalQuantity = 0;
+ string detailsHash = string.Empty;
+ if (stock?.Details != null && stock.Details.Any())
+ {
+ totalQuantity = stock.Details.Sum(d => d.StockQuantity);
+ detailsHash = GenerateDetailsHash(stock.Details.ToList());
+ }
+
+ var snapshot = new LocationSnapshot
+ {
+ LocationId = location.Id,
+ WarehouseId = location.WarehouseId,
+ LocationCode = location.LocationCode,
+ LocationStatus = location.LocationStatus,
+ PalletCode = stock?.PalletCode,
+ StockStatus = stock?.StockStatus ?? 0,
+ StockQuantity = totalQuantity,
+ DetailsHash = detailsHash
+ };
+
+ currentSnapshots.TryAdd(location.Id, snapshot);
+
+ // 妫�鏌ユ槸鍚︽湁鍙樺寲
+ if (_lastLocationSnapshots.TryGetValue(location.Id, out var lastSnapshot))
+ {
+ // 妫�娴嬪彉鍖栵細璐т綅鐘舵�併�佸簱瀛樼姸鎬併�佹暟閲忋�佹槑缁嗗彉鍖�
+ if (lastSnapshot.LocationStatus != snapshot.LocationStatus ||
+ lastSnapshot.StockStatus != snapshot.StockStatus ||
+ lastSnapshot.PalletCode != snapshot.PalletCode ||
+ Math.Abs(lastSnapshot.StockQuantity - snapshot.StockQuantity) > 0.001f ||
+ lastSnapshot.DetailsHash != snapshot.DetailsHash)
+ {
+ // 鏋勫缓鏇存柊DTO
+ var update = new StockUpdateDTO
+ {
+ LocationId = snapshot.LocationId,
+ WarehouseId = snapshot.WarehouseId,
+ PalletCode = snapshot.PalletCode,
+ StockQuantity = snapshot.StockQuantity,
+ StockStatus = snapshot.StockStatus,
+ LocationStatus = snapshot.LocationStatus,
+ Details = BuildDetailDtos(stock?.Details?.ToList())
+ };
+
+ await _hubContext.Clients.All.SendAsync("StockUpdated", update);
+ _logger.LogDebug("鏁版嵁鍙樺寲鎺ㄩ��: LocationId={LocationId}, LocStatus={LocStatus}, StockStatus={StockStatus}, Quantity={Quantity}",
+ snapshot.LocationId, snapshot.LocationStatus, snapshot.StockStatus, snapshot.StockQuantity);
+ }
+ }
+ }
+
+ // 鏇存柊蹇収鏁版嵁
+ _lastLocationSnapshots = currentSnapshots;
+ }
+
+ /// <summary>
+ /// 鐢熸垚鏄庣粏鏁版嵁鍝堝笇
+ /// </summary>
+ private string GenerateDetailsHash(List<Dt_StockInfoDetail> details)
+ {
+ if (details == null || !details.Any()) return string.Empty;
+
+ var hashString = string.Join("|", details
+ .OrderBy(d => d.Id)
+ .Select(d => $"{d.Id}:{d.MaterielCode}:{d.BatchNo}:{d.StockQuantity}"));
+ return hashString.GetHashCode().ToString();
+ }
+
+ /// <summary>
+ /// 鏋勫缓鏄庣粏DTO鍒楄〃
+ /// </summary>
+ private List<StockDetailUpdateDTO> BuildDetailDtos(List<Dt_StockInfoDetail> details)
+ {
+ if (details == null || !details.Any()) return new List<StockDetailUpdateDTO>();
+
+ return details.Select(d => new StockDetailUpdateDTO
+ {
+ Id = d.Id,
+ MaterielCode = d.MaterielCode,
+ MaterielName = d.MaterielName,
+ BatchNo = d.BatchNo,
+ StockQuantity = d.StockQuantity,
+ Unit = d.Unit,
+ Status = d.Status
+ }).ToList();
+ }
+
+ /// <summary>
+ /// 璐т綅蹇収
+ /// </summary>
+ private class LocationSnapshot
+ {
+ public int LocationId { get; set; }
+ public int WarehouseId { get; set; }
+ public string LocationCode { get; set; }
+ public int LocationStatus { get; set; }
+ public string PalletCode { get; set; }
+ public int StockStatus { get; set; }
+ public float StockQuantity { get; set; }
+ public string DetailsHash { get; set; }
+ }
+ }
+}
--
Gitblit v1.9.3