using Microsoft.Extensions.Caching.Memory; using SJZY.Expand.ABP.Core.Logger; using SJZY.Track.Domain; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Consts; using SJZY.Track.Domain.Dtos.Order; using SJZY.Track.Domain.Enums; using SJZY.Track.Domain.VEntities; namespace SJZY.Track.Repository.Repositories; public class OrderRepository : BasicRepository, IOrderRepository, ITransientDependency { //tips:后期提供缓存清理接口 //MemoryCache本身线程安全简单使用,短暂数据不一致可以容忍,每过固定时间强制清除缓存 private static readonly MemoryCache _memoryCache = new(new MemoryCacheOptions()); private static readonly Dictionary _empty = new(); private static readonly Dictionary> _empty2 = new(); private static readonly TimeSpan _thirdPartyOrderNumberCacheTimeSpan = TimeSpan.FromMinutes(3); private static readonly TimeSpan _mainDataEnumCacheTimeSpan = TimeSpan.FromMinutes(5); private static readonly TimeSpan _orderTypeCacheTimeSpan = TimeSpan.FromMinutes(1); private async Task> GetThirdPartyOrderNumberForCacheAsync(int[] orderIds) { const string key = "ThirdPartyOrderNumber"; if (orderIds.Length == 0) return _empty; var dict = _memoryCache.Get>(key); orderIds = orderIds.Distinct().ToArray(); if (dict != null && dict.Count != 0 && !orderIds.Except(dict.Keys).Any()) return dict; var orders = await Context.Queryable() .Includes(a => a.OrderPlatformNumbers) .LeftJoin((a, b) => a.Id == b.OrderId && b.IsDeleted == false) .Where((a, b) => orderIds.Contains(a.Id)) .Select(a => new OrderEntity() { Id = a.Id, OrderPlatformNumbers = a.OrderPlatformNumbers }) .ToListAsync(); if (orders.Count == 0) return _empty; orders = orders.DistinctBy(x => x.Id).ToList(); dict = orders.ToDictionary(x => x.Id, x => BuildThirdPartyOrderNumber(x.OrderPlatformNumbers)); _memoryCache.Set(key, dict, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = _thirdPartyOrderNumberCacheTimeSpan, Size = dict.Count }); return dict; } private async Task>> GetMainDataEnumForCacheAsync(List orderTypes, List questTypes) { const string key = "EnumForMainData"; if (orderTypes.Count == 0 && questTypes.Count == 0) return _empty2; var dict = _memoryCache.Get>>(key); if (dict != null && dict.Count != 0 && !orderTypes.Except(dict[GlobalConst.MainDataKey_OrderType].Keys).Any() && !questTypes.Except(dict[GlobalConst.MainDataKey_TrackQuestionType].Keys).Any()) { return dict; } //从主数据中心查询类型描述 dict = (await Context.Queryable() .Where(x => (x.Property == GlobalConst.MainDataKey_OrderType && orderTypes.Contains(x.Enumkey)) || (x.Property == GlobalConst.MainDataKey_TrackQuestionType && questTypes.Contains(x.Enumkey)) ) .Select(x => new { x.Property, x.Enumkey, x.Disp }) .ToListAsync() ) .GroupBy(x => x.Property) .ToDictionary( g => g.Key, y => y.ToDictionary(x => x.Enumkey, x => x.Disp) ); if (dict.Count == 0) return _empty2; _memoryCache.Set(key, dict, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = _mainDataEnumCacheTimeSpan, Size = dict.Count }); return dict; } public async Task> GetTrackOrderTypeDict() { const string key = "TrackOrderType"; var orderTypes = await _memoryCache.GetOrCreateAsync(key, async (entry) => { // 固定时间周期后强制更新 entry.AbsoluteExpirationRelativeToNow = _orderTypeCacheTimeSpan; // 查询主数据 return await Context.Queryable() .Where(x => x.Property == GlobalConst.MainDataKey_OrderType) .ToDictionaryAsync(x => x.Enumkey, x => x.Disp); }); // 更新redis缓存 await RedisHelper.Client.HMSetAsync(GlobalConst.TrackOrderTypeKey, orderTypes); // redis内数据只更新不清理 var dict = RedisHelper.Client.HGetAll(GlobalConst.TrackOrderTypeKey); return dict; } public async Task> GetTrackOrderPagedList(GetPageTrackOrderInputVO input) { //查询完去重 UnifyPageResultDto pageList; //没传递单号时 if (input.OrderNos.IsNullOrEmpty()) { pageList = await Context.Queryable() .LeftJoin((a, b) => a.CustomId == b.Id && b.IsDeleted == false) .LeftJoin((a, b, c) => a.SubSystemId == c.Id && c.IsDeleted == false) .WhereIF(input.SubSystemId > 0, (a, b, c) => a.SubSystemId == input.SubSystemId) .WhereIF(input.CreateTimeStart.HasValue && input.CreateTimeEnd.HasValue, (a, b, c) => a.CreateTime >= input.CreateTimeStart && a.CreateTime <= input.CreateTimeEnd) .WhereIF(input.CreateTimeStart.HasValue && (!input.CreateTimeEnd.HasValue), (a, b, c) => a.CreateTime >= input.CreateTimeStart) .WhereIF((!input.CreateTimeStart.HasValue) && input.CreateTimeEnd.HasValue, (a, b, c) => a.CreateTime <= input.CreateTimeEnd) .WhereIF(input.CustomerId > 0, (a, b, c) => a.CustomId == input.CustomerId) .WhereIF(input.TrackOrderType > 0, (a, b, c) => a.OrderType == input.TrackOrderType) .WhereIF(input.TabType > 0, (a, b, c) => a.QuestionType == input.TabType) .WhereIF(input.TabType == (int) TrackProblemType.Warn, (a, b, c) => !a.IsCompleted) .OrderByDescending((a, b, c) => a.CreateTime) .Select((a, b, c) => new TrackOrderVO { Id = a.Id, OrderNumber = a.OrderNo, OrderType = a.OrderType, QuestionType = a.QuestionType, CustomerShortName = b.Abbreviation, CustomerCode = b.Code, CustomerId = a.CustomId, NewestTrackContent = a.LatestTrackContent, NewestTrackEnContent = a.LatestTrackEnContent, NewestTrackPlace = a.LatestTrackPlace, NewestTrackTime = a.LatestTrackTime, IsFinish = a.IsCompleted, CreateTime = a.CreateTime, SubSystem = c.CnName, SubSystemId = c.Id, DestinationTimeZone = a.DestinationTimeZone, EstimatedDeliveryDate = a.EstimatedDeliveryDate }) .ToPagedListAsync(input.PageIndex, input.PageSize); } //传递单号时 else { var orderNos = input.OrderNos!.Distinct().ToList(); //匹配主单 var orderIds = await Context.Queryable() .Where(x => orderNos.Contains(x.OrderNo)) .Select(x => x.Id) .ToListAsync(); //匹配外部单 var orderIdsForThirdParty = await Context.Queryable() .Where(x => !string.IsNullOrWhiteSpace(x.OrderNumber) && orderNos.Contains(x.OrderNumber)) .Select(x => x.OrderId) .ToListAsync(); var orderHashset = orderIds.ToHashSet(); orderHashset.UnionWith(orderIdsForThirdParty); orderIds = orderHashset.ToList(); //没有任何单 if (orderIds.Count == 0) { return new UnifyPageResultDto { List = new List(), Pagination = new PageInfoOutput() { PageIndex = input.PageIndex, PageSize = input.PageSize, Total = 0 } }; } pageList = await Context.Queryable() .Where(x => orderIds.Contains(x.Id)) .LeftJoin((a, b) => a.CustomId == b.Id && b.IsDeleted == false) .LeftJoin((a, b, c) => a.SubSystemId == c.Id && c.IsDeleted == false) .WhereIF(input.SubSystemId > 0, (a, b, c) => a.SubSystemId == input.SubSystemId) .WhereIF(input.CreateTimeStart.HasValue && input.CreateTimeEnd.HasValue, (a, b, c) => a.CreateTime >= input.CreateTimeStart && a.CreateTime <= input.CreateTimeEnd) .WhereIF(input.CreateTimeStart.HasValue && (!input.CreateTimeEnd.HasValue), (a, b, c) => a.CreateTime >= input.CreateTimeStart) .WhereIF((!input.CreateTimeStart.HasValue) && input.CreateTimeEnd.HasValue, (a, b, c) => a.CreateTime <= input.CreateTimeEnd) .WhereIF(input.CustomerId > 0, (a, b, c) => a.CustomId == input.CustomerId) .WhereIF(input.TrackOrderType > 0, (a, b, c) => a.OrderType == input.TrackOrderType) .WhereIF(input.TabType > 0, (a, b, c) => a.QuestionType == input.TabType) .WhereIF(input.TabType == (int) TrackProblemType.Warn, (a, b, c) => !a.IsCompleted) .OrderByDescending((a, b, c) => a.CreateTime) .Select((a, b, c) => new TrackOrderVO { Id = a.Id, OrderNumber = a.OrderNo, OrderType = a.OrderType, QuestionType = a.QuestionType, CustomerShortName = b.Abbreviation, CustomerCode = b.Code, CustomerId = a.CustomId, NewestTrackContent = a.LatestTrackContent, NewestTrackEnContent = a.LatestTrackEnContent, NewestTrackPlace = a.LatestTrackPlace, NewestTrackTime = a.LatestTrackTime, IsFinish = a.IsCompleted, CreateTime = a.CreateTime, SubSystem = c.CnName, SubSystemId = c.Id, DestinationTimeZone = a.DestinationTimeZone, EstimatedDeliveryDate = a.EstimatedDeliveryDate, }) .ToPagedListAsync(input.PageIndex, input.PageSize); } //补三方单号,从缓存中加速查 var orderIdArr = pageList.List.Select(x => x.Id).ToArray(); var thirdPartyOrderNumbers = await GetThirdPartyOrderNumberForCacheAsync(orderIdArr); pageList.List.ForEach(x => x.ThirdPartyOrderNumber = thirdPartyOrderNumbers[x.Id]); //查询订单类型描述,从缓存中加速查 var orderTypes = pageList.List.Select(x => x.OrderType).Distinct().ToList(); var questTypes = pageList.List.Select(x => x.QuestionType).Distinct().ToList(); var typeDisplayDict = await GetMainDataEnumForCacheAsync(orderTypes, questTypes); //填充数描述信息 pageList.List.ForEach(x => { x.OrderTypeName = typeDisplayDict.TryGetValue(GlobalConst.MainDataKey_OrderType, out var orderTypeDisplayDict) ? orderTypeDisplayDict.TryGetValue(x.OrderType, out var orderTypeDisplay) ? orderTypeDisplay : $"未在主数据中心找到Property:[{GlobalConst.MainDataKey_OrderType}] " : $"未在主数据中心找到Property:[{GlobalConst.MainDataKey_OrderType}] EnumKey:[{x.OrderType}]"; x.QuestionTypeName = typeDisplayDict.TryGetValue(GlobalConst.MainDataKey_TrackQuestionType, out var questDisplayDict) ? questDisplayDict.TryGetValue(x.QuestionType, out var questDisplay) ? questDisplay : $"未在主数据中心找到Property:[{GlobalConst.MainDataKey_OrderType}]" : $"未在主数据中心找到Property:[{GlobalConst.MainDataKey_TrackQuestionType}] EnumKey:[{x.OrderType}]"; }); return pageList; } public async Task<(int MainOrderId, int CustomId)> GetTrackOrderByProviderAndOrderNo(string serviceProviderCode, string orderNo) { var orderList = await Context.Queryable() .LeftJoin((a, b) => a.Id == b.OrderId && b.IsDeleted == false) .Where((a, b) => a.OrderNo == orderNo || (b.Platform == serviceProviderCode && b.OrderNumber == orderNo)) .Select((a, b) => new { a.Id, a.CustomId, b.Platform, b.OrderNumber }) .ToListAsync(); if (orderList.Count == 0) { return default; } var order = orderList.First(); //当出现了多条记录匹配,优先使用外部单匹配提升精确度 var matchOrder = orderList.FirstOrDefault(x => x.Platform == serviceProviderCode && x.OrderNumber == orderNo); return matchOrder != null ? (matchOrder.Id, matchOrder.CustomId) : (order.Id, order.CustomId); } public async Task CreateOrderAsync(OrderEntity order) { using var tran = TransactionHelper.GetReadCommitted(); var orderId = await InsertReturnIdentityAsync(order); order.OrderFlights.ForEach(x => x.OrderId = orderId); await Context.Insertable(order.OrderFlights).ExecuteCommandAsync(); order.OrderPullStatus.ForEach(x => x.OrderId = orderId); await Context.Insertable(order.OrderPullStatus).ExecuteCommandAsync(); order.OrderPlatformNumbers.ForEach(x => x.OrderId = orderId); await Context.Insertable(order.OrderPlatformNumbers).ExecuteCommandAsync(); tran.Complete(); return orderId; } public Task UpdateBatchIdsAsync(List ids, int newValue) { var idsString = string.Join(",", ids); return Context.Ado.ExecuteCommandAsync( $"UPDATE `t_order` SET `DeficiencyTag` = @newValue ,UpdateTime= current_timestamp WHERE `Id` IN ({idsString})", new { newValue }); } public async Task UpdateOrderAsync(OrderEntity order) { using var tran = TransactionHelper.GetReadCommitted(); // 更新订单主表 await UpdateAsync(order, x => new { x.CustomId, x.SubSystemId }); // 更新订单模板 await Context.Deleteable().Where(x => x.OrderId == order.Id).ExecuteCommandAsync(); order.OrderPullStatus.ForEach(x => x.OrderId = order.Id); await Context.Insertable(order.OrderPullStatus).ExecuteCommandAsync(); // 更新订单三方关联 await Context.Deleteable().Where(x => x.OrderId == order.Id).ExecuteCommandAsync(); order.OrderPlatformNumbers.ForEach(x => x.OrderId = order.Id); await Context.Insertable(order.OrderPlatformNumbers).ExecuteCommandAsync(); // 更新订单航班 await Context.Deleteable().Where(x => x.OrderId == order.Id).ExecuteCommandAsync(); order.OrderFlights.ForEach(x => x.OrderId = order.Id); await Context.Insertable(order.OrderFlights).ExecuteCommandAsync(); tran.Complete(); } public async Task DeleteOrderAsync(int orderId) { using var tran = TransactionHelper.GetReadCommitted(); // 软删订单主表 await SoftDeleteAsync(x => x.Id == orderId); // 软删订单航班 // 软删订单关联关系 // 软删订单拉取状态 tran.Complete(); } /// /// 更新推送预警单 /// /// /// /// /// public async Task PushWarnOrdersAsync(int subSystemId, int pageSize, int pageindex) { var ordersToUpdate = await Context.Queryable() .Where(b => b.SubSystemId == subSystemId && !b.IsCompleted && b.QuestionType != 1) .Select(b => new { b.LatestPushTime, b.Id, b.QuestionType }) .ToPageListAsync(pageindex, pageSize); var latestPushTimeIds = new List(); var questionZeroIds = new List(); foreach (var item in ordersToUpdate) { var questionType = !item.LatestPushTime.HasValue || item.LatestPushTime < DateTime.Now.AddHours(-12) ? 2 : 0; if (item.QuestionType == questionType) continue; if (questionType == 2) latestPushTimeIds.AddLast(item.Id); else questionZeroIds.AddLast(item.Id); } if (latestPushTimeIds.Count > 0) UpdateOrderQuestionType(2, latestPushTimeIds); if (questionZeroIds.Count > 0) UpdateOrderQuestionType(0, questionZeroIds); return ordersToUpdate.Count; } public async Task> GetSubSystemListByCodesAsync(List codes) { var res = await Context.Queryable() .Where(x => codes.Contains(x.Key)) .Select(x => new { x.Key, x.Id }) .ToListAsync(); return res.Select(x => (x.Key, x.Id)).ToList(); } public Task> GetOrderTemplateAsync(int[] orderId) { return Context.Queryable() .Where(x => orderId.Contains(x.Id)) .Select(a => new OrderTemplateVO { OrderId = a.Id, OrderNo = a.OrderNo, TemplateCodes = a.TemplateCodes }) .ToListAsync(); } public Task> GetOrderTemplateAsync(int subSystemId, int orderType, List orderNos) { return Context.Queryable() .Where(x => orderNos.Contains(x.OrderNo) && x.SubSystemId == subSystemId && x.OrderType == orderType) .Select(a => new OrderTemplateVO { OrderId = a.Id, OrderNo = a.OrderNo, TemplateCodes = a.TemplateCodes }) .ToListAsync(); } public Task GetSystemInfoById(int id) { return Context.Queryable().FirstAsync(m => m.IsDeleted == false && m.Id == id); } public Task GetUserById(int id) { return Context.Queryable().FirstAsync(m => m.IsDeleted == false && m.Id == id); } private int UpdateOrderQuestionType(int questionType, IEnumerable ids) { var idsString = string.Join(",", ids); return Context.Ado.ExecuteCommand( $"UPDATE `t_order` SET `QuestionType` = @questionType ,UpdateTime= current_timestamp WHERE `Id` IN ({idsString})", new { questionType }); } /// /// 拼接多个三方单号,默认使用分隔符默认(、) /// 第三方单号展示: 0000000(服务商)、0000000(服务商) /// /// /// public string BuildThirdPartyOrderNumber(List orderPlatformNumberEntities) { if (orderPlatformNumberEntities.Count == 0) return GlobalConst.EmptyString; const string separator = "、"; var thirdPartyOrderNumbers = orderPlatformNumberEntities .Where(x => !string.IsNullOrWhiteSpace(x.OrderNumber)) .Select(x => $"{x.OrderNumber}({x.Platform})") .ToList(); if (thirdPartyOrderNumbers.Count == 0) return GlobalConst.EmptyString; return string.Join(separator, thirdPartyOrderNumbers); } #region 订单关联 public async Task> GetLinkedOrdersAsync(int[] orderIds) { return await Context.Queryable() .LeftJoin((a, b) => a.LinkOrderId == b.Id) .Where(a => orderIds.Contains(a.OrderId)) .Select((a, b) => new LinkedOrderVO { OwnerOrderId = a.OrderId, OrderId = b.Id, OrderNo = b.OrderNo, OrderType = b.OrderType, SubSystemId = b.SubSystemId, CustomId = b.CustomId }) .ToListAsync(); } public async Task> GetLinkedOrdersAsync(int orderId, int linkSubSystemId, int linkOrderType) { return await Context.Queryable() .LeftJoin((a, b) => a.LinkOrderId == b.Id) .Where((a, b) => orderId == a.OrderId && b.SubSystemId == linkSubSystemId && b.OrderType == linkOrderType) .Select((a, b) => b.OrderNo) .ToListAsync(); } public async Task> GetLinkedOrdersAsync(int orderId, int linkSubSystemId, int linkOrderType, List linkOrderNos) { return await Context.Queryable() .LeftJoin((a, b) => a.LinkOrderId == b.Id) .Where((a, b) => orderId == a.OrderId && b.SubSystemId == linkSubSystemId && b.OrderType == linkOrderType && linkOrderNos.Contains(b.OrderNo)) .Select((a, b) => a.Id) .ToListAsync(); } public async Task LinkOrderAsync(List orderLinkEntities) { await Context.Insertable(orderLinkEntities).ExecuteCommandAsync(); } public async Task UnLinkByIdsAsync(List linkIds) { await Context.Deleteable(linkIds).ExecuteCommandAsync(); } #endregion #region 订单拉取 public async Task UpdatePullStatusToSilenceAsync(List orderIds, string trackCode) { // var status = await Context.Queryable() // .Where(x => orderIds.Contains(x.OrderId) && x.StartTrackCode == trackCode) // .Where(x => x.PullStatus == TrackPullStatus.Init || x.PullStatus == TrackPullStatus.Pulling) // .Select(x => new // { // x.Id, // x.PullStatus, // x.SilenceBeforeStart, // x.SilenceAfterEnd, // x.StartTime, // x.EndTime // }) // .ToListAsync(); // // // 初始态-》静默前、拉取中-》静默后 // var changes = status.Select(x => new // { // x.Id, // PullStatus = x.PullStatus == TrackPullStatus.Init // ? x.SilenceBeforeStart > 0 // ? TrackPullStatus.SilenceBeforeStart // : TrackPullStatus.Pulling // : x.SilenceAfterEnd > 0 // ? TrackPullStatus.SilenceAfterEnd // : TrackPullStatus.Completed, // StartTime = // x.PullStatus == TrackPullStatus.Init ? DateTime.Now.AddHours(x.SilenceBeforeStart) : x.StartTime, // EndTime = x.PullStatus == TrackPullStatus.Pulling ? DateTime.Now.AddHours(x.SilenceAfterEnd) : x.EndTime // }); // await Context.Updateable(changes).ExecuteCommandAsync(); // 初始态-》静默前 await Context.Updateable() .SetColumns(x => x.PullStatus == TrackPullStatus.SilenceBeforeStart) .SetColumns(x => x.StartTime == DateTime.Now.AddHours(x.SilenceBeforeStart)) .Where(x => orderIds.Contains(x.OrderId) && x.StartTrackCode == trackCode) .Where(x => x.PullStatus == TrackPullStatus.Init) .ExecuteCommandAsync(); // 拉取中-》静默后 await Context.Updateable() .SetColumns(x => x.PullStatus == TrackPullStatus.SilenceAfterEnd) .SetColumns(x => x.EndTime == DateTime.Now.AddHours(x.SilenceAfterEnd)) // >0才有意义 .Where(x => orderIds.Contains(x.OrderId) && x.EndTrackCode == trackCode) .Where(x => x.PullStatus == TrackPullStatus.Pulling) .ExecuteCommandAsync(); } public async Task UpdatePullStatusBySilenceAsync() { var cnt1 = await Context.Updateable() .SetColumns(x => x.PullStatus == TrackPullStatus.Pulling) .Where(x => x.PullStatus == TrackPullStatus.SilenceBeforeStart && x.StartTime < DateTime.Now) .ExecuteCommandAsync(); LogHelper.Info( $"{cnt1}条订单的拉取状态由{nameof(TrackPullStatus.SilenceBeforeStart)}更新为{nameof(TrackPullStatus.Pulling)}"); var cnt2 = await Context.Updateable() .SetColumns(x => x.PullStatus == TrackPullStatus.Completed) .Where(x => x.PullStatus == TrackPullStatus.SilenceAfterEnd && x.EndTime < DateTime.Now) .ExecuteCommandAsync(); LogHelper.Info( $"{cnt2}条订单的拉取状态由{nameof(TrackPullStatus.SilenceAfterEnd)}更新为{nameof(TrackPullStatus.Completed)}"); return cnt1 + cnt2; } public async Task ClearPullStatusAsync() { var time = DateTime.Now.Subtract(TimeSpan.FromDays(30)); return await Context.Deleteable().Where(x => x.PullStatus == TrackPullStatus.Completed || x.StartTime < time) .ExecuteCommandAsync(); } public async Task UpdateOrderPullConfigForServiceProviderAsync( UpdateOrderPullConfigsInputVo inputVo) { // tips:如此更新,影响范围是否可控?状态是否正确? return await Context.Updateable() .SetColumns(x => x.ServiceProviderCode == inputVo.ServiceCode) .SetColumns(x => x.StartTrackCode == inputVo.StartCode) .SetColumns(x => x.EndTrackCode == inputVo.EndCode) .SetColumns(x => x.SilenceBeforeStart == inputVo.SilenceTime) .SetColumns(x => x.SilenceAfterEnd == inputVo.StopTime) // 直接将订单拉取状态更新为拉取中,不再考虑性能 .SetColumns(x => x.StartTime == DateTime.Now) .SetColumns(x => x.EndTime == GlobalConst.MaxDateTime) .SetColumns(x => x.PullStatus == TrackPullStatus.Pulling) .Where(x => inputVo.OrderIds.Contains(x.OrderId)) .ExecuteCommandAsync(); } #endregion }