diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackInput.cs new file mode 100644 index 00000000..5616f912 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackInput.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; + +public class BatchDeleteTrackInput +{ + /// + /// 订单类型,必填 + /// + [Required] + [Range(1, int.MaxValue)] + public int OrderType { get; set; } + + /// + /// 订单号,必填 + /// + [Required] + public List OrderNos { get; set; } = new(); + + /// + /// 轨迹code,必填 + /// + [Required] + public List TrackCodes { get; set; } = new(); +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs index 0e318811..bff3df6e 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs @@ -1,4 +1,6 @@ -namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; public class DeleteOrderInput : OrderNoInfo { @@ -9,5 +11,6 @@ public class DeleteTrackInput : OrderNoInfo /// /// 轨迹code /// + [Required] public List TrackCodes { get; set; } = new(); } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/LinkOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/LinkOrderInput.cs index cf3e5b79..7d30b3c5 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/LinkOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/LinkOrderInput.cs @@ -22,7 +22,11 @@ public class LinkOrderInput [Required] public string OrderNo { get; set; } - [Required] public int LinkOrderType { get; set; } + /// + /// 关联单类型 + /// + [Required] + public int LinkOrderType { get; set; } /// /// 关联的单号 diff --git a/src/SJZY.Track.Application.Contract/Events/OrderLinkAfterFillTrackEvent.cs b/src/SJZY.Track.Application.Contract/Events/OrderLinkAfterFillTrackEvent.cs new file mode 100644 index 00000000..5caa784e --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Events/OrderLinkAfterFillTrackEvent.cs @@ -0,0 +1,19 @@ +namespace SJZY.Track.Application.Contract.Events; + +public class OrderLinkAfterFillTrackEvent +{ + /// + /// 关联订单id + /// + public int OrderId { get; set; } + + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 关联订单 + /// + public List LinkedOrderIds { get; set; } = null!; +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/EventHandlers/OrderLinkAfterFillTrackHandler.cs b/src/SJZY.Track.Application/EventHandlers/OrderLinkAfterFillTrackHandler.cs new file mode 100644 index 00000000..4a014b49 --- /dev/null +++ b/src/SJZY.Track.Application/EventHandlers/OrderLinkAfterFillTrackHandler.cs @@ -0,0 +1,61 @@ +using DotNetCore.CAP; +using SJZY.Track.Application.Contract.Events; +using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.EventHandlers; + +public class OrderLinkAfterFillTrackHandler : ITransientDependency, ICapSubscribe +{ + private readonly ITrackDetailsRepository _trackDetailsRepository; + private readonly ICapPublisher _capPublisher; + private readonly IOrderRepository _orderRepository; + + public OrderLinkAfterFillTrackHandler(ITrackDetailsRepository trackDetailsRepository, ICapPublisher capPublisher, + IOrderRepository orderRepository) + { + _trackDetailsRepository = trackDetailsRepository; + _capPublisher = capPublisher; + _orderRepository = orderRepository; + } + + [CapSubscribe(GlobalConst.OrderLinkAfterFillTrack)] + public async Task ConsumerAsync(OrderLinkAfterFillTrackEvent @event) + { + var tracks = await _trackDetailsRepository.GetListAsync(@event.OrderId); + + + foreach (var linkOrderId in @event.LinkedOrderIds) + { + var order = await _orderRepository.GetFirstAsync(x => + x.Id == linkOrderId, x => new + { + x.OrderType, x.OrderNo, x.CustomId, x.IsPushCompleted, x.SubSystemId + }); + + if (order == default) continue; + + // 补轨迹 + foreach (var track in tracks) + { + var fillTrackEvent = new TrackUpsertEvent + { + OrderType = order.OrderType, + OrderNo = order.OrderNo, + TrackCode = track.TrackCode, + TrackPlace = track.TrackPlace, + TrackTime = track.TrackTime, + TrackTimeZone = track.TrackTimeZone, + OrderId = linkOrderId, + CustomId = order.CustomId, + SubSystemId = order.SubSystemId, + IsPushCompleted = order.IsPushCompleted, + UpSubSystemId = @event.SubSystemId, + }; + + await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, fillTrackEvent); + } + } + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/EventHandlers/TrackUpsertEventHandler.cs b/src/SJZY.Track.Application/EventHandlers/TrackUpsertEventHandler.cs index 29e14bc2..db60129a 100644 --- a/src/SJZY.Track.Application/EventHandlers/TrackUpsertEventHandler.cs +++ b/src/SJZY.Track.Application/EventHandlers/TrackUpsertEventHandler.cs @@ -68,6 +68,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe var orderIsCompleted = false; if (track == default) { + //todo:可以优化 // 根据轨迹code在订单模板内容中寻找匹配的 var trackContent = await _provideQueries.MatchTrackContentAsync(@event.OrderId, @event.TrackCode); if (trackContent == default) diff --git a/src/SJZY.Track.Application/Services/ProvideServices/OrderLinkService.cs b/src/SJZY.Track.Application/Services/ProvideServices/OrderLinkService.cs index c4a6d1db..56741efe 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/OrderLinkService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/OrderLinkService.cs @@ -1,4 +1,6 @@ -using SJZY.Track.Application.Contract.Dtos.ProvideServices; +using DotNetCore.CAP; +using SJZY.Track.Application.Contract.Dtos.ProvideServices; +using SJZY.Track.Application.Contract.Events; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Queries; @@ -13,11 +15,14 @@ public class OrderLinkService : ApplicationService { private readonly IOrderRepository _orderRepository; private readonly IBasicQuery _orderLinkQuery; + private readonly ICapPublisher _capPublisher; - public OrderLinkService(IBasicQuery orderLinkQuery, IOrderRepository orderRepository) + public OrderLinkService(IBasicQuery orderLinkQuery, IOrderRepository orderRepository, + ICapPublisher capPublisher) { _orderLinkQuery = orderLinkQuery; _orderRepository = orderRepository; + _capPublisher = capPublisher; } /// @@ -28,14 +33,17 @@ public class OrderLinkService : ApplicationService [HttpPost("Order/Link")] public async Task LinkOrderAsync(LinkOrderInput input) { - var orderId = await _orderRepository.GetFirstAsync(x => - x.OrderType == input.OrderType && x.OrderNo == input.OrderNo, x => x.Id); - if (orderId == default) + var order = await _orderRepository.GetFirstAsync(x => + x.OrderType == input.OrderType && x.OrderNo == input.OrderNo, x => new + { + x.Id, x.SubSystemId + }); + if (order == default) { throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); } - //todo:循环关联? + //tips:循环关联问题再消费时解决 if (input.OrderType == input.LinkOrderType) { throw new BusinessException(message: "不能同级关联"); @@ -52,10 +60,23 @@ public class OrderLinkService : ApplicationService if (!input.IsLink) return await UnLinkOrderAsync(input); // 关联订单 - return await DoLinkOrderAsync(input, orderId); + var linkedOrderIds = await DoLinkOrderAsync(input, order.Id); + + // 补轨迹 + if (linkedOrderIds.Count > 0) + { + await _capPublisher.PublishAsync(GlobalConst.OrderLinkAfterFillTrack, new OrderLinkAfterFillTrackEvent + { + OrderId = order.Id, + SubSystemId = order.SubSystemId, + LinkedOrderIds = linkedOrderIds, + }); + } + + return true; } - private async Task DoLinkOrderAsync(LinkOrderInput input, int orderId) + private async Task> DoLinkOrderAsync(LinkOrderInput input, int orderId) { // 获取已关联的订单 var linkOrderNos = await _orderLinkQuery.GetListAsync(x => @@ -70,6 +91,8 @@ public class OrderLinkService : ApplicationService x.OrderType == input.LinkOrderType && noLinkOrderNos.Contains(x.OrderNo), x => new { x.Id, x.OrderNo }); + if (canLinkOrders.Count == 0) return new List(); + await _orderRepository.LinkOrderAsync(canLinkOrders.Select(x => new OrderLinkEntity { OrderId = orderId, @@ -80,9 +103,7 @@ public class OrderLinkService : ApplicationService LinkOrderType = input.LinkOrderType }).ToList()); - // todo:补轨迹 - - return true; + return canLinkOrders.Select(x => x.Id).ToList(); } private async Task UnLinkOrderAsync(LinkOrderInput input) diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs index 18adf7fa..298cc2a4 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs @@ -65,14 +65,19 @@ public class TrackService : ApplicationService var order = await _orderRepository.GetFirstAsync(x => x.OrderType == input.OrderType && x.OrderNo == input.OrderNo, x => new { - x.Id, x.SubSystemId, x.IsPushCompleted, x.CustomId + x.Id, x.SubSystemId, x.IsCompleted }); if (order == default) { throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); } - var codes = await _provideQueries.GetCanDeleteTrackCodesAsync(order.Id, input.TrackCodes); + if (order.IsCompleted) + { + throw new BusinessException(message: $"订单:{input.OrderNo}已完成无法删除轨迹"); + } + + var codes = await _provideQueries.GetCanDeleteTrackCodesAsync(new List(order.Id), input.TrackCodes); if (codes.Count == 0) { @@ -83,11 +88,45 @@ public class TrackService : ApplicationService var @event = new TrackDeleteEvent { OrderId = order.Id, - TrackCodes = codes, + TrackCodes = codes.Select(x => x.TrackCode).ToList(), SubSystemId = order.SubSystemId, }; await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event); return true; } + + [HttpPost("Track/BatchDelete")] + public async Task BatchTrackAsync(BatchDeleteTrackInput input) + { + var orders = await _orderRepository.GetListAsync(x => + x.OrderType == input.OrderType && input.OrderNos.Contains(x.OrderNo) && !x.IsPushCompleted + , x => new + { + x.Id, x.SubSystemId + }); + + var codes = await _provideQueries.GetCanDeleteTrackCodesAsync( + orders.Select(x => x.Id).ToList(), input.TrackCodes + ); + + if (codes.Count == 0) + { + throw new BusinessException(message: "没有一个轨迹代码是可删的"); + } + + // 删除轨迹 + foreach (var group in codes.GroupBy(x => x.OrderId)) + { + var @event = new TrackDeleteEvent + { + OrderId = group.Key, + TrackCodes = codes.Select(x => x.TrackCode).ToList(), + SubSystemId = orders.First(x => x.Id == group.Key).SubSystemId, + }; + await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event); + } + + return true; + } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsRepository.cs b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsRepository.cs index f6578023..4025579e 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsRepository.cs @@ -11,8 +11,8 @@ public interface ITrackDetailsRepository Task> GetListAsync(int orderType, List orderNos); Task<(List list, long total)> GetListAsync(TrackDetailsQueryInput input); - Task?> GetListAsync(int orderId); - + Task> GetListAsync(int orderId); + /// /// 删除轨迹 /// @@ -20,5 +20,6 @@ public interface ITrackDetailsRepository /// /// Task DeleteTrackAsync(int orderId, List trackCodes); + Task?> GetListAsync(List orderIds); } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackTemplate/CanDeleteTrackCodeVO.cs b/src/SJZY.Track.Domain/Dtos/TrackTemplate/CanDeleteTrackCodeVO.cs new file mode 100644 index 00000000..b4376fe5 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/TrackTemplate/CanDeleteTrackCodeVO.cs @@ -0,0 +1,7 @@ +namespace SJZY.Track.Domain.Dtos.TrackTemplate; + +public class CanDeleteTrackCodeVO +{ + public int OrderId { get; set; } + public string TrackCode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/GlobalConst.cs b/src/SJZY.Track.Domain/GlobalConst.cs index 92545ea9..78fdd6a8 100644 --- a/src/SJZY.Track.Domain/GlobalConst.cs +++ b/src/SJZY.Track.Domain/GlobalConst.cs @@ -11,14 +11,14 @@ public class GlobalConst /// 轨迹新增/更新 mq订阅名称 /// public const string TrackUpsertSubscribe = "SJZY.Track.TrackUpsert"; - + /// /// 轨迹删除 mq订阅名称 /// public const string TrackDeleteSubscribe = "SJZY.Track.TrackDelete"; /// - /// 接口通批量轨迹刷新订阅 + /// 接口通批量轨迹刷新 mq订阅名称 /// public const string InterfaceRelayTrackRefreshSubscribe = "SJZY.Track.TrackRefresh"; @@ -27,6 +27,11 @@ public class GlobalConst /// public const string UpdateOrderTrackSubscribe = "SJZY.Track.UpdateOrderTrack"; + /// + /// 关联订单后补轨迹 mq订阅名称 + /// + public const string OrderLinkAfterFillTrack = "SJZY.Track.OrderLinkAfterFillTrack"; + /// /// 轨迹要推送的客户redis key前缀 /// diff --git a/src/SJZY.Track.Domain/Queries/IBasicQuery.cs b/src/SJZY.Track.Domain/Queries/IBasicQuery.cs index 894e72a9..460e58be 100644 --- a/src/SJZY.Track.Domain/Queries/IBasicQuery.cs +++ b/src/SJZY.Track.Domain/Queries/IBasicQuery.cs @@ -9,7 +9,7 @@ public interface IBasicQuery where T : IEntity /// Task GetFirstAsync(Expression> whereExpression); - Task GetFirstAsync(Expression> whereExpression, + Task GetFirstAsync(Expression> whereExpression, Expression> resultExpression); /// diff --git a/src/SJZY.Track.Domain/Queries/IProvideQueries.cs b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs index 9c17d1d4..710cb3f4 100644 --- a/src/SJZY.Track.Domain/Queries/IProvideQueries.cs +++ b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs @@ -1,4 +1,3 @@ -using SJZY.Track.Domain.Aggregates.TrackPullAggregate; using SJZY.Track.Domain.Dtos.Order; using SJZY.Track.Domain.Dtos.TrackDetails; using SJZY.Track.Domain.Dtos.TrackTemplate; @@ -32,14 +31,14 @@ public interface IProvideQueries /// 轨迹代码 /// Task MatchTrackContentAsync(int orderId, string code); - + /// /// 获取可删除的订单轨迹代码 /// - /// + /// /// /// - Task> GetCanDeleteTrackCodesAsync(int orderId, List trackCodes); + Task> GetCanDeleteTrackCodesAsync(List orderIds, List trackCodes); /// /// 根据子系统内容映射,得到映射后的轨迹code diff --git a/src/SJZY.Track.Repository/Queries/BasicQuery.cs b/src/SJZY.Track.Repository/Queries/BasicQuery.cs index 60a234ef..d8003be8 100644 --- a/src/SJZY.Track.Repository/Queries/BasicQuery.cs +++ b/src/SJZY.Track.Repository/Queries/BasicQuery.cs @@ -13,7 +13,7 @@ public class BasicQuery : SimpleClient, IBasicQuery where T : class, IE Context = SqlsugarScopeBasic.GetContextSafely("default"); } - public async Task GetFirstAsync(Expression> whereExpression, + public async Task GetFirstAsync(Expression> whereExpression, Expression> resultExpression) { return await Context.Queryable().Where(whereExpression).Select(resultExpression).FirstAsync(); diff --git a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs index 526d5e3d..78639986 100644 --- a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs +++ b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs @@ -80,14 +80,19 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, .FirstAsync(); } - public async Task> GetCanDeleteTrackCodesAsync(int orderId, List trackCodes) + public async Task> GetCanDeleteTrackCodesAsync(List orderIds, + List trackCodes) { return await Context.Queryable() .LeftJoin((x, a) => x.TrackTemplateId == a.TrackTemplateId && a.IsCanDelete) .LeftJoin((x, a, b) => a.TrackContentId == b.Id) - .Where((x, a, b) => x.OrderId == orderId + .Where((x, a, b) => orderIds.Contains(x.OrderId) && trackCodes.Contains(b.Code) && a.IsCanDelete) - .Select((x, a, b) => b.Code) + .Select((x, a, b) => new CanDeleteTrackCodeVO + { + OrderId = x.OrderId, + TrackCode = b.Code + }) .ToListAsync(); } diff --git a/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs index 70f62f7e..da6f784a 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs @@ -86,7 +86,7 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract?> GetListAsync(int orderId) + public async Task> GetListAsync(int orderId) { var data = await Context.SearchAsync(x => x .Query(q => @@ -95,7 +95,7 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract s.Field(f => f.TrackTime, SortOrder.Descending))); - return data?.Documents?.ToList(); + return data.Documents.ToList(); } public async Task?> GetListAsync(List orderIds)