using AutoMapper; using MailKit.Search; using Newtonsoft.Json; using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using WIDESEA_Common.StockEnum; using WIDESEA_Core; using WIDESEA_Core.BaseServices; using WIDESEA_Core.Enums; using WIDESEA_Core.Helper; using WIDESEA_DTO.Stock; using WIDESEA_IBasicRepository; using WIDESEA_IOutboundRepository; using WIDESEA_IRecordRepository; using WIDESEA_IRecordService; using WIDESEA_IStockRepository; using WIDESEA_IStockService; using WIDESEA_Model.Models; using WIDESEA_StockRepository; namespace WIDESEA_StockService { public partial class StockInfoService : ServiceBase, IStockInfoService { private readonly IMapper _mapper; private readonly IBasicRepository _basicRepository; private readonly IOutboundRepository _outboundRepository; public IStockInfoRepository Repository => BaseDal; public StockInfoService(IStockInfoRepository BaseDal, IMapper mapper, IBasicRepository basicRepository, IOutboundRepository outboundRepository) : base(BaseDal) { _mapper = mapper; _basicRepository = basicRepository; _outboundRepository = outboundRepository; } /// /// 查询订单立库库存视图 /// /// /// /// public List GetStockSelectViews(int orderId, string materielCode) { try { Dt_OutboundOrder outboundOrder = _outboundRepository.OutboundOrderRepository.QueryFirst(x => x.Id == orderId); if (outboundOrder == null) { throw new Exception($"未找到出库单信息"); } List locationCodes = _basicRepository.LocationInfoRepository.PPGetCanOutLocationCodes(outboundOrder.WarehouseId); return BaseDal.QueryTabs((a, b) => a.Id == b.StockId, (a, b) => new StockSelectViewDTO { LocationCode = a.LocationCode, MaterielCode = b.MaterielCode, MaterielName = b.MaterielName, PalletCode = a.PalletCode, UseableQuantity = b.StockQuantity - b.OutboundQuantity }, a => locationCodes.Contains(a.LocationCode), b => b.StockQuantity > b.OutboundQuantity && b.MaterielCode == materielCode, x => true).GroupBy(x => x.PalletCode).Select(x => new StockSelectViewDTO { LocationCode = x.FirstOrDefault()?.LocationCode ?? "", MaterielCode = x.FirstOrDefault()?.MaterielCode ?? "", MaterielName = x.FirstOrDefault()?.MaterielName ?? "", PalletCode = x.Key, UseableQuantity = x.Sum(x => x.UseableQuantity) }).ToList(); } catch (Exception ex) { return null; } } /// /// 查询订单平库库存视图 /// /// /// /// public List GetPKStockSelectViews(int orderId, string materielCode) { try { Dt_OutboundOrder outboundOrder = _outboundRepository.OutboundOrderRepository.QueryFirst(x => x.Id == orderId); if (outboundOrder == null) { throw new Exception($"未找到出库单信息"); } return BaseDal.QueryTabs((a, b) => a.Id == b.StockId && a.WarehouseId == outboundOrder.WarehouseId, (a, b) => new StockSelectViewDTO { LocationCode = a.LocationCode, MaterielCode = b.MaterielCode, MaterielName = b.MaterielName, PalletCode = a.PalletCode, UseableQuantity = b.StockQuantity - b.OutboundQuantity }, a => a.LocationCode == "平库位", b => b.StockQuantity > b.OutboundQuantity && b.MaterielCode == materielCode, x => true).GroupBy(x => x.PalletCode).Select(x => new StockSelectViewDTO { LocationCode = x.FirstOrDefault()?.LocationCode ?? "", MaterielCode = x.FirstOrDefault()?.MaterielCode ?? "", MaterielName = x.FirstOrDefault()?.MaterielName ?? "", PalletCode = x.Key, UseableQuantity = x.Sum(x => x.UseableQuantity) }).ToList(); } catch (Exception ex) { return null; } } /// /// 查询订单PP立库库存视图 /// /// /// /// public List PPGetStockSelectViews(int orderId, string materielCode) { try { Dt_MesPPCutOutboundOrder mesPPCutOutboundOrder = _outboundRepository.MesPPCutOutboundOrderRepository.QueryFirst(x => x.Id == orderId); if (mesPPCutOutboundOrder == null) { throw new Exception($"未找到出库单信息"); } List locationCodes = _basicRepository.LocationInfoRepository.PPGetCanOutLocationCodes(mesPPCutOutboundOrder.WarehouseId); return BaseDal.QueryTabs((a, b) => a.Id == b.StockId, (a, b) => new PPStockSelectViewDTO { LocationCode = a.LocationCode, MaterielCode = b.MaterielCode, MaterielName = b.MaterielName, PalletCode = a.PalletCode, Unit = b.Unit, CutedWidth = b.CutedWidth, UseableQuantity = b.StockQuantity - b.OutboundQuantity }, a => locationCodes.Contains(a.LocationCode), b => b.StockQuantity > b.OutboundQuantity && b.MaterielCode == materielCode, x => true).GroupBy(x => x.PalletCode).Select(x => new PPStockSelectViewDTO { LocationCode = x.FirstOrDefault()?.LocationCode ?? "", MaterielCode = x.FirstOrDefault()?.MaterielCode ?? "", MaterielName = x.FirstOrDefault()?.MaterielName ?? "", Unit = x.FirstOrDefault()?.Unit ?? "", CutedWidth = x.Sum(x => x.CutedWidth), PalletCode = x.Key, UseableQuantity = x.Sum(x => x.UseableQuantity) }).ToList(); } catch (Exception ex) { return null; } } /// /// 查询订单PP平库库存视图 /// /// /// /// public List PPGetPKStockSelectViews(int orderId, string materielCode) { try { Dt_MesPPCutOutboundOrder outboundOrder = _outboundRepository.MesPPCutOutboundOrderRepository.QueryFirst(x => x.Id == orderId); if (outboundOrder == null) { throw new Exception($"未找到出库单信息"); } return BaseDal.QueryTabs((a, b) => a.Id == b.StockId && a.WarehouseId == outboundOrder.WarehouseId, (a, b) => new PPStockSelectViewDTO { LocationCode = a.LocationCode, MaterielCode = b.MaterielCode, MaterielName = b.MaterielName, PalletCode = a.PalletCode, Unit = b.Unit, CutedWidth = b.CutedWidth, UseableQuantity = b.StockQuantity - b.OutboundQuantity }, a => a.LocationCode.Contains("AGV_PP"), b => b.StockQuantity > b.OutboundQuantity && b.MaterielCode == materielCode, x => true).GroupBy(x => x.PalletCode).Select(x => new PPStockSelectViewDTO { LocationCode = x.FirstOrDefault()?.LocationCode ?? "", MaterielCode = x.FirstOrDefault()?.MaterielCode ?? "", MaterielName = x.FirstOrDefault()?.MaterielName ?? "", Unit = x.FirstOrDefault()?.Unit ?? "", CutedWidth = x.Sum(x => x.CutedWidth), PalletCode = x.Key, UseableQuantity = x.Sum(x => x.UseableQuantity) }).ToList(); } catch (Exception ex) { return null; } } public WebResponseContent StockQueryData(SaveModel saveModel) { try { var barcode = saveModel.MainData["barcode"].ToString(); var warehouseId = saveModel.MainData["warehouseId"].ObjToInt(); Dt_StockInfo stockInfo = BaseDal.Db.Queryable().Where(x => x.PalletCode == barcode && x.WarehouseId == warehouseId).Includes(x => x.Details).First(); if (stockInfo == null) throw new Exception("未找到托盘信息"); return WebResponseContent.Instance.OK(data: stockInfo); } catch (Exception ex) { return WebResponseContent.Instance.Error(ex.Message); } } /// /// /// /// /// /// /// /// public List GetOutboundStocks(List stockInfos, string materielCode, float needQuantity, out float residueQuantity) { List outStocks = new List(); float stockTotalQuantity = stockInfos.Select(x => x.Details.Sum(v => v.StockQuantity - v.OutboundQuantity)).Sum(x => x); //stockInfos = stockInfos.OrderBy(x => x.Id).ToList(); if (stockTotalQuantity >= needQuantity)//库存够 { int index = 0; 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) { stockInfo.Details.ForEach(x => x.OutboundQuantity = x.StockQuantity); needQuantity -= useableStockQuantity; } else { stockInfo.Details.ForEach(x => { if (x.StockQuantity > x.OutboundQuantity && x.MaterielCode == materielCode) { if (x.StockQuantity - x.OutboundQuantity >= needQuantity) { x.OutboundQuantity += needQuantity; needQuantity = 0; } else { needQuantity -= (x.StockQuantity - x.OutboundQuantity); x.OutboundQuantity = x.StockQuantity; } } }); } outStocks.Add(stockInfo); index++; } } else { throw new Exception("库存不足"); } residueQuantity = needQuantity; return outStocks; } public List GetUseableStocks(string materielCode, string batchNo, int warehoseId) { List locationCodes = _basicRepository.LocationInfoRepository.GetCanOutLocationCodes(warehoseId); return BaseDal.GetStockInfos(materielCode, batchNo, locationCodes); } public List GetUseableStocks(string materielCode, string batchNo, string palletcode, int warehoseId) { Dt_StockInfo stockInfo = BaseDal.Db.Queryable().Where(x => x.PalletCode == palletcode && x.WarehouseId == warehoseId).Includes(x => x.Details).First(); List locationCodes = _basicRepository.LocationInfoRepository.GetCanOutLocationCodes(stockInfo.LocationCode); return BaseDal.GetStockInfos(materielCode, batchNo, locationCodes); } public WebResponseContent UpdateExpirationlabel() { try { var today = DateTime.Today; int batchSize = 1000; int totalUpdated = 0; int skipCount = 0; // 只查询需要的字段,减少数据传输和内存占用 var query = BaseDal.Db.Queryable() .InnerJoin((detail, master) => detail.StockId == master.Id) .Select((detail, master) => new { MasterId = master.Id, master.WarehouseId, detail.EffectiveDate, CurrentExpirationlabel = master.Expirationlabel }); while (true) { var batchData = query.Skip(skipCount).Take(batchSize).ToList(); if (!batchData.Any()) break; var groupedData = batchData.GroupBy(item => item.MasterId) .Select(g => new { MasterId = g.Key, WarehouseId = g.First().WarehouseId, // 取最早的有效日期 EarliestEffectiveDate = g.Min(item => { DateTime.TryParse(item.EffectiveDate, out DateTime date); return date; }), CurrentExpirationlabel = g.First().CurrentExpirationlabel }) .ToList(); var updateDic = new Dictionary(); foreach (var group in groupedData) { DateTime effectiveDate = group.EarliestEffectiveDate; if (effectiveDate == default(DateTime)) // 处理解析失败的情况 { Console.WriteLine($"主表ID {group.MasterId} 下无有效日期,跳过"); continue; } int newLabel; if (effectiveDate < today) { newLabel = ExpirationlabelEnum.过期.ObjToInt(); } else if (group.WarehouseId == 3) { int daysDiff = (effectiveDate - today).Days; newLabel = daysDiff < 60 ? ExpirationlabelEnum.临期预警.ObjToInt() : ExpirationlabelEnum.未临期.ObjToInt(); } else { int daysDiff = (effectiveDate - today).Days; newLabel = daysDiff < 30 ? ExpirationlabelEnum.临期预警.ObjToInt() : ExpirationlabelEnum.未临期.ObjToInt(); } if (newLabel != group.CurrentExpirationlabel && !updateDic.ContainsKey(group.MasterId)) { updateDic[group.MasterId] = newLabel; } } if (updateDic.Any()) { var updateBuilder = BaseDal.Db.Updateable(); var idsToUpdate = updateDic.Keys.ToList(); int updateValue = updateDic.First().Value; updateBuilder.SetColumns(m => m.Expirationlabel == updateValue) .Where(m => idsToUpdate.Contains(m.Id)); int batchUpdated = updateBuilder.ExecuteCommand(); totalUpdated += batchUpdated; Console.WriteLine($"批次更新:{batchUpdated} 条,累计更新:{totalUpdated} 条,更新条件:{JsonConvert.SerializeObject(idsToUpdate)}"); } skipCount += batchSize; } return WebResponseContent.Instance.OK($"更新成功,共更新 {totalUpdated} 条记录"); } catch (Exception ex) { return WebResponseContent.Instance.Error("更新失败,请联系管理员"); } } /// /// 钉钉机器人消息推送测试 /// /// public async Task T0DingTalkText(string webhookUrl, string secret) { try { if (webhookUrl == null || secret == null) { 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]}×tamp={timestamp}&sign={sign}"; var requestBody = new { msgtype = "text", text = new { content = "小洋主人说: 小洋要和小妍天下第一最最好" }, }; 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 errorContent = await response.Content.ReadAsStringAsync(); throw new Exception($"钉钉消息发送失败,状态码: {response.StatusCode},错误内容: {errorContent}"); } } catch(Exception ex) { throw new Exception($"钉钉消息发送失败,错误内容: {ex.Message}"); } } /// /// 生成加签签名 /// /// 时间戳 /// 签名 private string GenerateSign(long timestamp,string secret) { var stringToSign = $"{timestamp}\n{secret}"; using (var hmacsha256 = new System.Security.Cryptography.HMACSHA256(Encoding.UTF8.GetBytes(secret))) { var hashBytes = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)); return Convert.ToBase64String(hashBytes).Replace("+", "%2B").Replace("/", "%2F"); } } } }