diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs index 7d8d79c0..0e318811 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs @@ -2,4 +2,12 @@ public class DeleteOrderInput : OrderNoInfo { +} + +public class DeleteTrackInput : OrderNoInfo +{ + /// + /// 轨迹code + /// + public List TrackCodes { get; set; } = new(); } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs b/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs new file mode 100644 index 00000000..c88d5837 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs @@ -0,0 +1,24 @@ +namespace SJZY.Track.Application.Contract.Events; + +public class TrackDeleteEvent +{ + /// + /// 订单id + /// + public int OrderId { get; set; } + + /// + /// 轨迹代码 + /// + public List TrackCodes { get; set; } = null!; + + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 上游子系统id + /// + public int UpSubSystemId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/EventHandlers/TrackDeleteEventHandler.cs b/src/SJZY.Track.Application/EventHandlers/TrackDeleteEventHandler.cs new file mode 100644 index 00000000..d4e48a10 --- /dev/null +++ b/src/SJZY.Track.Application/EventHandlers/TrackDeleteEventHandler.cs @@ -0,0 +1,67 @@ +using DotNetCore.CAP; +using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Track.Application.Contract.Events; +using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Queries; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.EventHandlers; + +public class TrackDeleteEventHandler: ITransientDependency, ICapSubscribe +{ + private readonly IOrderRepository _orderRepository; + private readonly IProvideQueries _provideQueries; + private readonly ITrackDetailsRepository _trackDetailsRepository; + private readonly ICapPublisher _capPublisher; + + public TrackDeleteEventHandler(IProvideQueries provideQueries, ITrackDetailsRepository trackDetailsRepository, IOrderRepository orderRepository, ICapPublisher capPublisher) + { + _provideQueries = provideQueries; + _trackDetailsRepository = trackDetailsRepository; + _orderRepository = orderRepository; + _capPublisher = capPublisher; + } + + [CapSubscribe(GlobalConst.TrackUpsertSubscribe)] + public async Task ConsumerAsync(TrackDeleteEvent @event) + { + // 不同子系统需要做内容映射 + if (@event.UpSubSystemId != 0 && @event.UpSubSystemId != @event.SubSystemId) + { + var mapCodes = await _provideQueries + .MapTrackCodesAsync(@event.UpSubSystemId, @event.SubSystemId, @event.TrackCodes.ToArray()); + + if (mapCodes.Count == 0) + { + //throw new BusinessException(message: "订单跨子系统,未配置内容映射,请联系管理员"); + return; + } + + @event.TrackCodes = mapCodes; + } + + await DeleteTrackAsync(@event); + } + + private async Task DeleteTrackAsync(TrackDeleteEvent @event) + { + // 删除轨迹 + await _trackDetailsRepository.DeleteTrackAsync(@event.OrderId, @event.TrackCodes); + + // 根据订单关联关系异步向下刷 + var linkedOrders = await _orderRepository.GetLinkedOrdersAsync(@event.OrderId); + foreach (var linkedOrder in linkedOrders) + { + var nextEvent = new TrackDeleteEvent + { + OrderId = linkedOrder.OrderId, + TrackCodes = @event.TrackCodes, + SubSystemId = linkedOrder.SubSystemId, + UpSubSystemId = @event.SubSystemId + }; + + await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, nextEvent); + } + } +} \ 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 f354d19b..d8e480ef 100644 --- a/src/SJZY.Track.Application/EventHandlers/TrackUpsertEventHandler.cs +++ b/src/SJZY.Track.Application/EventHandlers/TrackUpsertEventHandler.cs @@ -33,10 +33,11 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe public async Task ConsumerAsync(TrackUpsertEvent @event) { // 不同子系统需要做内容映射 - if (@event.UpSubSystemId != @event.SubSystemId) + if (@event.UpSubSystemId != 0 && @event.UpSubSystemId != @event.SubSystemId) { - var mapCode = await _provideQueries - .MapTrackContentCodeAsync(@event.UpSubSystemId, @event.SubSystemId, @event.TrackCode); + var mapCode = (await _provideQueries + .MapTrackCodesAsync(@event.UpSubSystemId, @event.SubSystemId, @event.TrackCode)) + .FirstOrDefault(); if (mapCode == default) { @@ -50,7 +51,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe await UpsertTrackAsync(@event); } - internal async Task UpsertTrackAsync(TrackUpsertEvent @event) + private async Task UpsertTrackAsync(TrackUpsertEvent @event) { // 不存在则新增轨迹,存在则更新 var track = await _trackDetailsRepository.GetFirstOrDefaultAsync(@event.OrderId, @event.TrackCode); @@ -65,6 +66,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe //throw new BusinessException(message: $"订单:{@event.OrderNo} 所绑定的模板,没有一条内容可以匹配轨迹代码:{@event.TrackCode}"); return; } + orderIsCompleted = trackContent.IsCompleted; track = new TrackDetailsIndex @@ -82,7 +84,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe CreateUser = CurrentUserRequest.CurrentUser.Name, CreateTime = DateTime.Now }; - + // todo:推送只做新增的,更新和删除的不做先 if (!@event.IsPushCompleted && await _trackPushCacheService.IsPushAsync(@event.CustomId)) { @@ -99,7 +101,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } await _trackDetailsRepository.UpsertAsync(track); - + // 触发订单主表更新事件 await _capPublisher.PublishAsync(GlobalConst.UpdateOrderTrackSubscribe, new UpdateOrderTrackEvent { diff --git a/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs b/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs index 0f5fa583..007e06d0 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs @@ -16,11 +16,11 @@ public class OrderService : ApplicationService private readonly ITrackDetailsRepository _trackDetailsRepository; public OrderService(IOrderRepository orderRepository, - ITrackDetailsRepository trackDetailsRepository - ) + ITrackDetailsRepository trackDetailsRepository + ) { _orderRepository = orderRepository; - this._trackDetailsRepository=trackDetailsRepository; + _trackDetailsRepository = trackDetailsRepository; } /// diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs index dacb87fe..9fdbb37e 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs @@ -1,7 +1,8 @@ -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.Application.EventHandlers; using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Queries; namespace SJZY.Track.Application.Services.ProvideServices; @@ -12,12 +13,14 @@ namespace SJZY.Track.Application.Services.ProvideServices; public class TrackService : ApplicationService { private readonly IOrderRepository _orderRepository; - private readonly TrackUpsertEventHandler _trackUpsertEventHandler; + private readonly IProvideQueries _provideQueries; + private readonly ICapPublisher _capPublisher; - public TrackService(IOrderRepository orderRepository, TrackUpsertEventHandler trackUpsertEventHandler) + public TrackService(IOrderRepository orderRepository, IProvideQueries provideQueries, ICapPublisher capPublisher) { _orderRepository = orderRepository; - _trackUpsertEventHandler = trackUpsertEventHandler; + _provideQueries = provideQueries; + _capPublisher = capPublisher; } /// @@ -45,7 +48,45 @@ public class TrackService : ApplicationService @event.SubSystemId = order.SubSystemId; @event.IsPushCompleted = order.IsPushCompleted; @event.CustomId = order.CustomId; - await _trackUpsertEventHandler.UpsertTrackAsync(@event); + await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, @event); + return true; + } + + /// + /// 删除轨迹 + /// + /// + /// + /// + [HttpPost("Track/Delete")] + public async Task DeleteTrackAsync(DeleteTrackInput input) + { + var order = await _orderRepository.GetFirstAsync(x => + x.OrderType == input.OrderType && x.OrderNo == input.OrderNo, x => new + { + x.Id, x.SubSystemId, x.IsPushCompleted, x.CustomId + }); + if (order == default) + { + throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); + } + + var codes = await _provideQueries.GetCanDeleteTrackCodesAsync(order.Id, input.TrackCodes); + + if (codes.Count == 0) + { + throw new BusinessException(message: "没有一个轨迹代码是可删的"); + } + + // 删除轨迹 + var @event = new TrackDeleteEvent + { + OrderId = order.Id, + TrackCodes = codes, + SubSystemId = order.SubSystemId, + }; + await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, @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 3ab152d1..185f0874 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsRepository.cs @@ -4,7 +4,7 @@ namespace SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; public interface ITrackDetailsRepository { - Task GetFirstOrDefaultAsync(int orderId, string inputTrackCode); + Task GetFirstOrDefaultAsync(int orderId, string trackCode); Task UpsertAsync(TrackDetailsIndex index); @@ -12,4 +12,12 @@ public interface ITrackDetailsRepository Task<(List list, long total)> GetListAsync(TrackDetailsQueryInput input); Task> GetListAsync(int orderId); + + /// + /// 删除轨迹 + /// + /// + /// + /// + Task DeleteTrackAsync(int orderId, List trackCodes); } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackDetails/MatchTrackContentsDto.cs b/src/SJZY.Track.Domain/Dtos/TrackDetails/MatchTrackContentsDto.cs new file mode 100644 index 00000000..14441bc6 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/TrackDetails/MatchTrackContentsDto.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Dtos.TrackDetails; + +public class MatchTrackContentsDto +{ + /// + /// 轨迹代码 + /// + public string TrackCode { get; set; } = null!; + + /// + /// 是否支持删除 + /// + public bool IsCanDelete { 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 023db1aa..92545ea9 100644 --- a/src/SJZY.Track.Domain/GlobalConst.cs +++ b/src/SJZY.Track.Domain/GlobalConst.cs @@ -11,6 +11,11 @@ public class GlobalConst /// 轨迹新增/更新 mq订阅名称 /// public const string TrackUpsertSubscribe = "SJZY.Track.TrackUpsert"; + + /// + /// 轨迹删除 mq订阅名称 + /// + public const string TrackDeleteSubscribe = "SJZY.Track.TrackDelete"; /// /// 接口通批量轨迹刷新订阅 diff --git a/src/SJZY.Track.Domain/Queries/IProvideQueries.cs b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs index 7814e949..9c17d1d4 100644 --- a/src/SJZY.Track.Domain/Queries/IProvideQueries.cs +++ b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs @@ -32,15 +32,23 @@ public interface IProvideQueries /// 轨迹代码 /// Task MatchTrackContentAsync(int orderId, string code); + + /// + /// 获取可删除的订单轨迹代码 + /// + /// + /// + /// + Task> GetCanDeleteTrackCodesAsync(int orderId, List trackCodes); /// /// 根据子系统内容映射,得到映射后的轨迹code /// /// /// - /// + /// /// - Task MapTrackContentCodeAsync(int upSubSystemId, int subSystemId, string trackCode); + Task> MapTrackCodesAsync(int upSubSystemId, int subSystemId, params string[] trackCodes); /// /// 获取服务商模板配置 diff --git a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs index 0cb54b99..064f9445 100644 --- a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs +++ b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs @@ -70,7 +70,7 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, .LeftJoin((x, a, b, c) => b.TrackStatusId == c.Id) .LeftJoin((x, a, b, c, d) => b.TrackFlagId == d.Id) .Where((x, a, b) => x.OrderId == orderId && b.Code == code) - .Select((x, a, b, c,d) => new MatchTrackContentDto + .Select((x, a, b, c, d) => new MatchTrackContentDto { Content = b.Content, EnContent = b.EnContent, @@ -80,13 +80,24 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, .FirstAsync(); } - public async Task MapTrackContentCodeAsync(int upSubSystemId, int subSystemId, string trackCode) + public async Task> GetCanDeleteTrackCodesAsync(int orderId, 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 + && trackCodes.Contains(b.Code)) + .Select((x, a, b) => b.Code) + .ToListAsync(); + } + + public async Task> MapTrackCodesAsync(int upSubSystemId, int subSystemId, params string[] trackCodes) { return await Context.Queryable() .Where(x => x.SubSystemId == upSubSystemId && x.MapSubSystemId == subSystemId && - x.TrackContentCode == trackCode) + trackCodes.Contains(x.TrackContentCode)) .Select(x => x.MapTrackContentCode) - .FirstAsync(); + .ToListAsync(); } public async Task> GetPullContentConfigsAsync(int serviceProviderId) diff --git a/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs index c0ca92e4..590cb749 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs @@ -22,6 +22,7 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract q.Term(t => t.Field(f => f.OrderId).Value(orderId)) && q.Term(t => t.Field(f => f.TrackCode).Value(trackCode)) + && q.Term(t => t.Field(f => f.IsDeleted).Value(true)) ) ); @@ -39,7 +40,7 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract t .Field(f => f.OrderNo) .Terms(orderNos)) - && q.Term(f => f.OrderType, orderType)) + && q.Term(f => f.OrderType, orderType) + && q.Term(t => t.Field(f => f.IsDeleted).Value(true)) + ) ); return data.Documents.ToList(); @@ -71,18 +74,34 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract> GetListAsync(int orderId) { var data = await Context.SearchAsync(x => x - .Source(s => s.Includes(fd => fd - .Field(f => f.TrackStatus) - .Field(f => f.TrackCode) - .Field(f => f.TrackContent) - .Field(f => f.TrackPlace) - .Field(f => f.TrackTime) - .Field(f => f.TraceTimeZone) - )).Query(q => q.Terms(t => t.Field(f => f.OrderId).Terms(orderId)))); + .Source(s => s.Includes(fd => fd + .Field(f => f.TrackStatus) + .Field(f => f.TrackCode) + .Field(f => f.TrackContent) + .Field(f => f.TrackPlace) + .Field(f => f.TrackTime) + .Field(f => f.TraceTimeZone) + )).Query(q => + q.Terms(t => t.Field(f => f.OrderId).Terms(orderId)) + && q.Term(t => t.Field(f => f.IsDeleted).Value(true)) + ) + ); return data?.Documents?.ToList(); } + public async Task DeleteTrackAsync(int orderId, List trackCodes) + { + await Context.UpdateByQueryAsync(x => x + .Query( + q => + q.Term(t => t.OrderId, orderId) + && q.Terms(t => t.Field(f => f.TrackCode).Terms(trackCodes))) + .Script(s => s + .Source("ctx._source.IsDeleted = true")) + ); + } + public async Task<(List list, long total)> GetListAsync(TrackDetailsQueryInput input) { if (input.OrderNos.Count < 1) return default; @@ -91,9 +110,12 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract 500 ? 500 : input.PageSize; var data = await Context.SearchAsync(x => x - .Query(q => q.Terms(t => t - .Field(f => f.OrderNo) - .Terms(distinctOrderNumbers))) + .Query(q => + q.Terms(t => t + .Field(f => f.OrderNo) + .Terms(distinctOrderNumbers)) + && q.Term(t => t.Field(f => f.IsDeleted).Value(true)) + ) .Sort(s => s.Field(f => f.CreateTime, SortOrder.Descending)) .From((input.PageIndex - 1) * input.PageSize) .Size(finalSize)