using BBWY.Common.Models; using BBWY.Server.Model; using BBWY.Server.Model.Db; using BBWY.Server.Model.Dto; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Yitter.IdGenerator; namespace BBWY.Server.Business { public class JDStockNumWarningBusiness : BaseSyncBusiness, IDenpendency { private IList validStorageTypeList; private DingDingBusiness dingDingBusiness; public JDStockNumWarningBusiness(NLogManager nLogManager, IFreeSql fsql, IIdGenerator idGenerator, TaskSchedulerManager taskSchedulerManager, IEnumerable platformSDKBusinessList, VenderBusiness venderBusiness, DingDingBusiness dingDingBusiness) : base(nLogManager, fsql, idGenerator, taskSchedulerManager, platformSDKBusinessList, venderBusiness) { validStorageTypeList = new List() { Enums.StorageType.云仓, Enums.StorageType.京仓, Enums.StorageType.本地自发 }; this.dingDingBusiness = dingDingBusiness; } //public JDStockNumWarningBusiness( NLogManager nLogManager, IFreeSql fsql, IIdGenerator idGenerator, TaskSchedulerManager taskSchedulerManager, VenderBusiness venderBusiness, DingDingBusiness dingDingBusiness, IEnumerable platformSDKBusinessList) : base(restApiService, options, nLogManager, fsql, idGenerator, taskSchedulerManager, venderBusiness, platformSDKBusinessList) //{ // validStorageTypeList = new List() { // Enums.StorageType.云仓, // Enums.StorageType.京仓, // Enums.StorageType.本地自发 // }; // this.dingDingBusiness = dingDingBusiness; //} public void StartCheckStockNum() { var storeHouseList = fsql.Select().Where(s => s.Platform == Enums.Platform.京东).ToList(); var shopList = venderBusiness.GetShopList(platform: Enums.Platform.京东, filterTurnoverDays: true); foreach (var shop in shopList) { Task.Factory.StartNew(() => CheckStockNum(shop, storeHouseList), CancellationToken.None, TaskCreationOptions.LongRunning, taskSchedulerManager.StockNumWarningTaskScheduler); } } private void CheckStockNum(ShopResponse shop, IList storeHouseList) { var loggerName = $"库存预警-{shop.ShopName}"; try { long shopId = long.Parse(shop.ShopId); var yesterDayDate = DateTime.Now.Date.AddDays(-1); var ysterDayTime = DateTime.Now.Date.AddSeconds(-1); //查询符合条件的sku (昨日 && 非赠品 && 销量>取消 && 非代发和刷单) var yesterDaySkuIds = fsql.Select() .InnerJoin((s, osku, o) => s.Sku == osku.SkuId) .InnerJoin((s, osku, o) => osku.OrderId == o.Id) .Where((s, osku, o) => s.ShopId == shopId && s.Date == yesterDayDate && s.IsGift == false && s.ItemTotal > s.CancelItemTotal && o.StartTime >= yesterDayDate && o.StartTime <= ysterDayTime && validStorageTypeList.Contains(o.StorageType.Value)) .Distinct() .ToList((s, osku, o) => s.Sku); //查询近15天的销量(5个周期) var allCycleStartDate = yesterDayDate.AddDays(-14); var skuSaleDailyList = fsql.Select() .Where(s => s.Date >= allCycleStartDate && s.Date <= yesterDayDate) .Where(s => yesterDaySkuIds.Contains(s.Sku)) .ToList(); var firstCycleStartDate = yesterDayDate.AddDays(-8); var firstCycleEndDate = firstCycleStartDate.AddDays(2); var secondCycleStartDate = firstCycleEndDate.AddDays(1); var secondCycleEndDate = secondCycleStartDate.AddDays(2); var thirdCycleStartDate = secondCycleEndDate.AddDays(1); var thirdCycleEndDate = thirdCycleStartDate.AddDays(2); var _7dAvgStartDate = DateTime.Now.Date.AddDays(-7); bool isSendDingTalk = false; var dingdingContentBuilder = new StringBuilder(); dingdingContentBuilder.Append(shop.ShopName); dingdingContentBuilder.AppendLine(); foreach (var sku in yesterDaySkuIds) { //第一周期销量 var firstCycleSaleList = skuSaleDailyList.Where(s => s.Sku == sku && s.Date >= firstCycleStartDate && s.Date <= firstCycleEndDate); var firstCycleItemTotal = firstCycleSaleList.Count() > 0 ? firstCycleSaleList.Sum(s => s.ItemTotal - s.CancelItemTotal) : 0; if (firstCycleItemTotal < 0) firstCycleItemTotal = 0; //第二周期销量 var secondCycleSaleList = skuSaleDailyList.Where(s => s.Sku == sku && s.Date >= secondCycleStartDate && s.Date <= secondCycleEndDate); var secondCycleItemTotal = secondCycleSaleList.Count() > 0 ? secondCycleSaleList.Sum(s => s.ItemTotal - s.CancelItemTotal) : 0; if (secondCycleItemTotal < 0) secondCycleItemTotal = 0; //第三周期销量 var thirdCycleSaleList = skuSaleDailyList.Where(s => s.Sku == sku && s.Date >= thirdCycleStartDate && s.Date <= thirdCycleEndDate); var thirdCycleItemTotal = thirdCycleSaleList.Count() > 0 ? thirdCycleSaleList.Sum(s => s.ItemTotal - s.CancelItemTotal) : 0; if (thirdCycleItemTotal < 0) thirdCycleItemTotal = 0; //计算周期增幅 var _2Ratio = firstCycleItemTotal == 0 ? 0 : 1.0 * secondCycleItemTotal / firstCycleItemTotal - 1; var _3Ratio = secondCycleItemTotal == 0 ? 0 : 1.0 * thirdCycleItemTotal / secondCycleItemTotal - 1; var _7dSaleList = skuSaleDailyList.Where(s => s.Sku == sku && s.Date >= _7dAvgStartDate && s.Date <= yesterDayDate); var _15dSaleList = skuSaleDailyList.Where(s => s.Sku == sku); var _7dItemTotal = _7dSaleList.Sum(s => s.ItemTotal - s.CancelItemTotal); if (_7dItemTotal < 0) _7dItemTotal = 0; var _15dItemTotal = _15dSaleList.Sum(s => s.ItemTotal - s.CancelItemTotal); //近15天销量 if (_15dItemTotal < 0) _15dItemTotal = 0; var _7dAvgItemTotal = 1.0 * _7dItemTotal / 7; //近7天日均销量 Enums.SkuStockNumCycleType skuStockNumCycleType = Enums.SkuStockNumCycleType.暂无周期; if (_2Ratio >= 0.2 && _3Ratio >= 0.2) skuStockNumCycleType = Enums.SkuStockNumCycleType.增长期; else if (_2Ratio >= -0.2 && _2Ratio <= 0.2 && _3Ratio >= -0.2 && _3Ratio <= 0.2) skuStockNumCycleType = Enums.SkuStockNumCycleType.稳定期; else if (_2Ratio < -0.2 && _3Ratio < -0.2) skuStockNumCycleType = Enums.SkuStockNumCycleType.衰退期; var logContentBuilder = new StringBuilder(); logContentBuilder.AppendLine($"SKU:{sku}"); logContentBuilder.AppendLine($"商品状态:{skuStockNumCycleType}"); logContentBuilder.AppendLine($"第一周销量:{firstCycleItemTotal}"); logContentBuilder.AppendLine($"第二周期销量:{secondCycleItemTotal}, 相比第一周幅度:{_2Ratio * 100}%"); logContentBuilder.AppendLine($"第三周期销量:{thirdCycleItemTotal}, 相比第二周幅度:{_3Ratio * 100}%"); logContentBuilder.AppendLine($"近7天销量:{_7dItemTotal}"); logContentBuilder.AppendLine($"近15天销量:{_15dItemTotal}"); logContentBuilder.AppendLine($"7天日均销量:{_7dAvgItemTotal}"); //dingdingContentBuilder.Append($"SKU:{sku}\n"); //dingdingContentBuilder.Append($"商品状态:{skuStockNumCycleType}\n"); //dingdingContentBuilder.Append($"近7天销量:{_7dItemTotal}\n"); //dingdingContentBuilder.Append($"7天日均销量:{_7dItemTotal}\n"); if (skuStockNumCycleType == Enums.SkuStockNumCycleType.暂无周期) { nLogManager.GetLogger(loggerName).Info(logContentBuilder); continue; } Thread.Sleep(1000); var response = GetPlatformSDKBusiness(Enums.Platform.京东).GetStockNumBySku( new SearchProductSkuRequest() { AppKey = shop.AppKey, AppSecret = shop.AppSecret, AppToken = shop.AppToken, Platform = shop.PlatformId, Sku = sku }); //var restApiResult = restApiService.SendRequest(GetPlatformRelayAPIHost(shop.PlatformId), "api/platformsdk/GetStockNumBySku", new SearchProductSkuRequest() //{ // AppKey = shop.AppKey, // AppSecret = shop.AppSecret, // AppToken = shop.AppToken, // Platform = shop.PlatformId, // Sku = sku //}, GetYunDingRequestHeader(), HttpMethod.Post); //if (restApiResult.StatusCode != System.Net.HttpStatusCode.OK) // throw new Exception($"{sku} {restApiResult.Content}"); //var response = JsonConvert.DeserializeObject>(restApiResult.Content); if (response == null || response?.Count() == 0) { logContentBuilder.AppendLine("未查询到JD库存数据"); nLogManager.GetLogger(loggerName).Info(logContentBuilder); continue; } var skuStockNumList = response.Select(j => new { StockNum = j.Value("stockNum"), Store = storeHouseList.FirstOrDefault(s => s.Id == j.Value("storeId")), StoreId = j.Value("storeId"), SkuId = sku }); var skuStockNumGroups = skuStockNumList.GroupBy(s => s.Store?.Type ?? Enums.StockType.商家仓); //按仓库类型算库存 foreach (var skuStockNumGroup in skuStockNumGroups) { var totalStockNum = skuStockNumGroup.Sum(s => s.StockNum); //总库存 var lessDay = _7dAvgItemTotal == 0 ? 0 : totalStockNum / _7dAvgItemTotal; //剩余天数 bool isWarning = false; //是否触发提醒 int suggestStockNum = 0; //建议备货量 if (skuStockNumCycleType == Enums.SkuStockNumCycleType.增长期) { if (lessDay < 15) { isWarning = true; if (_15dItemTotal <= 2) suggestStockNum = 0; else if (shop.SkuSafeTurnoverDays == 28) suggestStockNum = (int)Math.Ceiling(_7dItemTotal * 4 * 1.5); else if (shop.SkuSafeTurnoverDays == 21) suggestStockNum = (int)Math.Ceiling(_7dItemTotal * 3 * 1.5); else if (shop.SkuSafeTurnoverDays == 14) suggestStockNum = (int)Math.Ceiling(_7dItemTotal * 2 * 1.5); } } else if (skuStockNumCycleType == Enums.SkuStockNumCycleType.稳定期) { if (lessDay < 8) { isWarning = true; if (_15dItemTotal <= 2) suggestStockNum = 0; else if (shop.SkuSafeTurnoverDays == 28) suggestStockNum = _7dItemTotal * 4; else if (shop.SkuSafeTurnoverDays == 21) suggestStockNum = _7dItemTotal * 3; else if (shop.SkuSafeTurnoverDays == 14) suggestStockNum = _7dItemTotal * 2; } } else if (skuStockNumCycleType == Enums.SkuStockNumCycleType.衰退期) { if (lessDay < 8) { isWarning = true; suggestStockNum = 0; } } if (isWarning) { isSendDingTalk = true; dingdingContentBuilder.Append($"SKU:{sku}\n"); dingdingContentBuilder.Append($"商品状态:{skuStockNumCycleType}\n"); dingdingContentBuilder.Append($"近7天销量:{_7dItemTotal}\n"); dingdingContentBuilder.Append($"近15天销量:{_15dItemTotal}\n"); foreach (var stockNumInfo in skuStockNumGroup) dingdingContentBuilder.Append($"{stockNumInfo.Store?.Name ?? stockNumInfo.StoreId}:{stockNumInfo.StockNum}件\n"); if (skuStockNumCycleType == Enums.SkuStockNumCycleType.增长期 || skuStockNumCycleType == Enums.SkuStockNumCycleType.稳定期) { if (suggestStockNum != 0) dingdingContentBuilder.Append($"{skuStockNumGroup.Key}库存低于安全周转天数,建议备货{suggestStockNum}件"); else dingdingContentBuilder.Append($"建议暂停备货,采购代发"); } else if (skuStockNumCycleType == Enums.SkuStockNumCycleType.衰退期) dingdingContentBuilder.Append($"{skuStockNumGroup.Key}库存商品进入衰退期,建议暂停备货,采购代发"); dingdingContentBuilder.AppendLine(); } #region 拼接sku在当前类型仓库的日志 logContentBuilder.AppendLine($"仓库类型:{skuStockNumGroup.Key}"); logContentBuilder.AppendLine($"总库存:{totalStockNum}"); logContentBuilder.AppendLine($"剩余天数:{lessDay}"); logContentBuilder.AppendLine($"建议备货量:{suggestStockNum}"); logContentBuilder.AppendLine($"是否触发预警:{isWarning}"); #endregion } nLogManager.GetLogger(loggerName).Info(logContentBuilder); } if (isSendDingTalk) { dingDingBusiness.SendDingDingBotMessage(shop.DingDingKey, shop.DingDingWebHook, dingdingContentBuilder.ToString()); //var secret = shop.DingDingKey; //var timestamp = DateTime.Now.DateTimeToStamp(); //var stringToSign = timestamp + "\n" + secret; //var sign = EncryptWithSHA256(stringToSign, secret); //var url = $"{shop.DingDingWebHook}×tamp={timestamp}&sign={sign}"; //var result = restApiService.SendRequest(url, string.Empty, new //{ // msgtype = "text", // text = new // { // content = dingdingContentBuilder.ToString() // } //}, null, HttpMethod.Post); //if (result.StatusCode != System.Net.HttpStatusCode.OK) // throw new Exception($"{shop.ShopName} 发送钉钉库存预警失败 {result.Content}"); } } catch (Exception ex) { nLogManager.GetLogger(loggerName).Error(ex, "查询sku库存失败"); } } } }