diff --git a/BBWY.Server.API/BBWY.Server.API.csproj b/BBWY.Server.API/BBWY.Server.API.csproj index 65f647df..1a20a366 100644 --- a/BBWY.Server.API/BBWY.Server.API.csproj +++ b/BBWY.Server.API/BBWY.Server.API.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/BBWY.Server.API/Controllers/OrderEstimateCostSyncController.cs b/BBWY.Server.API/Controllers/OrderEstimateCostSyncController.cs new file mode 100644 index 00000000..b5ae7c71 --- /dev/null +++ b/BBWY.Server.API/Controllers/OrderEstimateCostSyncController.cs @@ -0,0 +1,37 @@ +using BBWY.Server.Business; +using BBWY.Server.Model.Dto; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace BBWY.Server.API.Controllers +{ + + public class OrderEstimateCostSyncController : BaseApiController + { + private OrderEstimateCostSyncBusiness orderEstimateCostSyncBusiness; + + public OrderEstimateCostSyncController(IHttpContextAccessor httpContextAccessor, OrderEstimateCostSyncBusiness orderEstimateCostSyncBusiness) : base(httpContextAccessor) + { + this.orderEstimateCostSyncBusiness = orderEstimateCostSyncBusiness; + } + + /// + /// 同步所有店铺的SKU最近成本 + /// + [HttpPost] + public void SyncAllShopOrderSkuRecentCost() + { + orderEstimateCostSyncBusiness.SyncAllShopOrderSkuRecentCost(); + } + + /// + /// 同步指定条件的SKU最近成本 + /// + /// + [HttpPost] + public void SyncOrderSkuRecentCost([FromBody] SkuRecentCostRequest request) + { + orderEstimateCostSyncBusiness.SyncOrderSkuRecentCost(request); + } + } +} diff --git a/BBWY.Server.Business/BBWY.Server.Business.csproj b/BBWY.Server.Business/BBWY.Server.Business.csproj index 2fbbac4b..d872e01e 100644 --- a/BBWY.Server.Business/BBWY.Server.Business.csproj +++ b/BBWY.Server.Business/BBWY.Server.Business.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/BBWY.Server.Business/Sync/OrderEstimateCostSyncBusiness.cs b/BBWY.Server.Business/Sync/OrderEstimateCostSyncBusiness.cs new file mode 100644 index 00000000..28b12c52 --- /dev/null +++ b/BBWY.Server.Business/Sync/OrderEstimateCostSyncBusiness.cs @@ -0,0 +1,300 @@ +using BBWY.Common.Extensions; +using BBWY.Common.Models; +using BBWY.Server.Business.Extensions; +using BBWY.Server.Model; +using BBWY.Server.Model.Db; +using BBWY.Server.Model.Dto; +using FreeSql; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Yitter.IdGenerator; + +namespace BBWY.Server.Business +{ + public class OrderEstimateCostSyncBusiness : BaseBusiness, IDenpendency + { + private TaskSchedulerManager taskSchedulerManager; + private VenderBusiness venderBusiness; + private List invalidOrderStateList; + public OrderEstimateCostSyncBusiness(IFreeSql fsql, + NLogManager nLogManager, + IIdGenerator idGenerator, + TaskSchedulerManager taskSchedulerManager, + VenderBusiness venderBusiness) : base(fsql, nLogManager, idGenerator) + { + this.taskSchedulerManager = taskSchedulerManager; + this.venderBusiness = venderBusiness; + invalidOrderStateList = new List() { + Enums.OrderState.待付款, + Enums.OrderState.已取消, + Enums.OrderState.暂停 + }; + } + + #region 同步SKU最近成本 + + public void SyncAllShopOrderSkuRecentCost() + { + var date = DateTime.Now.Date.AddDays(-1); + SyncOrderSkuRecentCost(new SkuRecentCostRequest() + { + ShopId = null, + StartDate = date, + EndDate = date + }); + } + + /// + /// 同步昨天SKU的采购成本 + /// + /// + public void SyncOrderSkuRecentCost(SkuRecentCostRequest request) + { + var shopList = venderBusiness.GetShopList(request.ShopId, Model.Enums.Platform.京东); + request.EndDate = request.EndDate.Date.AddDays(1).AddSeconds(-1); + foreach (var shop in shopList) + { + var _shopId = long.Parse(shop.ShopId); + Task.Factory.StartNew(() => SyncOrderSkuRecentCost(shop, request.StartDate, request.EndDate), CancellationToken.None, TaskCreationOptions.LongRunning, taskSchedulerManager.SyncSkuYesterdayCostTaskScheduler); + } + } + + private void SyncOrderSkuRecentCost(ShopResponse shop, DateTime startDate, DateTime endDate) + { + try + { + var shopId = long.Parse(shop.ShopId); + var yesterdaycostDetailist = fsql.Select() + .InnerJoin((ocd, o) => ocd.OrderId == o.Id) + .Where((ocd, o) => o.ShopId == shopId && + o.StartTime >= startDate && + o.StartTime <= endDate && + !invalidOrderStateList.Contains(o.OrderState) && + o.IsGift == false && + o.StorageType != Enums.StorageType.SD && + ocd.IsEnabled == true && + ocd.IsEstimateCost == false) + .GroupBy((ocd, o) => ocd.SkuId) + .WithTempQuery(g => new { MaxId = g.Sum(g.Value.Item1.Id), SkuId = g.Key }) + .From() + .InnerJoin((ocd1, ocd2) => ocd1.MaxId == ocd2.Id) + .ToList((ocd1, ocd2) => ocd2); + if (yesterdaycostDetailist.Count() == 0) + return; + + var skuIdList = yesterdaycostDetailist.Select(ocd => ocd.SkuId).Distinct().ToList(); + var dbSkuRecentCostList = fsql.Select(skuIdList).ToList(); + + List insertSkuRecetCostList = new List(); + List> updateSkuRecentCostList = new List>(); + foreach (var yesocd in yesterdaycostDetailist) + { + var q = yesocd.DeductionQuantity; + if (q <= 0) + q = 1; + var singleConsumableAmount = yesocd.ConsumableAmount / q; + var singleDeliveryFreight = yesocd.DeliveryExpressFreight / q; + var singleFirstFreight = yesocd.FirstFreight / q; + var singleFreight = yesocd.PurchaseFreight / q; + var singleInStorageAmount = yesocd.InStorageAmount / q; + var singleOutStorageAmount = yesocd.OutStorageAmount / q; + var singleSkuAmount = yesocd.SkuAmount / q; + var singleStorageAmount = yesocd.StorageAmount / q; + + var skuRecentCost = dbSkuRecentCostList.FirstOrDefault(x => x.SkuId == yesocd.SkuId); + if (skuRecentCost != null) + { + skuRecentCost.SingleConsumableAmount = singleConsumableAmount; + skuRecentCost.SingleDeliveryFreight = singleDeliveryFreight; + skuRecentCost.SingleFirstFreight = singleFirstFreight; + skuRecentCost.SingleFreight = singleFreight; + skuRecentCost.SingleInStorageAmount = singleInStorageAmount; + skuRecentCost.SingleOutStorageAmount = singleOutStorageAmount; + skuRecentCost.SingleSkuAmount = singleSkuAmount; + skuRecentCost.SingleStorageAmount = singleStorageAmount; + skuRecentCost.UpdateTime = DateTime.Now; + var update = fsql.Update(yesocd.SkuId).SetSource(skuRecentCost); + updateSkuRecentCostList.Add(update); + } + else + { + skuRecentCost = new SkuRecentCost() + { + SkuId = yesocd.SkuId, + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + ProductId = yesocd.ProductId, + RecentOrderId = yesocd.OrderId, + ShopId = shopId, + SingleConsumableAmount = singleConsumableAmount, + SingleDeliveryFreight = singleDeliveryFreight, + SingleFirstFreight = singleFirstFreight, + SingleFreight = singleFreight, + SingleInStorageAmount = singleInStorageAmount, + SingleOutStorageAmount = singleOutStorageAmount, + SingleSkuAmount = singleSkuAmount, + SingleStorageAmount = singleStorageAmount + }; + insertSkuRecetCostList.Add(skuRecentCost); + } + } + + fsql.Transaction(() => + { + if (insertSkuRecetCostList.Count() > 0) + fsql.Insert(insertSkuRecetCostList).ExecuteAffrows(); + if (updateSkuRecentCostList.Count() > 0) + { + foreach (var update in updateSkuRecentCostList) + update.ExecuteAffrows(); + } + }); + } + catch (Exception ex) + { + nLogManager.GetLogger($"SKU最近成本-{shop.ShopName}").Error(ex); + } + } + + #endregion + + #region 预估成本 + public void EstimateCostForAllShopNoCostOrder() + { + EstimateCostForNoCostOrder(new SkuRecentCostRequest() + { + ShopId = null, + StartDate = DateTime.Now.AddHours(-3), + EndDate = DateTime.Now + }); + } + + public void EstimateCostForNoCostOrder(SkuRecentCostRequest request) + { + var shopList = venderBusiness.GetShopList(request.ShopId, Model.Enums.Platform.京东); + //request.EndDate = request.EndDate.Date.AddDays(1).AddSeconds(-1); + foreach (var shop in shopList) + { + Task.Factory.StartNew(() => EstimateCostForNoCostOrder(shop, request.StartDate, request.EndDate), CancellationToken.None, TaskCreationOptions.LongRunning, taskSchedulerManager.SyncSkuYesterdayCostTaskScheduler); + } + } + + private void EstimateCostForNoCostOrder(ShopResponse shop, DateTime startTime, DateTime endTime) + { + try + { + var shopId = long.Parse(shop.ShopId); + var orderList = fsql.Select().Where(o => o.ShopId == shopId && + o.ModifyTime >= startTime && + o.ModifyTime <= endTime && + !invalidOrderStateList.Contains(o.OrderState) && + o.IsGift == false && + o.StorageType == null && + !fsql.Select().Where(oc => oc.OrderId == o.Id).Any()) + .ToList(); + if (orderList.Count() == 0) + return; + var orderIdList = orderList.Select(o => o.Id).ToList(); + + + /* + decimal skuShouldPay, + decimal pingTaiChengDanYouHuiQuan, + decimal superRedEnvelope, + decimal xianPinLeiDongQuan, + decimal skuVenderFee, + decimal jingdou, + decimal dongquan, + decimal balance, + */ + + /* + SkuAmount + + PurchaseFreight + + FirstFreight + + InStorageAmount + + OutStorageAmount + + StorageAmount + + ConsumableAmount + */ + var orderSkuAndRecentList = fsql.Select() + .LeftJoin((osku, src) => osku.SkuId == src.SkuId) + .Where((osku, src) => orderIdList.Contains(osku.SkuId) && osku.Price > 0) + .ToList((osku, src) => new + { + osku.Id, + osku.OrderId, + osku.SkuId, + osku.ProductId, + osku.ShouldPay, + osku.PingTaiChengDanYouHuiQuan, + osku.SuperRedEnvelope, + osku.XianPinLeiDongQuan, + osku.VenderFee, + osku.JingDou, + osku.DongQuan, + osku.Balance, + osku.ItemTotal, + src.SingleConsumableAmount, + src.SingleSkuAmount, + src.SingleDeliveryFreight, + src.SingleFirstFreight, + src.SingleFreight, + src.SingleInStorageAmount, + src.SingleOutStorageAmount, + src.SingleStorageAmount + }); + foreach (var order in orderList) + { + var currentOrderSkuList = orderSkuAndRecentList.Where(osku => osku.OrderId == order.Id).ToList(); + if (currentOrderSkuList.Any(osku => osku.ShouldPay == null || + osku.ShouldPay == 0 || + osku.SingleSkuAmount == null || + osku.SingleSkuAmount == 0)) + continue; //预估成本和毛利,必须订单下的每一笔sku都具备最近成本 + + foreach (var osku in currentOrderSkuList) + { + var ocd = new OrderCostDetail() + { + Id = idGenerator.NewLong(), + ConsumableAmount = (osku.SingleConsumableAmount * osku.ItemTotal) ?? 0M, + DeductionQuantity = osku.ItemTotal ?? 1, + CreateTime = DateTime.Now, + DeliveryExpressFreight = (osku.SingleDeliveryFreight * osku.ItemTotal) ?? 0M, + FirstFreight = (osku.SingleFirstFreight * osku.ItemTotal) ?? 0M, + InStorageAmount = (osku.SingleInStorageAmount * osku.ItemTotal) ?? 0M, + OutStorageAmount = (osku.SingleOutStorageAmount * osku.ItemTotal) ?? 0M, + OrderId = order.Id, + ProductId = osku.ProductId, + PurchaseFreight = (osku.SingleFreight * osku.ItemTotal) ?? 0M, + PurchaseOrderPKId = 0, + SkuAmount = (osku.SingleSkuAmount * osku.ItemTotal) ?? 0M, + SkuId = osku.SkuId, + StorageAmount = (osku.SingleStorageAmount * osku.ItemTotal) ?? 0M, + IsEnabled = true, + IsEstimateCost = true + }; + ocd.CalculationSkuGrossProfit(osku.ShouldPay ?? 0M, + osku.PingTaiChengDanYouHuiQuan ?? 0M, + osku.SuperRedEnvelope ?? 0M, + osku.XianPinLeiDongQuan ?? 0M, + osku.VenderFee ?? 0M, + osku.JingDou ?? 0M, + osku.DongQuan ?? 0M, + osku.Balance ?? 0M, + shop.PlatformCommissionRatio ?? 0.05M); + } + } + } + catch (Exception ex) + { + nLogManager.GetLogger($"预估成本-{shop.ShopName}").Error(ex); + } + } + #endregion + } +} \ No newline at end of file diff --git a/BBWY.Server.Business/TaskSchedulerManager.cs b/BBWY.Server.Business/TaskSchedulerManager.cs index 572f021a..4e2df060 100644 --- a/BBWY.Server.Business/TaskSchedulerManager.cs +++ b/BBWY.Server.Business/TaskSchedulerManager.cs @@ -34,6 +34,8 @@ namespace BBWY.Server.Business public LimitedConcurrencyLevelTaskScheduler JDPromotionAutoStartTaskScheduler { get; private set; } + public LimitedConcurrencyLevelTaskScheduler SyncSkuYesterdayCostTaskScheduler { get; private set; } + public TaskSchedulerManager() { RepairOrderTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(5); @@ -55,6 +57,8 @@ namespace BBWY.Server.Business JDPromotionAutoStartTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); SyncPauseOrderTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); + + SyncSkuYesterdayCostTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); } } } diff --git a/BBWY.Server.Model/BBWY.Server.Model.csproj b/BBWY.Server.Model/BBWY.Server.Model.csproj index 9bee62e6..754c33d1 100644 --- a/BBWY.Server.Model/BBWY.Server.Model.csproj +++ b/BBWY.Server.Model/BBWY.Server.Model.csproj @@ -7,7 +7,7 @@ - + diff --git a/BBWY.Server.Model/Db/Order/OrderCost.cs b/BBWY.Server.Model/Db/Order/OrderCost.cs index c02bc436..46a0df8c 100644 --- a/BBWY.Server.Model/Db/Order/OrderCost.cs +++ b/BBWY.Server.Model/Db/Order/OrderCost.cs @@ -84,6 +84,12 @@ namespace BBWY.Server.Model.Db /// [Column(DbType = "decimal(20,2)")] public decimal AfterTotalCost { get; set; } = 0.0M; + + /// + /// 是否为预估成本 + /// + [Column(DbType = "bit")] + public bool IsEstimateCost { get; set; } } } diff --git a/BBWY.Server.Model/Db/Order/OrderCostDetail.cs b/BBWY.Server.Model/Db/Order/OrderCostDetail.cs index 19f8b9a8..9f9fd331 100644 --- a/BBWY.Server.Model/Db/Order/OrderCostDetail.cs +++ b/BBWY.Server.Model/Db/Order/OrderCostDetail.cs @@ -104,6 +104,7 @@ namespace BBWY.Server.Model.Db /// [Column(DbType = "decimal(20,2)")] public decimal SkuGrossProfit { get; set; } = 0.00M; + /// /// 总计 /// @@ -115,8 +116,12 @@ namespace BBWY.Server.Model.Db return SkuAmount + PurchaseFreight + FirstFreight + InStorageAmount + OutStorageAmount + StorageAmount + ConsumableAmount; } } - //[Column(DbType = "decimal(20,2)")] - //public decimal TotalCost { get; set; } = 0.00M; + + /// + /// 是否为预估成本 + /// + [Column(DbType = "bit")] + public bool IsEstimateCost { get; set; } } } diff --git a/BBWY.Server.Model/Db/Order/SkuRecentCost.cs b/BBWY.Server.Model/Db/Order/SkuRecentCost.cs new file mode 100644 index 00000000..3950d1ec --- /dev/null +++ b/BBWY.Server.Model/Db/Order/SkuRecentCost.cs @@ -0,0 +1,94 @@ +using FreeSql.DataAnnotations; +using System; + +namespace BBWY.Server.Model.Db +{ + + [Table(Name = "skurecentcost", DisableSyncStructure = true)] + public partial class SkuRecentCost + { + + [Column(StringLength = 50, IsPrimary = true, IsNullable = false)] + public string SkuId { get; set; } + + [Column(DbType = "datetime")] + public DateTime? CreateTime { get; set; } + + [Column(StringLength = 50)] + public string ProductId { get; set; } + + /// + /// 最近一笔订单来源 + /// + [Column(StringLength = 50)] + public string RecentOrderId { get; set; } + + [Column(DbType = "bigint")] + public long? ShopId { get; set; } + + /// + /// 耗材费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleConsumableAmount { get; set; } = 0.00M; + + /// + /// 发货运费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleDeliveryFreight { get; set; } = 0.00M; + + /// + /// 头程运费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleFirstFreight { get; set; } + + /// + /// 采购运费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleFreight { get; set; } = 0.00M; + + /// + /// 入仓操作费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleInStorageAmount { get; set; } = 0.00M; + + /// + /// 操作费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleOperationAmount { get; set; } = 0.00M; + + /// + /// 出仓操作费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleOutStorageAmount { get; set; } = 0.00M; + + /// + /// 退货入仓操作费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleRefundInStorageAmount { get; set; } = 0.00M; + + /// + /// SKU成本(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleSkuAmount { get; set; } + + /// + /// 仓储费(单件) + /// + [Column(DbType = "decimal(20,2)")] + public decimal? SingleStorageAmount { get; set; } = 0.00M; + + [Column(DbType = "datetime")] + public DateTime? UpdateTime { get; set; } + + } + +} diff --git a/BBWY.Server.Model/Dto/Request/Sync/SkuRecentCostRequest.cs b/BBWY.Server.Model/Dto/Request/Sync/SkuRecentCostRequest.cs new file mode 100644 index 00000000..4c16bdb9 --- /dev/null +++ b/BBWY.Server.Model/Dto/Request/Sync/SkuRecentCostRequest.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BBWY.Server.Model.Dto +{ + public class SkuRecentCostRequest + { + public long? ShopId { get; set; } + + public DateTime StartDate { get; set; } + + public DateTime EndDate { get; set; } + } +} diff --git a/BBWY.Test/BBWY.Test.csproj b/BBWY.Test/BBWY.Test.csproj index bd90d253..7ede10c4 100644 --- a/BBWY.Test/BBWY.Test.csproj +++ b/BBWY.Test/BBWY.Test.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/JD.API/JD.API.csproj b/JD.API/JD.API.csproj index a8271105..6f51e6c7 100644 --- a/JD.API/JD.API.csproj +++ b/JD.API/JD.API.csproj @@ -6,8 +6,8 @@ - - + +