diff --git a/src/SJZY.Track.Application.Contract/Services/IBroadcastingService.cs b/src/SJZY.Track.Application.Contract/Services/IBroadcastingService.cs index 4a1cb510..a3d1091e 100644 --- a/src/SJZY.Track.Application.Contract/Services/IBroadcastingService.cs +++ b/src/SJZY.Track.Application.Contract/Services/IBroadcastingService.cs @@ -22,6 +22,14 @@ public interface IBroadcastingService : ITransientDependency /// void TrackChangePublicTopicAsync(TrackDeleteEvent eto, OrderIdentifier[] orderIdentifiers, string trackCode, string trackStatusCode, string subSystemCode); + /// + /// 轨迹变更广播(删除) + /// + /// + /// + /// + void TrackChangePublicTopicAsync(TrackChangeEto eto, string subSystemCode); + /// /// 轨迹推送完成广播(推送完成回调) /// diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs index 7e798eba..1265a4d9 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs @@ -1,9 +1,12 @@ +using System.Collections.Immutable; +using System.Text.Json; using DotNetCore.CAP; using SJZY.Expand.ABP.Core.Logger; using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Track.Application.Contract.Dtos.ProvideServices; using SJZY.Track.Application.Contract.Events; using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Application.Helpers; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Enums; @@ -118,7 +121,7 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe return true; } - private async Task PublicTrackTopicAsync(TrackDeleteEvent eto, List trackList) + private async Task PublicTrackTopicAsync(TrackDeleteEvent @event, List trackList) { var systemIds = trackList.Select(x => x.SubSystemId).Distinct().ToArray(); var trackCodes = trackList.Select(x => x.TrackCode).Distinct().ToArray(); @@ -144,21 +147,63 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe var item = dataItem.First(); var content = contentList.First(m => m.TrackContentCode == item.TrackCode); - _broadcastingService.TrackChangePublicTopicAsync(eto, - dataItem.Select(m => new OrderIdentifier + // _broadcastingService.TrackChangePublicTopicAsync(track, + // dataItem.Select(m => new OrderIdentifier + // { + // OrderId = m.OrderId, + // OrderNo = m.OrderNo, + // OrderType = m.OrderType + // }).ToArray(), + // item.TrackCode, + // item.TrackStatusCode, + // content.SubSystemCode + // ); + + //将每个订单的每个轨迹的删除变更发布 + foreach (var track in dataItem) + { + _broadcastingService.TrackChangePublicTopicAsync(new TrackChangeEto { - OrderId = m.OrderId, - OrderNo = m.OrderNo, - OrderType = m.OrderType - }).ToArray(), - item.TrackCode, - item.TrackStatusCode, - content.SubSystemCode - ); + EstimatedDeliveryDate = track.EstimatedDeliveryDate, + TrackPlacePostcode = track.TrackPlacePostcode, + TrackPlaceNation = track.TrackPlaceNation, + TrackPlaceNationId = track.TrackPlaceNationId, + TrackPlaceProvince = track.TrackPlaceProvince, + TrackPlaceProvinceId = track.TrackPlaceProvinceId, + TrackPlaceCity = track.TrackPlaceCity, + TrackPlaceCityId = track.TrackPlaceCityId, + Files = track.Files?.Select(x => new FileItemDto() + { + FileName = x.FileName, + OssKey = x.OssKey + }).ToList(), + Remarks = track.Remarks, + Source = track.Source, + IsVisibility = track.IsVisibility, + TrackTimeZone = track.TrackTimeZone, + TrackTime = track.TrackTime, + TrackPlace = track.TrackPlace, + TrackStatusCode = track.TrackStatusCode, + EnStatus = track.TrackEnContent, + Content = track.TrackContent, + TrackCode = track.TrackCode, + OperationMan = @event.OperationMan, + OrderIdentifiers = new[] + { + new OrderIdentifier() + { + OrderId = track.OrderId, + OrderNo = track.OrderNo, + OrderType = track.OrderType + } + }, + SubSystemId = @event.SubSystemId, + Extra = track.Extra + }, content.SubSystemCode); + } } } - // todo:多个相同code的轨迹删除,也需要考虑?? private async Task> DeleteTrackAsync(TrackDeleteEvent @event) { List orderIds; @@ -196,10 +241,40 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe //根据订单查询出所有的轨迹,查所有是因为主单上需要进行回退更新 tracks = await _trackDetailsEsRepository.GetListAsync(@event.OrderIds); - //构建标识符集合 - var ids = @event.TrackIdentifiers.Select(x => $"{x.TrackCode}{x.TrackTime:G}").ToList(); - //根据标识符选出要删除的轨迹明细 - deleteTracks = tracks.Where(x => ids.Contains($"{x.TrackCode}{x.TrackTime:G}")).ToList(); + //比较字段为空则使用时间来作为额外的标识key + if (string.IsNullOrWhiteSpace(@event.CompareFields)) + { + //构建标识符集合 + var ids = @event.TrackIdentifiers.Select(x => $"{x.TrackCode}{x.TrackTime:G}").ToHashSet(); + //根据标识符选出要删除的轨迹明细 + deleteTracks = tracks.Where(x => ids.Contains($"{x.TrackCode}{x.TrackTime:G}")).ToList(); + } + //否则则使用比较字段,使用扩展字段中的内容进行比较 + else + { + var codes = @event.TrackIdentifiers.Select(x => x.TrackCode).ToImmutableHashSet(); + deleteTracks = tracks + .Where(x => + { + if (!codes.Contains(x.TrackCode)) + return false; + + if (string.IsNullOrWhiteSpace(x.Extra)) + { + throw new BusinessException(message: $"当前轨迹不存在扩展信息,无法利用扩展字段:{@event.CompareFields}"); + } + + var extraDict = GetExtraDictForTextJson(x.Extra); + if (extraDict == null || !extraDict.TryGetValue(@event.CompareFields, out var compareValue)) + { + throw new BusinessException(message: $"请检查拓展字段,为什么找不到比较字段:{@event.CompareFields}"); + } + + return TextJsonHelper.EqJsonElement(compareValue, @event.CompareValue); + }) + .ToList(); + } + if (deleteTracks.Count == 0) return deleteTracks; @@ -256,21 +331,9 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe order.EstimatedDeliveryDate = track.EstimatedDeliveryDate; order.DestinationTimeZone = track.DestinationTimeZone; } - - - // 写入操作日志 - _ = _orderOperationRecordEsRepository.AddAsync(new OrderOperationRecordIndex - { - OrderId = order.Id, - OrderNo = order.OrderNo, - OptionType = OrderOperationType.删除轨迹.ToString(), - Content = deleteTracks.Where(m => m.OrderId == order.Id).ToList().ToJson(), - CreateUserId = @event.OperationManId, - CreateUserName = @event.OperationMan ?? "", - CreateTime = DateTime.Now - }); } + //更新订单上冗余轨迹明细的字段 await _orderRepository.UpdateRangeAsync(orders, x => new { @@ -283,6 +346,83 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe x.DestinationTimeZone }); + //批量写入订单操作日志 + _ = _orderOperationRecordEsRepository.AddManyAsync(orders.Select(order => new OrderOperationRecordIndex + { + OrderId = order.Id, + OrderNo = order.OrderNo, + OptionType = OrderOperationType.删除轨迹.ToString(), + Content = deleteTracks.Where(m => m.OrderId == order.Id).ToList().ToJson(), + CreateUserId = @event.OperationManId, + CreateUserName = @event.OperationMan ?? "", + CreateTime = DateTime.Now + }).ToList()); + return deleteTracks; } + + private Dictionary? GetExtraDictForTextJson(string? extraStr) + { + return string.IsNullOrWhiteSpace(extraStr) + ? null + : System.Text.Json.JsonSerializer.Deserialize>(extraStr); + } + + //json Element和object的比较 + private bool EqJsonElementObject2(object? a, object? b) + { + if (a == null && b == null) + { + return true; + } + + if (a is JsonElement aJsonElement) + { + switch (aJsonElement.ValueKind) + { + case JsonValueKind.Undefined: + return false; + case JsonValueKind.Object: + return false; + case JsonValueKind.Array: + return false; + case JsonValueKind.String: + return string.Equals(aJsonElement.GetString(), b as string); + case JsonValueKind.Number: + { + if (b == null) return false; + + if (b is int bInt) + { + var aInt = aJsonElement.GetInt32(); + return aInt == bInt; + } + + if (b is long bLong) + { + var aLong = aJsonElement.GetInt64(); + return aLong == bLong; + } + + return false; + } + case JsonValueKind.True: + case JsonValueKind.False: + { + if (b is bool bl) + { + return aJsonElement.GetBoolean() == bl; + } + + return false; + } + case JsonValueKind.Null: + { + return false; + } + } + } + + return false; + } } diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs index d695a433..5aa98422 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs @@ -123,6 +123,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe TrackPushFailedPublicTopic($"推送顺序异常,下一个要推送的应为[{nextCode}]", @event, order); continue; } + config = resultConfig; // 是否自动推送 @@ -316,7 +317,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe { "descriptionCn", config.TrackContentValue }, { "code", config.TrackContentCodeValue }, { "status", config.TrackStatusValue }, - { "extra", DeserializeForTextJson(log.Extra) } + { "extra", TextJsonHelper.Deserialize(log.Extra) } }; body.Data.Add(dict); log.PushContent = body.ToJson(); @@ -344,7 +345,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe { "descriptionCn", config.TrackContentValue }, { "code", config.TrackContentCodeValue }, { "status", config.TrackStatusValue }, - { "extra", DeserializeForTextJson(log.Extra) } + { "extra", TextJsonHelper.Deserialize(log.Extra) } }; body.Data.Add(cur); @@ -353,7 +354,9 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe var history = await _trackPushRecordEsRepository.GetLatestTrackPushRecordAsync(log.OrderId); if (history != default) { - body = DeserializeForTextJson(history.PushContent); + body = TextJsonHelper.Deserialize(history.PushContent); + + //在历史选出推送成功的历史轨迹 if (config.IsAllPushWithLatest) { //同code同比较字段保证唯一 @@ -367,22 +370,22 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe }).WhereIf(!compareFields.IsNullOrWhiteSpace(), x => { if (x["extra"] == null || log.Extra == null) return false; - return EqExtraByFieldForTextJson(x["extra"]!, DeserializeForTextJson(log.Extra), compareFields!); + return TextJsonHelper.EqByJsonElementField(x["extra"]!, TextJsonHelper.Deserialize(log.Extra), compareFields!); }) .FirstOrDefault(); - if (old != null) { body.Data.Remove(old); } + //根据code和比较字段值分组,在每组里选出一个创建时间最新的 } body!.Data.Add(cur); } } - log.PushContent = SerializeForTextJson(body); + log.PushContent = TextJsonHelper.Serialize(body)!; return body; } @@ -431,31 +434,6 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe return dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffK"); } - private static string SerializeForTextJson(object? obj) - { - return obj == null ? string.Empty : System.Text.Json.JsonSerializer.Serialize(obj); - } - - private static T? DeserializeForTextJson(string? json) - { - return string.IsNullOrWhiteSpace(json) ? default : System.Text.Json.JsonSerializer.Deserialize(json); - } - - //比较扩展中的某个字段的相等性 - private bool EqExtraByFieldForTextJson(object a, object b, string fieldName) - { - if (a is not JsonElement aJsonElement || b is not JsonElement bJsonElement) - return false; - - if (aJsonElement.TryGetProperty(fieldName, out var aValue) - && bJsonElement.TryGetProperty(fieldName, out var bValue)) - { - return aValue.ToString() == bValue.ToString(); - } - - return false; - } - //轨迹推送失败(被拦截) private void TrackPushFailedPublicTopic( string failedMessage, @@ -503,7 +481,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe PushTime = DateTime.Now, OptionType = @event.OptionType.ToString(), IsManualPush = @event.OperationMan != null, - PushContent = SerializeForTextJson(pushInput), + PushContent = TextJsonHelper.Serialize(pushInput), OrderNo = pushInput.OrderNo, TrackStatus = @event.SystemPushInfo.TrackStatus, TrackContent = @event.SystemPushInfo.TrackStatus, diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs index dc8a327f..79f29ff8 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs @@ -1,11 +1,11 @@ using System.Diagnostics; -using System.Text.Json; using DotNetCore.CAP; using Microsoft.Extensions.Logging; using SJZY.Expand.ABP.Core.Logger; using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Track.Application.Contract.Events; using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Application.Helpers; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Dtos.TrackContents; @@ -290,7 +290,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe return false; } - return EqJsonElementObject(oldValue, compareValue); + return TextJsonHelper.EqJsonElement(oldValue, compareValue); }).ToList(); } @@ -437,16 +437,6 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe : System.Text.Json.JsonSerializer.Deserialize>(extraStr); } - private bool EqJsonElementObject(object? a, object? b) - { - if (a is JsonElement aJsonElement && b is JsonElement bJsonElement) - { - return aJsonElement.ToString() == bJsonElement.ToString(); - } - - return false; - } - private OrderOperationRecordIndex BuildOrderOperationRecord(TrackUpsertEvent @event, TrackDetailsIndex track, OrderIdentifier order) { return new OrderOperationRecordIndex diff --git a/src/SJZY.Track.Application/Helpers/TextJsonHelper.cs b/src/SJZY.Track.Application/Helpers/TextJsonHelper.cs new file mode 100644 index 00000000..387a9032 --- /dev/null +++ b/src/SJZY.Track.Application/Helpers/TextJsonHelper.cs @@ -0,0 +1,125 @@ +using System.Text.Json; + +namespace SJZY.Track.Application.Helpers; + +/// +/// 针对 Text.Json 的一些辅助方法 +/// +public static class TextJsonHelper +{ + public static string? Serialize(object? obj) + { + return obj == null ? default : System.Text.Json.JsonSerializer.Serialize(obj); + } + + public static T? Deserialize(string? json) + { + return string.IsNullOrWhiteSpace(json) ? default : System.Text.Json.JsonSerializer.Deserialize(json); + } + + /// + /// 两个jsonElement进行比较 + /// + /// + /// + /// + public static bool EqJsonElement(object? a, object? b) + { + if (a == null && b == null) + { + return true; + } + + if (a is JsonElement aJsonElement && b is JsonElement bJsonElement) + { + return aJsonElement.ToString() == bJsonElement.ToString(); + } + + return false; + } + + /// + /// jsonElement和object之间的比较 + /// + /// + /// + /// + public static bool EqJsonElementAndObject(object? jsonElement, object? obj) + { + if (jsonElement == null && obj == null) + { + return true; + } + + if (jsonElement is JsonElement aJsonElement) + { + switch (aJsonElement.ValueKind) + { + case JsonValueKind.Undefined: + return false; + case JsonValueKind.Object: + return false; + case JsonValueKind.Array: + return false; + case JsonValueKind.String: + return string.Equals(aJsonElement.GetString(), obj as string); + case JsonValueKind.Number: + { + if (obj == null) return false; + + if (obj is int bInt) + { + var aInt = aJsonElement.GetInt32(); + return aInt == bInt; + } + + if (obj is long bLong) + { + var aLong = aJsonElement.GetInt64(); + return aLong == bLong; + } + + return false; + } + case JsonValueKind.True: + case JsonValueKind.False: + { + if (obj is bool bl) + { + return aJsonElement.GetBoolean() == bl; + } + + return false; + } + case JsonValueKind.Null: + { + return false; + } + } + } + + return false; + } + + + /// + /// 比较两个jsonElement中的的fieldName字段的值 + /// + /// + /// + /// + /// + public static bool EqByJsonElementField(object a, object b, string fieldName) + { + if (a is not JsonElement aJsonElement || b is not JsonElement bJsonElement) + return false; + + if (aJsonElement.TryGetProperty(fieldName, out var aValue) + && bJsonElement.TryGetProperty(fieldName, out var bValue)) + { + return aValue.ToString() == bValue.ToString(); + } + + return false; + } +} diff --git a/src/SJZY.Track.Application/Services/Broadcasting/BroadcastingService.cs b/src/SJZY.Track.Application/Services/Broadcasting/BroadcastingService.cs index b0415664..4add29c6 100644 --- a/src/SJZY.Track.Application/Services/Broadcasting/BroadcastingService.cs +++ b/src/SJZY.Track.Application/Services/Broadcasting/BroadcastingService.cs @@ -81,6 +81,12 @@ public class BroadcastingService : IBroadcastingService Encoding.UTF8.GetBytes(data.ToJson())); } + public void TrackChangePublicTopicAsync(TrackChangeEto eto, string subSystemCode) + { + _rabbitClientBus.Publish(GlobalConst.TrackChangeExchange, $"TrackChange.{subSystemCode}", + Encoding.UTF8.GetBytes(eto.ToJson())); + } + /// /// 轨迹推送完成广播 /// diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs index 95f496ed..ec3289a3 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs @@ -5,6 +5,7 @@ using SJZY.Track.Application.Contract.Dtos.TrackOrder; using SJZY.Track.Application.Contract.Dtos.TrackPush; using SJZY.Track.Application.Contract.Events; using SJZY.Track.Application.Contract.Services.ProvideServices; +using SJZY.Track.Application.Helpers; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Aggregates.TrackPushAggregate; @@ -92,6 +93,28 @@ public class TrackPushService : ApplicationService, ITrackPushService throw new BusinessException(message: "没有需要推送的轨迹"); } + //存在比较字段,需要做过滤处理 + if (!string.IsNullOrWhiteSpace(input.CompareFields)) + { + //根据输入值来进行过滤,比如选取code1,批次1的轨迹,可能会有多条,下面使用创建时间最新的一条 + tracks = tracks.Where(x => + { + if (string.IsNullOrWhiteSpace(x.Extra)) + { + throw new BusinessException(message: $"当前轨迹不存在扩展信息,无法利用扩展字段:{input.CompareFields}"); + } + + var extraDict = GetExtraDictForTextJson(x.Extra); + if (extraDict == null || !extraDict.TryGetValue(input.CompareFields, out var compareValue)) + { + throw new BusinessException(message: $"请检查拓展字段,为什么找不到比较字段:{input.CompareFields}"); + } + + //补充轨迹内容 + return TextJsonHelper.EqJsonElementAndObject(compareValue, input.CompareValue); + }).ToList(); + } + //不同单可能会有不同的轨迹内容,先推送只推送最新的一条 foreach (var order in orders) { @@ -117,7 +140,7 @@ public class TrackPushService : ApplicationService, ITrackPushService TrackPlace = track.TrackPlace, TrackTime = track.TrackTime, TrackTimeZone = track.TrackTimeZone, - // todo: 这里也要改,重复code问题,到底推哪个 + CompareFields = input.CompareFields, Extra = track.Extra } }; @@ -186,4 +209,11 @@ public class TrackPushService : ApplicationService, ITrackPushService var res = ObjectMapper.Map, List>(records); return new UnifyResultDto>(true, res, "操作成功"); } + + private Dictionary? GetExtraDictForTextJson(string? extraStr) + { + return string.IsNullOrWhiteSpace(extraStr) + ? null + : System.Text.Json.JsonSerializer.Deserialize>(extraStr); + } }