wangxinhui
2025-10-14 0705cb6170a9ba77ba48bbb6dcebb9cf3d73cbea
´úÂë¹ÜÀí/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs
@@ -4,6 +4,7 @@
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using SqlSugar;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
@@ -238,11 +239,19 @@
                while (needQuantity > 0)
                {
                    Dt_StockInfo stockInfo = stockInfos[index];
                    float useableStockQuantity = stockInfo.Details.Where(x => x.MaterielCode == materielCode).Sum(x => x.StockQuantity - x.OutboundQuantity);
                    if (useableStockQuantity < needQuantity && useableStockQuantity>0)
                    // è®¡ç®—可用库存时转换为decimal
                    decimal useableStockQuantity = stockInfo.Details
                        .Where(x => x.MaterielCode == materielCode)
                        .Sum(x => (decimal)x.StockQuantity - (decimal)x.OutboundQuantity);
                    // å°†needQuantity转换为decimal进行比较
                    if (useableStockQuantity < (decimal)needQuantity && useableStockQuantity > 0)
                    {
                        stockInfo.Details.ForEach(x => x.OutboundQuantity = x.StockQuantity);
                        needQuantity -= useableStockQuantity;
                        stockInfo.Details.ForEach(x =>
                            x.OutboundQuantity = x.StockQuantity);
                        // ä½¿ç”¨decimal进行计算后再转回float
                        needQuantity = (float)((decimal)needQuantity - useableStockQuantity);
                    }
                    else
                    {
@@ -250,14 +259,20 @@
                        {
                            if (x.StockQuantity > x.OutboundQuantity && x.MaterielCode == materielCode)
                            {
                                if (x.StockQuantity - x.OutboundQuantity >= needQuantity)
                                // å°†ç›¸å…³å€¼è½¬æ¢ä¸ºdecimal进行精确计算
                                decimal currentStock = (decimal)x.StockQuantity;
                                decimal currentOutbound = (decimal)x.OutboundQuantity;
                                decimal currentNeed = (decimal)needQuantity;
                                decimal available = currentStock - currentOutbound;
                                if (available >= currentNeed)
                                {
                                    x.OutboundQuantity += needQuantity;
                                    x.OutboundQuantity = (float)(currentOutbound + currentNeed);
                                    needQuantity = 0;
                                }
                                else
                                {
                                    needQuantity -= (x.StockQuantity - x.OutboundQuantity);
                                    needQuantity = (float)(currentNeed - available);
                                    x.OutboundQuantity = x.StockQuantity;
                                }
                            }
@@ -266,6 +281,7 @@
                    outStocks.Add(stockInfo);
                    index++;
                }
            }
            else
            {
@@ -404,36 +420,134 @@
                    webhookUrl = "https://oapi.dingtalk.com/robot/send?access_token=fbc3aaf4133ea650d8116fb86b3ebfd0c5e0d46775966ce87893a41886bdf9dc";
                    secret = "SECf221842b26356f22ccac84c4e60714e5287408ee8332a8f63503791382c3f5fb";
                }
                HttpClient httpClient = new HttpClient();
                ///获取时间戳
                var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                ///生成签名
                var sign = GenerateSign(timestamp,secret);
                // æž„建请求URL
                var url = $"{webhookUrl.Split('?')[0]}?access_token={new Uri(webhookUrl).Query.Split('=')[1]}&timestamp={timestamp}&sign={sign}";
                var requestBody = new
                var warehouseIdToName = new Dictionary<int, string>
                {
                    msgtype = "text",
                    text = new { content = "小洋主人说: å°æ´‹è¦å’Œå°å¦å¤©ä¸‹ç¬¬ä¸€æœ€æœ€å¥½" },
                    { 2, "油墨仓" },
                    { 3, "板材仓" },
                    { 4, "PP仓" },
                    { 6, "测试架仓" },
                    { 11, "干膜仓" },
                    { 12, "阻焊仓" }
                };
                var jsonBody = JsonConvert.SerializeObject(requestBody);
                var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
                // å‘送POST请求
                var response = await httpClient.PostAsync(url, content);
                if (!response.IsSuccessStatusCode)
                var currentTime = DateTime.Now;
                var ninetyDaysAgo = currentTime.AddDays(-90);
                var expirationLabel3Stocks = BaseDal.Db.Queryable<Dt_StockInfo>()
                    .Where(s => s.Expirationlabel == 3 && s.WarehouseId != 5)
                    .ToList();
                var over90DaysStocks = BaseDal.Db.Queryable<Dt_StockInfo>()
                    .Where(s => s.ModifyDate <= ninetyDaysAgo && s.Expirationlabel != 3 && s.WarehouseId != 5)
                    .ToList();
                if (!expirationLabel3Stocks.Any() && !over90DaysStocks.Any())
                {
                    // å¤„理请求失败的情况
                    var errorContent = await response.Content.ReadAsStringAsync();
                    throw new Exception($"钉钉消息发送失败,状态码: {response.StatusCode},错误内容: {errorContent}");
                    return;
                }
                var expirationLabel3StockIds = expirationLabel3Stocks.Select(s => s.Id).ToList();
                var over90DaysStockIds = over90DaysStocks.Select(s => s.Id).ToList();
                var expirationLabel3Details = expirationLabel3StockIds.Any()
                    ? BaseDal.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(d => expirationLabel3StockIds.Contains(d.StockId))
                        .ToList()
                    : new List<Dt_StockInfoDetail>();
                var over90DaysDetails = over90DaysStockIds.Any()
                    ? BaseDal.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(d => over90DaysStockIds.Contains(d.StockId))
                        .ToList()
                    : new List<Dt_StockInfoDetail>();
                var stockIdToWarehouseId = expirationLabel3Stocks
                    .Concat(over90DaysStocks)
                    .ToDictionary(s => s.Id, s => s.WarehouseId);
                var expirationLabel3Groups = expirationLabel3Details
                    .GroupBy(d => stockIdToWarehouseId[d.StockId])
                    .ToDictionary(g => g.Key, g => g.Select(d => new
                    {
                        d.MaterielCode,
                        d.BatchNo
                    }).ToList());
                var over90DaysGroups = over90DaysDetails
                    .GroupBy(d => stockIdToWarehouseId[d.StockId])
                    .ToDictionary(g => g.Key, g => g.Select(d => new
                    {
                        d.MaterielCode,
                        d.BatchNo
                    }).ToList());
                var allWarehouseIds = expirationLabel3Groups.Keys
                    .Union(over90DaysGroups.Keys)
                    .ToList();
                using (HttpClient httpClient = new HttpClient())
                {
                    foreach (var warehouseId in allWarehouseIds)
                    {
                        // 9.1 å¤„理仓库名称显示:优先用映射名称,无映射时显示原始ID
                        var warehouseName = warehouseIdToName.TryGetValue(warehouseId, out var name)
                            ? name
                            : $"仓库{warehouseId}";
                        var messageParts = new List<string>
                {
                    $"【{warehouseName}】物料提醒"
                };
                        // æ·»åŠ è¿‡æœŸç‰©æ–™ä¿¡æ¯
                        if (expirationLabel3Groups.TryGetValue(warehouseId, out var label3Materials) && label3Materials.Any())
                        {
                            var label3Details = label3Materials
                                .Select(m => $"• ç‰©æ–™ç¼–码:{m.MaterielCode} | æ‰¹æ¬¡å·ï¼š{m.BatchNo}")
                                .Aggregate((current, next) => $"{current}\n{next}");
                            messageParts.Add($"一、过期物料\n{label3Details}");
                        }
                        // æ·»åŠ è¶…è¿‡90天未使用物料信息
                        if (over90DaysGroups.TryGetValue(warehouseId, out var over90Materials) && over90Materials.Any())
                        {
                            var over90Details = over90Materials
                                .Select(m => $"• ç‰©æ–™ç¼–码:{m.MaterielCode} | æ‰¹æ¬¡å·ï¼š{m.BatchNo}")
                                .Aggregate((current, next) => $"{current}\n{next}");
                            messageParts.Add($"二、超过90天未使用物料\n{over90Details}");
                        }
                        var messageContent = string.Join("\n\n", messageParts);
                        var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                        var sign = GenerateSign(timestamp, secret);
                        var url = $"{webhookUrl.Split('?')[0]}?access_token={new Uri(webhookUrl).Query.Split('=')[1]}&timestamp={timestamp}&sign={sign}";
                        // æž„建请求体(符合钉钉text类型消息格式)
                        var requestBody = new
                        {
                            msgtype = "text",
                            text = new { content = messageContent }
                        };
                        var jsonBody = JsonConvert.SerializeObject(requestBody);
                        var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
                        var response = await httpClient.PostAsync(url, content);
                        if (!response.IsSuccessStatusCode)
                        {
                            var errorContent = await response.Content.ReadAsStringAsync();
                            throw new Exception($"【{warehouseName}】消息发送失败,状态码:{response.StatusCode},错误信息:{errorContent}");
                        }
                    }
                }
            }
            catch(Exception ex)
            catch (Exception ex)
            {
                throw new Exception($"钉钉消息发送失败,错误内容: {ex.Message}");
                throw new Exception($"钉钉消息推送整体失败,错误详情:{ex.Message}", ex);
            }
        }
        /// <summary>
        /// ç”ŸæˆåŠ ç­¾ç­¾å
        /// </summary>