diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.Query.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.Query.cs index 7e4376cf..1a56a04e 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.Query.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.Query.cs @@ -14,7 +14,7 @@ public partial class TrackService private readonly ITrackTemplateRepository _trackTemplateRepository; /// - /// 查询轨迹 + /// 查询轨迹(时区补正) /// /// /// @@ -90,12 +90,17 @@ public partial class TrackService } // 排序 - var sortedList = await TracksOrdered(res, input); + var sortedList = await TracksOrdered(res, input, input.SortMethod, input.TrackTemplateCode); return new UnifyResultDto>(true, sortedList, "操作成功"); } + /// + /// 查询轨迹(原样返回) + /// + /// + /// [HttpPost("Track/QueryV2")] public async Task>> QueryV2Async(GetTrackInputV2 input) { @@ -163,15 +168,15 @@ public partial class TrackService } // 排序 - var sortedList = await TracksOrdered(res, input); + var sortedList = await TracksOrdered(res, input, input.SortMethod); return new UnifyResultDto>(true, sortedList, "操作成功"); } - private async Task> TracksOrdered(List list, GetTrackInput input) + private async Task> TracksOrdered(List list, OrderNosInfo orderNosInfo, SortMethod? sortMethod, string? trackTemplateCode = null) { - switch (input.SortMethod) + switch (sortMethod) { case SortMethod.ByTrackTime: case SortMethod.ByTemplateAndTrackTime: @@ -184,11 +189,11 @@ public partial class TrackService list.ForEach(item => item.TrackDetails.Sort((a, b) => b.Time.CompareTo(a.Time))); break; default: - throw new ArgumentOutOfRangeException(nameof(input.SortMethod), input.SortMethod, "不支持的排序方式"); + throw new ArgumentOutOfRangeException(nameof(sortMethod), sortMethod, "不支持的排序方式"); } - if (input.SortMethod != SortMethod.ByTemplateAndTrackTime - && input.SortMethod != SortMethod.ByTemplateAndTrackTimeDesc) + if (sortMethod != SortMethod.ByTemplateAndTrackTime + && sortMethod != SortMethod.ByTemplateAndTrackTimeDesc) { return list; } @@ -196,134 +201,178 @@ public partial class TrackService //查出订单模板 var templates = await _trackTemplateRepository.GetTrackTemplateListAsync(new GetTrackTemplateListInputVo { - SubSystemId = input.SubSystemId, - TemplateCode = input.TrackTemplateCode + SubSystemId = orderNosInfo.SubSystemId, + TemplateCode = trackTemplateCode }); if (templates.Count == 0) { - throw new BusinessException("轨迹模板不存在"); + return list; } + //查出订单和模板的关系,构建订单号和模板code的关系 + var orderNoTemplateDict = (await _orderRepository + .GetOrderTemplateAsync(orderNosInfo.SubSystemId, orderNosInfo.OrderType, orderNosInfo.OrderNos)) + .ToDictionary(x => x.OrderNo, x => x.TemplateCodes?.Split(",").ToHashSet()); + + //查询出订单和模板的关联关系,保证该订单使用自己的模板排序 foreach (var getTrackOutput in list) { - getTrackOutput.TrackDetails = SortByTemplate(getTrackOutput.TrackDetails, templates, input.SortMethod.Value); + //查看该订单具有的模板 + var curOrderNoTemplateCodes = orderNoTemplateDict[getTrackOutput.OrderNo]; + if (curOrderNoTemplateCodes == null || curOrderNoTemplateCodes.Count == 0) continue; + + //选出这个订单号所具有的模板 + var tipTemplates = templates.Where(x => curOrderNoTemplateCodes.Contains(x.TemplateCode)).ToList(); + if (tipTemplates.Count == 0) continue; + + getTrackOutput.TrackDetails = SortByTemplatesAsync(getTrackOutput.TrackDetails, tipTemplates, sortMethod == SortMethod.ByTemplateAndTrackTime); } return list; } - private async Task> TracksOrdered(List list, GetTrackInputV2 input) + /// + /// 轨迹发生时间(统一utc)排序,相同时间按模板排序,考虑多个模板的情况 + /// + /// + private static List SortByTemplatesAsync(List seq, List templates, bool isAsc = true) { - switch (input.SortMethod) + //转化为统一时间 + var trackWraps = seq.Select(x => new TrackWrap(x)); + var sortedTracks = (isAsc + ? trackWraps.OrderBy(x => x.Time) + : trackWraps.OrderByDescending(x => x.Time)).ToList(); + + + //用于统计时间相同的轨迹 + var timeEqTrackDict = new Dictionary>(); + for (var i = 0; i < sortedTracks.Count; i++) { - case SortMethod.ByTrackTime: - case SortMethod.ByTemplateAndTrackTime: - // tips:quick-sort 直接修改原list - list.ForEach(item => item.TrackDetails.Sort((a, b) => a.Time.CompareTo(b.Time))); - break; - case null: - case SortMethod.ByTrackTimeDesc: - case SortMethod.ByTemplateAndTrackTimeDesc: - list.ForEach(item => item.TrackDetails.Sort((a, b) => b.Time.CompareTo(a.Time))); - break; - default: - throw new ArgumentOutOfRangeException(nameof(input.SortMethod), input.SortMethod, "不支持的排序方式"); - } - - return list; - } - - //按模板排序 - private static List SortByTemplate(List tracks, List templates, SortMethod sortMethod) - { - if (templates.Count == 0 || - tracks.Count == 0) - { - return new List(); - } - - var isAsc = sortMethod == SortMethod.ByTemplateAndTrackTime; - - var allTemplateCodes = new HashSet(); - - foreach (var template in templates) - { - tracks = Sort(tracks, template, isAsc, allTemplateCodes); - } - // tracks = templates.Aggregate(tracks, - // (current, template) => Sort(current, template, isAsc, allTemplateCodes)); - - return tracks; - } - - private static List Sort(List seq, TrackTemplateOutputVo template, bool isAsc = true, HashSet? allTemplateCodes = null) - { - if (template.TemplateContents.Count == 0) - { - return seq; - } - - // 创建一个字典,用于存储排序序列中元素的索引 - var sortDict = new Dictionary(); - template.TemplateContents.Sort((x, y) => x.Sort.CompareTo(y.Sort)); - var sortSeq = template.TemplateContents - .Select(x => seq.FirstOrDefault(y => y.Code == x.Code)) - .Where(x => x != null) - .ToList(); - for (var i = 0; i < sortSeq.Count; i++) - { - sortDict[sortSeq[i]!] = isAsc ? i : sortSeq.Count - i; - } - - // 将模板code添加到上下文集合中 - if (allTemplateCodes != null) - { - allTemplateCodes.UnionWith(template.TemplateContents.Select(x => x.Code)); - } - - // 根据排序序列和字典中的索引对序列进行排序 - // 使用稳定排序算法,如OrderBy,它保持相等元素的相对顺序 - var sorted = seq.OrderBy(item => sortDict.GetValueOrDefault(item, int.MaxValue)).ToList(); - - // 存在 - if (allTemplateCodes != null) - { - //查询到没有命中到模板的轨迹 - var noTipTemplateTracks = sorted.Where(x => !allTemplateCodes.Contains(x.Code)).ToList(); - - //查询未命中轨迹的索引字典 - var trackIdxDict = new Dictionary(); - var trackSortDict = new SortedDictionary(); - foreach (var track in noTipTemplateTracks) - { - trackIdxDict[track] = sorted.IndexOf(track); - trackSortDict[trackIdxDict[track]] = track; - } - - //没有命中的轨迹排序 - var noTipTemplateTrackSorted = (isAsc - ? noTipTemplateTracks.OrderBy(x => x.Time) - : noTipTemplateTracks.OrderByDescending(x => x.Time)) - .ToList(); - - for (var i = 0; i < trackSortDict.Count; i++) - { - //在序列中的实际位置 - var (idx, st) = trackSortDict.ElementAt(0); - //在这个位置它应该是哪个轨迹 - var track = noTipTemplateTrackSorted[0]; - //交换位置 - if (track != st) + var trackWrap = sortedTracks[i]; + if (timeEqTrackDict.TryGetValue(trackWrap.Time, out var list)) + list.Add(new TrackIdxWarp(trackWrap.Track, i)); + else + timeEqTrackDict.Add(trackWrap.Time, new List { - var idx2 = trackIdxDict[track]; - sorted[idx2] = st; - sorted[idx] = track; + new TrackIdxWarp(trackWrap.Track, i) + }); + } + + + //选出需要按模板重排序的轨迹 + var timeEqTracks = timeEqTrackDict + .Where(kv => kv.Value.Count > 1) + .ToDictionary(kv => kv.Key, kv => kv.Value); + + //初始化为最后一个模板排序 + var preSortDict = templates[templates.Count - 1] + .TemplateContents + .OrderBy(x => x.Sort) + .ToDictionary(x => x.Code, x => x.Sort); + + for (var i = templates.Count - 2; i >= 0; i--) + { + var curCodeSortDict = templates[i] + .TemplateContents + .ToDictionary(x => x.Code, y => y.Sort); + + //没在之前模板出现的code + var noTipPreCodeSortDict = curCodeSortDict + .Where(x => !preSortDict.ContainsKey(x.Key)) + .ToDictionary(x => x.Key, y => y.Value); + + //插入未命中的模板code + foreach (var (noTipCode, curNoTipCodeSort) in noTipPreCodeSortDict) + { + //初始化插入位置 + var insertPos = -1; + foreach (var (preCode, _) in preSortDict) + { + if (curCodeSortDict.TryGetValue(preCode, out var preCodeSort)) + { + //未命中的相对于之前模板的偏后,设置插入位置在 + if (preCodeSort < curNoTipCodeSort) + { + insertPos = preSortDict[preCode] + 1; + } + } + } + + //结束了还没选好位置,未命中的code相对于之前的模板code在当前模板最前面 + if (insertPos == -1) + { + foreach (var (c, s) in preSortDict) + preSortDict[c] = s + 1; + preSortDict[noTipCode] = 0; + } + else + { + foreach (var (c, s) in preSortDict.Where(x => x.Value >= insertPos)) + preSortDict[c] = s + 1; + preSortDict[noTipCode] = insertPos; } } } - return sorted; + + foreach (var trackIdxWarps in timeEqTracks.Values) + { + var curIndex = trackIdxWarps.Min(x => x.Index); + //按模板从小到大排序 + var sortByTemplate = (isAsc + ? trackIdxWarps + .OrderBy(x => preSortDict.GetValueOrDefault(x.Track.Code, int.MaxValue)) + : trackIdxWarps + .OrderByDescending(x => preSortDict.GetValueOrDefault(x.Track.Code, int.MaxValue)) + ).ToList(); + for (var j = 0; j < sortByTemplate.Count; j++) + { + var track = sortByTemplate[j]; + //当前索引不对应 + if (track.Index != curIndex + j) + { + var temp1 = sortedTracks[track.Index].Track; + var temp2 = sortedTracks[curIndex + j].Track; + sortedTracks[track.Index].Track = temp2; + sortedTracks[curIndex + j].Track = temp1; + trackIdxWarps.First(x => x.Index == curIndex + j).Index = track.Index; + track.Index = curIndex + j; + } + } + } + + return sortedTracks.Select(x => x.Track).ToList(); + } + + + private class TrackIdxWarp + { + public TrackDetail Track { get; set; } + public int Index { get; set; } + + public TrackIdxWarp(TrackDetail track, int index) + { + Track = track; + Index = index; + } + } + + + private class TrackWrap + { + public TrackDetail Track { get; set; } + + public DateTime Time { get; set; } + + public TrackWrap(TrackDetail track) + { + Track = track; + var timeZone = track.TimeZone ?? GlobalConst.NoneTrackTimeZone; + Time = Math.Abs(timeZone - GlobalConst.NoneTrackTimeZone) < 1 + ? track.Time + : track.Time.Subtract(TimeSpan.FromHours(timeZone)); + } } } diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs index f20c415e..eca70b47 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs @@ -30,6 +30,7 @@ public partial class TrackService : ApplicationService, ITrackService IHttpClientProxy requestCurrentUserService, ITrackTemplateRepository trackTemplateRepository) { _orderRepository = orderRepository; + this._orderRepository = orderRepository; _capPublisher = capPublisher; _trackContentRepository = trackContentRepository; _requestCurrentUserService = requestCurrentUserService; diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs index 96be07a4..467ab9c0 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs @@ -12,7 +12,7 @@ public interface IOrderRepository : IBasicRepository /// Task> GetTrackOrderPagedList(GetPageTrackOrderInputVO input); - /// + /// /// 通过三方平台和三方单号查询订单 /// /// 三方平台 @@ -62,6 +62,27 @@ public interface IOrderRepository : IBasicRepository /// Task> GetSubSystemListByCodesAsync(List codes); + #region 订单模板 + + /// + /// 根据订单id,获取订单所属的模板 + /// + /// + /// + Task> GetOrderTemplateAsync(int[] orderId); + + /// + /// 根据子系统+订单号+类型,获取订单所属的模板 + /// + /// + /// + /// + /// + Task> GetOrderTemplateAsync(int subSystemId,int orderType,List orderNos); + + #endregion + + #region 订单关联 /// diff --git a/src/SJZY.Track.Domain/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs b/src/SJZY.Track.Domain/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs index efae67b2..e8a50f2d 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs @@ -45,6 +45,8 @@ public interface ITrackTemplateRepository : IBasicRepository> whereExpression); Task> GetTrackTemplateListAsync(GetTrackTemplateListInputVo input); + Task> GetPageTrackTemplateListAsync(GetPageTrackTemplateListInputVo input); + Task> GetContentByTemplateCodes(string[] codes, int systemId); } diff --git a/src/SJZY.Track.Domain/Dtos/Order/OrderTemplateVO.cs b/src/SJZY.Track.Domain/Dtos/Order/OrderTemplateVO.cs new file mode 100644 index 00000000..26c110a0 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Order/OrderTemplateVO.cs @@ -0,0 +1,13 @@ +namespace SJZY.Track.Domain.Dtos.Order; + +/// +/// 订单关联的模板模型 +/// +public class OrderTemplateVO +{ + public int OrderId { get; set; } + + public string OrderNo { get; set; } + + public string? TemplateCodes { get; set; } +} diff --git a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs index ac00889a..a071f693 100644 --- a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs @@ -396,6 +396,32 @@ public class OrderRepository : BasicRepository, IOrderRepository, I 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);