diff --git a/BBWYB.Server.API/Controllers/SkuOptimizationController.cs b/BBWYB.Server.API/Controllers/SkuOptimizationController.cs index a480db1..541e056 100644 --- a/BBWYB.Server.API/Controllers/SkuOptimizationController.cs +++ b/BBWYB.Server.API/Controllers/SkuOptimizationController.cs @@ -1,5 +1,4 @@ -using BBWYB.Common.Models; -using BBWYB.Server.Business; +using BBWYB.Server.Business; using BBWYB.Server.Model.Dto; using Microsoft.AspNetCore.Mvc; @@ -8,11 +7,11 @@ namespace BBWYB.Server.API.Controllers public class SkuOptimizationController : BaseApiController { - private SkuOptimizationBusiness skuOptimizationBusiness; + private OptimizationBusiness optimizationBusiness; - public SkuOptimizationController(IHttpContextAccessor httpContextAccessor, SkuOptimizationBusiness skuOptimizationBusiness) : base(httpContextAccessor) + public SkuOptimizationController(IHttpContextAccessor httpContextAccessor, OptimizationBusiness optimizationBusiness) : base(httpContextAccessor) { - this.skuOptimizationBusiness = skuOptimizationBusiness; + this.optimizationBusiness = optimizationBusiness; } /// @@ -22,7 +21,7 @@ namespace BBWYB.Server.API.Controllers [HttpPost] public void LKInventoryAlertNotification([FromBody] BatchLKInventoryAlertRequest request) { - skuOptimizationBusiness.LKInventoryAlertNotification(request); + optimizationBusiness.LKInventoryAlertNotification(request); } /// @@ -33,7 +32,7 @@ namespace BBWYB.Server.API.Controllers public void CompleteOptimization([FromRoute] long taskId) { var userId = GetUserId(); - skuOptimizationBusiness.CompleteOptimization(taskId, userId); + optimizationBusiness.CompleteOptimization(taskId, userId); } /// @@ -42,10 +41,10 @@ namespace BBWYB.Server.API.Controllers /// /// [HttpPost] - public ListResponse GetNoCompletionSkuOptimizationTask([FromBody] PageRequest request) + public ListResponse GetNoCompletionSkuOptimizationTask([FromBody] QueryNoCompletionOptimizationTaskRequest request) { var userId = GetUserId(); - return skuOptimizationBusiness.GetNoCompletionSkuOptimizationTask(request, userId); + return optimizationBusiness.GetNoCompletionSkuOptimizationTask(request, userId); } } } diff --git a/BBWYB.Server.Business/SkuOptimization/SkuOptimizationBusiness.cs b/BBWYB.Server.Business/SkuOptimization/OptimizationBusiness.cs similarity index 80% rename from BBWYB.Server.Business/SkuOptimization/SkuOptimizationBusiness.cs rename to BBWYB.Server.Business/SkuOptimization/OptimizationBusiness.cs index 46e7086..5d3b92e 100644 --- a/BBWYB.Server.Business/SkuOptimization/SkuOptimizationBusiness.cs +++ b/BBWYB.Server.Business/SkuOptimization/OptimizationBusiness.cs @@ -7,8 +7,7 @@ using BBWYB.Server.Model.Db.BBWY; using BBWYB.Server.Model.Dto; using FreeSql; using Newtonsoft.Json; -using Org.BouncyCastle.Tls.Crypto; -using System.Linq; +using System.Numerics; using Yitter.IdGenerator; namespace BBWYB.Server.Business @@ -414,8 +413,8 @@ namespace BBWYB.Server.Business throw new BusinessException("待优化任务已完成"); var spuOptimizationBargainTeamTaskList = fsql.Select() - .Where(sbt => sbt.SpuOptimizationTaskId == taskId && sbt.BelongTeamId == uInfo.department.Id) - .ToList(); + .Where(sbt => sbt.SpuOptimizationTaskId == taskId) + .ToList(); var currentspuOptimizationBargainTeamTask = spuOptimizationBargainTeamTaskList.FirstOrDefault(sc => sc.BelongTeamId == uInfo.department.Id); if (currentspuOptimizationBargainTeamTask == null) throw new BusinessException($"未找到议价组{uInfo.department.DepartmentName}的议价子任务"); @@ -426,35 +425,43 @@ namespace BBWYB.Server.Business .Where(s => s.SpuOptimizationTaskId == taskId) .ToList(s => s.SkuId); - //查询该议价组的竞标任务 - var competitiveTenderTaskList = fsql.Select() - .Where(ct => ct.BargainTeamId == uInfo.user.DepartmentId && - ct.SpuOptimizationTaskId == taskId) - .ToList(); - var competitiveTenderSchemeIdList = competitiveTenderTaskList.Select(ct => ct.SchemeId.Value).ToList(); + //查询全部竞标任务 + var allBargainTeamCompetitiveTenderTaskList = fsql.Select() + .Where(ct => ct.SpuOptimizationTaskId == taskId) + .ToList(); + + //查询当前议价组的竞标任务 + var currentBargainTeamCompetitiveTenderTaskList = allBargainTeamCompetitiveTenderTaskList.Where(ct => ct.BargainTeamId == uInfo.department.Id).ToList(); + if (currentBargainTeamCompetitiveTenderTaskList.Count() == 0) + throw new BusinessException($"议价组{uInfo.department.DepartmentName}没有参与竞标的采购方案,不能完成优化任务"); + var currentBargainTeamCompetitiveTenderSchemeIdList = currentBargainTeamCompetitiveTenderTaskList.Select(ct => ct.SchemeId).ToList(); + + #region 验证该议价组的符合sku条件的采购方案是否都参与投标 + var waitCheckPurchaseSchemeIdList = fsql.Select() + .Where(ps => ps.BelongBargainTeamId == uInfo.user.DepartmentId && + skuIdList.Contains(ps.SkuId)) + .ToList(ps => ps.Id); - //验证该议价组的符合sku条件的采购方案是否都参与投标 - var purchaseSchemeIdList = fsql.Select().Where(ps => ps.BelongBargainTeamId == uInfo.user.DepartmentId && - skuIdList.Contains(ps.SkuId)) - .ToList(ps => ps.Id); - if (purchaseSchemeIdList.Count() == 0) + if (waitCheckPurchaseSchemeIdList.Count() == 0) throw new BusinessException($"议价组{uInfo.department.DepartmentName}没有参与竞标的采购方案,不能完成优化任务"); - var noJoinCompetitiveTenderSchemeIdList = purchaseSchemeIdList.Except(competitiveTenderSchemeIdList); + var noJoinCompetitiveTenderSchemeIdList = waitCheckPurchaseSchemeIdList.Except(currentBargainTeamCompetitiveTenderSchemeIdList); if (noJoinCompetitiveTenderSchemeIdList.Count() > 0) throw new BusinessException($"议价组{uInfo.department.DepartmentName}存在{noJoinCompetitiveTenderSchemeIdList.Count()}个符合条件但未参与竞标的采购方案"); + #endregion - //验证该议价组参与投标的采购方案是否都完成报价 - if (competitiveTenderTaskList.Any(ct => ct.IsUpdateQuotedPrice == false)) + #region 验证该议价组参与投标的采购方案是否都完成报价 + if (currentBargainTeamCompetitiveTenderTaskList.Any(ct => ct.IsUpdateQuotedPrice == false)) throw new BusinessException($"议价组{uInfo.department.DepartmentName}存在未更新报价的投标"); + #endregion currentspuOptimizationBargainTeamTask.IsOptimizationCompleted = true; #endregion - IUpdate updateBargainTask = null; IUpdate updateSpuTask = null; IUpdate updateSpuSaleInfo = null; IUpdate updateTimeLimitTask = null; + IUpdate updateCompetitiveTenderTask = null; if (!spuOptimizationBargainTeamTaskList.Any(sc => sc.IsOptimizationCompleted == false)) { @@ -491,12 +498,26 @@ namespace BBWYB.Server.Business t.CompletionTime == null && t.TaskType == Enums.TimeLimitTaskType.待议价任务); #endregion + + #region 评选中标结果 + var allBargainTeamCompetitiveTenderSchemeIdList = allBargainTeamCompetitiveTenderTaskList.Select(ct => ct.SchemeId).ToList(); + var allBargainTeamPurchaseSchemeList = purchaseSchemeBusiness.GetPurchaseSchemeList(new QuerySchemeRequest() + { + BargainTeamIdList = new List() { uInfo.user.DepartmentId }, + SkuIdList = skuIdList, + IncludePurchaseSkuBasicInfo = 0 + }); + var victoryCompetitiveTenderTaskList = VictoryPlanSelection(allBargainTeamCompetitiveTenderTaskList, allBargainTeamPurchaseSchemeList); + var victoryCompetitiveTenderTaskIdList = victoryCompetitiveTenderTaskList.Select(ct => ct.Id).ToList(); + updateCompetitiveTenderTask = fsql.Update(victoryCompetitiveTenderTaskIdList) + .Set(ct => ct.IsWin, true); + #endregion } #region 更新待优化议价组任务 updateBargainTask = fsql.Update(currentspuOptimizationBargainTeamTask.Id) - .Set(sc => sc.IsOptimizationCompleted, true) - .Set(sc => sc.CompletionTime, DateTime.Now); + .Set(sc => sc.IsOptimizationCompleted, true) + .Set(sc => sc.CompletionTime, DateTime.Now); #endregion fsql.Transaction(() => @@ -505,9 +526,17 @@ namespace BBWYB.Server.Business updateBargainTask?.ExecuteAffrows(); updateSpuSaleInfo?.ExecuteAffrows(); updateTimeLimitTask?.ExecuteAffrows(); + updateCompetitiveTenderTask?.ExecuteAffrows(); }); } + /// + /// 查询待优化任务 + /// + /// + /// + /// + /// public ListResponse GetNoCompletionSkuOptimizationTask(QueryNoCompletionOptimizationTaskRequest request, string userId) { request.EndTime = request.EndTime.Date; @@ -568,6 +597,11 @@ namespace BBWYB.Server.Business }); #endregion + #region 获取采购方案分组 + var purchaseSchemeGroupIdList = purchaseSchemeList.Select(ps => ps.SchemeGroupId).Distinct().ToList(); + var purchaseSchemeGroupList = fsql.Select(purchaseSchemeGroupIdList).ToList(); + #endregion + foreach (var task in taskList) { task.BargainTeamTaskList = bargainTeamTaskList.Where(sbt => sbt.SpuOptimizationTaskId == task.Id).ToList(); @@ -579,8 +613,47 @@ namespace BBWYB.Server.Business #region 合并分组/采购商/配件 var currentTaskSkuIdList = task.SkuOptimizationTaskList.Select(st => st.SkuId).ToList(); - var currentPurchaseSchemeList = purchaseSchemeList.Where(p => currentTaskSkuIdList.Contains(p.SkuId) && - p.BelongBargainTeamId == uInfo.department.Id).ToList(); + var currentPurchaseSchemeGroups = purchaseSchemeList.Where(p => currentTaskSkuIdList.Contains(p.SkuId) && + p.BelongBargainTeamId == uInfo.department.Id) + .GroupBy(p => p.SchemeGroupId) + .ToList(); + task.MergePurchaseScemeGroupList = new List(); + foreach (var schemeGroup in currentPurchaseSchemeGroups) + { + var mergeSchemeGroup = new MergePurchaseSchemeGroupResponse() + { + Id = schemeGroup.Key, + GroupName = purchaseSchemeGroupList.FirstOrDefault(g => g.Id == schemeGroup.Key)?.GroupName + }; + task.MergePurchaseScemeGroupList.Add(mergeSchemeGroup); + + var schemeList = schemeGroup.ToList(); + + foreach (var scheme in schemeList) + { + foreach (var psp in scheme.PurchaseSchemeProductList) + { + var mergePurchaser = mergeSchemeGroup.PurchaserList.FirstOrDefault(p => p.Id == psp.PurchaserId); + if (mergePurchaser == null) + { + mergePurchaser = scheme.PurchaserList.FirstOrDefault(p => p.Id == psp.PurchaserId)?.Map(); + if (mergePurchaser == null) + throw new BusinessException($"匹配采购商异常 schemeId {scheme.Id} pspProductId {psp.ProductId}"); + mergeSchemeGroup.PurchaserList.Add(mergePurchaser); + } + foreach (var pss in psp.PurchaseSchemeProductSkuList) + { + var mergePss = mergePurchaser.MergePurchaseSchemeProductSkuList.FirstOrDefault(mpss => mpss.PurchaseSkuId == pss.PurchaseSkuId); + if (mergePss == null) + { + mergePss = pss.Map(); + mergePss.PurchaserId = mergePurchaser.Id; + mergePurchaser.MergePurchaseSchemeProductSkuList.Add(mergePss); + } + } + } + } + } #endregion } @@ -590,5 +663,35 @@ namespace BBWYB.Server.Business TotalCount = total }; } + + /// + /// 评选结果 + /// + /// + /// + /// + private IList VictoryPlanSelection(IList competitiveTenderTaskList, + IList purchaseSchemeList) + { + var victoryList = new List(); + var psGroups = purchaseSchemeList.GroupBy(ps => ps.SkuId); + foreach (var psGroup in psGroups) + { + var psListOrderByCostDescList = psGroup.OrderByDescending(ps => ps.BargainingCost ?? ps.DefaultCost).ToList(); + var minCostScheme = psListOrderByCostDescList.FirstOrDefault(); + var competitiveTenderTask = competitiveTenderTaskList.FirstOrDefault(ct => ct.SchemeId == minCostScheme.Id); + victoryList.Add(competitiveTenderTask); + } + return victoryList; + } + + /// + /// 限时任务超时回调 + /// + /// + public void TimeLimitTaskTimeOutCallBack(long taskId) + { + + } } } diff --git a/BBWYB.Server.Business/TaskSchedulerManager.cs b/BBWYB.Server.Business/TaskSchedulerManager.cs index fa58a83..5fb7f43 100644 --- a/BBWYB.Server.Business/TaskSchedulerManager.cs +++ b/BBWYB.Server.Business/TaskSchedulerManager.cs @@ -11,12 +11,15 @@ namespace BBWYB.Server.Business public LimitedConcurrencyLevelTaskScheduler AggregationPurchaserTaskScheduler { get; private set; } + public LimitedConcurrencyLevelTaskScheduler OptimizationTaskScheduler { get; private set; } + public TaskSchedulerManager() { SyncOrderTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); PurchaseOrderCallbackTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); SyncProductTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); AggregationPurchaserTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(1); + OptimizationTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(3); } } } diff --git a/BBWYB.Server.Business/TimeLimitTask/TimeLimitTaskBusiness.cs b/BBWYB.Server.Business/TimeLimitTask/TimeLimitTaskBusiness.cs index 783ee79..bf98724 100644 --- a/BBWYB.Server.Business/TimeLimitTask/TimeLimitTaskBusiness.cs +++ b/BBWYB.Server.Business/TimeLimitTask/TimeLimitTaskBusiness.cs @@ -3,19 +3,32 @@ using BBWYB.Common.Models; using BBWYB.Server.Model; using BBWYB.Server.Model.Db; using BBWYB.Server.Model.Dto; +using Microsoft.Extensions.DependencyInjection; using Yitter.IdGenerator; namespace BBWYB.Server.Business { public class TimeLimitTaskBusiness : BaseBusiness, IDenpendency { - private VenderBusiness venderBusiness; + private List hgzTaskTypeList; private TimeLimitRules timeLimitRules; + private IServiceProvider serviceProvider; + + private Lazy optimizationBusinessLazy; + private Lazy venderBusinessLazy; + private Lazy taskSchedulerManagerLazy; + private OptimizationBusiness optimizationBusiness => optimizationBusinessLazy.Value; + private VenderBusiness venderBusiness => venderBusinessLazy.Value; + private TaskSchedulerManager taskSchedulerManager => taskSchedulerManagerLazy.Value; - public TimeLimitTaskBusiness(IFreeSql fsql, NLogManager nLogManager, IIdGenerator idGenerator, VenderBusiness venderBusiness, TimeLimitRules timeLimitRules) : base(fsql, nLogManager, idGenerator) + public TimeLimitTaskBusiness(IFreeSql fsql, NLogManager nLogManager, IIdGenerator idGenerator, VenderBusiness venderBusiness, TimeLimitRules timeLimitRules, IServiceProvider serviceProvider) : base(fsql, nLogManager, idGenerator) { - this.venderBusiness = venderBusiness; + this.serviceProvider = serviceProvider; + this.optimizationBusinessLazy = new Lazy(() => serviceProvider.GetRequiredService()); + this.venderBusinessLazy = new Lazy(() => serviceProvider.GetRequiredService()); + this.taskSchedulerManagerLazy = new Lazy(() => serviceProvider.GetRequiredService()); + hgzTaskTypeList = new List() { Enums.TimeLimitTaskType.合格证拟定任务, @@ -25,10 +38,27 @@ namespace BBWYB.Server.Business } public void CheckTask() { - fsql.Update().Set(t => t.IsTimely, false) - .Where(t => t.CompletionTime == null) - .Where(t => t.ExpirationTime < DateTime.Now) - .ExecuteAffrows(); + //fsql.Update().Set(t => t.IsTimely, false) + // .Where(t => t.CompletionTime == null) + // .Where(t => t.ExpirationTime < DateTime.Now) + // .ExecuteAffrows(); + + var timeoutTaskList = fsql.Select().Where(t => t.CompletionTime == null && t.ExpirationTime < DateTime.Now).ToList(); + if (timeoutTaskList.Count() > 0) + { + var timeoutTaskIdList = timeoutTaskList.Select(t => t.Id).ToList(); + fsql.Update(timeoutTaskIdList).Set(t => t.IsTimely, false).ExecuteAffrows(); + } + + var optimizationTaskList = timeoutTaskList.Where(t => t.TaskType == Enums.TimeLimitTaskType.待议价任务).ToList(); + if (optimizationTaskList.Count() > 0) + { + //待议价任务超时检查 + foreach (var task in optimizationTaskList) + { + Task.Factory.StartNew(() => optimizationBusiness.TimeLimitTaskTimeOutCallBack(task.TaskId), CancellationToken.None, TaskCreationOptions.LongRunning, taskSchedulerManager.OptimizationTaskScheduler); + } + } } /// diff --git a/BBWYB.Server.Model/Db/SpuOptimization/Spuoptimizationcompetitivetendertask.cs b/BBWYB.Server.Model/Db/SpuOptimization/Spuoptimizationcompetitivetendertask.cs index 30188a5..de47914 100644 --- a/BBWYB.Server.Model/Db/SpuOptimization/Spuoptimizationcompetitivetendertask.cs +++ b/BBWYB.Server.Model/Db/SpuOptimization/Spuoptimizationcompetitivetendertask.cs @@ -40,7 +40,7 @@ namespace BBWYB.Server.Model.Db /// 采购方案Id /// [Column(DbType = "bigint")] - public long? SchemeId { get; set; } + public long SchemeId { get; set; } [Column(StringLength = 50)] public string SkuId { get; set; } @@ -57,6 +57,17 @@ namespace BBWYB.Server.Model.Db [Column(DbType = "bigint")] public long SpuOptimizationTaskId { get; set; } + /// + /// 是否中标 + /// + [Column(DbType = "bit")] + public bool IsWin { get; set; } = false; + + /// + /// 运费 + /// + public decimal? Freight { get; set; } = 0.00M; + } } diff --git a/BBWYB.Server.Model/Dto/Request/Optimization/BatchUpdateCompetitiveTenderQuotationRequest.cs b/BBWYB.Server.Model/Dto/Request/Optimization/BatchUpdateCompetitiveTenderQuotationRequest.cs index 60a0a3a..c0df1a2 100644 --- a/BBWYB.Server.Model/Dto/Request/Optimization/BatchUpdateCompetitiveTenderQuotationRequest.cs +++ b/BBWYB.Server.Model/Dto/Request/Optimization/BatchUpdateCompetitiveTenderQuotationRequest.cs @@ -16,6 +16,11 @@ /// 配件列表 /// public IList ItemList { get; set; } + + /// + /// 运费 + /// + public decimal Freight { get; set; } } public class BatchUpdateCompetitiveTenderQuotationItemRequest diff --git a/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeGroupResponse.cs b/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeGroupResponse.cs index 1000dd1..88a6cd9 100644 --- a/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeGroupResponse.cs +++ b/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeGroupResponse.cs @@ -2,6 +2,11 @@ { public class MergePurchaseSchemeGroupResponse { + public MergePurchaseSchemeGroupResponse() + { + PurchaserList = new List(); + } + /// /// 分组Id /// diff --git a/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeProductSkuResponse.cs b/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeProductSkuResponse.cs index 0b53696..3b1cc28 100644 --- a/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeProductSkuResponse.cs +++ b/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaseSchemeProductSkuResponse.cs @@ -3,7 +3,7 @@ /// /// 合并配件信息 /// - public class MergePurchaseSchemeProductSkuResponse : PurchaseSchemeProductSkuResponse + public class MergePurchaseSchemeProductSkuResponse { /// /// 采购商Id diff --git a/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaserResponse.cs b/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaserResponse.cs index bc4eefc..6fc4310 100644 --- a/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaserResponse.cs +++ b/BBWYB.Server.Model/Dto/Response/Optimization/MergePurchaserResponse.cs @@ -4,6 +4,10 @@ namespace BBWYB.Server.Model.Dto { public class MergePurchaserResponse : Purchaser { - public List PurchaseSchemeProductSkuList { get; set; } + public MergePurchaserResponse() + { + MergePurchaseSchemeProductSkuList = new List(); + } + public List MergePurchaseSchemeProductSkuList { get; set; } } } diff --git a/BBWYB.Server.Model/MappingProfiles.cs b/BBWYB.Server.Model/MappingProfiles.cs index 2a35bf9..bacd4b0 100644 --- a/BBWYB.Server.Model/MappingProfiles.cs +++ b/BBWYB.Server.Model/MappingProfiles.cs @@ -62,9 +62,8 @@ namespace BBWYB.Server.Model CreateMap(); - - - + CreateMap(); + CreateMap(); } } }