using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.Caching.Distributed; using Newtonsoft.Json; using NLog; using SBF.Common.Log; using SBF.Common.Models; using SBF.Model.Db; using SBF.Model.Db.Trusteeship; using SBF.Model.Dto; using SiNan.Business; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Yitter.IdGenerator; namespace SBF.Business { /// /// 执行托管策略 /// public class ExecuteTrusteeshipPolicyBusiness : BaseBusiness, IDenpendency { ILogger logger; public ExecuteTrusteeshipPolicyBusiness(IFreeSql fsql, NLogManager nLogManager, IIdGenerator idGenerator, IDistributedCache cache) : base(fsql, nLogManager, idGenerator) { logger = nLogManager.GetLogger("执行托管策略"); _cache = cache; } readonly IDistributedCache _cache; string host = "http://api.sbf.qiyue666.com/"; /// /// 回调出价和花费 /// /// /// /// public async Task BatchUpdateBidPrice(BatchUpdateBidPriceRequest[] requestList) { var AdgroupIdList = requestList.Select(r => long.Parse(r.AdgroupId)).ToList(); var trusteeshipTaskList = fsql.Select().Where(s => s.AdGroupId != null && AdgroupIdList.Contains(s.AdGroupId.Value)).ToList(); foreach (var trusteeshipTask in trusteeshipTaskList) { var newData = requestList.FirstOrDefault(t => t.AdgroupId == trusteeshipTask.AdGroupId?.ToString()); if (newData != null) { trusteeshipTask.BidPrice = newData.SearchFee;//当前出价 trusteeshipTask.NowCost = newData.TotalCost;//当前总花费 continue; } } if (trusteeshipTaskList.Any()) await fsql.Update(trusteeshipTaskList).ExecuteUpdatedAsync(); logger.Info($"回调出价和花费成功!回调消息:{JsonConvert.SerializeObject(requestList)}"); } /// /// 回调预算 /// /// /// /// public async Task BatchUpdateBudget(BatchUpdateBudgetRequest[] requestList) { var CampaignIdList = requestList.Select(r => long.Parse(r.CampaignId)).ToList(); var trusteeshipTaskList = fsql.Select().Where(s => s.CampaignId != null && CampaignIdList.Contains(s.CampaignId.Value)).ToList(); foreach (var trusteeshipTask in trusteeshipTaskList) { var newData = requestList.FirstOrDefault(t => t.CampaignId == trusteeshipTask.CampaignId?.ToString()); if (newData != null) { trusteeshipTask.Budget = newData.Budget; continue; } } if (trusteeshipTaskList.Any()) await fsql.Update(trusteeshipTaskList).ExecuteUpdatedAsync(); logger.Info($"回调预算成功!回调消息:{JsonConvert.SerializeObject(requestList)}"); } /// /// 提交 /// /// /// public async Task UpdateTrusteeshipPolicyData() { //获取当前待执行列表的数据 var searchTrusteeshipTaskList = FirstWeekUpdateTrusteeshipPolicyData(); searchTrusteeshipTaskList.AddRange(OverFirstWeekUpdateTrusteeshipPolicyData()); await UpdateTrusteeshipPolicyData(searchTrusteeshipTaskList); await ThreeWeekUpdateBudget();//第三周或三周以上 每天初始化预算 } private async Task UpdateTrusteeshipPolicyData(List searchTrusteeshipTaskList) { var shopIds = searchTrusteeshipTaskList.Select(s => s.ShopId).Distinct().ToList(); foreach (var shopId in shopIds) { var CampaignIds = searchTrusteeshipTaskList.Where(s => s.ShopId == shopId).Select(s => s.CampaignId).Distinct().ToArray(); /* { "UserPin": "string", "ShopId": "string", "Start": "2023-12-02T06:08:15.492Z", "End": "2023-12-02T06:08:15.492Z", "ApiUrl": "string", "CampaignIds": [ "string" ] } */ string url = "http://txapi.sbf.qiyue666.com/SanBanFu/Api/SendGetBudgetByCampaignList"; HttpClient client = new HttpClient(); client.Timeout = TimeSpan.FromMinutes(1); var postData = JsonConvert.SerializeObject(new { ShopId = shopId.ToString(), Start = DateTime.Now.ToString(), End = DateTime.Now.ToString(), ApiUrl = host + "api/ExecuteTrusteeshipPolicy/BatchUpdateBudget", CampaignIds = CampaignIds }); HttpContent content = new StringContent(postData); var res = await client.PostAsync(url, content); if (res.IsSuccessStatusCode) { //发送成功 logger.Info($"请求预算接口成功! 店铺id:{shopId},请求参数{postData}"); //todo: redis //await _cache.SetStringAsync("SBF:GetBudget:shopId", "0");//0未处理 三板斧批量获取预算 } else { logger.Error($"请求预算接口失败,返回消息:{res?.Content?.ReadAsStringAsync()?.Result}, 店铺id:{shopId},请求参数{postData}"); } Thread.Sleep(500); /* { "UserPin": "string", "ShopId": "string", "Start": "2023-12-02T06:59:26.115Z", "End": "2023-12-02T06:59:26.115Z", "ApiUrl": "string", "AdgroupIds": [ "string" ] } */ var adgroupIds = searchTrusteeshipTaskList.Where(s => s.ShopId == shopId).Select(s => s.AdGroupId).Distinct().ToArray(); url = "http://txapi.sbf.qiyue666.com/SanBanFu/Api/SendGetFeeByAdgroupList"; postData = JsonConvert.SerializeObject(new { ShopId = shopId.ToString(), Start = DateTime.Now.ToString(), End = DateTime.Now.ToString(), ApiUrl = host + "api/ExecuteTrusteeshipPolicy/BatchUpdateBudget", AdgroupIds = adgroupIds }); content = new StringContent(postData); res = await client.PostAsync(url, content); if (res.IsSuccessStatusCode) { //发送成功 logger.Info($"请求出价接口成功! 店铺id:{shopId},请求参数{postData}"); } else { logger.Error($"请求出价接口失败,返回消息:{res?.Content?.ReadAsStringAsync()?.Result}, 店铺id:{shopId},请求参数{postData}"); } Thread.Sleep(500); } } /// /// 第一周 只需更新一次 /// /// private List FirstWeekUpdateTrusteeshipPolicyData() { List excuteDate = new List(); DateTime nowDate = DateTime.Now.Date; excuteDate.Add(nowDate.AddDays(4)); excuteDate.Add(nowDate.AddDays(7)); return fsql.Select().Where(s => s.IsEnd == false && s.PolicyType == Model.Enums.PolicyType.成长期策略包 && s.StartTrusteeshipDate != null && excuteDate.Contains(s.StartTrusteeshipDate.Value)).ToList();// } /// /// 两周以上的(12:00 18:00 23:00要更新) /// /// private List OverFirstWeekUpdateTrusteeshipPolicyData() { List excuteDate = new List(); DateTime nowDate = DateTime.Now.Date; excuteDate.Add(nowDate.AddDays(11)); excuteDate.Add(nowDate.AddDays(14)); return fsql.Select().Where(s => s.IsEnd == false && s.PolicyType == Model.Enums.PolicyType.成长期策略包 && s.StartTrusteeshipDate != null && (excuteDate.Contains(s.StartTrusteeshipDate.Value) || nowDate.AddDays(-15) >= s.StartTrusteeshipDate.Value)).ToList(); } /// /// 更新 /// private async Task ThreeWeekUpdateBudget() { DateTime nowDate = DateTime.Now.Date; //第三周 或者三周以上的数据 var trusteeshipTaskList = fsql.Select().Where(s => s.IsEnd == false && s.PolicyType == Model.Enums.PolicyType.成长期策略包 && s.StartTrusteeshipDate != null && (nowDate.AddDays(-15) >= s.StartTrusteeshipDate.Value)).ToList(); var primarySkuIdList = trusteeshipTaskList.Select(d => d.PrimarySkuId).Distinct().ToList(); List excuteDate = new List { nowDate.AddDays(-1), nowDate.AddDays(-2), nowDate.AddDays(-3) }; var primarySkuDailyList = fsql.Select().Where(a => primarySkuIdList.Contains(a.SkuId) && a.Date != null && excuteDate.Contains(a.Date.Value)).ToList(); List AdjustLogList = new List(); var update = fsql.Update(); foreach (var trusteeshipTask in trusteeshipTaskList) { var primarySkuProfit = primarySkuDailyList.Where(p => p.SkuId == trusteeshipTask.PrimarySkuId).Select(p => p.ProductLevelProfit - p.Cost).Average();//3天日均盈利 if (trusteeshipTask.AnchorBudget == null) { //todo: 提醒设置锚定预算 continue; } decimal newBudget = primarySkuProfit.Value + trusteeshipTask.AnchorBudget.Value; string adjustContent = string.Empty; if (primarySkuProfit < 0) { newBudget = trusteeshipTask.AnchorBudget.Value; } adjustContent = $"主Sku:{trusteeshipTask.PrimarySkuId},近三天日均收益:{primarySkuProfit}"; decimal oldBudget = trusteeshipTask.Budget.Value;// trusteeshipTask.Budget = newBudget;//更新预算 update.Where(u => u.Id == trusteeshipTask.Id).Set(u=>u.Budget, newBudget); //修改预算 //更新快车的 出价 string url = "http://txapi.sbf.qiyue666.com/SanBanFu/Api/SendBudgetUpdate"; HttpClient client = new HttpClient(); client.Timeout = TimeSpan.FromMinutes(1); var postData = JsonConvert.SerializeObject(new { ShopId = trusteeshipTask.ShopId?.ToString(), CampaignId = trusteeshipTask.CampaignId?.ToString(), DayBudget = newBudget }); HttpContent content = new StringContent(postData); var res = await client.PostAsync(url, content); if (res.IsSuccessStatusCode) { //发送成功 logger.Info($"请求变更出价接口成功! 店铺id:{trusteeshipTask.ShopId},请求参数{postData}"); AdjustLogList.Add(new Sbf_AdjustLog { AdjustType = Model.Enums.AdjustType.出价, AdjustWay = Model.Enums.AdjustWay.调高, CreateTime = DateTime.Now, PolicyType = Model.Enums.PolicyType.成长期策略包, TrusteeshipTaskId = trusteeshipTask.Id, OperateContent = $"{adjustContent},原预算:{oldBudget},调整后预算:{newBudget}" }); } else { logger.Error($"请求变更出价接口失败,返回消息:{res?.Content?.ReadAsStringAsync()?.Result}, 店铺id:{trusteeshipTask.ShopId},请求参数{postData}"); } Thread.Sleep(500); } await update.ExecuteUpdatedAsync();//批量更新 if (AdjustLogList.Any()) await fsql.Insert(AdjustLogList).ExecuteInsertedAsync(); } /// /// 更新第二周 4 7天 和三周以上的数据 /// /// public async Task JudgeBudgetCostOutUpdateTrusteeshipPolicyData() { var searchTrusteeshipTaskList = OverFirstWeekUpdateTrusteeshipPolicyData(); await UpdateTrusteeshipPolicyData(searchTrusteeshipTaskList); } /// /// 调高出价策略 /// /// /// public async Task RunGrowthPeriodIncreaseBidPriceTrusteeshipPolicy() { /* 4 7 11 14 14天以上 */ //获取当前待执行列表的数据 var searchTrusteeshipTaskList = FirstWeekUpdateTrusteeshipPolicyData();// 4 7天 searchTrusteeshipTaskList.AddRange(OverFirstWeekUpdateTrusteeshipPolicyData());//满足条件的数据 var adgroupIds = searchTrusteeshipTaskList.Select(s => s.AdGroupId).Distinct().ToArray(); //获取满足条件的所有单元 前三天花费的数据 List excuteDate = new List(); DateTime nowDate = DateTime.Now.Date; excuteDate.Add(nowDate.AddDays(-1)); excuteDate.Add(nowDate.AddDays(-2)); excuteDate.Add(nowDate.AddDays(-3)); //获取前三天花费 var adgroupCostList = fsql.Select().Where(s => adgroupIds.Contains(s.AdGroupId) && s.Date != null && excuteDate.Contains(s.Date.Value)).ToList(); List AdjustLogList = new List(); var update = fsql.Update(); foreach (var trusteeshipTask in searchTrusteeshipTaskList) { var beforeThreeDayMaxCost = adgroupCostList.Where(a => a.AdGroupId == trusteeshipTask.AdGroupId).Select(s => s.Cost).Max();//前三天花费最高 if (trusteeshipTask.BidPrice == null || trusteeshipTask.BidPrice == 0) { logger.Error($"出价未获取,或者获取的出价为0,店铺id:{trusteeshipTask.ShopId},任务Id{trusteeshipTask.Id}"); continue; } if (trusteeshipTask.Budget == null || trusteeshipTask.Budget == 0) { logger.Error($"预算未获取,或者获取的预算为0,店铺id:{trusteeshipTask.ShopId},任务Id{trusteeshipTask.Id}"); continue; } decimal newBidPrice = 0m; string adjustContent = string.Empty; if (beforeThreeDayMaxCost < trusteeshipTask.Budget.Value * 0.3m)//前三天花费最高的一天低于预算的30% 出价调高50% { newBidPrice = Math.Round(trusteeshipTask.BidPrice.Value * (1 + 0.5m), 2); adjustContent = "出价调高50%"; } else if (beforeThreeDayMaxCost < trusteeshipTask.Budget.Value * 0.5m)//前三天花费最高的一天低于预算的50% 出价调高35% { newBidPrice = Math.Round(trusteeshipTask.BidPrice.Value * (1 + 0.35m), 2); adjustContent = "出价调高35%"; } else if (beforeThreeDayMaxCost < trusteeshipTask.Budget.Value * 0.7m)//前三天花费最高的一天低于预算的70% 出价调高20% { newBidPrice = Math.Round(trusteeshipTask.BidPrice.Value * (1 + 0.2m), 2); adjustContent = "出价调高20%"; } else { logger.Info($"任务Id:{trusteeshipTask.Id},三天最高花费:{beforeThreeDayMaxCost},预算:{trusteeshipTask.BidPrice}"); continue; } decimal oldBidPrice = trusteeshipTask.BidPrice.Value; trusteeshipTask.BidPrice = newBidPrice;//表里更新 新的出价 update.Where(u => u.Id == trusteeshipTask.Id).Set(u => u.BidPrice, newBidPrice); //更新快车的 出价 string url = "http://txapi.sbf.qiyue666.com/SanBanFu/Api/SendFeeUpdate"; HttpClient client = new HttpClient(); client.Timeout = TimeSpan.FromMinutes(1); var postData = JsonConvert.SerializeObject(new { ShopId = trusteeshipTask.ShopId?.ToString(), AdgroupId = trusteeshipTask.AdGroupId?.ToString(), SearchFee = newBidPrice }); HttpContent content = new StringContent(postData); var res = await client.PostAsync(url, content); if (res.IsSuccessStatusCode) { //发送成功 logger.Info($"请求变更出价接口成功! 店铺id:{trusteeshipTask.ShopId},请求参数{postData}"); AdjustLogList.Add(new Sbf_AdjustLog { AdjustType = Model.Enums.AdjustType.出价, AdjustWay = Model.Enums.AdjustWay.调高, CreateTime = DateTime.Now, PolicyType = Model.Enums.PolicyType.成长期策略包, TrusteeshipTaskId = trusteeshipTask.Id, OperateContent = $"{adjustContent},原出价:{oldBidPrice},调整后出价:{newBidPrice}" }); } else { logger.Error($"请求变更出价接口失败,返回消息:{res?.Content?.ReadAsStringAsync()?.Result}, 店铺id:{trusteeshipTask.ShopId},请求参数{postData}"); } Thread.Sleep(500); } await update.ExecuteUpdatedAsync(); if (AdjustLogList.Any()) await fsql.Insert(AdjustLogList).ExecuteInsertedAsync(); } /// /// 降低出价策略 /// /// /// public async Task RunGrowthPeriodLowerBidPriceTrusteeshipPolicy() { var searchTrusteeshipTaskList = OverFirstWeekUpdateTrusteeshipPolicyData();//获取两周的数据 List AdjustLogList = new List(); var hour = DateTime.Now.Hour; List historyAdjustLogList = new List(); if (hour != 12) { //获取当天的降低出价日志记录 historyAdjustLogList = fsql.Select().Where(l => l.CreateTime.Value.Date == DateTime.Now.Date && l.PolicyType == Model.Enums.PolicyType.成长期策略包 && l.AdjustType == Model.Enums.AdjustType.出价 && l.AdjustWay == Model.Enums.AdjustWay.降低).ToList(); } var update = fsql.Update(); foreach (var trusteeshipTask in searchTrusteeshipTaskList) { if (trusteeshipTask.BidPrice == null || trusteeshipTask.BidPrice == 0) { logger.Error($"出价未获取,或者获取的出价为0,店铺id:{trusteeshipTask.ShopId},任务Id{trusteeshipTask.Id}"); continue; } if (trusteeshipTask.Budget == null || trusteeshipTask.Budget == 0) { logger.Error($"预算未获取,或者获取的预算为0,店铺id:{trusteeshipTask.ShopId},任务Id{trusteeshipTask.Id}"); continue; } if (trusteeshipTask.Budget == trusteeshipTask.NowCost)//花费耗尽 { decimal newBidPrice = 0m; string adjustContent = string.Empty; if (hour == 12)//预算早于12:00前花完 降低出价30% { newBidPrice = Math.Round(trusteeshipTask.BidPrice.Value * (1 - 0.3m), 2); adjustContent = "降低出价30%"; } else if (hour == 18)//预算早于12:00前花完 降低出价20% { if (historyAdjustLogList.Any(h => h.TrusteeshipTaskId == trusteeshipTask.Id))//当天已经降低出价过 continue; adjustContent = "降低出价20%"; newBidPrice = Math.Round(trusteeshipTask.BidPrice.Value * (1 - 0.2m), 2); } else if (hour == 23)//预算早于12:00前花完 降低出价10% { if (historyAdjustLogList.Any(h => h.TrusteeshipTaskId == trusteeshipTask.Id))//当天已经降低出价过 continue; adjustContent = "降低出价10%"; newBidPrice = Math.Round(trusteeshipTask.BidPrice.Value * (1 - 0.1m), 2); } else {//出错 continue; } decimal oldBidPrice = trusteeshipTask.BidPrice.Value; trusteeshipTask.BidPrice = newBidPrice;//表里更新 新的出价 update.Where(u => u.Id == trusteeshipTask.Id).Set(u => u.BidPrice, newBidPrice); //更新快车的 出价 string url = "http://txapi.sbf.qiyue666.com/SanBanFu/Api/SendFeeUpdate"; HttpClient client = new HttpClient(); client.Timeout = TimeSpan.FromMinutes(1); var postData = JsonConvert.SerializeObject(new { ShopId = trusteeshipTask.ShopId?.ToString(), AdgroupId = trusteeshipTask.AdGroupId?.ToString(), SearchFee = newBidPrice }); HttpContent content = new StringContent(postData); var res = await client.PostAsync(url, content); if (res.IsSuccessStatusCode) { //发送成功 logger.Info($"请求变更出价接口成功! 店铺id:{trusteeshipTask.ShopId},请求参数{postData}"); AdjustLogList.Add(new Sbf_AdjustLog { AdjustType = Model.Enums.AdjustType.出价, AdjustWay = Model.Enums.AdjustWay.调高, CreateTime = DateTime.Now, PolicyType = Model.Enums.PolicyType.成长期策略包, TrusteeshipTaskId = trusteeshipTask.Id, OperateContent = $"{adjustContent},原出价:{oldBidPrice},调整后出价:{newBidPrice}" }); } else { logger.Error($"请求变更出价接口失败,返回消息:{res?.Content?.ReadAsStringAsync()?.Result}, 店铺id:{trusteeshipTask.ShopId},请求参数{postData}"); } Thread.Sleep(500); } } await update.ExecuteAffrowsAsync(); if (AdjustLogList.Any()) await fsql.Insert(AdjustLogList).ExecuteInsertedAsync(); } } }