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<Dt_StockInfo, IStockInfoRepository>, 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;
|
}
|
|
/// <summary>
|
/// 查询订单立库库存视图
|
/// </summary>
|
/// <param name="orderId"></param>
|
/// <param name="materielCode"></param>
|
/// <returns></returns>
|
public List<StockSelectViewDTO> GetStockSelectViews(int orderId, string materielCode)
|
{
|
try
|
{
|
Dt_OutboundOrder outboundOrder = _outboundRepository.OutboundOrderRepository.QueryFirst(x => x.Id == orderId);
|
if (outboundOrder == null)
|
{
|
throw new Exception($"未找到出库单信息");
|
}
|
|
List<string> locationCodes = _basicRepository.LocationInfoRepository.PPGetCanOutLocationCodes(outboundOrder.WarehouseId);
|
|
return BaseDal.QueryTabs<Dt_StockInfo, Dt_StockInfoDetail, StockSelectViewDTO>((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;
|
}
|
|
}
|
/// <summary>
|
/// 查询订单平库库存视图
|
/// </summary>
|
/// <param name="orderId"></param>
|
/// <param name="materielCode"></param>
|
/// <returns></returns>
|
public List<StockSelectViewDTO> 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<Dt_StockInfo, Dt_StockInfoDetail, StockSelectViewDTO>((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;
|
}
|
}
|
/// <summary>
|
/// 查询订单PP立库库存视图
|
/// </summary>
|
/// <param name="orderId"></param>
|
/// <param name="materielCode"></param>
|
/// <returns></returns>
|
public List<PPStockSelectViewDTO> PPGetStockSelectViews(int orderId, string materielCode)
|
{
|
try
|
{
|
Dt_MesPPCutOutboundOrder mesPPCutOutboundOrder = _outboundRepository.MesPPCutOutboundOrderRepository.QueryFirst(x => x.Id == orderId);
|
if (mesPPCutOutboundOrder == null)
|
{
|
throw new Exception($"未找到出库单信息");
|
}
|
List<string> locationCodes = _basicRepository.LocationInfoRepository.PPGetCanOutLocationCodes(mesPPCutOutboundOrder.WarehouseId);
|
|
return BaseDal.QueryTabs<Dt_StockInfo, Dt_StockInfoDetail, PPStockSelectViewDTO>((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;
|
}
|
}
|
|
/// <summary>
|
/// 查询订单PP平库库存视图
|
/// </summary>
|
/// <param name="orderId"></param>
|
/// <param name="materielCode"></param>
|
/// <returns></returns>
|
public List<PPStockSelectViewDTO> 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<Dt_StockInfo, Dt_StockInfoDetail, PPStockSelectViewDTO>((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<Dt_StockInfo>().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);
|
}
|
}
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="stockInfos"></param>
|
/// <param name="materielCode"></param>
|
/// <param name="needQuantity"></param>
|
/// <param name="residueQuantity"></param>
|
/// <returns></returns>
|
public List<Dt_StockInfo> GetOutboundStocks(List<Dt_StockInfo> stockInfos, string materielCode, float needQuantity, out float residueQuantity)
|
{
|
List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>();
|
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<Dt_StockInfo> GetUseableStocks(string materielCode, string batchNo, int warehoseId)
|
{
|
List<string> locationCodes = _basicRepository.LocationInfoRepository.GetCanOutLocationCodes(warehoseId);
|
|
return BaseDal.GetStockInfos(materielCode, batchNo, locationCodes);
|
}
|
|
public List<Dt_StockInfo> GetUseableStocks(string materielCode, string batchNo, string palletcode, int warehoseId)
|
{
|
Dt_StockInfo stockInfo = BaseDal.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletCode == palletcode && x.WarehouseId == warehoseId).Includes(x => x.Details).First();
|
|
List<string> 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<Dt_StockInfoDetail>()
|
.InnerJoin<Dt_StockInfo>((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<long, int>();
|
|
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<Dt_StockInfo>();
|
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("更新失败,请联系管理员");
|
}
|
}
|
/// <summary>
|
/// 钉钉机器人消息推送测试
|
/// </summary>
|
/// <returns></returns>
|
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}");
|
}
|
}
|
|
/// <summary>
|
/// 生成加签签名
|
/// </summary>
|
/// <param name="timestamp">时间戳</param>
|
/// <returns>签名</returns>
|
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");
|
}
|
}
|
}
|
}
|