From c44c4e3af2d60e6b55ab6d47f4165f992e70b47f Mon Sep 17 00:00:00 2001 From: hardstylewyl <827778732@qq.com> Date: Wed, 5 Mar 2025 10:46:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=A2=84=E8=AD=A6=E8=BF=AD=E4=BB=A3?= =?UTF-8?q?=E5=88=9D=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Complete/CompleteWarningDetailsDto.cs | 158 ++-- .../Complete/CompleteWarningRuleItemDto.cs | 3 +- .../EditCompleteWarningRuleItemInput.cs | 72 +- .../DataAnnotations/IdRequiredAttribute.cs | 10 +- .../Dtos/Warning/NotifyUserDto.cs | 20 +- .../Return/EditReturnWarningRuleItemInput.cs | 56 +- .../Warning/Return/ReturnWarningDetailsDto.cs | 17 +- .../EditTransportWarningSceneRuleItemInput.cs | 86 +-- .../GetTransportWarningSceneInput.cs | 10 +- .../GetTransportWarningSceneRuleItemInput.cs | 20 +- .../Transport/TransportWarningDetailsDto.cs | 220 +++--- .../Transport/TransportWarningSceneDto.cs | 2 +- .../TransportWarningSceneRuleItemDto.cs | 4 +- .../UpsertTransportWarningSceneInput.cs | 3 +- .../Dtos/Warning/UpsertWarningRuleInput.cs | 58 +- .../Dtos/Warning/WarningRuleDto.cs | 12 +- .../AutoMapper/WarningMapperProfile.cs | 17 + .../EventHandlers/TrackDeleteEventHandler.cs | 1 + .../EventHandlers/TrackPushEventHandler.cs | 1 + .../EventHandlers/TrackUpsertEventHandler.cs | 1 + .../ProvideServices/TrackPushService.cs | 1 + .../Services/ProvideServices/TrackService.cs | 1 + .../Services/TrackOrders/TrackOrderService.cs | 2 +- .../Services/TrackPush/TrackPushService.cs | 2 +- .../Warning/CompleteWarningService.cs | 213 +++++- .../Handlers/CompleteWarningHandler.cs | 170 +++++ .../Model/CompleteWarningRuleItemBO.cs | 91 +++ .../Handlers/Model/ReturnWarningRuleItemBO.cs | 81 ++ .../Handlers/Model/TransportWarningSceneBO.cs | 161 ++++ .../Model/TransportWarningSceneRuleItemBO.cs | 123 +++ .../Warning/Handlers/ReturnWarningHandler.cs | 162 ++++ .../Handlers/TransportWarningHandler.cs | 418 +++++++++++ .../Warning/ICompleteWarningHandler.cs | 24 + .../Handlers/Warning/IReturnWarningHandler.cs | 28 + .../Warning/ITransportWarningHandler.cs | 31 + .../Services/Warning/ReturnWarningService.cs | 205 ++++- .../Warning/TransportWarningService.cs | 318 ++++++-- .../Services/Warning/WarningService.cs | 74 +- .../OrderAggregate/IOrderRepository.cs | 2 +- .../ITrackDetailsEsRepository.cs | 3 +- .../Complete/CompleteWarningDetailsIndex.cs | 45 ++ .../Complete/CompleteWarningRuleItemEntity.cs | 84 +++ .../IWarningDetailsEsRepository.cs | 85 +++ .../IWarningRuleRepository.cs | 92 +++ .../IWarningTaskRepository.cs | 76 ++ .../Return/ReturnMessageRecordIndex.cs | 17 + .../Return/ReturnWarningDetailsIndex.cs | 82 ++ .../ReturnWarningRetryPushTaskEntity.cs | 41 + .../Return/ReturnWarningRuleItemEntity.cs | 47 ++ .../Signals/IWarningSignal.cs | 16 + .../Signals/TrackAddedSignal.cs | 13 + .../Signals/TrackDeletedSignal.cs | 15 + .../Signals/TrackUpdatedSignal.cs | 16 + ...ansportWarningDetailsGenerateTaskEntity.cs | 44 ++ .../Transport/TransportWarningDetailsIndex.cs | 144 ++++ .../Transport/TransportWarningSceneEntity.cs | 48 ++ .../TransportWarningSceneRuleItemEntity.cs | 62 ++ .../WarningNotifyTaskEntity.cs | 56 ++ .../WarningAggregate/WarningRuleEntity.cs | 66 ++ .../GetPageCompleteWarningDetailsInputVO.cs | 60 ++ .../GetPageReturnWarningDetailsInputVO.cs | 93 +++ .../GetPageTransportWarningDetailsInputVO.cs | 52 ++ .../Enums/CompleteWarningStatus.cs | 8 + .../Enums/ReturnWarningStatus.cs | 9 + .../Enums/TransportWarningStatus.cs | 10 + .../Extensions/CollectionExtensions.cs | 120 +++ .../Extensions/PollyHelper.cs | 34 + .../Extensions}/TextJsonHelper.cs | 10 +- .../Extensions/VersionGenerate.cs | 15 + .../Queries/IVExternalSubjectQueries.cs | 7 + .../VEntities/VExternalSubjectEntity.cs | 2 +- .../Queries/VExternalSubjectQueries.cs | 8 + .../Repositories/OrderRepository.cs | 2 +- .../Repositories/TrackDetailsEsRepository.cs | 38 + .../WarningDetailsEsRepository.cs | 707 ++++++++++++++++++ .../Repositories/WarningRuleRepository.cs | 217 ++++++ .../Repositories/WarningTaskRepository.cs | 159 ++++ 77 files changed, 4984 insertions(+), 497 deletions(-) create mode 100644 src/SJZY.Track.Application/AutoMapper/WarningMapperProfile.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/CompleteWarningHandler.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/Model/CompleteWarningRuleItemBO.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/Model/ReturnWarningRuleItemBO.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneBO.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneRuleItemBO.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/ReturnWarningHandler.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/TransportWarningHandler.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ICompleteWarningHandler.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/Warning/IReturnWarningHandler.cs create mode 100644 src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ITransportWarningHandler.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningDetailsIndex.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningRuleItemEntity.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningDetailsEsRepository.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnMessageRecordIndex.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningDetailsIndex.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRetryPushTaskEntity.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRuleItemEntity.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/IWarningSignal.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackAddedSignal.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackDeletedSignal.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackUpdatedSignal.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsGenerateTaskEntity.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningNotifyTaskEntity.cs create mode 100644 src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs create mode 100644 src/SJZY.Track.Domain/Dtos/Warning/GetPageCompleteWarningDetailsInputVO.cs create mode 100644 src/SJZY.Track.Domain/Dtos/Warning/GetPageReturnWarningDetailsInputVO.cs create mode 100644 src/SJZY.Track.Domain/Dtos/Warning/GetPageTransportWarningDetailsInputVO.cs create mode 100644 src/SJZY.Track.Domain/Enums/CompleteWarningStatus.cs create mode 100644 src/SJZY.Track.Domain/Enums/ReturnWarningStatus.cs create mode 100644 src/SJZY.Track.Domain/Enums/TransportWarningStatus.cs create mode 100644 src/SJZY.Track.Domain/Extensions/CollectionExtensions.cs create mode 100644 src/SJZY.Track.Domain/Extensions/PollyHelper.cs rename src/{SJZY.Track.Application/Helpers => SJZY.Track.Domain/Extensions}/TextJsonHelper.cs (94%) create mode 100644 src/SJZY.Track.Domain/Extensions/VersionGenerate.cs create mode 100644 src/SJZY.Track.Domain/Queries/IVExternalSubjectQueries.cs create mode 100644 src/SJZY.Track.Repository/Queries/VExternalSubjectQueries.cs create mode 100644 src/SJZY.Track.Repository/Repositories/WarningDetailsEsRepository.cs create mode 100644 src/SJZY.Track.Repository/Repositories/WarningRuleRepository.cs create mode 100644 src/SJZY.Track.Repository/Repositories/WarningTaskRepository.cs diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningDetailsDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningDetailsDto.cs index 0bd2961d..b2c0d5be 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningDetailsDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningDetailsDto.cs @@ -3,104 +3,104 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Complete; /// /// 完整预警 /// -public class CompleteWarningDetailsDto // todo:需要根据状态排序 +public class CompleteWarningDetailsDto { - /// - /// 预警明细唯一id - /// - public string EsId { get; set; } + /// + /// 预警明细唯一id + /// + public string EsId { get; set; } - /// - /// 子系统id - /// - public int SubSystemId { get; set; } + /// + /// 子系统id + /// + public int SubSystemId { get; set; } - /// - /// 子系统名称 - /// - public string SubSystemName { get; set; } // todo:id换名称 + /// + /// 子系统名称 + /// + public string SubSystemName { get; set; } // todo:id换名称 - /// - /// 订单类型 - /// - public int OrderType { get; set; } + /// + /// 订单类型 + /// + public int OrderType { get; set; } - /// - /// 订单类型名称 - /// - public string OrderTypeName { get; set; } // todo:type换名称 + /// + /// 订单类型名称 + /// + public string OrderTypeName { get; set; } // todo:type换名称 - /// - /// 订单id - /// - public int OrderId { get; set; } + /// + /// 订单id + /// + public int OrderId { get; set; } - /// - /// 订单号 - /// - public string OrderNo { get; set; } + /// + /// 订单号 + /// + public string OrderNo { get; set; } - /// - /// 状态 (待处理样式标红) - /// 1:待处理,2:已处理,3:已关闭 - /// - public int Status { get; set; } + /// + /// 状态 (待处理样式标红) + /// 1:待处理,2:已处理,3:已关闭 + /// + public int Status { get; set; } - /// - /// 客户id - /// - public int CustomId { get; set; } + /// + /// 客户id + /// + public int CustomId { get; set; } - /// - /// 客户简称 - /// - public int CustomName { get; set; } // todo:id换名称 + /// + /// 客户简称 + /// + public string CustomName { get; set; } - /// - /// 缺失轨迹状态 - /// - public string MissingTrackStatus { get; set; } // todo:id换名称 + /// + /// 缺失轨迹状态 + /// + public string MissingTrackStatus { get; set; } // todo:id换名称 - //视图属性 todo:可以从订单主表拿到 + //视图属性 tips:从es即时拿取 - /// - /// 最新轨迹状态 - /// - public string LatestTrackStatus { get; set; } + /// + /// 最新轨迹状态 + /// + public string? LatestTrackStatus { get; set; } - /// - /// 最新轨迹内容 - /// - public string LatestTrackContent { get; set; } + /// + /// 最新轨迹内容 + /// + public string? LatestTrackContent { get; set; } - /// - /// 最新轨迹发生地 - /// - public string LatestTrackPlace { get; set; } + /// + /// 最新轨迹发生地 + /// + public string? LatestTrackPlace { get; set; } - /// - /// 最新轨迹发生时间 - /// - public DateTime LatestTrackTime { get; set; } + /// + /// 最新轨迹发生时间 + /// + public DateTime? LatestTrackTime { get; set; } - /// - /// 最新轨迹发生时区 - /// - public float LatestTrackTimeZone { get; set; } + /// + /// 最新轨迹发生时区 + /// + public float? LatestTrackTimeZone { get; set; } - /// - /// 通知对象 - /// - public string NotifyUserNames { get; set; } + /// + /// 通知对象 + /// + public string NotifyUserNames { get; set; } - /// - /// 预警创建时间 - /// - public DateTime CreateTime { get; set; } + /// + /// 预警创建时间 + /// + public DateTime CreateTime { get; set; } - /// - /// 预警处理时间 - /// - public DateTime? ProcessedTime { get; set; } + /// + /// 预警处理时间 + /// + public DateTime? ProcessedTime { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningRuleItemDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningRuleItemDto.cs index 2e4e0335..7de3d710 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningRuleItemDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/CompleteWarningRuleItemDto.cs @@ -1,4 +1,3 @@ -using SJZY.Track.Application.Contract.Dtos.Warning; using SJZY.Track.Application.Contract.Dtos.Warning.Return; namespace SJZY.Track.Application.Contract.Dtos.Warning.Complete; @@ -21,7 +20,7 @@ public class CompleteWarningRuleItemDto /// /// 订单类型 e.g FBA订单、海运单 /// - public List OrderType { get; set; } + public List OrderTypes { get; set; } /// /// 检查点轨迹状态id diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/EditCompleteWarningRuleItemInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/EditCompleteWarningRuleItemInput.cs index 80ab9829..a53c5b19 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/EditCompleteWarningRuleItemInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Complete/EditCompleteWarningRuleItemInput.cs @@ -8,16 +8,16 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Complete; /// public class EditCompleteWarningRuleItemInput { - /// - /// 预警规主体id - /// - [IdRequired] - public int WarningId { get; set; } + /// + /// 预警规主体id + /// + [IdRequired] + public int WarningId { get; set; } - /// - /// 预警规则配置项目(列表内容传递) - /// - public List Items { get; set; } = new(); + /// + /// 预警规则配置项目(列表内容传递) + /// + public List Items { get; set; } = new(); } /// @@ -25,35 +25,35 @@ public class EditCompleteWarningRuleItemInput /// public class EditCompleteWarningRuleItemDto { - /// - /// 规则项目唯一id(传0视为新增) - /// - public int Id { get; set; } + /// + /// 规则项目唯一id(传0视为新增) + /// + public int Id { get; set; } - /// - /// 订单类型集合(,分割) e.g. 1,2,3 - /// - [Required] - public string OrderTypes { get; set; } + /// + /// 订单类型集合(,分割) e.g. 1,2,3 + /// + [Required] + public string OrderTypes { get; set; } - /// - /// 检查点轨迹状态id - /// - [IdRequired] - public int CheckPointTrackStatusId { get; set; } + /// + /// 检查点轨迹状态id + /// + [IdRequired] + public int CheckPointTrackStatusId { get; set; } - /// - /// 要求的轨迹状态id集合 e.g. [[1,3],[5,7]] - /// required: 集合元素(子集合)不要有交集! - /// - [Required] - [MinLength(1, ErrorMessage = "要求的轨迹状态id集合不能为空")] - public List> RequireTrackStatus { get; set; } + /// + /// 要求的轨迹状态id集合 e.g. [[1,3],[5,7]] + /// required: 集合元素(子集合)不要有交集! + /// + [Required] + [MinLength(1, ErrorMessage = "要求的轨迹状态id集合不能为空")] + public List> RequireTrackStatus { get; set; } - /// - /// 通知对象用户集合 - /// - [Required] - [MinLength(1, ErrorMessage = "通知对象用户集合不能为空")] - public List NotifyUsers { get; set; } = new(); + /// + /// 通知对象用户集合 + /// + [Required] + [MinLength(1, ErrorMessage = "通知对象用户集合不能为空")] + public List NotifyUsers { get; set; } = new(); } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/DataAnnotations/IdRequiredAttribute.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/DataAnnotations/IdRequiredAttribute.cs index 199ba7e8..cd4767f8 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/DataAnnotations/IdRequiredAttribute.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/DataAnnotations/IdRequiredAttribute.cs @@ -8,9 +8,9 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.DataAnnotations; [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] public sealed class IdRequiredAttribute : RangeAttribute { - public IdRequiredAttribute() - : base(1, int.MaxValue) - { - ErrorMessage = "{0} is required and must be greater than 0"; - } + public IdRequiredAttribute() + : base(1, int.MaxValue) + { + ErrorMessage = "{0} is required and must be greater than 0"; + } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/NotifyUserDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/NotifyUserDto.cs index 55548ac7..ec7d43af 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/NotifyUserDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/NotifyUserDto.cs @@ -5,15 +5,15 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning; public sealed class NotifyUserDto { - /// - /// 用户id - /// - [IdRequired] - public int UserId { get; set; } + /// + /// 用户id + /// + [IdRequired] + public int UserId { get; set; } - /// - /// 用户名 - /// - [Required] - public string UserName { get; set; } + /// + /// 用户名 + /// + [Required] + public string UserName { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/EditReturnWarningRuleItemInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/EditReturnWarningRuleItemInput.cs index 16e34ed7..3af60ebe 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/EditReturnWarningRuleItemInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/EditReturnWarningRuleItemInput.cs @@ -8,16 +8,16 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Return; /// public class EditReturnWarningRuleItemInput { - /// - /// 预警规主体id - /// - [IdRequired] - public int WarningId { get; set; } + /// + /// 预警规主体id + /// + [IdRequired] + public int WarningId { get; set; } - /// - /// 预警规则配置项目(列表内容传递) - /// - public List Items { get; set; } = new(); + /// + /// 预警规则配置项目(列表内容传递) + /// + public List Items { get; set; } = new(); } /// @@ -25,26 +25,26 @@ public class EditReturnWarningRuleItemInput /// public class EditReturnWarningRuleItemDto { - /// - /// 唯一id(传0视为新增) - /// - public int Id { get; set; } + /// + /// 唯一id(传0视为新增) + /// + public int Id { get; set; } - /// - /// 轨迹状态Id集合(,分割) - /// - [Required] - public string TrackStatusIds { get; set; } + /// + /// 轨迹状态Id集合(,分割) + /// + [Required] + public string TrackStatusIds { get; set; } - /// - /// 时效(单位h) - /// - [Range(0.1, float.MaxValue, ErrorMessage = "时效必须大于0.1")] - public float ReturnTimeSpan { get; set; } + /// + /// 时效(单位h) + /// + [Range(0.1, float.MaxValue, ErrorMessage = "时效必须大于0.1")] + public float ReturnTimeSpan { get; set; } - /// - /// 通知对象用户集合 - /// - [MinLength(1, ErrorMessage = "通知对象用户集合不能为空")] - public List NotifyUsers { get; set; } = new(); + /// + /// 通知对象用户集合 + /// + [MinLength(1, ErrorMessage = "通知对象用户集合不能为空")] + public List NotifyUsers { get; set; } = new(); } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/ReturnWarningDetailsDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/ReturnWarningDetailsDto.cs index 1165d6b9..8036fc15 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/ReturnWarningDetailsDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Return/ReturnWarningDetailsDto.cs @@ -2,7 +2,6 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Return; /// /// 回传预警明细 -/// todo:需要根据状态排序 /// public sealed class ReturnWarningDetailsDto { @@ -17,7 +16,7 @@ public sealed class ReturnWarningDetailsDto public int SubSystemId { get; set; } /// - /// 子系统名称 + /// 子系统名称 /// public string SubSystemName { get; set; } // todo:id换名称 @@ -27,7 +26,7 @@ public sealed class ReturnWarningDetailsDto public int OrderType { get; set; } /// - /// 订单类型名称 + /// 订单类型名称 /// public string OrderTypeName { get; set; } // todo:type换名称 @@ -52,17 +51,17 @@ public sealed class ReturnWarningDetailsDto public int CustomId { get; set; } /// - /// 客户简称 + /// 客户简称 /// - public string CustomName { get; set; } // todo:id换名称 + public string CustomName { get; set; } // todo:id换名称 /// - /// 轨迹状态 + /// 轨迹状态 /// public string TrackStatus { get; set; } /// - /// 轨迹代码 + /// 轨迹代码 /// public string TrackCode { get; set; } @@ -87,7 +86,7 @@ public sealed class ReturnWarningDetailsDto public string PushTrackContent { get; set; } /// - /// 轨迹发生地 + /// 轨迹发生地 /// public string TrackPlace { get; set; } @@ -112,7 +111,7 @@ public sealed class ReturnWarningDetailsDto public DateTime? TrackReturnTime { get; set; } /// - /// 回传时效 + /// 回传时效 (单位h) /// public float? ReturnTimeSpan { get; set; } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/EditTransportWarningSceneRuleItemInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/EditTransportWarningSceneRuleItemInput.cs index 5471b64c..fedef9e6 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/EditTransportWarningSceneRuleItemInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/EditTransportWarningSceneRuleItemInput.cs @@ -5,22 +5,22 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Transport; public class EditTransportWarningSceneRuleItemInput { - /// - /// 预警规主体id - /// - [IdRequired] - public int WarningId { get; set; } + /// + /// 预警规主体id + /// + [IdRequired] + public int WarningId { get; set; } - /// - /// 运输预警场景id - /// - [IdRequired] - public int SceneId { get; set; } + /// + /// 运输预警场景id + /// + [IdRequired] + public int SceneId { get; set; } - /// - /// 预警规则配置项目(列表内容传递) - /// - public List Items { get; set; } = new(); + /// + /// 预警规则配置项目(列表内容传递) + /// + public List Items { get; set; } = new(); } /// @@ -28,38 +28,38 @@ public class EditTransportWarningSceneRuleItemInput /// public class EditTransportWarningSceneRuleItemDto { - /// - /// 规则项目id(传0视为新增) - /// - public int Id { get; set; } + /// + /// 规则项目id(传0视为新增) + /// + public int Id { get; set; } - /// - /// 起始轨迹状态Id - /// - [IdRequired] - public int StartTrackStatusId { get; set; } + /// + /// 起始轨迹状态Id + /// + [IdRequired] + public int StartTrackStatusId { get; set; } - /// - /// 截止轨迹状态Id - /// - [IdRequired] - public int EndTrackStatusId { get; set; } + /// + /// 截止轨迹状态Id + /// + [IdRequired] + public int EndTrackStatusId { get; set; } - /// - /// 时效 (单位h) - /// - [Range(0.1, float.MaxValue, ErrorMessage = "时效必须大于0.1")] - public float TransportTimeSpan { get; set; } + /// + /// 时效 (单位h) + /// + [Range(0.1, float.MaxValue, ErrorMessage = "时效必须大于0.1")] + public float TransportTimeSpan { get; set; } - /// - /// 起始轨迹生成预警时间 (单位h) - /// - [Range(0.1, float.MaxValue, ErrorMessage = "起始轨迹生成预警时间必须大于0.1")] - public float StartTrackWarningTimeSpan { get; set; } + /// + /// 起始轨迹生成预警时间 (单位h) + /// + [Range(0.1, float.MaxValue, ErrorMessage = "起始轨迹生成预警时间必须大于0.1")] + public float StartTrackWarningTimeSpan { get; set; } - /// - /// 通知对象用户集合 - /// - [MinLength(1, ErrorMessage = "通知对象用户集合不能为空")] - public List NotifyUsers { get; set; } = new(); + /// + /// 通知对象用户集合 + /// + [MinLength(1, ErrorMessage = "通知对象用户集合不能为空")] + public List NotifyUsers { get; set; } = new(); } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneInput.cs index 0b9bcebe..13fde467 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneInput.cs @@ -7,9 +7,9 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Transport; /// public class GetTransportWarningSceneInput { - /// - /// 预警主体id - /// - [IdRequired] - public int WarningId { get; set; } + /// + /// 预警主体id + /// + [IdRequired] + public int WarningId { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneRuleItemInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneRuleItemInput.cs index f1c7f56a..6538d1e4 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneRuleItemInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/GetTransportWarningSceneRuleItemInput.cs @@ -7,15 +7,15 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Transport; /// public class GetTransportWarningSceneRuleItemInput { - /// - /// 预警主体id - /// - [IdRequired] - public int WarningId { get; set; } + /// + /// 预警主体id + /// + [IdRequired] + public int WarningId { get; set; } - /// - /// 运输预警场景id - /// - [IdRequired] - public int SceneId { get; set; } + /// + /// 运输预警场景id + /// + [IdRequired] + public int SceneId { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningDetailsDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningDetailsDto.cs index 4b4e01b8..1ef707f3 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningDetailsDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningDetailsDto.cs @@ -5,145 +5,145 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning.Transport; /// public sealed class TransportWarningDetailsDto // todo:需要根据状态排序 { - /// - /// 预警明细唯一id - /// - public string EsId { get; set; } + /// + /// 预警明细唯一id + /// + public string EsId { get; set; } - /// - /// 子系统id - /// - public int SubSystemId { get; set; } + /// + /// 子系统id + /// + public int SubSystemId { get; set; } - /// - /// 子系统名称 - /// - public string SubSystemName { get; set; } // todo:id换名称 + /// + /// 子系统名称 + /// + public string SubSystemName { get; set; } // todo:id换名称 - /// - /// 客户id - /// - public int CustomId { get; set; } + /// + /// 客户id + /// + public int CustomId { get; set; } - /// - /// 客户简称 - /// - public string CustomName { get; set; } // todo:id换名称 + /// + /// 客户简称 + /// + public string CustomName { get; set; } // todo:id换名称 - /// - /// 订单id - /// - public int OrderId { get; set; } + /// + /// 订单id + /// + public int OrderId { get; set; } - /// - /// 订单号 - /// - public string OrderNo { get; set; } + /// + /// 订单号 + /// + public string OrderNo { get; set; } - /// - /// 订单类型 - /// - public int OrderType { get; set; } + /// + /// 订单类型 + /// + public int OrderType { get; set; } - /// - /// 订单类型名称 - /// - public string OrderTypeName { get; set; } // todo:type换名称 + /// + /// 订单类型名称 + /// + public string OrderTypeName { get; set; } // todo:type换名称 - // 实时属性 todo:从订单查询 + // 实时属性 todo:从es查询 - /// - /// 最新轨迹内容 - /// - public string LatestTrackContent { get; set; } + /// + /// 最新轨迹内容 + /// + public string? LatestTrackContent { get; set; } - /// - /// 最新轨迹时间 - /// - public DateTime LatestTrackTime { get; set; } + /// + /// 最新轨迹时间 + /// + public DateTime? LatestTrackTime { get; set; } - /// - /// 最新轨迹发生时区 - /// - public float LatestTrackTimeZone { get; set; } + /// + /// 最新轨迹发生时区 + /// + public float? LatestTrackTimeZone { get; set; } - /// - /// 最新轨迹发生地 - /// - public string LatestTrackPlace { get; set; } + /// + /// 最新轨迹发生地 + /// + public string? LatestTrackPlace { get; set; } - /// - /// 通知对象 - /// - public string NotifyUserNames { get; set; } + /// + /// 通知对象 + /// + public string NotifyUserNames { get; set; } - /// - /// 起始轨迹状态 - /// - public string StartTrackStatus { get; set; } + /// + /// 起始轨迹状态 + /// + public string StartTrackStatus { get; set; } - /// - /// 起始轨迹内容 - /// - public string StartTrackContent { get; set; } + /// + /// 起始轨迹内容 + /// + public string StartTrackContent { get; set; } - /// - /// 起始轨迹发生时间 - /// - public DateTime StartTrackTime { get; set; } + /// + /// 起始轨迹发生时间 + /// + public DateTime StartTrackTime { get; set; } - /// - /// 起始轨迹时区 - /// - public float StartTrackTimeZone { get; set; } + /// + /// 起始轨迹时区 + /// + public float StartTrackTimeZone { get; set; } - /// - /// 截止轨迹状态 - /// - public string EndTrackStatus { get; set; } + /// + /// 截止轨迹状态 + /// + public string EndTrackStatus { get; set; } - /// - /// 截止轨迹内容 - /// - public string? EndTrackContent { get; set; } + /// + /// 截止轨迹内容 + /// + public string? EndTrackContent { get; set; } - /// - /// 截止轨迹发生时间 - /// - public DateTime? EndTrackTime { get; set; } + /// + /// 截止轨迹发生时间 + /// + public DateTime? EndTrackTime { get; set; } - /// - /// 截止轨迹时区 - /// - public float? EndTrackTimeZone { get; set; } + /// + /// 截止轨迹时区 + /// + public float? EndTrackTimeZone { get; set; } - /// - /// 预警名称 (预警主体名称 如字节全程时效) - /// - public string WarningName { get; set; } + /// + /// 预警名称 (预警主体名称 如字节全程时效) + /// + public string WarningName { get; set; } - /// - /// 预警规则描述(用于tooltip展示) - /// - public string WarningRuleDescription { get; set; } + /// + /// 预警规则描述(用于tooltip展示) + /// + public string WarningRuleDescription { get; set; } - /// - /// 累计运输时间 - /// - public string CumulativeTimeSpan { get; set; } // todo:需要转换为某个格式 + /// + /// 累计运输时间 + /// + public string CumulativeTimeSpan { get; set; } // todo:需要转换为某个格式 - /// - /// 预警状态 (根据key展示对应名称,即将超时需要标红) - /// 1:即将超时 2:已超时 3:已完成 4:超时完成 5:已关闭,初始为【即将超时】 - /// - public int Status { get; set; } + /// + /// 预警状态 (根据key展示对应名称,即将超时需要标红) + /// 1:即将超时 2:已超时 3:已完成 4:超时完成 5:已关闭,初始为【即将超时】 + /// + public int Status { get; set; } - /// - /// 预警创建时间 - /// - public DateTime CreateTime { get; set; } + /// + /// 预警创建时间 + /// + public DateTime CreateTime { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneDto.cs index 4aaf36ec..d4ddf002 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneDto.cs @@ -16,7 +16,7 @@ public class TransportWarningSceneDto public int WarningId { get; set; } /// - /// 场景摘要(用于展示场景tabs) + /// 场景摘要(用于展示场景tabs) /// public string Summary { get; set; } // todo:将规则场景一些列因素转换为一段描述 diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneRuleItemDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneRuleItemDto.cs index b5d84089..d0c1a94b 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneRuleItemDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/TransportWarningSceneRuleItemDto.cs @@ -1,5 +1,3 @@ -using SJZY.Track.Application.Contract.Dtos.Warning; - namespace SJZY.Track.Application.Contract.Dtos.Warning.Transport; /// @@ -46,4 +44,4 @@ public class TransportWarningSceneRuleItemDto /// 通知对象用户集合 /// public List NotifyUsers { get; set; } = new(); -} \ No newline at end of file +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs index cc27b52a..07d64f2f 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs @@ -1,7 +1,8 @@ using SJZY.Track.Application.Contract.Dtos.Warning.DataAnnotations; + namespace SJZY.Track.Application.Contract.Dtos.Warning.Transport; -public class UpsertTransportWarningSceneInput +public sealed class UpsertTransportWarningSceneInput { /// /// 场景唯一id (传0视为新增) diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/UpsertWarningRuleInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/UpsertWarningRuleInput.cs index 2c5d8f87..8d226d4a 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/UpsertWarningRuleInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/UpsertWarningRuleInput.cs @@ -8,38 +8,38 @@ namespace SJZY.Track.Application.Contract.Dtos.Warning; /// public class UpsertWarningRuleInput { - /// - /// 唯一id(传0视为新增) - /// - public int Id { get; set; } + /// + /// 唯一id(传0视为新增) + /// + public int Id { get; set; } - /// - /// 子系统id - /// - [IdRequired] - public int SubSystemId { get; set; } //子系统id + /// + /// 子系统id + /// + [IdRequired] + public int SubSystemId { get; set; } //子系统id - /// - /// 客户id - /// - [IdRequired] - public int CustomId { get; set; } //客户id + /// + /// 客户id + /// + [IdRequired] + public int CustomId { get; set; } //客户id - /// - /// 预警规则名称 - /// - [Required] - public string Name { get; set; } + /// + /// 预警规则名称 + /// + [Required] + public string Name { get; set; } - /// - /// 是否启用 - /// - [Required] - public bool IsEnabled { get; set; } + /// + /// 是否启用 + /// + [Required] + public bool IsEnabled { get; set; } - /// - /// 预警类型,1:运输时效,2:回传时效,3:完整预警 - /// - [Range(1, 3, ErrorMessage = "预警类型只能选取以下值,1:运输时效,2:回传时效,3:完整预警")] - public int WarningType { get; set; } + /// + /// 预警类型,1:运输时效,2:回传时效,3:完整预警 + /// + [Range(1, 3, ErrorMessage = "预警类型只能选取以下值,1:运输时效,2:回传时效,3:完整预警")] + public int WarningType { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Warning/WarningRuleDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Warning/WarningRuleDto.cs index 57ea30fe..6b71a42a 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/Warning/WarningRuleDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/Warning/WarningRuleDto.cs @@ -13,22 +13,22 @@ public sealed class WarningRuleDto /// /// 子系统id /// - public int SubSystemId { get; set; } //子系统id + public int SubSystemId { get; set; } //子系统id /// - /// 子系统名称 + /// 子系统名称 /// - public int SubSystemName { get; set; } // todo:id换name + public string SubSystemName { get; set; } /// /// 客户id /// - public int CustomId { get; set; } //客户id + public int CustomId { get; set; } //客户id /// - /// 客户简称 + /// 客户简称 /// - public int CustomName { get; set; } // todo:id换name + public string CustomName { get; set; } /// /// 预警规则名称 diff --git a/src/SJZY.Track.Application/AutoMapper/WarningMapperProfile.cs b/src/SJZY.Track.Application/AutoMapper/WarningMapperProfile.cs new file mode 100644 index 00000000..4fef273f --- /dev/null +++ b/src/SJZY.Track.Application/AutoMapper/WarningMapperProfile.cs @@ -0,0 +1,17 @@ +using AutoMapper; +using SJZY.Track.Application.Contract.Dtos.Warning.Complete; +using SJZY.Track.Application.Contract.Dtos.Warning.Return; +using SJZY.Track.Application.Contract.Dtos.Warning.Transport; +using SJZY.Track.Domain.Dtos.Warning; + +namespace SJZY.Track.Application.AutoMapper; + +public class WarningMapperProfile : Profile +{ + public WarningMapperProfile() + { + CreateMap(MemberList.None); + CreateMap(MemberList.None); + CreateMap(MemberList.None); + } +} diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs index 5ce98e81..ca773013 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs @@ -10,6 +10,7 @@ using SJZY.Track.Application.Helpers; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Extensions; using SJZY.Track.Domain.Queries; using Volo.Abp.DependencyInjection; diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs index 110f5c73..72b5a080 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs @@ -15,6 +15,7 @@ using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Aggregates.TrackPushAggregate; using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Extensions; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.DynamicProxying; diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs index 9bc5140a..2bd5112c 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs @@ -10,6 +10,7 @@ using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Dtos.TrackContents; using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Extensions; using SJZY.Track.Domain.Queries; using Volo.Abp.DependencyInjection; using OrderIdentifier = SJZY.Track.Application.Contract.Events.OrderIdentifier; diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs index ca74ebe4..119e8c25 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs @@ -10,6 +10,7 @@ using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Aggregates.TrackPushAggregate; using SJZY.Track.Domain.Dtos.TrackPush; +using SJZY.Track.Domain.Extensions; using SJZY.Track.Domain.Queries; using SJZY.UserCenter.HttpApi.Services.User; using Volo.Abp.Http.Client.DynamicProxying; diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs index d6e268a8..1931a805 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs @@ -9,6 +9,7 @@ using SJZY.Track.Domain.Aggregates.TrackContentAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Aggregates.TrackTemplateAggregate; using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Extensions; using SJZY.Track.Domain.Queries; using SJZY.UserCenter.HttpApi.Services; using SJZY.UserCenter.HttpApi.Services.User; diff --git a/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs b/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs index 89041d1f..df7553fc 100644 --- a/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs +++ b/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs @@ -67,7 +67,7 @@ public class TrackOrderService : ApplicationService [HttpGet("GetOrderTypeDict")] public async Task> GetOrderTypeDict() { - return await _orderRepository.GetTrackOrderTypeDict(); + return await _orderRepository.GetTrackOrderTypeDictAsync(); } /// diff --git a/src/SJZY.Track.Application/Services/TrackPush/TrackPushService.cs b/src/SJZY.Track.Application/Services/TrackPush/TrackPushService.cs index fcf66ef8..f36b1c62 100644 --- a/src/SJZY.Track.Application/Services/TrackPush/TrackPushService.cs +++ b/src/SJZY.Track.Application/Services/TrackPush/TrackPushService.cs @@ -57,7 +57,7 @@ public class TrackPushService : ApplicationService, ISingletonDependency { var orderTypes = await _trackPushConfigRepository.GetConfigTags(inputVo); if (orderTypes.Count == 0) return new List(); - var trackOrderTypes = await _orderRepository.GetTrackOrderTypeDict(); + var trackOrderTypes = await _orderRepository.GetTrackOrderTypeDictAsync(); return orderTypes.Select(x => new TrackPushConfigTagDto { diff --git a/src/SJZY.Track.Application/Services/Warning/CompleteWarningService.cs b/src/SJZY.Track.Application/Services/Warning/CompleteWarningService.cs index dbdb41d9..2a52edbf 100644 --- a/src/SJZY.Track.Application/Services/Warning/CompleteWarningService.cs +++ b/src/SJZY.Track.Application/Services/Warning/CompleteWarningService.cs @@ -1,4 +1,18 @@ +using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Track.Application.Contract.Dtos.Warning; using SJZY.Track.Application.Contract.Dtos.Warning.Complete; +using SJZY.Track.Application.Contract.Dtos.Warning.Return; +using SJZY.Track.Application.Helpers; +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Application.Services.Warning.Handlers.Warning; +using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; +using SJZY.Track.Domain.Dtos.Warning; +using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Extensions; namespace SJZY.Track.Application.Services.Warning; @@ -9,15 +23,21 @@ namespace SJZY.Track.Application.Services.Warning; [Route("Warning/Complete")] public class CompleteWarningService : ApplicationService { - /// - /// 编辑完整预警规则列表 - /// - /// - /// - [HttpPost("EditCompleteWarningRuleItem")] - public Task EditCompleteWarningRuleItem(EditCompleteWarningRuleItemInput input) + private readonly IOrderRepository _orderRepository; + private readonly IWarningRuleRepository _warningRuleRepository; + private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; + private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; + private readonly ICompleteWarningHandler _completeWarningHandler; + private readonly ITrackStatusRepository _trackStatusRepository; + + public CompleteWarningService(IWarningRuleRepository warningRuleRepository, IWarningDetailsEsRepository warningDetailsEsRepository, ITrackDetailsEsRepository trackDetailsEsRepository, ICompleteWarningHandler completeWarningHandler, ITrackStatusRepository trackStatusRepository, IOrderRepository orderRepository) { - return Task.FromResult(new UnifyResultDto()); + _warningRuleRepository = warningRuleRepository; + _warningDetailsEsRepository = warningDetailsEsRepository; + _trackDetailsEsRepository = trackDetailsEsRepository; + _completeWarningHandler = completeWarningHandler; + _trackStatusRepository = trackStatusRepository; + _orderRepository = orderRepository; } /// @@ -26,10 +46,70 @@ public class CompleteWarningService : ApplicationService /// /// [HttpPost("GetCompleteWarningRuleItem")] - public Task>> - GetCompleteWarningRuleItem(GetCompleteWarningRuleItemInput input) + public async Task>> GetCompleteWarningRuleItem(GetCompleteWarningRuleItemInput input) { - return Task.FromResult(new UnifyResultDto>()); + var result = await _warningRuleRepository.GetCompleteRuleItemsAsync(input.WarningId); + if (result.Count == 0) + return new UnifyResultDto>(true, new List()); + + // todo:缓存优化 + var trackStatusIds = result + .SelectMany(x => + { + // todo:逻辑优化 + var checkStatusId = x.CheckPointTrackStatusId; + var requireStatusIds = x.GetRequireTrackStatusIdGroups(); + requireStatusIds.Add(new HashSet(1) + { + checkStatusId + }); + return requireStatusIds.SelectMany(y => y); + }).ToHashSet(); + + var trackStatusDict = (await _trackStatusRepository.GetListAsync(x => trackStatusIds.Contains(x.Id), x => new + { + x.Id, + x.Status + })).ToDictionary(x => x.Id, y => y.Status); + + // 查订单类型映射 + var orderTypeDict = await _orderRepository.GetTrackOrderTypeDictAsync(); + + var map = result.Select(x => new CompleteWarningRuleItemDto + { + Id = x.Id, + WarningId = x.WarningId, + OrderTypes = BuildOrderTypes(x), + CheckPointTrackStatusId = x.CheckPointTrackStatusId, + CheckPointTrackStatus = trackStatusDict.GetValueOrDefault(x.CheckPointTrackStatusId), + RequireTrackStatus = BuildRequireTrackStatus(x), + NotifyUsers = TextJsonHelper.Deserialize>(x.NotifyUsers)! + }); + + return new UnifyResultDto>(true, map.ToList()); + + List BuildRequireTrackStatus(CompleteWarningRuleItemEntity entity) + { + return entity.GetRequireTrackStatusIdGroups() + .Select(g => new RequireTrackStatusItem + { + Statuses = g.Select(id => new TrackStatusDto + { + TrackStatusId = id, + TrackStatus = trackStatusDict.GetValueOrDefault(id) + }).ToList() + }).ToList(); + } + + List BuildOrderTypes(CompleteWarningRuleItemEntity entity) + { + return entity.GetRuleOrderTypes() + .Select(x => new OrderTypeDto + { + OrderType = x, + OrderTypeName = orderTypeDict.GetValueOrDefault(x.ToString()) + }).ToList(); + } } /// @@ -38,9 +118,114 @@ public class CompleteWarningService : ApplicationService /// /// [HttpPost("GetPageCompleteWarningDetails")] - public Task> - GetPageCompleteWarningDetails(GetPageCompleteWarningDetailsInput input) + public async Task> GetPageCompleteWarningDetails(GetPageCompleteWarningDetailsInput input) { - return Task.FromResult(new UnifyPageResultDto()); + var vo = ObjectMapper.Map(input); + var result = await _warningDetailsEsRepository.GetPageCompleteWarningDetailsAsync(vo); + + if (result.List.Count == 0) + return new UnifyPageResultDto(new List(), input.PageIndex, input.PageSize, 0); + + // todo:缓存优化 + var trackStatusIds = result.List + .SelectMany(x => x.MissingTrackStatusIds.Split(',').Select(int.Parse)) + .ToHashSet(); + var trackStatusDict = (await _trackStatusRepository.GetListAsync(x => trackStatusIds.Contains(x.Id), x => new + { + x.Id, + x.Status + })).ToDictionary(x => x.Id, y => y.Status); + + // 查最新轨迹 + var orderIds = result.List.Select(x => x.OrderId).ToHashSet(); + var orderTracks = await _trackDetailsEsRepository.GetLatestListAsync(orderIds); + var orderIdTrackDict = orderTracks.ToDictionary(x => x.OrderId); + + // :填充子系统名称(冗余?) + // :填充客户简称(冗余?) + // :填充订单类型名称(冗余?) + var map = result.List.Select(x => + { + var orderIdTrack = orderIdTrackDict.GetValueOrDefault(x.OrderId); + return new CompleteWarningDetailsDto + { + EsId = x.Id, + SubSystemId = x.SubSystemId, + SubSystemName = x.SubSystemName, + OrderType = x.OrderType, + OrderTypeName = x.OrderTypeName, + OrderId = x.OrderId, + OrderNo = x.OrderNo, + Status = x.Status, + CustomId = x.CustomId, + CustomName = x.CustomName, + MissingTrackStatus = BuildMissingTrackStatus(x.MissingTrackStatusIds), + LatestTrackStatus = orderIdTrack?.TrackStatus, + LatestTrackContent = orderIdTrack?.TrackContent, + LatestTrackPlace = orderIdTrack?.TrackPlace, + LatestTrackTime = orderIdTrack?.TrackTime, + LatestTrackTimeZone = orderIdTrack?.TrackTimeZone, + + NotifyUserNames = ToUserNames(x.NotifyUsers), + CreateTime = x.CreateTime, + ProcessedTime = x.ProcessedTime + }; + }); + + var sorted = map.OrderBy(x => + { + // 待处理置顶 + if (x.Status == 1) return x.Status; + // 其他按创建时间从早到晚 + return DateTimeHelper.ConvertToUnixTimestamp(x.CreateTime); + }); + + return new UnifyPageResultDto( + sorted.ToList(), + result.Pagination.PageIndex, + result.Pagination.PageSize, + result.Pagination.Total); + + // 通知用户提取用户名称 + string ToUserNames(string notifyUsers) + { + if (string.IsNullOrWhiteSpace(notifyUsers)) + return string.Empty; + return string.Join(',', notifyUsers.ToObject>().Select(x => x.UserName)); + } + + string BuildMissingTrackStatus(string statusIds) + { + var trackStatus = statusIds.Split(',').Select(x => + { + if (int.TryParse(x, out var id)) + return trackStatusDict.GetValueOrDefault(id) + ?? $"丢失{id}轨迹状态"; + + return null; + }).Where(x => x != null); + + return string.Join(',', trackStatus); + } + } + + + /// + /// 编辑完整预警规则列表 + /// + /// + /// + [HttpPost("EditCompleteWarningRuleItem")] + public async Task EditCompleteWarningRuleItem(EditCompleteWarningRuleItemInput input) + { + var rule = await _warningRuleRepository.GetWarningRuleByIdAsync(input.WarningId, WarningRuleType.完整预警); + if (rule == null) + { + throw new BusinessException(message: $"预警规则主体不存在,WarningId=[{input.WarningId}]"); + } + + var bos = CompleteWarningRuleItemBO.From(input); + await _completeWarningHandler.HandleEditAsync(rule, bos); + return new UnifyResultDto(true); } } diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/CompleteWarningHandler.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/CompleteWarningHandler.cs new file mode 100644 index 00000000..4dafef7d --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/CompleteWarningHandler.cs @@ -0,0 +1,170 @@ +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Application.Services.Warning.Handlers.Warning; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; + +namespace SJZY.Track.Application.Services.Warning.Handlers; + +public sealed class CompleteWarningHandler : ICompleteWarningHandler +{ + private readonly IWarningRuleRepository _warningRuleRepository; + + public CompleteWarningHandler(IWarningRuleRepository warningRuleRepository) + { + _warningRuleRepository = warningRuleRepository; + } + + public Task TrackDeletedHandleAsync(List deletedSignals) + { + throw new NotImplementedException(); + } + + public Task TrackAddedHandleAsync(List addedSignals) + { + throw new NotImplementedException(); + } + + public Task WarningRuleToDisabledHandleAsync(WarningRuleEntity warningRule) + { + throw new NotImplementedException(); + } + + #region todo: 规则项目变更 + + public async Task HandleEditAsync(WarningRuleEntity rule, List postRuleItems) + { + var addRuleItems = postRuleItems.Where(x => x.Id == 0).ToList(); + // 1.找到之前的配置项目 + var entities = await _warningRuleRepository.GetCompleteRuleItemsAsync(rule.Id); + var oldRuleItems = CompleteWarningRuleItemBO.From(entities); + // 1.1之前的配置数量为0,做只新增处理 + if (oldRuleItems.Count == 0) + { + if (addRuleItems.Count != 0) + await OnlyAddHandleAsync(rule, addRuleItems); + + return; + } + + // 1.2之前的配置数量不为0,提交的数量为0,做只删除处理 + if (oldRuleItems.Count != 0 && postRuleItems.Count == 0) + { + await OnlyDeleteHandleAsync(rule, oldRuleItems); + return; + } + + // 关心规则修改/删除 + var updateRuleItems = new List(); + var deleteRuleItems = new List(); + + // 构建id配置项字典 + var oldDict = oldRuleItems.ToDictionary(x => x.Id); + var updateAndAddRuleItems = postRuleItems.Where(x => x.Id == 0).ToList(); + var postDict = postRuleItems + .Except(updateAndAddRuleItems) //tips:差异化比较时忽略新增的规则条目 + .ToDictionary(x => x.Id); + + // 2.差异化比较 + var ruleItemIds = oldDict.Keys.Concat(postDict.Keys).ToHashSet(); + foreach (var ruleItemId in ruleItemIds) + { + var oldRule = oldDict.GetValueOrDefault(ruleItemId); + var postRule = postDict.GetValueOrDefault(ruleItemId); + if (oldRule != null && postRule != null) + { + //规则属性变更,(状态减少,时效变更) + if (CanNeedFollow(oldRule, postRule)) + updateRuleItems.Add(postRule); //更新 关注新配置 + + continue; + } + + //规则条目被删除 + if (oldRule != null && postRule == null) + deleteRuleItems.Add(oldRule); //删除 关注旧配置 + } + + // 3.处理变更 + await HandleRuleItemsChangeAsync(rule, addRuleItems, updateRuleItems, deleteRuleItems); + } + + private async Task OnlyDeleteHandleAsync(WarningRuleEntity rule, List bos) + { + var ruleItemIds = bos.Select(x => x.Id).ToHashSet(); + await _warningRuleRepository.DeleteCompleteRuleItemsAsync(ruleItemIds); + + // 更新规则主体版本号 + await _warningRuleRepository.UpdateVersionAsync(rule); + + // todo:暂时不处理后续逻辑 + return; + + } + + private async Task OnlyAddHandleAsync(WarningRuleEntity rule, List bos) + { + var entities = CompleteWarningRuleItemBO.ToEntities(bos); + await _warningRuleRepository.BulkInsertCompleteRuleItemsAsync(entities); + // 更新规则主体版本号 + await _warningRuleRepository.UpdateVersionAsync(rule); + } + + private async Task HandleRuleItemsChangeAsync( + WarningRuleEntity rule, + List addRuleItems, + List updateRuleItems, + List deleteRuleItems + ) + { + // 新增/修改/删除 实体,稍后做处理 + var addEntities = CompleteWarningRuleItemBO.ToEntities(addRuleItems); + var updateEntities = CompleteWarningRuleItemBO.ToEntities(updateRuleItems); + var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet(); + await _warningRuleRepository.BulkInsertCompleteRuleItemsAsync(addEntities); + await _warningRuleRepository.BulkUpdateCompleteRuleItemsAsync(updateEntities); + await _warningRuleRepository.DeleteCompleteRuleItemsAsync(deleteRuleItemIds); + + // todo:暂时不启用后续规则 + return; + } + + private bool CanNeedFollow(CompleteWarningRuleItemBO oldRule, CompleteWarningRuleItemBO postRule) + { + // 关注的订单类型减少 + var oldTypes = oldRule.OrderTypes; + var postTypes = postRule.OrderTypes; + foreach (var type in oldTypes) + { + if (!postTypes.Contains(type)) + return true; + } + + // 检查点发生变化 + if (oldRule.CheckPointTrackStatusId != postRule.CheckPointTrackStatusId) + return true; + + // 要求的轨迹状态, + // 条目内或关系状态变更,比如 案例1:S1/S2,S4 => S1/S3,案例2: S1/S2,S4 => S1,S4 + // 条目减少,S1/S2,S4 => S1/S2 + var oldStatusIds = oldRule.RequireTrackStatus.SelectMany(x => x).ToHashSet(); + var postStatusIds = postRule.RequireTrackStatus.SelectMany(x => x).ToHashSet(); + + if (oldStatusIds.Count > postStatusIds.Count) //数量减少,一定需要关注 + return true; + + foreach (var statusId in oldStatusIds) + { + if (!postStatusIds.Contains(statusId)) //状态减少,需要关注 + return true; + } + + return false; + } + + #endregion + + public List FilterAndHandleNeedNotifyTask(List notifyTasks) + { + throw new NotImplementedException(); + } +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/Model/CompleteWarningRuleItemBO.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/CompleteWarningRuleItemBO.cs new file mode 100644 index 00000000..31fd5ea3 --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/CompleteWarningRuleItemBO.cs @@ -0,0 +1,91 @@ +using SJZY.Track.Application.Contract.Dtos.Warning; +using SJZY.Track.Application.Contract.Dtos.Warning.Complete; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; +using SJZY.Track.Domain.Extensions; + +namespace SJZY.Track.Application.Services.Warning.Handlers.Model; + +public sealed class CompleteWarningRuleItemBO +{ + /// + /// 规则项目唯一id(传0视为新增) + /// + public int Id { get; set; } + + /// + /// 预警规主体id + /// + public int WarningId { get; set; } + + /// + /// 订单类型集合(,分割) e.g. 1,2,3 + /// + public HashSet OrderTypes { get; set; } + + /// + /// 检查点轨迹状态id + /// + public int CheckPointTrackStatusId { get; set; } + + /// + /// 要求的轨迹状态id集合 e.g. [[1,3],[5,7]] + /// required: 集合元素(子集合)不要有交集! + /// + public List> RequireTrackStatus { get; set; } + + /// + /// 通知对象用户集合 + /// + public List NotifyUsers { get; set; } = new(); + + public static List From(EditCompleteWarningRuleItemInput input) + { + return input.Items.Select(x => new CompleteWarningRuleItemBO + { + Id = x.Id, + WarningId = input.WarningId, + OrderTypes = x.OrderTypes.Split(",").Select(int.Parse).ToHashSet(), + CheckPointTrackStatusId = x.CheckPointTrackStatusId, + RequireTrackStatus = x.RequireTrackStatus.Select(y => y.ToHashSet()).ToList(), + NotifyUsers = x.NotifyUsers + }) + .ToList(); + } + + public CompleteWarningRuleItemEntity ToEntity() + { + var requireTrackStatusIdExpression = string.Join(',', RequireTrackStatus.Select(sub => string.Join('/', sub))); + + return new CompleteWarningRuleItemEntity( + Id, + WarningId, + string.Join(",", OrderTypes), + CheckPointTrackStatusId, + requireTrackStatusIdExpression, + TextJsonHelper.Serialize(NotifyUsers)! + ); + } + + public static List ToEntities(List bos) + { + return bos.Select(x => x.ToEntity()).ToList(); + } + + public static CompleteWarningRuleItemBO From(CompleteWarningRuleItemEntity entity) + { + return new CompleteWarningRuleItemBO + { + Id = entity.Id, + WarningId = entity.WarningId, + OrderTypes = entity.GetRuleOrderTypes(), + CheckPointTrackStatusId = entity.CheckPointTrackStatusId, + RequireTrackStatus = entity.GetRequireTrackStatusIdGroups(), + NotifyUsers = TextJsonHelper.Deserialize>(entity.NotifyUsers)! + }; + } + + public static List From(List entities) + { + return entities.Select(From).ToList(); + } +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/Model/ReturnWarningRuleItemBO.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/ReturnWarningRuleItemBO.cs new file mode 100644 index 00000000..bbe6070c --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/ReturnWarningRuleItemBO.cs @@ -0,0 +1,81 @@ +using SJZY.Track.Application.Contract.Dtos.Warning; +using SJZY.Track.Application.Contract.Dtos.Warning.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Extensions; + +namespace SJZY.Track.Application.Services.Warning.Handlers.Model; + +public sealed class ReturnWarningRuleItemBO +{ + /// + /// 规则项目唯一id + /// + public int Id { get; set; } + + /// + /// 预警规则主体id + /// + public int WarningId { get; set; } + + /// + /// 轨迹状态 + /// + public HashSet TrackStatusIds { get; set; } + + /// + /// 回传预警时间 + /// + public float ReturnTimeSpan { get; set; } //时效(单位小时),只是用于判断状态 + + /// + /// 通知用户对象集合 + /// + public List NotifyUsers { get; set; } + + + public static List From(EditReturnWarningRuleItemInput input) + { + var warningId = input.WarningId; + return input.Items.Select(x => new ReturnWarningRuleItemBO + { + Id = x.Id, + WarningId = warningId, + TrackStatusIds = x.TrackStatusIds.Split(',').Select(int.Parse).ToHashSet(), + ReturnTimeSpan = x.ReturnTimeSpan, + NotifyUsers = x.NotifyUsers + }).ToList(); + } + + public static List From(List entities) + { + return entities.Select(From).ToList(); + } + + public static ReturnWarningRuleItemBO From(ReturnWarningRuleItemEntity entity) + { + return new ReturnWarningRuleItemBO + { + Id = entity.Id, + WarningId = entity.WarningId, + TrackStatusIds = entity.TrackStatusIds.Split(',').Select(int.Parse).ToHashSet(), + ReturnTimeSpan = entity.ReturnTimeSpan, + NotifyUsers = TextJsonHelper.Deserialize>(entity.NotifyUsers)! + }; + } + + public ReturnWarningRuleItemEntity ToEntity() + { + return new ReturnWarningRuleItemEntity( + Id, + WarningId, + string.Join(',', TrackStatusIds), + ReturnTimeSpan, + TextJsonHelper.Serialize(NotifyUsers)! + ); + } + + public static List ToEntities(List bos) + { + return bos.Select(x => x.ToEntity()).ToList(); + } +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneBO.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneBO.cs new file mode 100644 index 00000000..09452eb5 --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneBO.cs @@ -0,0 +1,161 @@ +using SJZY.Track.Application.Contract.Dtos.Warning.Transport; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; + +namespace SJZY.Track.Application.Services.Warning.Handlers.Model; + +public sealed class TransportWarningSceneBO +{ + /// + /// 场景唯一id (传0视为新增) + /// + public int Id { get; set; } + + /// + /// 预警规则主体id + /// + public int WarningId { get; set; } + + /// + /// 是否中转(单一) + /// + public bool? IsTransit { get; set; } + + /// + /// 货物类型(多个,分割) + /// + public string? CargoType { get; set; } + + /// + /// 起运港口(多个,分割) + /// + public string? LeavePort { get; set; } + + /// + /// 产品编码(多个,分割) + /// + public string? ProductCode { get; set; } + + /// + /// 月份(多个/分割) + /// + public string? Months { get; set; } + + /// + /// 区域Id(单一) + /// + public int? AreaId { get; set; } + + + public bool CanHitScene(OrderEntity order, List? ruleAreas) + { + // 是否中转 + if (IsTransit != null && IsTransit != order.IsTransit) return false; + + // 货物类型 + if (!string.IsNullOrWhiteSpace(CargoType) && order.CargoType != CargoType) return false; + + // 启运港口 + if (!string.IsNullOrWhiteSpace(LeavePort) && order.LeavePort != LeavePort) return false; + + // 产品编码 + if (!string.IsNullOrWhiteSpace(ProductCode) && order.ProductCode != ProductCode) return false; + + // 月份 + if (!string.IsNullOrWhiteSpace(Months)) + { + var months = string.IsNullOrEmpty(Months) + ? new HashSet() + : Months.Split('/').Select(int.Parse).ToHashSet(); + // 订单创建时间月份 + var orderCreateTimeMonth = order.CreateTime.Month; + if (!months.Contains(orderCreateTimeMonth)) return false; + } + + // 区域 + if (ruleAreas != null && ruleAreas.Count > 0) + { + // 使用订单到达国家的二字码匹配区域 + var areas = ruleAreas.Where(area => order.ArrivalCountry == area.CountryCode).ToList(); + foreach (var area in areas) + { + // 邮编是否在关注的范围内? + if (!string.IsNullOrWhiteSpace(area.LeftPostcode) && !string.IsNullOrWhiteSpace(area.RightPostcode)) + { + // tips: 参考小包的邮编匹配,字典序 + if (string.CompareOrdinal(order.ArrivalPostCode, area.LeftPostcode) < 0 || + string.CompareOrdinal(order.ArrivalPostCode, area.RightPostcode) > 0) + return false; + } + + // 城市是否匹配? + if (!string.IsNullOrWhiteSpace(area.City) && order.ArrivalCity != area.City) return false; + + // 省份是否匹配? + if (!string.IsNullOrWhiteSpace(area.Province) && order.ArrivalProvince != area.Province) return false; + } + } + + return true; + } + + + public static TransportWarningSceneEntity ToEntity(TransportWarningSceneBO bo) + { + return new TransportWarningSceneEntity( + bo.Id, + bo.WarningId, + bo.CargoType, + bo.Months, + bo.AreaId, + bo.IsTransit, + bo.LeavePort, + bo.ProductCode + ); + } + + public TransportWarningSceneEntity ToEntity() + { + return new TransportWarningSceneEntity( + Id, + WarningId, + CargoType, + Months, + AreaId, + IsTransit, + LeavePort, + ProductCode + ); + } + + public static TransportWarningSceneBO From(TransportWarningSceneEntity entity) + { + return new TransportWarningSceneBO + { + Id = entity.Id, + WarningId = entity.WarningId, + CargoType = entity.CargoType, + Months = entity.Months, + AreaId = entity.AreaId, + IsTransit = entity.IsTransit, + LeavePort = entity.LeavePort, + ProductCode = entity.ProductCode + }; + } + + public static TransportWarningSceneBO From(UpsertTransportWarningSceneInput input) + { + return new TransportWarningSceneBO + { + Id = input.Id, + WarningId = input.WarningId, + CargoType = input.CargoType, + Months = input.Months, + AreaId = input.AreaId, + IsTransit = input.IsTransit, + LeavePort = input.LeavePort, + ProductCode = input.ProductCode + }; + } +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneRuleItemBO.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneRuleItemBO.cs new file mode 100644 index 00000000..5ed000ac --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/Model/TransportWarningSceneRuleItemBO.cs @@ -0,0 +1,123 @@ +using SJZY.Track.Application.Contract.Dtos.Warning; +using SJZY.Track.Application.Contract.Dtos.Warning.Transport; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using SJZY.Track.Domain.Extensions; + +namespace SJZY.Track.Application.Services.Warning.Handlers.Model; + +/// +/// 处理运输时效规则项目的业务模型 +/// 可以通过入参/持久化模型转化 +/// +public sealed class TransportWarningSceneRuleItemBO +{ + /// + /// 规则项目id(传0视为新增) + /// + public int Id { get; set; } + + /// + /// 预警规主体id + /// + public int WarningId { get; set; } + + /// + /// 运输预警场景id + /// + public int SceneId { get; set; } + + /// + /// 起始轨迹状态Id + /// + public int StartTrackStatusId { get; set; } + + /// + /// 截止轨迹状态Id + /// + public int EndTrackStatusId { get; set; } + + /// + /// 时效 (单位h) + /// + public float TransportTimeSpan { get; set; } + + /// + /// 起始轨迹生成预警时间 (单位h) + /// + public float StartTrackWarningTimeSpan { get; set; } + + /// + /// 通知对象用户集合 + /// + public List NotifyUsers { get; set; } + + public static List From(EditTransportWarningSceneRuleItemInput input) + { + var warningId = input.WarningId; + var sceneId = input.SceneId; + return input.Items.Select(x => new TransportWarningSceneRuleItemBO + { + Id = x.Id, + WarningId = warningId, + SceneId = sceneId, + StartTrackStatusId = x.StartTrackStatusId, + EndTrackStatusId = x.EndTrackStatusId, + TransportTimeSpan = x.TransportTimeSpan, + StartTrackWarningTimeSpan = x.StartTrackWarningTimeSpan, + NotifyUsers = x.NotifyUsers + }) + .ToList(); + } + + public static TransportWarningSceneRuleItemBO From(TransportWarningSceneRuleItemEntity entity) + { + return new TransportWarningSceneRuleItemBO + { + Id = entity.Id, + WarningId = entity.WarningId, + SceneId = entity.TransportWarningSceneId, + StartTrackStatusId = entity.StartTrackStatusId, + EndTrackStatusId = entity.EndTrackStatusId, + TransportTimeSpan = entity.TransportTimeSpan, + StartTrackWarningTimeSpan = entity.StartTrackWarningTimeSpan, + NotifyUsers = TextJsonHelper.Deserialize>(entity.NotifyUsers)! + }; + } + + public static List From(List entities) + { + return entities.Select(From).ToList(); + } + + public TransportWarningSceneRuleItemEntity ToEntity() + { + return new TransportWarningSceneRuleItemEntity( + Id, + WarningId, + SceneId, + StartTrackStatusId, + EndTrackStatusId, + TransportTimeSpan, + StartTrackWarningTimeSpan, + TextJsonHelper.Serialize(NotifyUsers)! + ); + } + + public static List ToEntities(List bos) + { + return bos.Select(x=>x.ToEntity()).ToList(); + } + + public bool CanHitRule(int trackStatusId, HashSet trackStatusIds) + { + return trackStatusId == StartTrackStatusId //命中起始轨迹状态 + && !trackStatusIds.Contains(EndTrackStatusId); //缺失截止轨迹状态 + } + + // 重新命中,用于规则扫描 + public bool CanReHit(HashSet trackStatusIds) + { + return trackStatusIds.Contains(StartTrackStatusId) //存在起始 + && !trackStatusIds.Contains(EndTrackStatusId); //不存在截止 + } +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/ReturnWarningHandler.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/ReturnWarningHandler.cs new file mode 100644 index 00000000..0de719dd --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/ReturnWarningHandler.cs @@ -0,0 +1,162 @@ +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Application.Services.Warning.Handlers.Warning; +using SJZY.Track.Domain.Aggregates.TrackPushAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; + +namespace SJZY.Track.Application.Services.Warning.Handlers; + +public sealed class ReturnWarningHandler : IReturnWarningHandler +{ + private readonly IWarningRuleRepository _warningRuleRepository; + + public ReturnWarningHandler(IWarningRuleRepository warningRuleRepository) + { + _warningRuleRepository = warningRuleRepository; + } + + public Task TrackUpdatedOrDeletedHandleAsync(List updateOrDeleteSignals) + { + throw new NotImplementedException(); + } + + public Task ReceiveTrackPushRecordHandleAsync(TrackPushRecordIndex record) + { + throw new NotImplementedException(); + } + + + public Task WarningRuleToDisabledHandleAsync(WarningRuleEntity warningRule) + { + throw new NotImplementedException(); + } + + public Task PushConfigChangedHandleAsync(TrackPushConfigEntity pushConfig, TrackPushContentConfigEntity pushContentConfig) + { + throw new NotImplementedException(); + } + + + #region todo: 规则项目变更 + + public async Task HandleEditAsync(WarningRuleEntity rule, List postRuleItems) + { + var addRuleItems = postRuleItems.Where(x => x.Id == 0).ToList(); + // 1.找到之前的配置项目 + var entities = await _warningRuleRepository.GetReturnRuleItemsAsync(rule.Id); + var oldRuleItems = ReturnWarningRuleItemBO.From(entities); + // 1.1之前的配置数量为0,做只新增处理 + if (oldRuleItems.Count == 0) + { + if (addRuleItems.Count != 0) + await OnlyAddHandleAsync(rule, addRuleItems); + + return; + } + + // 1.2之前的配置数量不为0,提交的数量为0,做只删除处理 + if (oldRuleItems.Count != 0 && postRuleItems.Count == 0) + { + await OnlyDeleteHandleAsync(rule, oldRuleItems); + return; + } + + // 关心规则修改/删除 + var updateRuleItems = new List(); + var deleteRuleItems = new List(); + + // 2.差异化比较 + // 构建配置id字段 + var oldDict = oldRuleItems.ToDictionary(x => x.Id); + var postDict = postRuleItems + .Where(x => x.Id != 0) //tips:忽略新增的规则条目 + .ToDictionary(x => x.Id); + var ruleItemIds = oldDict.Keys.Concat(postDict.Keys).ToHashSet(); + foreach (var ruleItemId in ruleItemIds) + { + var oldRule = oldDict.GetValueOrDefault(ruleItemId); + var postRule = postDict.GetValueOrDefault(ruleItemId); + if (oldRule != null && postRule != null) + { + //规则属性变更,(状态减少,时效变更) + if (CanNeedFollow(oldRule, postRule)) + updateRuleItems.Add(postRule); + continue; + } + + //规则条目被删除 + if (oldRule != null && postRule == null) + deleteRuleItems.Add(oldRule); + } + + // 3.处理变更 + await HandleRuleItemsChangeAsync(rule, addRuleItems, updateRuleItems, deleteRuleItems); + } + + private async Task OnlyAddHandleAsync(WarningRuleEntity rule, List bos) + { + var entities = ReturnWarningRuleItemBO.ToEntities(bos); + await _warningRuleRepository.BulkInsertReturnRuleItemsAsync(entities); + // 更新规则主体版本号 + await _warningRuleRepository.UpdateVersionAsync(rule); + } + + private async Task OnlyDeleteHandleAsync(WarningRuleEntity rule, List bos) + { + var ruleItemIds = bos.Select(x => x.Id).ToHashSet(); + await _warningRuleRepository.DeleteReturnRuleItemsAsync(ruleItemIds); + + // 更新规则主体版本号 + await _warningRuleRepository.UpdateVersionAsync(rule); + + // todo:暂时不处理后续逻辑 + return; + } + + private async Task HandleRuleItemsChangeAsync(WarningRuleEntity rule, + List addRuleItems, + List updateRuleItems, + List deleteRuleItems) + { + // 新增/修改/删除 实体,稍后做处理 + var addEntities = ReturnWarningRuleItemBO.ToEntities(addRuleItems); + var updateEntities = ReturnWarningRuleItemBO.ToEntities(updateRuleItems); + var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet(); + await _warningRuleRepository.BulkInsertReturnRuleItemsAsync(addEntities); + await _warningRuleRepository.BulkUpdateReturnRuleItemsAsync(updateEntities); + await _warningRuleRepository.DeleteReturnRuleItemsAsync(deleteRuleItemIds); + + // todo:暂时不启用后续规则 + return; + } + + private static bool CanNeedFollow(ReturnWarningRuleItemBO oldRule, ReturnWarningRuleItemBO postRule) + { + // 时效发生更改 + if (Math.Abs(oldRule.ReturnTimeSpan - postRule.ReturnTimeSpan) > 1.0) + return true; + + // 关注的轨迹状态减少 + var oldStatusIds = oldRule.TrackStatusIds; + var postStatusIds = postRule.TrackStatusIds; + foreach (var statusId in oldStatusIds) + if (!postStatusIds.Contains(statusId)) + return true; + + return false; + } + + #endregion + + + public List DiffFilterReturnWarningRuleItems(WarningRuleEntity rule, List postRuleItems) + { + throw new NotImplementedException(); + } + + public List FilterAndHandleNeedNotifyTask(List notifyTasks) + { + throw new NotImplementedException(); + } +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/TransportWarningHandler.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/TransportWarningHandler.cs new file mode 100644 index 00000000..49ba57e2 --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/TransportWarningHandler.cs @@ -0,0 +1,418 @@ +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Application.Services.Warning.Handlers.Warning; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Services.Warning.Handlers; + +public class TransportWarningHandler : ITransportWarningHandler +{ + private readonly IKpiAreaRepository _kpiAreaRepository; + private readonly IOrderRepository _orderRepository; + private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; + private readonly IWarningRuleRepository _warningRuleRepository; + private readonly IWarningTaskRepository _warningTaskRepository; + private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; + + public TransportWarningHandler(IWarningTaskRepository warningTaskRepository, IWarningDetailsEsRepository warningDetailsEsRepository, IWarningRuleRepository warningRuleRepository, IOrderRepository orderRepository, ITrackDetailsEsRepository trackDetailsEsRepository, IKpiAreaRepository kpiAreaRepository) + { + _warningTaskRepository = warningTaskRepository; + _warningDetailsEsRepository = warningDetailsEsRepository; + _warningRuleRepository = warningRuleRepository; + _orderRepository = orderRepository; + _trackDetailsEsRepository = trackDetailsEsRepository; + _kpiAreaRepository = kpiAreaRepository; + } + + #region 预警信号处理 + + public Task TrackDeletedHandleAsync(List deletedSignals) + { + throw new NotImplementedException(); + } + + public Task TrackAddedOrUpdatedHandleAsync(List addOrDeleteSignals) + { + throw new NotImplementedException(); + } + + #endregion + + + public Task WarningRuleToDisabledHandleAsync(WarningRuleEntity warningRule) + { + throw new NotImplementedException(); + } + + + #region todo: 规则场景变更 + + public async Task HandleSceneChangedAsync(WarningRuleEntity rule, TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene) + { + if (!CanSceneChange(oldScene, postScene)) return; + + // todo:暂时不做后续处理 + return; + } + + private bool CanSceneChange(TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene) + { + return oldScene.CargoType != postScene.CargoType || //多个 普货,危险品 + oldScene.Months != postScene.Months || //多个 约束提交的月份组从小到大 1/3/4,不能出现 4/1/3这种 (如果出现则SequenceEqual) + oldScene.AreaId != postScene.AreaId || // 单一,area1 + oldScene.IsTransit != postScene.IsTransit || //单一,true + oldScene.LeavePort != postScene.LeavePort || //多个 + oldScene.ProductCode != postScene.ProductCode; //多个 + } + + #endregion + + + #region todo: 规则项目变更 + + public async Task HandleEditAsync(WarningRuleEntity rule, TransportWarningSceneEntity scene, List postRuleItems) + { + var addRuleItems = postRuleItems.Where(x => x.Id == 0).ToList(); + + // 1.找到之前的配置项目 + var entities = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(rule.Id, scene.Id); + var oldRuleItems = TransportWarningSceneRuleItemBO.From(entities); + // 1.1之前的配置数量为0,做只新增处理 + if (oldRuleItems.Count == 0) + { + if (addRuleItems.Count != 0) + await OnlyAddHandleAsync(rule, scene, addRuleItems); + + return; + } + + // 1.2之前的配置数量不为0,提交的数量为0,做只删除处理 + if (oldRuleItems.Count != 0 && postRuleItems.Count == 0) + { + await OnlyDeleteHandleAsync(rule, scene, oldRuleItems); + return; + } + + // 可能同时存在 新增/修改/删除 + var updateRuleItems = new List(); + var deleteRuleItems = new List(); + + // 2.差异化比较 + // 2.1构建配置id字段 + var oldRuleIdDict = oldRuleItems.ToDictionary(x => x.Id); + var postRuleIdDict = postRuleItems + .Except(addRuleItems) //tips:忽略新增的规则条目 + .ToDictionary(x => x.Id); + var ruleItemIds = oldRuleIdDict.Keys.Concat(postRuleIdDict.Keys).ToHashSet(); + // 2.2比较 + foreach (var ruleItemId in ruleItemIds) + { + var oldRule = oldRuleIdDict.GetValueOrDefault(ruleItemId); + var postRule = postRuleIdDict.GetValueOrDefault(ruleItemId); + if (oldRule != null && postRule != null) + { + //规则属性变更,(起/止状态,时效变更) + if (CanNeedFollow(oldRule, postRule)) + updateRuleItems.Add(postRule); //更新 关注新的配置 + + continue; + } + + //规则条目被删除 + if (oldRule != null && postRule == null) + deleteRuleItems.Add(oldRule); //删除 关注旧配置 + } + + // 3.处理变更 + await HandleRuleItemsChangeAsync(rule, scene, addRuleItems, updateRuleItems, deleteRuleItems); + } + + private async Task OnlyAddHandleAsync(WarningRuleEntity rule, TransportWarningSceneEntity scene, List bos) + { + var entities = TransportWarningSceneRuleItemBO.ToEntities(bos); + await _warningRuleRepository.BulkInsertTransportSceneRuleItemsAsync(entities); + // 更新规则主体版本号 + await _warningRuleRepository.UpdateVersionAsync(rule); + } + + private async Task OnlyDeleteHandleAsync(WarningRuleEntity rule, TransportWarningSceneEntity scene, List bos) + { + var ruleItemIds = bos.Select(x => x.Id).ToHashSet(); + await _warningRuleRepository.DeleteTransportSceneRuleItemsAsync(ruleItemIds); + + // 更新规则主体版本号 + await _warningRuleRepository.UpdateVersionAsync(rule); + + // todo:暂时不处理后续逻辑 + return; + // 处理轨迹删除 + await HandleDeleteAsync(ruleItemIds); + } + + private async Task HandleRuleItemsChangeAsync( + WarningRuleEntity rule, + TransportWarningSceneEntity scene, + List addRuleItems, + List updateRuleItems, + List deleteRuleItems + ) + { + // 新增/修改/删除 实体,稍后做处理 + var addEntities = TransportWarningSceneRuleItemBO.ToEntities(addRuleItems); + var updateEntities = TransportWarningSceneRuleItemBO.ToEntities(updateRuleItems); + var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet(); + await _warningRuleRepository.BulkInsertTransportSceneRuleItemsAsync(addEntities); + await _warningRuleRepository.BulkUpdateTransportSceneRuleItemsAsync(updateEntities); + await _warningRuleRepository.DeleteTransportSceneRuleItemsAsync(deleteRuleItemIds); + + // todo:暂时不启用后续规则 + return; + // 1.查询变更配置相关的预警生成任务(未关闭的) + var updateRuleItemIds = updateRuleItems.Select(x => x.Id).ToHashSet(); + var generateTasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x => + !x.IsClosed + && x.WarningId == scene.WarningId + && updateRuleItemIds.Contains(x.WarningSceneRuleItemId) + ); + + // 2.查询变更配置相关的预警通知任务(未关闭的) + var warningNotifyTasks = await _warningTaskRepository.GetWarningNotifyTaskAsync(x => + !x.IsClosed + && x.WarningId == rule.Id + && x.WarningType == WarningRuleType.运输时效预警.GetHashCode() + && updateRuleItemIds.Contains(x.WarningRuleId) + ); + + // 3.查询变更配置相关的预警明细(即将超时/已超时) + var warningDetails = await _warningDetailsEsRepository.GetTransportWarningDetailsAsync( + updateRuleItemIds, + new HashSet(2) + { + TransportWarningStatus.即将超时.GetHashCode(), + TransportWarningStatus.已超时.GetHashCode() + } + ); + + // 构建处理快 + var handleBlocks = new List(generateTasks.Count + warningNotifyTasks.Count + warningDetails.Count); + // 使用【预警明细】【预警生成任务】【预警通知任务】填充处理块 + handleBlocks.AddRange(generateTasks.Select(x => new HandleBlock(x.OrderId, x.WarningSceneRuleItemId, x))); + handleBlocks.AddRange(warningNotifyTasks.Select(x => new HandleBlock(x.OrderId, x.WarningRuleId, x))); + handleBlocks.AddRange(warningDetails.Select(x => new HandleBlock(x.OrderId, x.WarningSceneRuleItemId, x))); + + // tips:无需在通过预警通知任务反找订单,因为产生预警通知任务时预警明细已经存在! + // 查询订单 + var orderIds = handleBlocks.Select(x => x.OrderId).ToHashSet(); + var orders = await _orderRepository.GetListAsync(x => orderIds.Contains(x.Id)); + orderIds = orders.Select(x => x.Id).ToHashSet(); + // 查询这些订单相关的轨迹 + var allOrderTracks = await _trackDetailsEsRepository.GetListAsync(orderIds.ToArray()); + var orderTrackStatusIdsDict = allOrderTracks.GroupBy(x => x.OrderId) + .ToDictionary(x => x.Key, y => y.ToList()); + + // 3.检查是否重新命中规则 + var ruleArea = scene.AreaId.HasValue ? await _kpiAreaRepository.GetFirstAsync(x => x.Id == scene.AreaId.Value) : null; + var updateRuleItemIdDict = updateRuleItems.ToDictionary(x => x.Id); + foreach (var group in handleBlocks.GroupBy(x => (x.OrderId, x.RuleItemId))) + { + var key = group.Key; + var ruleItem = updateRuleItemIdDict[key.RuleItemId]; + var orderTracks = orderTrackStatusIdsDict[key.OrderId]; + var earliestTrack = orderTracks + .Where(x => x.TrackStatusId == ruleItem.StartTrackStatusId) + .OrderBy(x => x.TrackTime) + .First(); + var orderStatusIds = orderTracks + .Select(x => x.TrackStatusId) + .ToHashSet(); + + var reHit = ruleItem.CanReHit(orderStatusIds); //存在起始,不存在截止,视为命中 + foreach (var block in group) + { + block.ReHitRule = reHit; + if (reHit && block.Type == HandleType.Detail) //重新命中的轨迹,需要更新预警信息中的规则描述,以及轨迹信息(起始) + { + // 配置相关信息 + block.Detail.WarningName = rule.Name; + block.Detail.WarningRuleDescription = block.Detail.GetWarningRuleDescription(scene, ruleItem.ToEntity(), ruleArea); + block.Detail.TransportTimeSpan = ruleItem.TransportTimeSpan; + block.Detail.StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan; + // 起始轨迹相关信息 + block.Detail.StartTrackStatusId = earliestTrack.TrackStatusId; + block.Detail.StartTrackStatus = earliestTrack.TrackStatus; + block.Detail.StartTrackContentId = earliestTrack.TrackContentId; + block.Detail.StartTrackContent = earliestTrack.TrackContent; + block.Detail.StartTrackTime = earliestTrack.TrackTime; //用于更新累计运输时间 + block.Detail.StartTrackTimeZone = earliestTrack.TrackTimeZone; + continue; + } + + // tips:更改后直接引发任务进行,等待下一轮任务扫描就会注意到 + if (reHit && block.Type == HandleType.GenerateTask) //重新命中的轨迹,需要更新预警生成任务 + { + // 更新生成任务当中的相关信息 + block.GenerateTask.StartTrackStatusId = earliestTrack.TrackStatusId; + block.GenerateTask.EndTrackStatusId = ruleItem.EndTrackStatusId; + block.GenerateTask.StartTrackTime = earliestTrack.TrackTime; + block.GenerateTask.StartTrackTimeZone = earliestTrack.TrackTimeZone; + block.GenerateTask.StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan; + block.GenerateTask.PlanExecutionTime = earliestTrack.TrackTime.AddHours(ruleItem.StartTrackWarningTimeSpan); + } + } + } + + // 重新命中规则的处理块 + var reHitRuleBlocks = handleBlocks.Where(x => x.ReHitRule).ToList(); + + // 3.1 更新预警明细(重新命中规则) + var updateWarningDetails = reHitRuleBlocks + .Where(x => x.Type == HandleType.Detail) + .Select(x => x.Detail) + .ToList(); + await _warningDetailsEsRepository.BulkUpdateTransportWarningDetailsAsync(updateWarningDetails); + + // 3.2 更新预警生成任务(重新命中规则) + var updateGenerateTasks = reHitRuleBlocks + .Where(x => x.Type == HandleType.GenerateTask) + .Select(x => x.GenerateTask) + .ToList(); + await _warningTaskRepository.BulkUpdateTransportWarningDetailsGenerateTaskAsync(updateGenerateTasks); + + // 3.3 处理需要关闭的【预警明细】【预警生成任务】【预警通知任务】(未命中规则) + var typeIdsDict = handleBlocks + .Except(reHitRuleBlocks) + .GroupBy(x => x.Type) + .ToDictionary(x => x.Key, y => + { + return y.Key switch + { + HandleType.Detail => y.Select(x => x.Detail.Id).ToHashSet(), + HandleType.GenerateTask => y.Select(x => x.GenerateTask.Id.ToString()).ToHashSet(), + HandleType.NotifyTask => y.Select(x => x.NotifyTask.Id.ToString()).ToHashSet(), + _ => throw new ArgumentOutOfRangeException() + }; + }); + + // 4.处理删除规则的相关联的【预警明细】【预警生成任务】【预警通知任务】 + // 预警信息状态修改为已关闭(即将超时/已超时) + var closeDetailIds = typeIdsDict.GetValueOrDefault(HandleType.Detail, new HashSet()); + await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(closeDetailIds); + + // 关闭相关的预警生成任务(未关闭的) + var closeGenerateTaskIds = typeIdsDict.GetValueOrDefault(HandleType.GenerateTask, new HashSet()); + await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x => + !x.IsClosed + && ( + closeGenerateTaskIds.Contains(x.Id.ToString()) + || (x.WarningId == rule.Id && deleteRuleItemIds.Contains(x.WarningSceneRuleItemId)) + ) + ); + + // 关闭关联的预警通知任务(未关闭的) + var closeNotifyTaskIds = typeIdsDict.GetValueOrDefault(HandleType.NotifyTask, new HashSet()); + await _warningTaskRepository.CloseWarningNotifyTaskAsync(x => + !x.IsClosed + && ( + closeNotifyTaskIds.Contains(x.Id.ToString()) + || (x.WarningId == rule.Id + && x.WarningType == WarningRuleType.运输时效预警.GetHashCode() + && deleteRuleItemIds.Contains(x.WarningRuleId)) + )); + } + + private async Task HandleDeleteAsync(HashSet deleteRuleItemIds) + { + // 处理删除规则的相关联的【预警明细】【预警生成任务】【预警通知任务】 + // 预警信息状态修改为已关闭(即将超时/已超时) + await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(deleteRuleItemIds); + + // 关闭相关的预警生成任务(未关闭的) + await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x => + !x.IsClosed + && deleteRuleItemIds.Contains(x.WarningSceneRuleItemId) + ); + + // 关闭关联的预警通知任务(未关闭的) + await _warningTaskRepository.CloseWarningNotifyTaskAsync(x => + !x.IsClosed + && x.WarningType == WarningRuleType.运输时效预警.GetHashCode() + && deleteRuleItemIds.Contains(x.WarningRuleId) + ); + } + + private static bool CanNeedFollow(TransportWarningSceneRuleItemBO oldRule, TransportWarningSceneRuleItemBO postRule) + { + if (oldRule.StartTrackStatusId != postRule.StartTrackStatusId) + return true; + + if (oldRule.EndTrackStatusId != postRule.EndTrackStatusId) + return true; + + if (Math.Abs(oldRule.TransportTimeSpan - postRule.TransportTimeSpan) > 1.0f) + return true; + + if (Math.Abs(oldRule.StartTrackWarningTimeSpan - postRule.StartTrackWarningTimeSpan) > 1.0f) + return true; + + return false; + } + + private enum HandleType + { + Detail = 0, + GenerateTask = 1, + NotifyTask = 2 + } + + private sealed class HandleBlock + { + public int OrderId { get; } + public int RuleItemId { get; } + public HandleType Type { get; } + +#nullable disable + public TransportWarningDetailsIndex Detail { get; } + public TransportWarningDetailsGenerateTaskEntity GenerateTask { get; } + public WarningNotifyTaskEntity NotifyTask { get; } + public bool ReHitRule { get; set; } //是否重新命中规则条目 + + private HandleBlock(int orderId, int ruleItemId) + { + OrderId = orderId; + RuleItemId = ruleItemId; + } + + public HandleBlock(int orderId, int ruleItemId, TransportWarningDetailsIndex detail) + : this(orderId, ruleItemId) + { + Type = HandleType.Detail; + Detail = detail; + } + + public HandleBlock(int orderId, int ruleItemId, TransportWarningDetailsGenerateTaskEntity task) + : this(orderId, ruleItemId) + { + Type = HandleType.GenerateTask; + GenerateTask = task; + } + + public HandleBlock(int orderId, int ruleItemId, WarningNotifyTaskEntity task) + : this(orderId, ruleItemId) + { + Type = HandleType.NotifyTask; + NotifyTask = task; + } + } + + #endregion + + public List FilterAndHandleNeedNotifyTask(List notifyTasks) + { + throw new NotImplementedException(); + } +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ICompleteWarningHandler.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ICompleteWarningHandler.cs new file mode 100644 index 00000000..3249df10 --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ICompleteWarningHandler.cs @@ -0,0 +1,24 @@ +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.Services.Warning.Handlers.Warning; + +public interface ICompleteWarningHandler : ITransientDependency +{ + // 处理轨迹 删除 todo:未实现,用于处理信号,需要支持批量处理 + Task TrackDeletedHandleAsync(List deletedSignals); + + // 处理轨迹 新增 todo:未实现,用于处理信号,需要支持批量处理 + Task TrackAddedHandleAsync(List addedSignals); + + // 完整预警主体禁用 todo:未实现 + Task WarningRuleToDisabledHandleAsync(WarningRuleEntity warningRule); + + // 处理完整预警规则项变更 + Task HandleEditAsync(WarningRuleEntity rule, List postRuleItems); + + // 预警通知任务可行性过滤 todo:未实现 + List FilterAndHandleNeedNotifyTask(List notifyTasks); +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/IReturnWarningHandler.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/IReturnWarningHandler.cs new file mode 100644 index 00000000..7faaf421 --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/IReturnWarningHandler.cs @@ -0,0 +1,28 @@ +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Domain.Aggregates.TrackPushAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.Services.Warning.Handlers.Warning; + +public interface IReturnWarningHandler : ITransientDependency +{ + // 处理轨迹 修改/删除 todo:未实现,用于处理信号,需要支持批量处理 + Task TrackUpdatedOrDeletedHandleAsync(List updateOrDeleteSignals); + + // 收到轨迹推送记录(接口通回调)(人工介入/重试 阶段的回传) todo:未实现 + Task ReceiveTrackPushRecordHandleAsync(TrackPushRecordIndex record); + + // 回传预警主体禁用 todo:未实现 + Task WarningRuleToDisabledHandleAsync(WarningRuleEntity warningRule); + + // 处理推送映射配置变更 todo:未实现 + Task PushConfigChangedHandleAsync(TrackPushConfigEntity pushConfig, TrackPushContentConfigEntity pushContentConfig); + + // 处理编辑回传预警规则项 + Task HandleEditAsync(WarningRuleEntity rule, List postRuleItems); + + // 预警通知任务可行性过滤 todo:未实现 + List FilterAndHandleNeedNotifyTask(List notifyTasks); +} diff --git a/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ITransportWarningHandler.cs b/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ITransportWarningHandler.cs new file mode 100644 index 00000000..7982f919 --- /dev/null +++ b/src/SJZY.Track.Application/Services/Warning/Handlers/Warning/ITransportWarningHandler.cs @@ -0,0 +1,31 @@ +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.Services.Warning.Handlers.Warning; + +/// +/// todo:提供业务模型的转化方法 +/// +public interface ITransportWarningHandler : ITransientDependency +{ + // 处理轨迹 删除 todo:用于处理信号,需要支持批量处理 + Task TrackDeletedHandleAsync(List deletedSignals); + + // 处理轨迹 新增/修改 todo:用于处理信号,需要支持批量处理 + Task TrackAddedOrUpdatedHandleAsync(List addOrDeleteSignals); + + // 运输预警主体禁用 todo:未实现 + Task WarningRuleToDisabledHandleAsync(WarningRuleEntity warningRule); + + // 运输预警场景变更 + Task HandleSceneChangedAsync(WarningRuleEntity rule, TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene); + + // 处理配置项列表保存 + Task HandleEditAsync(WarningRuleEntity rule, TransportWarningSceneEntity scene, List bos); + + // 预警通知可行性筛选 todo:未实现 + List FilterAndHandleNeedNotifyTask(List notifyTasks); +} diff --git a/src/SJZY.Track.Application/Services/Warning/ReturnWarningService.cs b/src/SJZY.Track.Application/Services/Warning/ReturnWarningService.cs index a687a53a..3b206961 100644 --- a/src/SJZY.Track.Application/Services/Warning/ReturnWarningService.cs +++ b/src/SJZY.Track.Application/Services/Warning/ReturnWarningService.cs @@ -1,4 +1,13 @@ +using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Track.Application.Contract.Dtos.Warning; using SJZY.Track.Application.Contract.Dtos.Warning.Return; +using SJZY.Track.Application.Helpers; +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Application.Services.Warning.Handlers.Warning; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Dtos.Warning; +using SJZY.Track.Domain.Enums; namespace SJZY.Track.Application.Services.Warning; @@ -9,35 +18,17 @@ namespace SJZY.Track.Application.Services.Warning; [Route("Warning/Return")] public class ReturnWarningService : ApplicationService { - /// - /// 编辑回传预警规则列表 - /// - /// - /// - [HttpPost("EditReturnWarningRuleItem")] - public Task EditReturnWarningRuleItem(EditReturnWarningRuleItemInput input) - { - return Task.FromResult(new UnifyResultDto()); - } + private readonly IReturnWarningHandler _returnWarningHandler; + private readonly IWarningRuleRepository _warningRuleRepository; + private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; + private readonly ITrackStatusRepository _trackStatusRepository; - /// - /// 分页获取回传预警明细 - /// - /// - /// - [HttpPost("GetPageReturnWarningDetails")] - public Task> GetPageReturnWarningDetails(GetPageReturnWarningDetailsInput input) + public ReturnWarningService(IWarningRuleRepository warningRuleRepository, IWarningDetailsEsRepository warningDetailsEsRepository, IReturnWarningHandler returnWarningHandler, ITrackStatusRepository trackStatusRepository) { - return Task.FromResult(new UnifyPageResultDto()); - } - - /// - /// 获取回传报文日志 - /// - [HttpPost("GetReturnMessageRecord")] - public Task>> GetReturnMessageRecord(GetReturnMessageRecordInput input) - { - return Task.FromResult(new UnifyResultDto>()); + _warningRuleRepository = warningRuleRepository; + _warningDetailsEsRepository = warningDetailsEsRepository; + _returnWarningHandler = returnWarningHandler; + _trackStatusRepository = trackStatusRepository; } /// @@ -46,9 +37,163 @@ public class ReturnWarningService : ApplicationService /// /// [HttpPost("GetReturnWarningRuleItem")] - public Task>> - GetReturnWarningRuleItem(GetReturnWarningRuleItemInput input) + public async Task>> GetReturnWarningRuleItem(GetReturnWarningRuleItemInput input) { - return Task.FromResult(new UnifyResultDto>()); + var result = await _warningRuleRepository.GetReturnRuleItemsAsync(input.WarningId); + + if (result.Count == 0) + return new UnifyResultDto>(true, new List()); + + // todo:缓存优化 + var trackStatusIds = result + .SelectMany(x => x.TrackStatusIds.Split(',').Select(int.Parse)) + .ToHashSet(); + var trackStatusDict = (await _trackStatusRepository.GetListAsync(x => trackStatusIds.Contains(x.Id), x => new + { + x.Id, + x.Status + })).ToDictionary(x => x.Id, y => y.Status); + + + var map = result.Select(x => new ReturnWarningRuleItemDto + { + Id = x.Id, + TrackStatus = BuildTrackStatus(x.TrackStatusIds), + ReturnTimeSpan = x.ReturnTimeSpan, + NotifyUsers = x.NotifyUsers.ToObject>() + }); + + return new UnifyResultDto>(true, map.ToList()); + + // todo:拆解优化 + List BuildTrackStatus(string statusIds) + { + return statusIds.Split(',') + .Select(x => + { + var id = int.Parse(x); + return new TrackStatusDto + { + TrackStatus = trackStatusDict.GetValueOrDefault(id), + TrackStatusId = id + }; + }).ToList(); + } + } + + /// + /// 分页获取回传预警明细 + /// + /// + /// + [HttpPost("GetPageReturnWarningDetails")] + public async Task> GetPageReturnWarningDetails(GetPageReturnWarningDetailsInput input) + { + var vo = ObjectMapper.Map(input); + var result = await _warningDetailsEsRepository.GetPageReturnWarningDetailsAsync(vo); + + // :填充子系统名称(冗余?) + // :填充客户简称(冗余?) + // :填充订单类型名称(冗余?) + var map = result.List.Select(x => new ReturnWarningDetailsDto + { + EsId = x.Id, + SubSystemId = x.SubSystemId, + SubSystemName = x.SubSystemName, + OrderType = x.OrderType, + OrderTypeName = x.OrderTypeName, + OrderId = x.OrderId, + OrderNo = x.OrderNo, + Status = x.Status, + CustomId = x.CustomId, + CustomName = x.CustomName, + // 轨迹信息 + TrackStatus = x.TrackStatus, + TrackCode = x.TrackCode, + TrackContent = x.TrackContent, + TrackPlace = x.TrackPlace, + TrackTime = x.TrackTime, + TrackTimeZone = x.TrackTimeZone, + TrackCreateTime = x.TrackCreateTime, + // 轨迹信息(映射后) + PushTrackCode = x.PushTrackCode, + PushTrackStatus = x.PushTrackStatus, + PushTrackContent = x.PushTrackContent, + // todo:计算属性 + ActualReturnTimeSpan = x.ActualReturnTimeSpan, + SystemReturnTimeSpan = x.SystemReturnTimeSpan, + TrackCreateTimeSpan = x.TrackCreateTimeSpan, + + // 具体描述 + TrackReturnTime = x.TrackReturnTime, + ReturnTimeSpan = x.ReturnTimeSpan, + IsExistsKpi = x.IsExistsKpi, + RetryCount = x.RetryCount, + NotifyUserNames = ToUserNames(x.NotifyUsers), + CreateTime = x.CreateTime, + }); + + var sorted = map.OrderBy(x => + { + // 人工介入置顶 + if (x.Status == 1) return x.Status; + // 其他按创建时间从早到晚 + return DateTimeHelper.ConvertToUnixTimestamp(x.CreateTime); + }); + + return new UnifyPageResultDto( + sorted.ToList(), + result.Pagination.PageIndex, + result.Pagination.PageSize, + result.Pagination.Total); + + // 通知用户提取用户名称 + string ToUserNames(string notifyUsers) + { + if (string.IsNullOrWhiteSpace(notifyUsers)) + return string.Empty; + return string.Join(',', notifyUsers.ToObject>().Select(x => x.UserName)); + } + } + + /// + /// 获取回传报文日志 + /// + [HttpPost("GetReturnMessageRecord")] + public async Task>> GetReturnMessageRecord(GetReturnMessageRecordInput input) + { + var result = await _warningDetailsEsRepository.GetReturnMessageRecordListByIdAsync(input.WarningDetailsId); + + var map = result.Select(x => new ReturnMessageRecordDto + { + EsId = x.Id, + ReturnWarningDetailsId = x.WarningDetailsId, + IsSuccess = x.IsSuccess, + Message = x.Message, + CreateTime = x.CreateTime + }); + + return new UnifyResultDto>(true, map.ToList()); + } + + + /// + /// 编辑回传预警规则列表 + /// + /// + /// + [HttpPost("EditReturnWarningRuleItem")] + public async Task EditReturnWarningRuleItem(EditReturnWarningRuleItemInput input) + { + var rule = await _warningRuleRepository.GetWarningRuleByIdAsync(input.WarningId, WarningRuleType.回传时效预警); + if (rule == null) + { + throw new BusinessException(message: $"预警规则主体不存在,WarningId=[{input.WarningId}]"); + } + + var bos = ReturnWarningRuleItemBO.From(input); + await _returnWarningHandler.HandleEditAsync(rule, bos); + + return new UnifyResultDto(true); } } diff --git a/src/SJZY.Track.Application/Services/Warning/TransportWarningService.cs b/src/SJZY.Track.Application/Services/Warning/TransportWarningService.cs index c9881318..c5899c38 100644 --- a/src/SJZY.Track.Application/Services/Warning/TransportWarningService.cs +++ b/src/SJZY.Track.Application/Services/Warning/TransportWarningService.cs @@ -1,4 +1,18 @@ +using System.Text; +using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Track.Application.Contract.Dtos.Warning; using SJZY.Track.Application.Contract.Dtos.Warning.Transport; +using SJZY.Track.Application.Helpers; +using SJZY.Track.Application.Services.Warning.Handlers.Model; +using SJZY.Track.Application.Services.Warning.Handlers.Warning; +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using SJZY.Track.Domain.Dtos.Warning; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Services.Warning; /// /// 运输时效预警服务 @@ -7,65 +21,259 @@ using SJZY.Track.Application.Contract.Dtos.Warning.Transport; [Route("Warning/Transport")] public class TransportWarningService : ApplicationService { + private readonly IWarningRuleRepository _warningRuleRepository; + private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; + private readonly ITransportWarningHandler _transportWarningHandler; + private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; + private readonly ITrackStatusRepository _trackStatusRepository; - /// - /// 编辑预警规则项目(全量) - /// - /// - /// - [HttpPost("EditTransportWarningSceneRuleItem")] - public Task - EditTransportWarningSceneRuleItem(EditTransportWarningSceneRuleItemInput input) - { - return Task.FromResult(new UnifyResultDto()); - } + public TransportWarningService(IWarningRuleRepository warningRuleRepository, IWarningDetailsEsRepository warningDetailsEsRepository, ITransportWarningHandler transportWarningHandler, ITrackDetailsEsRepository trackDetailsEsRepository, ITrackStatusRepository trackStatusRepository) + { + _warningRuleRepository = warningRuleRepository; + _warningDetailsEsRepository = warningDetailsEsRepository; + _transportWarningHandler = transportWarningHandler; + _trackDetailsEsRepository = trackDetailsEsRepository; + _trackStatusRepository = trackStatusRepository; + } + /// + /// 获取预警规则场景 + /// + /// + /// + [HttpPost("GetTransportWarningScene")] + public async Task>> GetTransportWarningScene(GetTransportWarningSceneInput input) + { + var entities = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId); - /// - /// 分页获取运输时效预警明细 - /// - /// - /// - [HttpPost("GetPageTransportWarningDetails")] - public Task> - GetPageTransportWarningDetails(GetPageTransportWarningDetailsInput input) - { - return Task.FromResult(new UnifyPageResultDto()); - } + var map = entities.Select(x => new TransportWarningSceneDto + { + Id = x.Id, + WarningId = x.WarningId, + Summary = BuildSummary(x), + IsTransit = x.IsTransit, + CargoType = x.CargoType, + LeavePort = x.LeavePort, + ProductCode = x.ProductCode, + Months = x.Months, + AreaId = x.AreaId + }); - /// - /// 获取预警规则场景 - /// - /// - /// - [HttpPost("GetTransportWarningScene")] - public Task>> - GetTransportWarningScene(GetTransportWarningSceneInput input) - { - return Task.FromResult(new UnifyResultDto>()); - } + return new UnifyResultDto>(true, map.ToList()); - /// - /// 获取运输时效场景规则项目 - /// - /// - /// - [HttpPost("GetTransportWarningSceneRuleItem")] - public Task>> - GetTransportWarningSceneRuleItem(GetTransportWarningSceneRuleItemInput input) - { - return Task.FromResult(new UnifyResultDto>()); - } + // 构建场景摘要 + string BuildSummary(TransportWarningSceneEntity entity) + { + var sb = new StringBuilder(); + // 货物类型 + if (!string.IsNullOrWhiteSpace(entity.CargoType)) + sb.Append($"{entity.CargoType}"); + // 月份 + if (!string.IsNullOrWhiteSpace(entity.Months)) + sb.Append($"/{entity.Months.Replace("/", string.Empty)}月份"); + // 中转 + if (entity.IsTransit != null) + sb.Append($"/中转为{(entity.IsTransit.Value ? "是" : "否")}"); + // 起运港口 + if (!string.IsNullOrWhiteSpace(entity.LeavePort)) + sb.Append($"/{entity.LeavePort}"); + // 产品编码 + if (!string.IsNullOrWhiteSpace(entity.ProductCode)) + sb.Append($"/{entity.ProductCode}"); - /// - /// 新增/更新 运输时效场景 - /// - /// - /// - [HttpPost("UpsertTransportWarningScene")] - public Task> - UpsertTransportWarningScene(UpsertTransportWarningSceneInput input) - { - return Task.FromResult(new UnifyResultDto()); - } + return sb.ToString(); + } + } + + /// + /// 获取运输时效场景规则项目 + /// + /// + /// + [HttpPost("GetTransportWarningSceneRuleItem")] + public async Task>> GetTransportWarningSceneRuleItem(GetTransportWarningSceneRuleItemInput input) + { + var entities = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(input.WarningId, input.SceneId); + + // todo:缓存优化 + var trackStatusIds = entities.SelectMany(x => new List(2) + { + x.StartTrackStatusId, + x.EndTrackStatusId + }).ToHashSet(); + var trackStatusDict = (await _trackStatusRepository.GetListAsync(x => trackStatusIds.Contains(x.Id), x => new + { + x.Id, + x.Status + })).ToDictionary(x => x.Id, y => y.Status); + + var map = entities.Select(x => new TransportWarningSceneRuleItemDto + { + Id = x.Id, + StartTrackStatusId = x.StartTrackStatusId, + StartTrackStatus = trackStatusDict.GetValueOrDefault(x.StartTrackStatusId), + EndTrackStatusId = x.EndTrackStatusId, + EndTrackStatus = trackStatusDict.GetValueOrDefault(x.EndTrackStatusId), + TransportTimeSpan = x.TransportTimeSpan, + StartTrackWarningTimeSpan = x.StartTrackWarningTimeSpan, + NotifyUsers = x.NotifyUsers.ToObject>() + }); + + return new UnifyResultDto>(true, map.ToList()); + } + + /// + /// 分页获取运输时效预警明细 + /// + /// + /// + [HttpPost("GetPageTransportWarningDetails")] + public async Task> GetPageTransportWarningDetails(GetPageTransportWarningDetailsInput input) + { + var vo = ObjectMapper.Map(input); + var result = await _warningDetailsEsRepository.GetPageTransportWarningDetailsAsync(vo); + if (result.List.Count == 0) + { + return new UnifyPageResultDto(new List(), input.PageIndex, input.PageSize, 0); + } + + var orderIds = result.List.Select(x => x.OrderId).ToHashSet(); + var orderTracks = await _trackDetailsEsRepository.GetLatestListAsync(orderIds); + var orderIdTrackDict = orderTracks.ToDictionary(x => x.OrderId); + + // :填充子系统名称(冗余?) + // :填充客户简称(冗余?) + // :填充订单类型名称(冗余?) + var map = result.List.Select(x => + { + var orderIdTrack = orderIdTrackDict.GetValueOrDefault(x.OrderId); + return new TransportWarningDetailsDto + { + EsId = x.Id, + SubSystemId = x.SubSystemId, + SubSystemName = x.SubSystemName, + CustomId = x.CustomId, + CustomName = x.CustomName, + OrderId = x.OrderId, + OrderNo = x.OrderNo, + OrderType = x.OrderType, + OrderTypeName = x.OrderTypeName, + NotifyUserNames = ToUserNames(x.NotifyUsers), + + // 最新轨迹 (不冗余) + LatestTrackContent = orderIdTrack?.TrackContent, + LatestTrackTime = orderIdTrack?.TrackTime, + LatestTrackTimeZone = orderIdTrack?.TrackTimeZone, + LatestTrackPlace = orderIdTrack?.TrackContent, + + // 起始轨迹 + StartTrackStatus = x.StartTrackStatus, + StartTrackContent = x.StartTrackContent, + StartTrackTime = x.StartTrackTime, + StartTrackTimeZone = x.StartTrackTimeZone, + + // 截止轨迹 + EndTrackStatus = x.EndTrackStatus, + EndTrackContent = x.EndTrackContent, + EndTrackTime = x.EndTrackTime, + EndTrackTimeZone = x.EndTrackTimeZone, + + WarningName = x.WarningName, + WarningRuleDescription = x.WarningRuleDescription, + + // 累计运输时间 + CumulativeTimeSpan = FormatCumulativeTimeSpan(x.CumulativeTimeSpan), + + Status = x.Status, + CreateTime = x.CreateTime + }; + }); + + var sorted = map.OrderBy(x => + { + // 即将超时,已超时置顶 + if (x.Status is 1 or 2) return x.Status; + // 其他按创建时间从早到晚 + return DateTimeHelper.ConvertToUnixTimestamp(x.CreateTime); + }); + + return new UnifyPageResultDto( + sorted.ToList(), + result.Pagination.PageIndex, + result.Pagination.PageSize, + result.Pagination.Total); + + // 通知用户提取用户名称 + string ToUserNames(string notifyUsers) + { + if (string.IsNullOrWhiteSpace(notifyUsers)) + return string.Empty; + return string.Join(',', notifyUsers.ToObject>().Select(x => x.UserName)); + } + + // 格式化累计运输时间为文本形式 + string FormatCumulativeTimeSpan(long cumulativeTimeSpan) + { + var timeSpan = TimeSpan.FromMilliseconds(cumulativeTimeSpan); + + return $"{timeSpan.Days:00}天{timeSpan.Hours:00}小时{timeSpan.Minutes:00}分钟"; + } + } + + /// + /// 新增/更新 运输时效场景 + /// + /// + /// + [HttpPost("UpsertTransportWarningScene")] + public async Task> UpsertTransportWarningScene(UpsertTransportWarningSceneInput input) + { + var rule = await _warningRuleRepository.GetWarningRuleByIdAsync(input.WarningId, WarningRuleType.运输时效预警); + if (rule == null) + { + throw new BusinessException(message: $"预警规则主体不存在,WarningId=[{input.WarningId}]"); + } + + var postBo = TransportWarningSceneBO.From(input); + // 查出旧场景 + var oldScene = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId, input.Id); + var oldBo = oldScene != null ? TransportWarningSceneBO.From(oldScene) : null; + + // 更新或者新增 + var id = await _warningRuleRepository.UpsertTransportSceneReturnIdAsync(postBo.ToEntity()); + + if (oldBo != null) + await _transportWarningHandler.HandleSceneChangedAsync(rule, oldBo, postBo); + + return new UnifyResultDto(true, new IdBaseDto + { + Id = id + }); + } + + /// + /// 编辑预警规则项目(全量) + /// + /// + /// + [HttpPost("EditTransportWarningSceneRuleItem")] + public async Task EditTransportWarningSceneRuleItem(EditTransportWarningSceneRuleItemInput input) + { + var rule = await _warningRuleRepository.GetWarningRuleByIdAsync(input.WarningId, WarningRuleType.运输时效预警); + if (rule == null) + { + throw new BusinessException(message: $"预警规则主体不存在,WarningId=[{input.WarningId}]"); + } + + var ruleScene = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId, input.SceneId); + if (ruleScene == null) + { + throw new BusinessException(message: $"运输时效规则场景不存在,WarningIdId=[{input.WarningId}] SceneId=[{input.SceneId}]"); + } + + var postRuleItems = TransportWarningSceneRuleItemBO.From(input); + await _transportWarningHandler.HandleEditAsync(rule, ruleScene, postRuleItems); + + return new UnifyResultDto(true); + } } diff --git a/src/SJZY.Track.Application/Services/Warning/WarningService.cs b/src/SJZY.Track.Application/Services/Warning/WarningService.cs index a0be7481..19d8f8a1 100644 --- a/src/SJZY.Track.Application/Services/Warning/WarningService.cs +++ b/src/SJZY.Track.Application/Services/Warning/WarningService.cs @@ -1,4 +1,6 @@ using SJZY.Track.Application.Contract.Dtos.Warning; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Queries; namespace SJZY.Track.Application.Services.Warning; @@ -9,6 +11,17 @@ namespace SJZY.Track.Application.Services.Warning; [Route("Warning")] public class WarningService : ApplicationService { + private readonly IWarningRuleRepository _warningRuleRepository; + private readonly IVSystemQueries _vSystemQueries; + private readonly IVExternalSubjectQueries _vExternalSubjectQueries; + + public WarningService(IWarningRuleRepository warningRuleRepository, IVSystemQueries vSystemQueries, IVExternalSubjectQueries vExternalSubjectQueries) + { + _warningRuleRepository = warningRuleRepository; + _vSystemQueries = vSystemQueries; + _vExternalSubjectQueries = vExternalSubjectQueries; + } + /// /// 新增/修改预警规则主体 /// @@ -17,18 +30,24 @@ public class WarningService : ApplicationService [HttpPost("UpsertWarningRule")] public async Task> UpsertWarningRule(UpsertWarningRuleInput input) { - // var id = await _warningRuleRepository.UpsertReturnIdAsync(new WarningRuleEntity( - // input.Id, - // input.Name, - // input.SubSystemId, - // input.CustomId, - // input.IsEnabled, - // input.WarningType) - // ); + // 要求 SubSystemId+CustomId+Name 联合不能重复 + if (await _warningRuleRepository.CheckNameDuplicateAsync(input.Id, input.SubSystemId, input.CustomId, input.Name)) + { + throw new BusinessException(message: $"SubSystemId:[{input.SubSystemId}],CustomId:[{input.CustomId}],已存在名称为[{input.Name}]规则,不允许重复添加/修改"); + } + + var id = await _warningRuleRepository.UpsertReturnIdAsync(new WarningRuleEntity( + input.Id, + input.Name, + input.SubSystemId, + input.CustomId, + input.IsEnabled, + input.WarningType) + ); return new UnifyResultDto(true, new IdBaseDto { - Id = input.Id + Id = id }); } @@ -38,9 +57,40 @@ public class WarningService : ApplicationService /// /// [HttpPost("GetWarningRule")] - public Task>> GetWarningRule(GetWarningRuleInput input) + public async Task>> GetWarningRule(GetWarningRuleInput input) { - //var result = await _warningRuleRepository.GetWarningRuleListAsync(input.SubSystemId, input.KeyWord, input.IsEnabled, input.WarningType); - throw new NotImplementedException(); + var result = await _warningRuleRepository.GetWarningRuleListAsync(input.SubSystemId, input.KeyWord, input.IsEnabled, input.WarningType); + if (result.Count == 0) + return new UnifyResultDto>(true, new List()); + + // todo:缓存优化 + var customIds = result.Select(x => x.CustomId).ToHashSet(); + var customDict = (await _vExternalSubjectQueries + .GetListAsync(x => customIds.Contains(x.Id), x => new + { + x.Id, + x.Abbreviation + })).ToDictionary(x => x.Id, x => x.Abbreviation); + + var subSystemIds = result.Select(x => x.SubSystemId).ToHashSet(); + var subSystemDict = (await _vSystemQueries + .GetListAsync(x => subSystemIds.Contains(x.Id), x => new + { + x.Id, + x.CnName + })).ToDictionary(x => x.Id, x => x.CnName); + + return new UnifyResultDto>(true, result.Select(x => new WarningRuleDto + { + Id = x.Id, + SubSystemId = x.SubSystemId, + SubSystemName = customDict.GetValueOrDefault(x.SubSystemId), + CustomId = x.CustomId, + CustomName = subSystemDict.GetValueOrDefault(x.CustomId), + Name = x.Name, + IsEnabled = x.IsEnabled, + WarningType = x.WarningType, + Version = x.Version + }).ToList()); } } diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs index 7548d8d8..718124c6 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs @@ -10,7 +10,7 @@ public interface IOrderRepository : IBasicRepository /// key:OrderType value:TypeName /// /// - Task> GetTrackOrderTypeDict(); + Task> GetTrackOrderTypeDictAsync(); /// /// 获取分页的轨迹订单 diff --git a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs index ecd623be..916f6ded 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs @@ -8,8 +8,7 @@ public interface ITrackDetailsEsRepository : IElasticsearchClientService> GetListAsync(int[] orderIds, string trackCode, bool forUpsert = false); - // todo:s07 获取这些订单的最新一条轨迹 - // Task> GetLatestListAsync(HashSet eventOrderIds); + Task> GetLatestListAsync(HashSet eventOrderIds); Task> GetLatestListAsync(int[] eventOrderIds, List trackCodes); diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningDetailsIndex.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningDetailsIndex.cs new file mode 100644 index 00000000..eb124570 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningDetailsIndex.cs @@ -0,0 +1,45 @@ +using Nest; +using SJZY.Expand.ElasticSearch; +using SJZY.Track.Domain.Consts; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; + +[ElasticsearchType(RelationName = GlobalConst.CompleteWarningDetailsIndex)] +public sealed class CompleteWarningDetailsIndex : IdBaseIndex +{ + public string IndexName { get; set; } + public int WarningId { get; set; } + public int CompleteWarningRuleId { get; set; } + public string Version { get; set; } + + //public string NotifyModes { get; set; } //通知方式,多个方式(,)分割 eg: 1,2 + public string NotifyUsers { get; set; } //通知人Json eg: [{"UserId":1,"UserName":"张三"},{"UserId":2,"UserName":"李四"}] + public int CheckPointTrackStatusId { get; set; } // (冗余)检查点轨迹状态Id + public string MissingTrackStatusIds { get; set; } //(冗余)缺失的轨迹状态 + + //订单信息 + public int SubSystemId { get; set; } //(冗余)子系统Id + public string SubSystemName { get; set; } //(冗余)子系统名称 + public int CustomId { get; set; } //(冗余)客户Id + public string CustomName { get; set; } //(冗余)客户名称 + public int OrderId { get; set; } + public string OrderNo { get; set; } //订单号 + public int OrderType { get; set; } //(冗余)订单类型 + public string OrderTypeName { get; set; } //(冗余)订单类型名称 + + //可变属性 + public int Status { get; set; } // 1:待处理,2:已处理,3:已关闭 + + //视图属性(todo:查es 最新) + // public int LatestTrackStatusId { get; set; } //最新轨迹状态Id + // public string LatestTrackStatus { get; set; } //(冗余)最新轨迹状态 + // public int LatestTrackContentId { get; set; } //最新轨迹内容Id + // public string LatestTrackContent { get; set; } //(冗余)最新轨迹内容名称 + // public DateTime LatestTrackTime { get; set; } //最新轨迹发生时间 + // public float LatestTrackTimeZone { get; set; } //最新轨迹发生时区 + // public string LatestTrackPlace { get; set; } //最新轨迹发生地 + + //审计属性 + public DateTime CreateTime { get; set; } // 预警创建时间 + public DateTime? ProcessedTime { get; set; } // 预警处理时间 +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningRuleItemEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningRuleItemEntity.cs new file mode 100644 index 00000000..04921828 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Complete/CompleteWarningRuleItemEntity.cs @@ -0,0 +1,84 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; + +[SugarTable("t_warning_complete_rule_item", TableDescription = "完整预警规则项")] +public sealed class CompleteWarningRuleItemEntity : IFullAuditedBasicEntitys, IEntity +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则主体id")] + public int WarningId { get; set; } + + [SugarColumn(ColumnName = "OrderTypes", ColumnDescription = "订单类型集合(,分割)")] + public string OrderTypes { get; set; } //订单类型, 逗号分隔(1,3) + + [SugarColumn(ColumnName = "CheckPointTrackStatusId", ColumnDescription = "检查点轨迹状态id")] + public int CheckPointTrackStatusId { get; set; } //检查点轨迹状态id + + [SugarColumn(ColumnName = "RequireTrackStatusIdExpression", ColumnDescription = "要求该订单持有的轨迹状态的表达式 S1/S2,S3 代表: 存在状态S1或者S2并且存在S3才算")] + public string RequireTrackStatusIdExpression { get; set; } //要求该订单持有的轨迹状态的表达式 S1/S2,S3 代表: 存在状态S1或者S2并且存在S3 + + // [SugarColumn(ColumnName = "NotifyModes", ColumnDescription = "通知方式,多个方式(,)分割 eg: 1,2")] + // public string NotifyModes { get; set; } //通知方式,多个方式(,)分割 eg: 1,2 + + [SugarColumn(ColumnName = "NotifyUsers", ColumnDescription = "通知人Json eg: [{\"UserId\":1,\"UserName\":\"张三\"},{\"UserId\":2,\"UserName\":\"李四\"}]")] + public string NotifyUsers { get; set; } //通知人Json eg: [{"UserId":1,"UserName":"张三"},{"UserId":2,"UserName":"李四"}] + + public CompleteWarningRuleItemEntity() + { + } + + + public CompleteWarningRuleItemEntity(int id, int warningId, string orderTypes, int checkPointTrackStatusId, string requireTrackStatusIdExpression, string notifyUsers) + { + Id = id; + WarningId = warningId; + OrderTypes = orderTypes; + CheckPointTrackStatusId = checkPointTrackStatusId; + RequireTrackStatusIdExpression = requireTrackStatusIdExpression; + NotifyUsers = notifyUsers; + } + + public HashSet GetRuleOrderTypes() + { + return OrderTypes.Split(',').Select(int.Parse).ToHashSet(); + } + + public List> GetRequireTrackStatusIdGroups() + { + return RequireTrackStatusIdExpression + .Split(",") + .Select(g => g.Split('/').Select(int.Parse).ToHashSet()) + .ToList(); + } + + // 查看是否能命中这个规则 + public bool CanHitRule(int orderType, int trackStatusId) + { + var orderTypes = GetRuleOrderTypes(); + //没有命中订单类型 + if (!orderTypes.Contains(orderType)) return false; + //没有命中状态检查点 + if (trackStatusId != CheckPointTrackStatusId) return false; + + return true; + } + + // 根据轨迹状态id集合判断是否缺失轨迹状态 + public bool ExistsMissingTrackStatus(HashSet trackStatusIds) + { + if (trackStatusIds.Count == 0) return true; + + //组之间是并且关系,组内为或的关系 + var groups = GetRequireTrackStatusIdGroups(); + + // 当没有某个组没有交集产生,则认定为轨迹缺失 + foreach (var g in groups) + { + if (!g.Intersect(trackStatusIds).Any()) + return true; + } + + return false; + } +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningDetailsEsRepository.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningDetailsEsRepository.cs new file mode 100644 index 00000000..335e2337 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningDetailsEsRepository.cs @@ -0,0 +1,85 @@ +using SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using SJZY.Track.Domain.Dtos.Warning; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate; + +public interface IWarningDetailsEsRepository +{ + #region 运输预警明细 + + // 分页获取运输预警明细 + Task> GetPageTransportWarningDetailsAsync(GetPageTransportWarningDetailsInputVO input); + + // 获取运输预警明细(预警规则id+状态) + Task> GetTransportWarningDetailsAsync(HashSet ruleItemIds, HashSet status); + + // 更新运输预警明细 + Task UpdateTransportWarningDetailsAsync(TransportWarningDetailsIndex detail); + + // 批量更新运输预警明细 + Task BulkUpdateTransportWarningDetailsAsync(List details, int maxRetries = 3); + + // 批量关闭运输预警明细 + Task BulkCloseTransportWarningDetailsAsync(HashSet detailIds, int maxRetries = 3); + // 批量关闭运输预警明细 + Task BulkCloseTransportWarningDetailsAsync(HashSet ruleItemIds, int maxRetries = 3); + + // 新增运输预警明细 + Task InsertTransportWarningDetailsAsync(TransportWarningDetailsIndex detail); + + // 批量新增运输预警明细 + Task BulkInsertTransportWarningDetailsAsync(List details, int maxRetries = 3); + + #endregion + + #region 回传预警明细 + + // 分页获取回传预警明细 + Task> GetPageReturnWarningDetailsAsync(GetPageReturnWarningDetailsInputVO input); + + // 获取回传日志列表 + Task> GetReturnMessageRecordListByIdAsync(string esId); + + // 更新回传预警明细 + Task UpdateReturnWarningDetailsAsync(ReturnWarningDetailsIndex detail); + + // 批量更新回传预警明细 + Task BulkUpdateReturnWarningDetailsAsync(List details, int maxRetries = 3); + + // 新增回传预警明细 + Task InsertReturnWarningDetailsAsync(ReturnWarningDetailsIndex detail); + + // 批量新增回传预警明细 + Task BulkInsertReturnWarningDetailsAsync(List details, int maxRetries = 3); + + // 新增回传日志 + Task InsertReturnMessageRecordAsync(ReturnMessageRecordIndex detail); + + // 批量新增回传日志 + Task BulkInsertReturnMessageRecordAsync(List details, int maxRetries = 3); + + #endregion + + #region 完整预警明细 + + // 分页获取完整预警明细 + Task> GetPageCompleteWarningDetailsAsync(GetPageCompleteWarningDetailsInputVO input); + + // 更新完整预警明细 + Task UpdateCompleteWarningDetailsAsync(CompleteWarningDetailsIndex detail); + + // 批量完整回传预警明细 + Task BulkUpdateCompleteWarningDetailsAsync(List details, int maxRetries = 3); + + // 新增完整预警明细 + Task InsertCompleteWarningDetailsAsync(CompleteWarningDetailsIndex detail); + + // 批量新增完整预警明细 + Task BulkInsertCompleteWarningDetailsAsync(List details, int maxRetries = 3); + + #endregion + + Task CreateAliasAsync(); +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs new file mode 100644 index 00000000..59415873 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs @@ -0,0 +1,92 @@ +using SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate; + +/// +/// 预警规则仓储 +/// todo:需要使用vo +/// +public interface IWarningRuleRepository : IBasicRepository +{ + #region 规则主体 + + // 检查名称是否重复 + Task CheckNameDuplicateAsync(int id,int subsystemId, int customId, string name); + + // 新增/更新预警规则主体,并返回id + Task UpsertReturnIdAsync(WarningRuleEntity entity); + + // 更新版本号 + Task UpdateVersionAsync(WarningRuleEntity entity); + + // 模糊查询预警规则主体 + Task> GetWarningRuleListAsync(int? subsystemId, string? keyword, bool? isEnabled, int? warningType); + + // 获取启用的预警规则主体 + Task GetWarningRuleForEnabledAsync(int subSystemId, int customId, WarningRuleType warningType); + + // 获取预警规则主体根据id + Task GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null); + + #endregion + + #region 运输时效预警规则 + + // 获取运输预警场景列表 + Task> GetTransportSceneAsync(int warningId); + + // 获取运输预警场景列表 + Task GetTransportSceneAsync(int warningId, int sceneId); + + // 全量获取的运输时效场景规则项目 + Task> GetTransportSceneRuleItemsAsync(int warningId, int sceneId); + + // 新增/更新 运输预警场景 + Task UpsertTransportSceneReturnIdAsync(TransportWarningSceneEntity entity); + + // 批量更新运输时效场景规则项目 + Task BulkUpdateTransportSceneRuleItemsAsync(List entities); + + // 批量新增运输时效场景规则项目 + Task BulkInsertTransportSceneRuleItemsAsync(List entities); + + // 删除运输预警场景规则项目 + Task DeleteTransportSceneRuleItemsAsync(HashSet ruleItemIds); + + #endregion + + #region 回传时效预警规则 + + // 获取全量回传预警规则规则项目 + Task> GetReturnRuleItemsAsync(int warningId); + + // 批量新增回传时效场景规则项目 + Task BulkInsertReturnRuleItemsAsync(List entities); + + // 批量更新回传预警规则规则项目 + Task BulkUpdateReturnRuleItemsAsync(List entities); + + // 删除回传预警规则项目 + Task DeleteReturnRuleItemsAsync(HashSet ruleItemIds); + + #endregion + + #region 完整预警规则 + + // 获取全量预警规则规则项目 + Task> GetCompleteRuleItemsAsync(int warningId); + + // 批量更新预警规则规则项目 + Task BulkUpdateCompleteRuleItemsAsync(List entities); + + // 批量新增完整时效规则项目 + Task BulkInsertCompleteRuleItemsAsync(List entities); + + // 删除完整预警场景规则项目 + Task DeleteCompleteRuleItemsAsync(HashSet ruleItemIds); + + #endregion +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs new file mode 100644 index 00000000..367e8998 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs @@ -0,0 +1,76 @@ +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate; + +public interface IWarningTaskRepository +{ + #region 预警通知任务 + + // 初步扫描出可能要进行的预警通知任务 + Task> ScanMayNeedExecutedTaskAsync(); + + // 获取预警通知任务 + Task> GetWarningNotifyTaskAsync(Expression> expression); + + // 添加一个预警通知任务 + Task AddWarningNotifyTaskAsync(WarningNotifyTaskEntity entity); + + // 批量添加预警通知任务 + Task BulkInsertWarningNotifyTaskAsync(List entities); + + // 关闭预警通知任务 + Task CloseWarningNotifyTaskAsync(Expression> expression); + + // 关闭预警通知任务 + Task CloseWarningNotifyTaskAsync(WarningNotifyTaskEntity entity); + + #endregion + + #region 回传预警_重试推送任务 + + // 扫描重试推送任务 + Task> ScanReturnWarningRetryPushTaskAsync(); + + // 条件获取重试推送任务 + Task> GetReturnWarningRetryPushTaskAsync(Expression> expression); + + // 添加回传预警_重试推送任务 + Task AddReturnWarningRetryPushTaskAsync(ReturnWarningRetryPushTaskEntity entity); + + // 批量添加回传预警_重试推送任务 + Task BulkInsertReturnWarningRetryPushTaskAsync(List entities); + + // 条件关闭回传预警_重试推送任务 + Task CloseReturnWarningRetryPushTaskAsync(Expression> expression); + + // 关闭回传预警_重试推送任务 + Task CloseReturnWarningRetryPushTaskAsync(ReturnWarningRetryPushTaskEntity entity); + + #endregion + + #region 运输预警_预警明细生成任务 + + // 扫描预警明细生成任务 + Task> ScanTransportWarningDetailsGenerateTaskAsync(); + + // 条件获取运输预警明细生成任务 + Task> GetTransportWarningDetailsGenerateTaskAsync(Expression> expression); + + // 添加运输预警明细生成任务 + Task AddTransportWarningDetailsGenerateTaskAsync(TransportWarningDetailsGenerateTaskEntity entity); + + // 批量添加运输预警明细生成任务 + Task BulkInsertTransportWarningDetailsGenerateTaskAsync(List entities); + + // 批量更新运输预警明细生成任务 + Task BulkUpdateTransportWarningDetailsGenerateTaskAsync(List entities); + + // 条件关闭运输预警明细生成任务 + Task CloseTransportWarningDetailsGenerateTaskAsync(Expression> expression); + + // 关闭运输预警明细生成任务 + Task CloseTransportWarningDetailsGenerateTaskAsync(TransportWarningDetailsGenerateTaskEntity entity); + + #endregion +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnMessageRecordIndex.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnMessageRecordIndex.cs new file mode 100644 index 00000000..1915550c --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnMessageRecordIndex.cs @@ -0,0 +1,17 @@ +using Nest; +using SJZY.Expand.ElasticSearch; +using SJZY.Track.Domain.Consts; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Return; + +[ElasticsearchType(RelationName = GlobalConst.ReturnMessageRecordIndex)] +public class ReturnMessageRecordIndex : IdBaseIndex +{ + public string IndexName { get; set; } + public string WarningDetailsId { get; set; } //回传预警明细Id + public bool IsSuccess { get; set; } //是否成功 + public string Message { get; set; } //报文内容 Json字符串 + + //审计属性 + public DateTime CreateTime { get; set; } //报文创建时间 +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningDetailsIndex.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningDetailsIndex.cs new file mode 100644 index 00000000..a064b689 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningDetailsIndex.cs @@ -0,0 +1,82 @@ +using Nest; +using SJZY.Expand.ElasticSearch; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Extensions; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Return; + +// 回传时效预警明细 +[ElasticsearchType(RelationName = GlobalConst.ReturnWarningDetailsIndex)] +public class ReturnWarningDetailsIndex : IdBaseIndex +{ + public string IndexName { get; set; } + //是否存在KPI考核,根据是否命中规则 + public bool IsExistsKpi => ReturnWarningRuleId != null && ReturnWarningRuleId > 0; + public int? WarningId { get; set; } + public int? ReturnWarningRuleId { get; set; } + public string? Version { get; set; } //预警规则版本 + public float? ReturnTimeSpan { get; set; } //(冗余)回传时效(单位小时) + //public string NotifyModes { get; set; } //通知方式,多个方式(,)分割 eg: 1,2 + public string NotifyUsers { get; set; } //通知人Json eg: [{"UserId":1,"UserName":"张三"},{"UserId":2,"UserName":"李四"}] + + //订单信息 + public int SubSystemId { get; set; } //(冗余)子系统Id + public string SubSystemName { get; set; } //(冗余)子系统名称 + public int CustomId { get; set; } //(冗余)客户Id + public string CustomName { get; set; } //(冗余)客户名称 + public int OrderId { get; set; } + public string OrderNo { get; set; } //订单号 + public int OrderType { get; set; } //(冗余)订单类型 + public string OrderTypeName { get; set; } //(冗余)订单类型名称 + + //轨迹信息-原始 + public int TrackStatusId { get; set; } + public string TrackStatus { get; set; } //(冗余)轨迹状态名称 + public string? CompareFields { get; set; } //(冗余)比较字段 + public string? Extra { get; set; } //扩展字段 + public int TrackContentId { get; set; } + public string TrackContent { get; set; } + public string TrackCode { get; set; } + public string TrackPlace { get; set; } //轨迹发生地 + public DateTime TrackTime { get; set; } //轨迹发生时间 + public float TrackTimeZone { get; set; } //轨迹发生时区 + public DateTime TrackCreateTime { get; set; } //轨迹创建时间 + + //轨迹信息-映射后 + public string PushTrackStatus { get; set; } //(冗余)推送轨迹状态 + public string PushTrackCode { get; set; } + public string PushTrackContent { get; set; } + public string PushTrackEnContent { get; set; } + + //可变属性 + public DateTime? TrackReturnTime { get; set; } //轨迹回传时间, tips:只有回传成功了才写入 + public int RetryCount { get; set; } // 回传重试次数 + public int Status { get; set; } // 1:人工介入 2:重试中 3:推送完成 4:已关闭 + + //计算属性 + public long? ActualReturnTimeSpan { get; set; } //实际回传用时(单位毫秒) + public long? SystemReturnTimeSpan { get; set; } //系统回传用时 (单位毫秒) + public long TrackCreateTimeSpan { get; set; } //轨迹创建用时(单位毫秒) + + //审计属性 + public DateTime CreateTime { get; set; } // 预警创建时间 + public DateTime? UpdateTime { get; set; } // 明细更新时间 + + public bool MatchExtra(string? compareFields, string? extra) + { + // 匹配字段为空,明细明细字段为空 + if (string.IsNullOrWhiteSpace(compareFields)) + return string.IsNullOrWhiteSpace(CompareFields); + + if (string.IsNullOrWhiteSpace(CompareFields)) + return false; + + if (!string.Equals(CompareFields, compareFields)) //todo:当比较字段存放到轨迹明细中,后面修改了比较字段后会发生什么? + return false; + + if (string.IsNullOrEmpty(extra) || string.IsNullOrEmpty(Extra)) + return false; + + return TextJsonHelper.EqJsonStringField(extra, Extra, compareFields); + } +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRetryPushTaskEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRetryPushTaskEntity.cs new file mode 100644 index 00000000..73c626dc --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRetryPushTaskEntity.cs @@ -0,0 +1,41 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Return; + +// 预警重试推送任务 +[SugarTable("t_warning_return_retry_task", TableDescription = "回传预警重试推送任务")] +public sealed class ReturnWarningRetryPushTaskEntity : IFullAuditedBasicEntitys, IEntity +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "OrderId", ColumnDescription = "订单Id")] + public int OrderId { get; set; } + + [SugarColumn(ColumnName = "WarningDetailId", ColumnDescription = "预警明细id")] + public string WarningDetailId { get; set; } //预警明细id + + // 轨迹明细 + [SugarColumn(ColumnName = "TrackCode", ColumnDescription = "轨迹code")] + public string TrackCode { get; set; } + + [SugarColumn(ColumnName = "TrackContent", ColumnDescription = "轨迹内容")] + public string TrackContent { get; set; } + + [SugarColumn(ColumnName = "CompareFields", ColumnDescription = "比较字段")] + public string? CompareFields { get; set; } //轨迹明细中的比较字段 + + [SugarColumn(ColumnName = "Extra", ColumnDescription = "扩展字段")] + public string? Extra { get; set; } //轨迹明细中的扩展字段 + + + [SugarColumn(ColumnName = "OptionType", ColumnDescription = "操作类型")] + public string OptionType { get; set; } //操作类型, + + [SugarColumn(ColumnName = "ServiceProviderCode", ColumnDescription = "服务商code")] + public string ServiceProviderCode { get; set; } //服务商code + + [SugarColumn(ColumnName = "PushContent", ColumnDescription = "推送内容")] + public string PushContent { get; set; } //推送数据的序列化值,为TrackPushInput/TrackUpdateInput集合 + + [SugarColumn(ColumnName = "IsClosed", ColumnDescription = "是否关闭")] + public bool IsClosed { get; set; } // 已关闭的任务不会在执行 +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRuleItemEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRuleItemEntity.cs new file mode 100644 index 00000000..8f734a61 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Return/ReturnWarningRuleItemEntity.cs @@ -0,0 +1,47 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Return; + +// 回传预警规则项 +[SugarTable("t_warning_return_rule_item", TableDescription = "回传预警规则项")] +public sealed class ReturnWarningRuleItemEntity : IFullAuditedBasicEntitys, IEntity +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则主体id")] + public int WarningId { get; set; } + + [SugarColumn(ColumnName = "TrackStatusIds", ColumnDescription = "关注的轨迹状态Id集合")] + public string TrackStatusIds { get; set; } //关注的轨迹状态Id,可以是多个使用,分割 + + [SugarColumn(ColumnName = "ReturnTimeSpan", ColumnDescription = "时效(单位小时)")] + public float ReturnTimeSpan { get; set; } //时效(单位小时),只是用于判断状态 + + // [SugarColumn(ColumnName = "NotifyModes", ColumnDescription = "通知方式,多个方式(,)分割 eg: 1,2")] + // public string NotifyModes { get; set; } //通知方式,多个方式(,)分割 eg: 1,2 + + [SugarColumn(ColumnName = "NotifyUsers", ColumnDescription = "通知人Json eg: [{\"UserId\":1,\"UserName\":\"张三\"},{\"UserId\":2,\"UserName\":\"李四\"}]")] + public string NotifyUsers { get; set; } //通知人Json eg: [{"UserId":1,"UserName":"张三"},{"UserId":2,"UserName":"李四"}] + + public ReturnWarningRuleItemEntity() + { + } + + public ReturnWarningRuleItemEntity(int id, int warningId,string trackStatusIds, float returnTimeSpan, string notifyUsers) + { + Id = id; + WarningId = warningId; + TrackStatusIds = trackStatusIds; + ReturnTimeSpan = returnTimeSpan; + NotifyUsers = notifyUsers; + } + + public HashSet GetRuleStatusIds() + { + return TrackStatusIds.Split(',').Select(int.Parse).ToHashSet(); + } + + public bool CanHitRule(int trackStatusId) + { + return GetRuleStatusIds().Contains(trackStatusId); + } +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/IWarningSignal.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/IWarningSignal.cs new file mode 100644 index 00000000..d4fe7cbd --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/IWarningSignal.cs @@ -0,0 +1,16 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; + +public interface IWarningSignal +{ + public int OrderId { get; set; } + public WarningSignalType Type { get; set; } +} + +public enum WarningSignalType +{ + 轨迹新增 = 0, + + 轨迹删除 = 1, + + 轨迹更新 = 2, +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackAddedSignal.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackAddedSignal.cs new file mode 100644 index 00000000..6e3f2bcf --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackAddedSignal.cs @@ -0,0 +1,13 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; + +/// +/// 轨迹新增信号 +/// +public sealed class TrackAddedSignal : IWarningSignal +{ + public WarningSignalType Type { get; set; } = WarningSignalType.轨迹新增; + + public int OrderId { get; set; } + + public string EsId { get; set; } //新增轨迹的esId +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackDeletedSignal.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackDeletedSignal.cs new file mode 100644 index 00000000..564669a2 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackDeletedSignal.cs @@ -0,0 +1,15 @@ +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; + +/// +/// 轨迹删除信号 +/// +public class TrackDeletedSignal : IWarningSignal +{ + public WarningSignalType Type { get; set; } = WarningSignalType.轨迹删除; + + public int OrderId { get; set; } + + public TrackDetailsIndex DeletedTrack { get; set; } //已删除的轨迹 +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackUpdatedSignal.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackUpdatedSignal.cs new file mode 100644 index 00000000..dd0e25be --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Signals/TrackUpdatedSignal.cs @@ -0,0 +1,16 @@ +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Signals; + + +/// +/// 轨迹更新信号 +/// +public class TrackUpdatedSignal:IWarningSignal +{ + public WarningSignalType Type { get; set; } = WarningSignalType.轨迹更新; + + public int OrderId { get; set; } + + public TrackDetailsIndex BeforeChangeTrack { get; set; } //更改前的轨迹明细 +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsGenerateTaskEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsGenerateTaskEntity.cs new file mode 100644 index 00000000..4ce986e7 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsGenerateTaskEntity.cs @@ -0,0 +1,44 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; + +[SugarTable("t_warning_transport_generate_task", TableDescription = "运输时效预警明细生成任务")] +[SugarIndex("IDX_IsClosed_PlanExecutionTime", nameof(IsClosed), OrderByType.Desc, nameof(PlanExecutionTime), OrderByType.Asc)] +public sealed class TransportWarningDetailsGenerateTaskEntity : IFullAuditedBasicEntitys, IEntity +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "OrderId", ColumnDescription = "订单Id")] + public int OrderId { get; set; } + + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警主体Id")] + public int WarningId { get; set; } + + [SugarColumn(ColumnName = "WarningSceneId", ColumnDescription = "运输时效场景Id")] + public int WarningSceneId { get; set; } + + [SugarColumn(ColumnName = "WarningSceneRuleItemId", ColumnDescription = "运输时效预警规则项Id")] + public int WarningSceneRuleItemId { get; set; } //运输时效预警规则项目Id + + + [SugarColumn(ColumnName = "StartTrackStatusId", ColumnDescription = "起始轨迹状态Id")] + public int StartTrackStatusId { get; set; } //起始轨迹状态Id + + [SugarColumn(ColumnName = "EndTrackStatusId", ColumnDescription = "截止轨迹状态Id")] + public int EndTrackStatusId { get; set; } //截止轨迹状态Id + + [SugarColumn(ColumnName = "StartTrackTime", ColumnDescription = "起始轨迹发生时间")] + public DateTime StartTrackTime { get; set; } //起始轨迹发生时间 + + [SugarColumn(ColumnName = "StartTrackTimeZone", ColumnDescription = "起始轨迹发生时区")] + public float StartTrackTimeZone { get; set; } //起始轨迹发生时区 + + [SugarColumn(ColumnName = "StartTrackWarningTimeSpan", ColumnDescription = "起始轨迹生成预警时间")] + public float StartTrackWarningTimeSpan { get; set; } //起始轨迹生成预警时间 (单位小时) + + [SugarColumn(ColumnName = "PlanExecutionTime", ColumnDescription = "计划执行时间")] + public DateTime PlanExecutionTime { get; set; } //计划执行时间 tips:缓存【起始轨迹发生时间】+【起始轨迹生成预警时间】 结果,用于查询时过滤 + + + [SugarColumn(ColumnName = "IsClosed", ColumnDescription = "是否关闭")] + public bool IsClosed { get; set; } //是否关闭,执行完成被关闭/手动关闭 +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs new file mode 100644 index 00000000..3d66b23a --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs @@ -0,0 +1,144 @@ +using System.Collections.Immutable; +using System.Text; +using Nest; +using SJZY.Expand.ElasticSearch; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Consts; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; + +// 运输时效预警明细 +[ElasticsearchType(RelationName = GlobalConst.TransportWarningDetailsIndex)] +public sealed class TransportWarningDetailsIndex : IdBaseIndex +{ + public string IndexName { get; set; } + public int WarningId { get; set; } + public int WarningSceneId { get; set; } + public int WarningSceneRuleItemId { get; set; } + public string Version { get; set; } //预警版本号 + + public string WarningName { get; set; } //(冗余)预警规则名称,规则主体名称 + public string WarningRuleDescription { get; set; } // 预警规则描述 (场景+场景规则项目构建) + + public float TransportTimeSpan { get; set; } //时效 (单位小时),用于计算状态 + public float StartTrackWarningTimeSpan { get; set; } //起始轨迹生成预警时间 (单位小时),用于计算状态 + //public string NotifyModes { get; set; } //通知方式,多个方式(,)分割 eg: 1,2 + public string NotifyUsers { get; set; } //通知人Json eg: [{"UserId":1,"UserName":"张三"},{"UserId":2,"UserName":"李四"}] + + //订单信息 + public int SubSystemId { get; set; } //(冗余)子系统Id + public string SubSystemName { get; set; } //(冗余)子系统名称 + public int CustomId { get; set; } //(冗余)客户Id + public string CustomName { get; set; } //冗余 客户简称(用于查询条件) + public int OrderId { get; set; } + public string OrderNo { get; set; } //订单号 + public int OrderType { get; set; } //(冗余)订单类型 + public string OrderTypeName { get; set; } //(冗余)订单类型名称 + + //轨迹信息-起始(更新用例:新增轨迹时,场景/规则项变更并且重新命中) + public int StartTrackStatusId { get; set; } //起始轨迹状态Id + public string StartTrackStatus { get; set; } //(冗余)起始轨迹状态名称 + public int StartTrackContentId { get; set; } //起始轨迹内容Id + public string StartTrackContent { get; set; } //(冗余)起始轨迹内容名称 + public DateTime StartTrackTime { get; set; } //起始轨迹发生时间 + public float StartTrackTimeZone { get; set; } //起始轨迹发生时区 + + //轨迹信息-截止(更新用例:新增轨迹时) + public int EndTrackStatusId { get; set; } //截止轨迹状态Id + public string EndTrackStatus { get; set; } //(冗余)截止轨迹状态名称 + public int? EndTrackContentId { get; set; } //截止轨迹内容Id + public string? EndTrackContent { get; set; } //(冗余)截止轨迹内容名称 + public DateTime? EndTrackTime { get; set; } //截止轨迹发生时间 + public float? EndTrackTimeZone { get; set; } //截止轨迹发生时区 + + // // 实时属性 todo:后面拆出vo + // public int LatestTrackContentId { get; set; } //最新轨迹内容Id + // public string LatestTrackContent { get; set; } //(冗余)最新轨迹内容名称 + // public DateTime LatestTrackTime { get; set; } //最新轨迹发生时间 + // public float LatestTrackTimeZone { get; set; } //最新轨迹发生时区 + // public string LatestTrackPlace { get; set; } //最新轨迹发生地 + + + //计算属性 + public long CumulativeTimeSpan { get; set; } //累计运输时间:当前时间-起始轨迹状态中的最早轨迹发生时间(利用 轨迹信息-起始 完成计算)单位(毫秒) + public int Status { get; set; } //1:即将超时 2:已超时 3:已完成 4:超时完成 5:已关闭,初始为【即将超时】 + + //审计属性 + public DateTime CreateTime { get; set; } //预警创建时间 + + private static readonly ImmutableHashSet CompletedStatus = ImmutableHashSet.Create(3, 4, 5); + + // todo:计算并设置状态,后续维护到vo当中 + public int CalcStatus() + { + // 结束态无需计算,直接返回 + if (CompletedStatus.Contains(Status)) return Status; + + //时效 + var transportTimeSpan = TimeSpan.FromHours(TransportTimeSpan); //小时 + //累计运输时间 + var cumulativeTimeSpan = TimeSpan.FromMilliseconds(CumulativeTimeSpan); //毫秒 + //预警时效(预警发出的时间对比于最早轨迹状态时间的跨度) + var startTrackWarningTimeSpan = TimeSpan.FromHours(StartTrackWarningTimeSpan); //小时 + + //时效>累计运输时间>=预警时效 状态调整为【即将超时】 + if (transportTimeSpan > cumulativeTimeSpan && cumulativeTimeSpan >= startTrackWarningTimeSpan) + return Status = 1; + + //时效<累计运输时间 状态调整为【已超时】 + if (transportTimeSpan < cumulativeTimeSpan) + return Status = 2; + + return Status; + } + + // 预警规则描述,创建预警明细,更新场景(需要重新匹配),更新规则项目(需要重新匹配),更新规则区域名称 + // todo:缺失更新规则区域名称目前不被感知,出现预警明细和对于的规则区域名称不一致 + // todo:转化为bo的使用 + public string GetWarningRuleDescription(TransportWarningSceneEntity scene, TransportWarningSceneRuleItemEntity ruleItem, RuleAreaEntity? ruleArea) + { + var res = new StringBuilder("{"); + // 起始轨迹状态-截止轨迹状态 + res.Append($"{StartTrackStatus}-{EndTrackStatus}"); + + // 货物类型 (普货/危险品/...) + if (!string.IsNullOrWhiteSpace(scene.CargoType)) + res.Append($",{scene.CargoType}"); + // 月份 (1/3/5/...) + if (!string.IsNullOrWhiteSpace(scene.Months)) + res.Append($",{scene.Months}"); + // 区域名称 (美国xxx区域) + if (ruleArea != null) + res.Append($",{ruleArea.Name}"); + // 是否中转(中转/直飞) + if (scene.IsTransit != null) + res.Append($",{(scene.IsTransit ?? false ? "中转" : "直飞")}"); + // 出发港(xxx港) + if (!string.IsNullOrWhiteSpace(scene.LeavePort)) + res.Append($",{scene.LeavePort}"); + // 产品代码(xxx) + if (!string.IsNullOrWhiteSpace(scene.ProductCode)) + res.Append($",{scene.ProductCode}"); + + // 规则配置信息 + res.Append($",生成预警时间{ruleItem.StartTrackWarningTimeSpan}小时" + + $",提醒时效{ruleItem.TransportTimeSpan}}}"); + + return res.ToString(); + } + + public DateTime GetUtc0StartTrackTime() + { + return Math.Abs(StartTrackTimeZone - GlobalConst.NoneTrackTimeZone) < 0.1f + ? StartTrackTime + : StartTrackTime.Subtract(TimeSpan.FromHours(StartTrackTimeZone)); + } + + public DateTime? GetUtc0EndTrackTime() + { + if (EndTrackTime == null || EndTrackTimeZone == null) return null; + return Math.Abs(EndTrackTimeZone.Value - GlobalConst.NoneTrackTimeZone) < 0.1f + ? EndTrackTime + : EndTrackTime.Value.Subtract(TimeSpan.FromHours(EndTrackTimeZone.Value)); + } +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs new file mode 100644 index 00000000..2239ebbe --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs @@ -0,0 +1,48 @@ + + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; + +// 运输时效场景 +[SugarTable("t_warning_transport_scene", TableDescription = "运输时效规则场景")] +public sealed class TransportWarningSceneEntity : IFullAuditedBasicEntitys, IEntity +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则id")] + public int WarningId { get; set; } + + [SugarColumn(ColumnName = "IsTransit", ColumnDescription = "是否中转")] + public bool? IsTransit { get; set; } //是否中转 + + [SugarColumn(ColumnName = "CargoType", ColumnDescription = "货物类型")] + public string? CargoType { get; set; } //货物类型, + + [SugarColumn(ColumnName = "LeavePort", ColumnDescription = "起运港口")] + public string? LeavePort { get; set; } //起运港口 + + [SugarColumn(ColumnName = "ProductCode", ColumnDescription = "产品编码")] + public string? ProductCode { get; set; } //产品编码 + + [SugarColumn(ColumnName = "Months", ColumnDescription = "月份")] + public string? Months { get; set; } //月份, 1/2/3 + + [SugarColumn(ColumnName = "AreaId", ColumnDescription = "区域id")] + public int? AreaId { get; set; } //区域Id,单分区 + + public TransportWarningSceneEntity() + { + } + + public TransportWarningSceneEntity(int id, int warningId, string? cargoType, string? months, int? areaId, bool? isTransit, string? leavePort, string? productCode) + { + Id = id; + WarningId = warningId; + CargoType = cargoType; + Months = months; + AreaId = areaId; + IsTransit = isTransit; + LeavePort = leavePort; + ProductCode = productCode; + } +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs new file mode 100644 index 00000000..ffb8db44 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs @@ -0,0 +1,62 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; + +// 运输时效场景规则项 +[SugarTable("t_warning_transport_scene_rule_item", TableDescription = "运输时效规则场景规则项")] +public sealed class TransportWarningSceneRuleItemEntity : IFullAuditedBasicEntitys, IEntity +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则id")] + public int WarningId { get; set; } + + [SugarColumn(ColumnName = "TransportWarningSceneId", ColumnDescription = "运输场景id")] + public int TransportWarningSceneId { get; set; } + + [SugarColumn(ColumnName = "StartTrackStatusId", ColumnDescription = "起始轨迹状态Id")] + public int StartTrackStatusId { get; set; } //起始轨迹状态Id,tips:用于匹配轨迹 + + [SugarColumn(ColumnName = "EndTrackStatusId", ColumnDescription = "截止轨迹状态Id")] + public int EndTrackStatusId { get; set; } //截止轨迹状态Id, tips:用于验证预警明细是否可以产生,预警通知是否可以进行 + + [SugarColumn(ColumnName = "TransportTimeSpan", ColumnDescription = "时效 (单位小时)")] + public float TransportTimeSpan { get; set; } //时效 (单位小时) + + [SugarColumn(ColumnName = "StartTrackWarningTimeSpan", ColumnDescription = "起始轨迹生成预警时间 (单位小时)")] + public float StartTrackWarningTimeSpan { get; set; } //起始轨迹生成预警时间 (单位小时) + + // [SugarColumn(ColumnName = "NotifyModes", ColumnDescription = "通知方式,多个方式(,)分割 eg: 1,2")] + // public string NotifyModes { get; set; } //通知方式,多个方式(,)分割 eg: 1,2 + + [SugarColumn(ColumnName = "NotifyUsers", ColumnDescription = "通知人Json eg: [{\"UserId\":1,\"UserName\":\"张三\"},{\"UserId\":2,\"UserName\":\"李四\"}]")] + public string NotifyUsers { get; set; } //通知人Json eg: [{"UserId":1,"UserName":"张三"},{"UserId":2,"UserName":"李四"}] + + public TransportWarningSceneRuleItemEntity() + { + } + + public TransportWarningSceneRuleItemEntity(int id, int warningId, int transportWarningSceneId, int startTrackStatusId, int endTrackStatusId, float transportTimeSpan, float startTrackWarningTimeSpan, string notifyUsers) + { + Id = id; + WarningId = warningId; + TransportWarningSceneId = transportWarningSceneId; + StartTrackStatusId = startTrackStatusId; + EndTrackStatusId = endTrackStatusId; + TransportTimeSpan = transportTimeSpan; + StartTrackWarningTimeSpan = startTrackWarningTimeSpan; + NotifyUsers = notifyUsers; + } + + public bool CanHitRule(int trackStatusId, HashSet trackStatusIds) + { + return trackStatusId == StartTrackStatusId //命中起始轨迹状态 + && !trackStatusIds.Contains(EndTrackStatusId); //缺失截止轨迹状态 + } + + // 重新命中,用于规则扫描 + public bool CanReHit(HashSet trackStatusIds) + { + return trackStatusIds.Contains(StartTrackStatusId) //存在起始 + && !trackStatusIds.Contains(EndTrackStatusId); //不存在截止 + } +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningNotifyTaskEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningNotifyTaskEntity.cs new file mode 100644 index 00000000..bf57036b --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningNotifyTaskEntity.cs @@ -0,0 +1,56 @@ +namespace SJZY.Track.Domain.Aggregates.WarningAggregate; + +[SugarTable("t_warning_notify_task", TableDescription = "预警通知任务")] +public sealed class WarningNotifyTaskEntity : IFullAuditedBasicEntitys, IAggregateRoot +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "OrderId", ColumnDescription = "订单Id")] + public int OrderId { get; set; } //(冗余)订单id + + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警主体Id")] + public int WarningId { get; set; } // 预警Id + + [SugarColumn(ColumnName = "WarningRuleId", ColumnDescription = "预警规则Id")] + public int WarningRuleId { get; set; } // 预警规则项目Id + + [SugarColumn(ColumnName = "WarningDetailsId", ColumnDescription = "预警明细Id")] + public string WarningDetailsId { get; set; } //预警明细id + + [SugarColumn(ColumnName = "AuxiliaryId", ColumnDescription = "辅助id,用于过滤匹配")] + public string? AuxiliaryId { get; set; } //辅助标识,用于增强匹配(例如运输时效预警有会有不同场景,可以通过这个字段完成匹配) + + [SugarColumn(ColumnName = "WarningType", ColumnDescription = "预警类型,1:运输时效,2:回传时效,3:完整预警")] + public int WarningType { get; set; } // 预警类型,1:运输时效,2:回传时效,3:完整预警 + + // [SugarColumn(ColumnName = "NotifyModes", ColumnDescription = "通知方式,多个方式(,)分割 eg: 1,2")] + // public string NotifyModes { get; set; } //通知方式,多个方式(,)分割 eg: 1,2 + + [SugarColumn(ColumnName = "NotifyUsers", ColumnDescription = "通知人Json eg: [{\"UserId\":1,\"UserName\":\"张三\"},{\"UserId\":2,\"UserName\":\"李四\"}]")] + public string NotifyUsers { get; set; } //通知人Json eg: [{"UserId":1,"UserName":"张三"},{"UserId":2,"UserName":"李四"}] + + + [SugarColumn(ColumnName = "Tags", ColumnDescription = "标签集合(,分割)")] + public string? Tags { get; set; } // 标签(加急?) + + [SugarColumn(ColumnName = "IsClosed", ColumnDescription = "是否关闭")] + public bool IsClosed { get; set; } //是否已经关闭,使用时过滤已关闭的通知任务 + + public WarningNotifyTaskEntity() + { + } + + public WarningNotifyTaskEntity(int orderId, int warningId, int warningRuleId, string warningDetailsId, int warningType, string notifyUsers, string? tags = null) + { + OrderId = orderId; + WarningId = warningId; + WarningRuleId = warningRuleId; + WarningDetailsId = warningDetailsId; + WarningType = warningType; + NotifyUsers = notifyUsers; + Tags = tags; + CreateTime = DateTime.Now; + IsClosed = false; + } +} diff --git a/src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs new file mode 100644 index 00000000..fc4ae6c1 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs @@ -0,0 +1,66 @@ +using SJZY.Track.Domain.Extensions; + +namespace SJZY.Track.Domain.Aggregates.WarningAggregate; + +// 预警规则主体 +[SugarTable("t_warning_0", TableDescription = "预警规则主体(用于将来替换t_warning)")] +public sealed class WarningRuleEntity : IFullAuditedBasicEntitys, IAggregateRoot +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + [SugarColumn(ColumnName = "SubSystemId", ColumnDescription = "子系统id")] + public int SubSystemId { get; set; } //子系统id + + [SugarColumn(ColumnName = "CustomId", ColumnDescription = "客户id")] + public int CustomId { get; set; } //客户id + + // [SugarColumn(ColumnName = "CustomName", ColumnDescription = "客户名称")] + // public string CustomName { get; set; } //冗余客户简称,用于查询 + + [SugarColumn(ColumnName = "Name", ColumnDescription = "预警规则名称")] + public string Name { get; set; } //预警规则名称 + + [SugarColumn(ColumnName = "IsEnabled", ColumnDescription = "是否启用")] + public bool IsEnabled { get; set; } + + [SugarColumn(ColumnName = "WarningType", ColumnDescription = "预警类型,1:运输时效,2:回传时效,3:完整预警")] + public int WarningType { get; set; } //预警类型,1:运输时效,2:回传时效,3:完整预警 + + [SugarColumn(ColumnName = "Version", ColumnDescription = "规则版本,用于标识规则产物(如预警信息)")] + public string Version { get; set; } //规则版本,用于标识规则产物(如预警信息) + + + public WarningRuleEntity() + { + } + + + public WarningRuleEntity(int id, string name, int subSystemId, int customId, bool isEnabled, int warningType, string version) + { + Id = id; + Name = name; + SubSystemId = subSystemId; + CustomId = customId; + IsEnabled = isEnabled; + WarningType = warningType; + Version = version; + } + + public WarningRuleEntity(int id, string name, int subSystemId, int customId, bool isEnabled, int warningType) + { + Id = id; + Name = name; + SubSystemId = subSystemId; + CustomId = customId; + IsEnabled = isEnabled; + WarningType = warningType; + Version = VersionGenerate.GenVersion(); + } + + // 子系统+客户id确定命中规则主体 + public bool CanHitRule(int subSystemId, int customId) + { + return SubSystemId == subSystemId && CustomId == customId; + } +} diff --git a/src/SJZY.Track.Domain/Dtos/Warning/GetPageCompleteWarningDetailsInputVO.cs b/src/SJZY.Track.Domain/Dtos/Warning/GetPageCompleteWarningDetailsInputVO.cs new file mode 100644 index 00000000..2f0682f0 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Warning/GetPageCompleteWarningDetailsInputVO.cs @@ -0,0 +1,60 @@ +namespace SJZY.Track.Domain.Dtos.Warning; + +public class GetPageCompleteWarningDetailsInputVO : PageInfoInput +{ + /// + /// 子系统id + /// + public int? SubSystemId { get; set; } + + /// + /// 订单号,多个订单号(,分割) + /// + public string? OrderNos { get; set; } + + /// + /// 订单类型 + /// + public int? OrderType { get; set; } + + /// + /// 状态 + /// 1:待处理,2:已处理,3:已关闭 + /// + public int? Status { get; set; } + + /// + /// 客户id + /// + public int? CustomId { get; set; } + + /// + /// 缺失轨迹状态id + /// + public int? MissingTrackStatusId { get; set; } + + /// + /// 通知对象id + /// + public int? NotifyUserId { get; set; } + + /// + /// 预警创建时间开始 + /// + public DateTime? WarningCreateTimeStart { get; set; } + + /// + /// 预警创建时间截止 + /// + public DateTime? WarningCreateTimeEnd { get; set; } + + /// + /// 预警处理时间开始 + /// + public DateTime? WarningHandleTimeStart { get; set; } + + /// + /// 预警处理时间截止 + /// + public DateTime? WarningHandleTimeEnd { get; set; } +} diff --git a/src/SJZY.Track.Domain/Dtos/Warning/GetPageReturnWarningDetailsInputVO.cs b/src/SJZY.Track.Domain/Dtos/Warning/GetPageReturnWarningDetailsInputVO.cs new file mode 100644 index 00000000..aceaacf1 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Warning/GetPageReturnWarningDetailsInputVO.cs @@ -0,0 +1,93 @@ +namespace SJZY.Track.Domain.Dtos.Warning; + +/// +/// 分页获取回传预警明细输入 +/// +public class GetPageReturnWarningDetailsInputVO : PageInfoInput +{ + /// + /// 子系统id + /// + public int? SubSystemId { get; set; } + + /// + /// 订单号,多个订单号(,分割) + /// + public string? OrderNos { get; set; } + + /// + /// 订单类型 + /// + public int? OrderType { get; set; } + + /// + /// 状态 + /// 1:人工介入 2:重试中 3:推送完成 4:已关闭 + /// + public int? Status { get; set; } + + /// + /// 客户id + /// + public int? CustomId { get; set; } + + /// + /// 轨迹状态id + /// + public int? TrackStatusId { get; set; } + + /// + /// 轨迹内容id + /// + public int? TrackContentId { get; set; } + + /// + /// 是否有Kpi考核 (传null为查全部) + /// + public bool? IsKpi { get; set; } + + /// + /// 通知对象用户id + /// + public int? NotifyUserId { get; set; } + + /// + /// 轨迹发生时间开始 + /// + public DateTime? TrackTimeStart { get; set; } + + /// + /// 轨迹发生时间截止 + /// + public DateTime? TrackTimeEnd { get; set; } + + /// + /// 轨迹创建时间开始 + /// + public DateTime? TrackCreateTimeStart { get; set; } + + /// + /// 轨迹创建时间截止 + /// + public DateTime? TrackCreateTimeEnd { get; set; } + + /// + /// 轨迹回传时间开始 + /// + public DateTime? TrackReturnTimeStart { get; set; } + + /// + /// 轨迹回传时间截止 + /// + public DateTime? TrackReturnTimeEnd { get; set; } + + /// + /// 预警创建时间开始 + /// + public DateTime? WarningCreateTimeStart { get; set; } + + /// + /// 预警创建时间截止 + /// + public DateTime? WarningCreateTimeEnd { get; set; } +} diff --git a/src/SJZY.Track.Domain/Dtos/Warning/GetPageTransportWarningDetailsInputVO.cs b/src/SJZY.Track.Domain/Dtos/Warning/GetPageTransportWarningDetailsInputVO.cs new file mode 100644 index 00000000..6fecc8aa --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Warning/GetPageTransportWarningDetailsInputVO.cs @@ -0,0 +1,52 @@ +namespace SJZY.Track.Domain.Dtos.Warning; + +/// +/// 分页获取运输预警明细 +/// +public class GetPageTransportWarningDetailsInputVO : PageInfoInput +{ + /// + /// 子系统id + /// + public int? SubSystemId { get; set; } + + /// + /// 订单号,多个订单号(,分割) + /// + public string? OrderNos { get; set; } + + /// + /// 订单类型 + /// + public int? OrderType { get; set; } + + /// + /// 状态 + /// + public int? Status { get; set; } + + /// + /// 客户Id + /// + public int? CustomId { get; set; } + + /// + /// 预警名称 + /// + public string? WarningName { get; set; } + + /// + /// 通知对象用户Id + /// + public int? NotifyUserId { get; set; } + + /// + /// 创建时间开始 + /// + public DateTime? CreateTimeStart { get; set; } + + /// + /// 创建时间截止 + /// + public DateTime? CreateTimeEnd { get; set; } +} diff --git a/src/SJZY.Track.Domain/Enums/CompleteWarningStatus.cs b/src/SJZY.Track.Domain/Enums/CompleteWarningStatus.cs new file mode 100644 index 00000000..943dccf7 --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/CompleteWarningStatus.cs @@ -0,0 +1,8 @@ +namespace SJZY.Track.Domain.Enums; + +public enum CompleteWarningStatus +{ + 待处理 = 1, + 已处理 = 2, + 已关闭 = 3 +} diff --git a/src/SJZY.Track.Domain/Enums/ReturnWarningStatus.cs b/src/SJZY.Track.Domain/Enums/ReturnWarningStatus.cs new file mode 100644 index 00000000..a0d28b00 --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/ReturnWarningStatus.cs @@ -0,0 +1,9 @@ +namespace SJZY.Track.Domain.Enums; + +public enum ReturnWarningStatus +{ + 人工介入 = 1, + 重试中 = 2, + 推送完成 = 3, + 已关闭 = 4 +} diff --git a/src/SJZY.Track.Domain/Enums/TransportWarningStatus.cs b/src/SJZY.Track.Domain/Enums/TransportWarningStatus.cs new file mode 100644 index 00000000..fe4d605d --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/TransportWarningStatus.cs @@ -0,0 +1,10 @@ +namespace SJZY.Track.Domain.Enums; + +public enum TransportWarningStatus +{ + 即将超时 = 1, + 已超时 = 2, + 已完成 = 3, + 超时完成 = 4, + 已关闭 = 5, +} diff --git a/src/SJZY.Track.Domain/Extensions/CollectionExtensions.cs b/src/SJZY.Track.Domain/Extensions/CollectionExtensions.cs new file mode 100644 index 00000000..fc788ba9 --- /dev/null +++ b/src/SJZY.Track.Domain/Extensions/CollectionExtensions.cs @@ -0,0 +1,120 @@ +using System.Collections.Concurrent; +using Nest; +using SJZY.Expand.ElasticSearch; + +namespace SJZY.Track.Domain.Extensions; + +public class ListWrapperFactory +{ + public static ListWrapper, QueryContainer>> CreateListWrapper(TInput input) + where TIndex : IdBaseIndex + { + return new ListWrapper, QueryContainer>>(new (), input); + } +} +public class ListWrapper : List +{ + public List List { get; set; } + public TInput Input { get; set; } + + public ListWrapper(List list, TInput input) + { + List = list; + Input = input; + } + + public void AddIf(Predicate predicate,TItem item) + { + if(predicate(Input)) + List.Add(item); + } +} + +public static class CollectionExtensions +{ + private static readonly ConcurrentDictionary DefaultValueCache = new(); + + public static void AddNotEmpty(this ICollection list, TInput input, TItem item) + { + if (input is null) return; + + var t = typeof(TInput); + if (t == typeof(string)) + { + if (!string.IsNullOrWhiteSpace(input as string)) + list.Add(item); + return; + } + + // 如果是Nullable类型,则获取其UnderlyingType + if (t.Name.StartsWith("Nullable")) + { + t = Nullable.GetUnderlyingType(t)!; + // 判断是否为数值类型 + if (t.IsNumericType()) + { + // 动态转换为数值比较 + dynamic val = Convert.ChangeType(input, t); + dynamic defaultVal = Activator.CreateInstance(t); + + // 当值为0时视为默认值 + if (val != defaultVal) + { + list.Add(item); + } + } + + return; + } + + // 获取类型对应的默认值(带缓存) + var defaultValue = (TInput)DefaultValueCache.GetOrAdd(t, _ => default(TInput))!; + + // 使用 EqualityComparer 避免装箱拆箱 + if (!EqualityComparer.Default.Equals(input, defaultValue)) + { + list.Add(item); + } + } + + public static void AddNotEmpty(this ICollection list, TInput input, Func factory) + { + AddNotEmpty(list, input, factory(input)); + } + + + public static void AddIf(this ICollection list, TInput input, Func predicate, TItem item) + { + if (predicate(input)) + list.Add(item); + } + + public static void AddIf(this ICollection list, TInput input, Func predicate, Func factory) + { + if (predicate(input)) + list.Add(factory(input)); + } + + + private static bool IsNumericType(this Type? type) + { + if (type == null) return false; + + return Type.GetTypeCode(type) switch + { + TypeCode.Byte or + TypeCode.SByte or + TypeCode.UInt16 or + TypeCode.UInt32 or + TypeCode.UInt64 or + TypeCode.Int16 or + TypeCode.Int32 or + TypeCode.Int64 or + TypeCode.Decimal or + TypeCode.Double or + TypeCode.Single => true, + _ => false + }; + } + +} diff --git a/src/SJZY.Track.Domain/Extensions/PollyHelper.cs b/src/SJZY.Track.Domain/Extensions/PollyHelper.cs new file mode 100644 index 00000000..5a7a6d85 --- /dev/null +++ b/src/SJZY.Track.Domain/Extensions/PollyHelper.cs @@ -0,0 +1,34 @@ +namespace SJZY.Track.Domain.Extensions; + +public static class PollyHelper +{ + public static async Task RetryPollyExecAsync(Func> func, Func successValidator, int maxRetries = 3, bool throwOtherException = true) + { + TResult result = default!; + var retryCount = 0; + + try + { + while (retryCount < maxRetries) + { + result = await func(); + + // 如果认为成功则直返回值 + if (successValidator(result)) + return result; + + // 超过最大重试次数,抛出异常 + retryCount++; + if (retryCount >= maxRetries) + throw new InvalidOperationException($"操作失败超过最大重试次数{maxRetries}次,原因:{result}。"); + } + } + catch (Exception) + { + if (throwOtherException) + throw; + } + + return result; + } +} diff --git a/src/SJZY.Track.Application/Helpers/TextJsonHelper.cs b/src/SJZY.Track.Domain/Extensions/TextJsonHelper.cs similarity index 94% rename from src/SJZY.Track.Application/Helpers/TextJsonHelper.cs rename to src/SJZY.Track.Domain/Extensions/TextJsonHelper.cs index 306e22e2..b53d682a 100644 --- a/src/SJZY.Track.Application/Helpers/TextJsonHelper.cs +++ b/src/SJZY.Track.Domain/Extensions/TextJsonHelper.cs @@ -1,7 +1,6 @@ -using System.Globalization; using System.Text.Json; -namespace SJZY.Track.Application.Helpers; +namespace SJZY.Track.Domain.Extensions; /// /// 针对 Text.Json 的一些辅助方法 @@ -178,4 +177,11 @@ public static class TextJsonHelper return false; } + + public static bool EqJsonStringField(string json1, string json2, string fieldName) + { + var aJsonElement = JsonDocument.Parse(json1); + var bJsonElement = JsonDocument.Parse(json2); + return EqByJsonElementField(aJsonElement, bJsonElement, fieldName); + } } diff --git a/src/SJZY.Track.Domain/Extensions/VersionGenerate.cs b/src/SJZY.Track.Domain/Extensions/VersionGenerate.cs new file mode 100644 index 00000000..71927df7 --- /dev/null +++ b/src/SJZY.Track.Domain/Extensions/VersionGenerate.cs @@ -0,0 +1,15 @@ +namespace SJZY.Track.Domain.Extensions; + +public static class VersionGenerate +{ + /// + /// 生成版本号 guid+日期 + /// + /// + public static string GenVersion() + { + var guid = Guid.NewGuid().ToString("N"); + var date = DateTime.Now.ToString("s"); + return $"{guid}-{date}"; + } +} diff --git a/src/SJZY.Track.Domain/Queries/IVExternalSubjectQueries.cs b/src/SJZY.Track.Domain/Queries/IVExternalSubjectQueries.cs new file mode 100644 index 00000000..81c8a988 --- /dev/null +++ b/src/SJZY.Track.Domain/Queries/IVExternalSubjectQueries.cs @@ -0,0 +1,7 @@ +using SJZY.Track.Domain.VEntities; + +namespace SJZY.Track.Domain.Queries; + +public interface IVExternalSubjectQueries : IBasicQuery +{ +} diff --git a/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs b/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs index 376ae476..f088c3e8 100644 --- a/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs +++ b/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs @@ -4,7 +4,7 @@ /// 外部主体 /// [SugarTable("v_external_subject")] -public class VExternalSubjectEntity : IFullAuditedBasicEntitys +public class VExternalSubjectEntity : IFullAuditedBasicEntitys,IEntity { /// /// diff --git a/src/SJZY.Track.Repository/Queries/VExternalSubjectQueries.cs b/src/SJZY.Track.Repository/Queries/VExternalSubjectQueries.cs new file mode 100644 index 00000000..163803e0 --- /dev/null +++ b/src/SJZY.Track.Repository/Queries/VExternalSubjectQueries.cs @@ -0,0 +1,8 @@ +using SJZY.Track.Domain.Queries; +using SJZY.Track.Domain.VEntities; + +namespace SJZY.Track.Repository.Queries; + +public class VExternalSubjectQueries : BasicQuery, IVExternalSubjectQueries, ITransientDependency +{ +} diff --git a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs index 9ee2a1cb..c308d980 100644 --- a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs @@ -62,7 +62,7 @@ public class OrderRepository : BasicRepository, IOrderRepository, I return dict; } - public async Task> GetTrackOrderTypeDict() + public async Task> GetTrackOrderTypeDictAsync() { const string key = "TrackOrderType"; var orderTypes = await _memoryCache.GetOrCreateAsync(key, async (entry) => diff --git a/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs index 46f97255..0cf9cd6c 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs @@ -61,6 +61,44 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract> GetLatestListAsync(HashSet eventOrderIds) + { + var r = await Context.SearchAsync(x => x + .Index(GlobalConst.TrackDetailsIndex) + .Source() + .Query(q => q + .Bool(b => b + .Must( + mu => mu.Terms(t => t.Field(f => f.OrderId).Terms(eventOrderIds)) + ) + ) + ) + .Size(SafelyLimit) + .Aggregations(a => a + .Terms("orders", t => t + .Field(f => f.OrderId) + .Aggregations(aa => aa + .TopHits("latest_track", th => th + .Size(1) + .Sort(so => so + .Field(f => f.ZeroTrackTime, SortOrder.Descending) + ) + .Source() + ) + ) + ) + ) + ); + + if (!r.IsValid) + { + throw new InvalidOperationException( + $"最新轨迹查询失败,orderIds:[{eventOrderIds.JoinAsString(", ")}],原因:{r.DebugInformation}。"); + } + + return r.Documents.ToList(); + } + public async Task> GetLatestListAsync(int[] orderIds, List trackCodes) { var excludeTrackCodes = trackCodes.ToList(); diff --git a/src/SJZY.Track.Repository/Repositories/WarningDetailsEsRepository.cs b/src/SJZY.Track.Repository/Repositories/WarningDetailsEsRepository.cs new file mode 100644 index 00000000..d5cd6074 --- /dev/null +++ b/src/SJZY.Track.Repository/Repositories/WarningDetailsEsRepository.cs @@ -0,0 +1,707 @@ +using Nest; +using SJZY.Expand.ElasticSearch.Services; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Warning; +using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Extensions; + +namespace SJZY.Track.Repository.Repositories; + +public class WarningDetailsEsRepository : IWarningDetailsEsRepository, ITransientDependency +{ + private readonly IElasticClient _client; + + public WarningDetailsEsRepository(ConnectionFactory connection) + { + _client = connection.GetContext(typeof(TransportWarningDetailsIndex)); + } + + #region 运输时效 + + public async Task> GetPageTransportWarningDetailsAsync(GetPageTransportWarningDetailsInputVO input) + { + var musts = ListWrapperFactory.CreateListWrapper(input); + + var orderNos = input.OrderNos?.Split(','); + + // 精确匹配 + musts.AddIf(x => x.CustomId > 0, c => c.Term(f => f.CustomId, input.CustomId)); + musts.AddIf(_ => orderNos?.Length > 0, c => c.Terms(f => f.Field(d => d.OrderNo.Suffix("keyword")).Terms(orderNos))); + musts.AddIf(x => x.OrderType > 0, c => c.Term(f => f.OrderType, input.OrderType)); + musts.AddIf(x => x.Status > 0, c => c.Term(f => f.Status, input.Status)); + musts.AddIf(x => x.CustomId > 0, c => c.Term(f => f.CustomId, input.CustomId)); + musts.AddIf(x => !string.IsNullOrWhiteSpace(x.WarningName), c => c.Term(f => f.WarningName, input.WarningName)); + musts.AddIf(x => x.CreateTimeStart.HasValue, c => c.DateRange(r => r.Field(f => f.CreateTime).GreaterThanOrEquals(input.CreateTimeStart))); + musts.AddIf(x => x.CreateTimeEnd.HasValue, c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.CreateTimeEnd))); + + // 模糊匹配userId + musts.AddIf(x => x.NotifyUserId.HasValue, c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId.ToString()))); + + var resp = await _client.SearchAsync(x => x + .Index(GlobalConst.TransportWarningDetailsIndex) + .Query(q => q.Bool(b => b.Must(musts))) + .From((input.PageIndex - 1) * input.PageSize) + .Size(input.PageSize)); + + return new UnifyPageResultDto(resp.ToIdList(), input.PageIndex, input.PageSize, (int) resp.Total); + } + + public async Task> GetTransportWarningDetailsAsync(HashSet ruleItemIds, HashSet status) + { + var r = await _client.SearchAsync(x => x + .Index(GlobalConst.TransportWarningDetailsIndex) + .Query(q => q + .Bool(b => b + .Must( + mu => mu.Terms(t => t.Field(f => f.WarningSceneRuleItemId).Terms(ruleItemIds)), + mu => mu.Terms(t => t.Field(f => f.Status).Terms(status))) + ))); + + return r.Documents.ToList(); + } + + public async Task UpdateTransportWarningDetailsAsync(TransportWarningDetailsIndex detail) + { + var r = await _client.UpdateAsync(detail.Id, + u => u.Index(detail.IndexName) + .Doc(new + { + detail.WarningId, + detail.WarningSceneId, + detail.WarningSceneRuleItemId, + detail.Version, + detail.WarningName, + detail.WarningRuleDescription, + detail.TransportTimeSpan, + detail.StartTrackWarningTimeSpan, + //detail.NotifyModes, + detail.NotifyUsers, + detail.SubSystemId, + detail.CustomId, + detail.CustomName, + detail.OrderId, + detail.OrderNo, + detail.OrderType, + detail.StartTrackStatusId, + detail.StartTrackContentId, + detail.StartTrackTime, + detail.StartTrackTimeZone, + detail.EndTrackStatusId, + detail.EndTrackContentId, + detail.EndTrackTime, + detail.EndTrackTimeZone, + // detail.LatestTrackContentId, + // detail.LatestTrackContent, + // detail.LatestTrackTime, + // detail.LatestTrackTimeZone, + // detail.LatestTrackPlace, + detail.CumulativeTimeSpan, + detail.Status, + detail.CreateTime, + })); + } + + public async Task BulkUpdateTransportWarningDetailsAsync(List details, int maxRetries = 3) + { + if (details.Count == 0) return; + + //构建批量更新操作描述符 + var desc = new BulkDescriptor().UpdateMany(details, + (u, detail) => u.Id(detail.Id) + .Index(detail.IndexName) + .Script(s => s.Source(@" + ctx._source.WarningId = param.WarningId; + ctx._source.WarningSceneId = param.WarningSceneId; + ctx._source.WarningSceneRuleItemId = param.WarningSceneRuleItemId; + ctx._source.Version = param.Version; + ctx._source.WarningName = param.WarningName; + ctx._source.WarningRuleDescription = param.WarningRuleDescription; + ctx._source.TransportTimeSpan = param.TransportTimeSpan; + ctx._source.StartTrackWarningTimeSpa = param.StartTrackWarningTimeSpa;n + //ctx._source.NotifyModes = param.NotifyModes; + ctx._source.NotifyUsers = param.NotifyUsers; + ctx._source.SubSystemId = param.SubSystemId; + ctx._source.CustomId = param.CustomId; + ctx._source.CustomName = param.CustomName; + ctx._source.OrderId = param.OrderId; + ctx._source.OrderNo = param.OrderNo; + ctx._source.OrderType = param.OrderType; + ctx._source.StartTrackStatusId = param.StartTrackStatusId; + ctx._source.StartTrackContentId = param.StartTrackContentId; + ctx._source.StartTrackTime = param.StartTrackTime; + ctx._source.StartTrackTimeZone = param.StartTrackTimeZone; + ctx._source.EndTrackStatusId = param.EndTrackStatusId; + ctx._source.EndTrackContentId = param.EndTrackContentId; + ctx._source.EndTrackTime = param.EndTrackTime; + ctx._source.EndTrackTimeZone = param.EndTrackTimeZone; + //ctx._source.LatestTrackContentId = param.LatestTrackContentId; + //ctx._source.LatestTrackContent = param.LatestTrackContent; + //ctx._source.LatestTrackTime = param.LatestTrackTime; + //ctx._source.LatestTrackTimeZone = param.LatestTrackTimeZone; + //ctx._source.LatestTrackPlace = param.LatestTrackPlace; + ctx._source.CumulativeTimeSpan = param.CumulativeTimeSpan; + ctx._source.Status = param.Status; + ctx._source.CreateTime = param.CreateTime; + ").Params(p => p + .Add("WarningId", detail.WarningId) + .Add("WarningSceneId", detail.WarningSceneId) + .Add("WarningSceneRuleItemId", detail.WarningSceneRuleItemId) + .Add("Version", detail.Version) + .Add("WarningName", detail.WarningName) + .Add("WarningRuleDescription", detail.WarningRuleDescription) + .Add("TransportTimeSpan", detail.TransportTimeSpan) + .Add("StartTrackWarningTimeSpan", detail.StartTrackWarningTimeSpan) + //.Add("NotifyModes", detail.NotifyModes) + .Add("NotifyUsers", detail.NotifyUsers) + .Add("SubSystemId", detail.SubSystemId) + .Add("CustomId", detail.CustomId) + .Add("CustomName", detail.CustomName) + .Add("OrderId", detail.OrderId) + .Add("OrderNo", detail.OrderNo) + .Add("OrderType", detail.OrderType) + .Add("StartTrackStatusId", detail.StartTrackStatusId) + .Add("StartTrackContentId", detail.StartTrackContentId) + .Add("StartTrackTime", detail.StartTrackTime) + .Add("StartTrackTimeZone", detail.StartTrackTimeZone) + .Add("EndTrackStatusId", detail.EndTrackStatusId) + .Add("EndTrackContentId", detail.EndTrackContentId) + .Add("EndTrackTime", detail.EndTrackTime) + .Add("EndTrackTimeZone", detail.EndTrackTimeZone) + // .Add("LatestTrackContentId", detail.LatestTrackContentId) + // .Add("LatestTrackContent", detail.LatestTrackContent) + // .Add("LatestTrackTime", detail.LatestTrackTime) + // .Add("LatestTrackTimeZone", detail.LatestTrackTimeZone) + // .Add("LatestTrackPlace", detail.LatestTrackPlace) + // .Add("CumulativeTimeSpan", detail.CumulativeTimeSpan) + .Add("Status", detail.Status) + .Add("CreateTime", detail.CreateTime) + ))); + + await PollyHelper.RetryPollyExecAsync(() => _client.BulkAsync(desc), response => + { + if (response.IsValid) // 认为成功 + return true; + + if (response.ServerError.Status == 409) //版本冲突认为失败,则重试 + return false; + + throw new InvalidOperationException($"ES更新失败:{details.Count}条,原因:{response.DebugInformation}。"); + }); + } + + public async Task BulkCloseTransportWarningDetailsAsync(HashSet detailIds, int maxRetries = 3) + { + var status = new HashSet(2) + { + TransportWarningStatus.即将超时.GetHashCode(), + TransportWarningStatus.已超时.GetHashCode() + }; + + var r = await _client.UpdateByQueryAsync(u => + u.Index(GlobalConst.TransportWarningDetailsIndex) + .Query(q => q + .Bool(b => b.Must( + mu => mu.Terms(t => t.Field(f => f.Status).Terms(status)), + mu => mu.Terms(t => t.Field(f => f.Id).Terms(detailIds)) + ) + ) + ) + .Script(s => s + .Source("ctx._source.status = params.status") + .Params(p => p.Add("status", (int) TransportWarningStatus.已关闭)) + ) + ); + + if (!r.IsValid) + { + throw new InvalidOperationException( + $"运输时效预警明细更新失败,detailsIds:{detailIds},原因:{r.DebugInformation}。"); + } + } + + public async Task BulkCloseTransportWarningDetailsAsync(HashSet ruleItemIds, int maxRetries = 3) + { + var status = new HashSet(2) + { + TransportWarningStatus.即将超时.GetHashCode(), + TransportWarningStatus.已超时.GetHashCode() + }; + + var r = await _client.UpdateByQueryAsync(u => + u.Index(GlobalConst.TransportWarningDetailsIndex) + .Query(q => q + .Bool(b => b.Must( + mu => mu.Terms(t => t.Field(f => f.Status).Terms(status)), + mu => mu.Terms(t => t.Field(f => f.WarningSceneRuleItemId).Terms(ruleItemIds)) + ) + ) + ) + .Script(s => s + .Source("ctx._source.status = params.status") + .Params(p => p.Add("status", (int) TransportWarningStatus.已关闭)) + ) + ); + + if (!r.IsValid) + { + throw new InvalidOperationException( + $"运输时效预警明细更新失败,ruleItemIds:{ruleItemIds},原因:{r.DebugInformation}。"); + } + } + + public async Task InsertTransportWarningDetailsAsync(TransportWarningDetailsIndex detail) + { + var indexName = $"{GlobalConst.TransportWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + detail.IndexName = indexName; + var r = await _client.IndexAsync(detail, s => s.Index(indexName)); + + if (!r.IsValid) + { + throw new InvalidOperationException($"运输预警明细新增失败,{detail},原因:{r.DebugInformation}。"); + } + } + + public async Task BulkInsertTransportWarningDetailsAsync(List details, int maxRetries = 3) + { + // 按天写入es + var indexName = $"{GlobalConst.TransportWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + details.ForEach(x => x.IndexName = indexName); + + var r = await _client.IndexManyAsync(details, indexName); + + if (!r.IsValid) + { + throw new InvalidOperationException($"运输预警明细新增失败,原因:{r.DebugInformation}。"); + } + } + + #endregion + + #region 回传时效 + + public async Task> GetPageReturnWarningDetailsAsync(GetPageReturnWarningDetailsInputVO input) + { + var musts = ListWrapperFactory.CreateListWrapper(input); + + var orderNos = input.OrderNos?.Split(','); + + // 精确匹配 + musts.AddIf(x => x.SubSystemId > 0, c => c.Term(f => f.SubSystemId, input.SubSystemId)); + musts.AddIf(_ => orderNos?.Length > 0, c => c.Terms(f => f.Field(d => d.OrderNo.Suffix("keyword")).Terms(orderNos))); + musts.AddIf(x => x.OrderType > 0, c => c.Term(f => f.OrderType, input.OrderType)); + musts.AddIf(x => x.Status > 0, c => c.Term(f => f.Status, input.Status)); + musts.AddIf(x => x.CustomId > 0, c => c.Term(f => f.CustomId, input.CustomId)); + musts.AddIf(x => x.TrackStatusId > 0, c => c.Term(f => f.TrackStatusId, input.TrackStatusId)); + musts.AddIf(x => x.TrackContentId > 0, c => c.Term(f => f.TrackContentId, input.TrackContentId)); + musts.AddIf(x => x.IsKpi.HasValue, c => c.Term(f => f.IsExistsKpi, input.IsKpi!.Value)); + musts.AddIf(x => x.TrackTimeStart.HasValue, c => c.DateRange(r => r.Field(f => f.TrackTime).GreaterThanOrEquals(input.TrackTimeStart))); + musts.AddIf(x => x.TrackTimeEnd.HasValue, c => c.DateRange(r => r.Field(f => f.TrackTime).LessThanOrEquals(input.TrackTimeEnd))); + musts.AddIf(x => x.TrackCreateTimeStart.HasValue, c => c.DateRange(r => r.Field(f => f.TrackCreateTime).LessThanOrEquals(input.TrackCreateTimeStart))); + musts.AddIf(x => x.TrackCreateTimeEnd.HasValue, c => c.DateRange(r => r.Field(f => f.TrackCreateTime).LessThanOrEquals(input.TrackCreateTimeEnd))); + musts.AddIf(x => x.TrackReturnTimeStart.HasValue, c => c.DateRange(r => r.Field(f => f.TrackReturnTime).LessThanOrEquals(input.TrackReturnTimeStart))); + musts.AddIf(x => x.TrackReturnTimeEnd.HasValue, c => c.DateRange(r => r.Field(f => f.TrackReturnTime).LessThanOrEquals(input.TrackReturnTimeEnd))); + musts.AddIf(x => x.WarningCreateTimeStart.HasValue, c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.WarningCreateTimeStart))); + musts.AddIf(x => x.WarningCreateTimeEnd.HasValue, c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.WarningCreateTimeEnd))); + + // 模糊匹配userId(json字符串包含userId) + musts.AddIf(x => x.NotifyUserId.HasValue, c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId.ToString()))); + + var resp = await _client.SearchAsync(x => x + .Index(GlobalConst.ReturnWarningDetailsIndex) + .Query(q => q.Bool(b => b.Must(musts))) + .From((input.PageIndex - 1) * input.PageSize) + .Size(input.PageSize)); + + return new UnifyPageResultDto(resp.ToIdList(), input.PageIndex, input.PageSize, (int) resp.Total); + } + + public async Task> GetReturnMessageRecordListByIdAsync(string esId) + { + var resp = await _client.SearchAsync(x => x + .Index(GlobalConst.ReturnMessageRecordIndex) + .Query(q => q.Term(t => t.WarningDetailsId.Suffix("keyword"), esId)) + .Size(1000) + ); + + return resp.ToIdList(); + } + + public async Task UpdateReturnWarningDetailsAsync(ReturnWarningDetailsIndex detail) + { + var r = await _client.UpdateAsync(detail.Id, + u => u.Index(detail.IndexName) + .Doc(new + { + detail.ReturnWarningRuleId, + detail.WarningId, + detail.Version, + detail.ReturnTimeSpan, + //detail.NotifyModes, + detail.NotifyUsers, + detail.SubSystemId, + detail.CustomId, + detail.OrderId, + detail.OrderNo, + detail.OrderType, + detail.TrackStatusId, + detail.TrackStatus, + detail.CompareFields, + detail.Extra, + detail.TrackContentId, + detail.TrackContent, + detail.TrackCode, + detail.TrackPlace, + detail.TrackTime, + detail.TrackTimeZone, + detail.TrackCreateTime, + detail.PushTrackStatus, + detail.PushTrackCode, + detail.PushTrackContent, + detail.PushTrackEnContent, + detail.TrackReturnTime, + detail.RetryCount, + detail.Status, + detail.ActualReturnTimeSpan, + detail.SystemReturnTimeSpan, + detail.TrackCreateTimeSpan, + detail.CreateTime, + detail.UpdateTime, + })); + } + + public async Task BulkUpdateReturnWarningDetailsAsync(List details, int maxRetries = 3) + { + if (details.Count == 0) return; + + //构建批量更新操作描述符 + var desc = new BulkDescriptor().UpdateMany(details, + (u, detail) => u.Id(detail.Id) + .Index(detail.IndexName) + .Script(s => s.Source(@" + ctx._source.ReturnWarningRuleId = param.ReturnWarningRuleId; + ctx._source.WarningId = param.WarningId; + ctx._source.Version = param.Version; + ctx._source.ReturnTimeSpan = param.ReturnTimeSpan; + //ctx._source.NotifyModes = param.NotifyModes; + ctx._source.NotifyUsers = param.NotifyUsers; + ctx._source.SubSystemId = param.SubSystemId; + ctx._source.CustomId = param.CustomId; + ctx._source.OrderId = param.OrderId; + ctx._source.OrderNo = param.OrderNo; + ctx._source.OrderType = param.OrderType; + ctx._source.TrackStatusId = param.TrackStatusId; + ctx._source.TrackStatus = param.TrackStatus; + ctx._source.CompareFields = param.CompareFields; + ctx._source.Extra = param.Extra; + ctx._source.TrackContentId = param.TrackContentId; + ctx._source.TrackContent = param.TrackContent; + ctx._source.TrackCode = param.TrackCode; + ctx._source.TrackPlace = param.TrackPlace; + ctx._source.TrackTime = param.TrackTime; + ctx._source.TrackTimeZone = param.TrackTimeZone; + ctx._source.TrackCreateTime = param.TrackCreateTime; + ctx._source.PushTrackStatus = param.PushTrackStatus; + ctx._source.PushTrackCode = param.PushTrackCode; + ctx._source.PushTrackContent = param.PushTrackContent; + ctx._source.PushTrackEnContent = param.PushTrackEnContent; + ctx._source.TrackReturnTime = param.TrackReturnTime; + ctx._source.RetryCount = param.RetryCount; + ctx._source.Status = param.Status; + ctx._source.ActualReturnTimeSpan = param.ActualReturnTimeSpan; + ctx._source.SystemReturnTimeSpan = param.SystemReturnTimeSpan; + ctx._source.TrackCreateTimeSpan = param.TrackCreateTimeSpan; + ctx._source.CreateTime = param.CreateTime; + ctx._source.UpdateTime = param.UpdateTime; + ").Params(p => p + .Add("ReturnWarningRuleId", detail.ReturnWarningRuleId) + .Add("WarningId", detail.WarningId) + .Add("Version", detail.Version) + .Add("ReturnTimeSpan", detail.ReturnTimeSpan) + //.Add("NotifyModes", detail.NotifyModes) + .Add("NotifyUsers", detail.NotifyUsers) + .Add("SubSystemId", detail.SubSystemId) + .Add("CustomId", detail.CustomId) + .Add("OrderId", detail.OrderId) + .Add("OrderNo", detail.OrderNo) + .Add("OrderType", detail.OrderType) + .Add("TrackStatusId", detail.TrackStatusId) + .Add("TrackStatus", detail.TrackStatus) + .Add("CompareFields", detail.CompareFields) + .Add("Extra", detail.Extra) + .Add("TrackContentId", detail.TrackContentId) + .Add("TrackContent", detail.TrackContent) + .Add("TrackCode", detail.TrackCode) + .Add("TrackPlace", detail.TrackPlace) + .Add("TrackTime", detail.TrackTime) + .Add("TrackTimeZone", detail.TrackTimeZone) + .Add("TrackCreateTime", detail.TrackCreateTime) + .Add("PushTrackStatus", detail.PushTrackStatus) + .Add("PushTrackCode", detail.PushTrackCode) + .Add("PushTrackContent", detail.PushTrackContent) + .Add("PushTrackEnContent", detail.PushTrackEnContent) + .Add("TrackReturnTime", detail.TrackReturnTime) + .Add("RetryCount", detail.RetryCount) + .Add("Status", detail.Status) + .Add("ActualReturnTimeSpan", detail.ActualReturnTimeSpan) + .Add("SystemReturnTimeSpan", detail.SystemReturnTimeSpan) + .Add("TrackCreateTimeSpan", detail.TrackCreateTimeSpan) + .Add("CreateTime", detail.CreateTime) + .Add("UpdateTime", detail.UpdateTime) + ) + ) + ); + + await PollyHelper.RetryPollyExecAsync(() => _client.BulkAsync(desc), response => + { + if (response.IsValid) // 认为成功 + return true; + + if (response.ServerError.Status == 409) //版本冲突认为失败,则重试 + return false; + + throw new InvalidOperationException($"ES更新失败:{details.Count}条,原因:{response.DebugInformation}。"); + }); + } + + public async Task InsertReturnWarningDetailsAsync(ReturnWarningDetailsIndex detail) + { + var indexName = $"{GlobalConst.ReturnWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + detail.IndexName = indexName; + var r = await _client.IndexAsync(detail, s => s.Index(indexName)); + + if (!r.IsValid) + { + throw new InvalidOperationException($"回传预警明细新增失败,{detail},原因:{r.DebugInformation}。"); + } + } + + public async Task BulkInsertReturnWarningDetailsAsync(List details, int maxRetries = 3) + { + // 按天写入es + var indexName = $"{GlobalConst.ReturnWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + details.ForEach(x => x.IndexName = indexName); + + var r = await _client.IndexManyAsync(details, indexName); + + if (!r.IsValid) + { + throw new InvalidOperationException($"回传预警明细新增失败,原因:{r.DebugInformation}。"); + } + } + + public async Task InsertReturnMessageRecordAsync(ReturnMessageRecordIndex detail) + { + var indexName = $"{GlobalConst.ReturnMessageRecordIndex}-{DateTime.Now:yyyy.MM.dd}"; + detail.IndexName = indexName; + var r = await _client.IndexAsync(detail, s => s.Index(indexName)); + + if (!r.IsValid) + { + throw new InvalidOperationException($"回传日志新增失败,{detail},原因:{r.DebugInformation}。"); + } + } + + public async Task BulkInsertReturnMessageRecordAsync(List details, int maxRetries = 3) + { + // 按天写入es + var indexName = $"{GlobalConst.ReturnMessageRecordIndex}-{DateTime.Now:yyyy.MM.dd}"; + details.ForEach(x => x.IndexName = indexName); + + var r = await _client.IndexManyAsync(details, indexName); + + if (!r.IsValid) + { + throw new InvalidOperationException($"回传日志新增失败,原因:{r.DebugInformation}。"); + } + } + + #endregion + + #region 完整 + + public async Task> GetPageCompleteWarningDetailsAsync(GetPageCompleteWarningDetailsInputVO input) + { + var musts = ListWrapperFactory.CreateListWrapper(input); + + var orderNos = input.OrderNos?.Split(','); + + musts.AddIf(x => x.SubSystemId > 0, c => c.Term(f => f.SubSystemId, input.SubSystemId)); + musts.AddIf(_ => orderNos?.Length > 0, c => c.Terms(f => f.Field(d => d.OrderNo.Suffix("keyword")).Terms(orderNos))); + musts.AddIf(x => x.OrderType > 0, c => c.Term(f => f.OrderType, input.OrderType)); + musts.AddIf(x => x.Status > 0, c => c.Term(f => f.Status, input.Status)); + musts.AddIf(x => x.CustomId > 0, c => c.Term(f => f.CustomId, input.CustomId)); + musts.AddIf(x => x.MissingTrackStatusId > 0, c => c.Match(f => f.Field(d => d.MissingTrackStatusIds).Query(input.MissingTrackStatusId.ToString()))); + musts.AddIf(x => x.WarningCreateTimeStart.HasValue, c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.WarningCreateTimeStart))); + musts.AddIf(x => x.WarningCreateTimeEnd.HasValue, c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.WarningCreateTimeEnd))); + musts.AddIf(x => x.WarningHandleTimeStart.HasValue, c => c.DateRange(r => r.Field(f => f.ProcessedTime).LessThanOrEquals(input.WarningHandleTimeStart))); + musts.AddIf(x => x.WarningHandleTimeEnd.HasValue, c => c.DateRange(r => r.Field(f => f.ProcessedTime).LessThanOrEquals(input.WarningHandleTimeEnd))); + + // 模糊匹配userId + musts.AddIf(x => x.NotifyUserId.HasValue, c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId.ToString()))); + + var resp = await _client.SearchAsync(x => x + .Index(GlobalConst.CompleteWarningDetailsIndex) + .Query(q => q.Bool(b => b.Must(musts))) + .From((input.PageIndex - 1) * input.PageSize) + .Size(input.PageSize)); + + return new UnifyPageResultDto(resp.ToIdList(), input.PageIndex, input.PageSize, (int) resp.Total); + } + + public async Task UpdateCompleteWarningDetailsAsync(CompleteWarningDetailsIndex detail) + { + var r = await _client.UpdateAsync(detail.Id, + u => u.Index(detail.IndexName) + .Doc(new + { + detail.WarningId, + detail.CompleteWarningRuleId, + detail.Version, + //detail.NotifyModes, + detail.NotifyUsers, + detail.CheckPointTrackStatusId, + detail.MissingTrackStatusIds, + detail.SubSystemId, + detail.CustomId, + detail.OrderId, + detail.OrderNo, + detail.OrderType, + detail.Status, + detail.CreateTime, + detail.ProcessedTime, + })); + } + + public async Task BulkUpdateCompleteWarningDetailsAsync(List details, int maxRetries = 3) + { + if (details.Count == 0) return; + + //构建批量更新操作描述符 + var desc = new BulkDescriptor().UpdateMany(details, + (u, detail) => u.Id(detail.Id) + .Index(detail.IndexName) + .Script(s => s.Source(@" + ctx._source.WarningId = param.WarningId; + ctx._source.CompleteWarningRuleId = param.CompleteWarningRuleId; + ctx._source.Version = param.Version; + //ctx._source.NotifyModes = param.NotifyModes; + ctx._source.NotifyUsers = param.NotifyUsers; + ctx._source.CheckPointTrackStatusId = param.CheckPointTrackStatusId; + ctx._source.MissingTrackStatusIds = param.MissingTrackStatusIds; + ctx._source.SubSystemId = param.SubSystemId; + ctx._source.CustomId = param.CustomId; + ctx._source.OrderId = param.OrderId; + ctx._source.OrderNo = param.OrderNo; + ctx._source.OrderType = param.OrderType; + ctx._source.Status = param.Status; + ctx._source.CreateTime = param.CreateTime; + ctx._source.ProcessedTime = param.ProcessedTime; + ") + .Params(p => p + .Add("WarningId", detail.WarningId) + .Add("CompleteWarningRuleId", detail.CompleteWarningRuleId) + .Add("Version", detail.Version) + //.Add("NotifyModes", detail.NotifyModes) + .Add("NotifyUsers", detail.NotifyUsers) + .Add("CheckPointTrackStatusId", detail.CheckPointTrackStatusId) + .Add("MissingTrackStatusIds", detail.MissingTrackStatusIds) + .Add("SubSystemId", detail.SubSystemId) + .Add("CustomId", detail.CustomId) + .Add("OrderId", detail.OrderId) + .Add("OrderNo", detail.OrderNo) + .Add("OrderType", detail.OrderType) + .Add("Status", detail.Status) + .Add("CreateTime", detail.CreateTime) + .Add("ProcessedTime", detail.ProcessedTime) + ) + ) + ); + + await PollyHelper.RetryPollyExecAsync(() => _client.BulkAsync(desc), response => + { + if (response.IsValid) // 认为成功 + return true; + + if (response.ServerError.Status == 409) //版本冲突认为失败,则重试 + return false; + + throw new InvalidOperationException($"ES更新失败:{details.Count}条,原因:{response.DebugInformation}。"); + }); + } + + public async Task InsertCompleteWarningDetailsAsync(CompleteWarningDetailsIndex detail) + { + var indexName = $"{GlobalConst.CompleteWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + detail.IndexName = indexName; + var r = await _client.IndexAsync(detail, s => s.Index(indexName)); + + if (!r.IsValid) + { + throw new InvalidOperationException($"完整预警明细新增失败,{detail},原因:{r.DebugInformation}。"); + } + } + + public async Task BulkInsertCompleteWarningDetailsAsync(List details, int maxRetries = 3) + { + // 按天写入es + var indexName = $"{GlobalConst.CompleteWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + details.ForEach(x => x.IndexName = indexName); + + var r = await _client.IndexManyAsync(details, indexName); + + if (!r.IsValid) + { + throw new InvalidOperationException($"完整预警明细新增失败,原因:{r.DebugInformation}。"); + } + } + + #endregion + + public async Task CreateAliasAsync() + { + var indexName1 = $"{GlobalConst.TransportWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + var indexName2 = $"{GlobalConst.ReturnWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + var indexName3 = $"{GlobalConst.CompleteWarningDetailsIndex}-{DateTime.Now:yyyy.MM.dd}"; + var indexName4 = $"{GlobalConst.ReturnMessageRecordIndex}-{DateTime.Now:yyyy.MM.dd}"; + + var exists1 = await _client.Indices.ExistsAsync(indexName1); + if (!exists1.Exists) + { + await _client.Indices.CreateAsync(indexName1); + await _client.MapAsync(m => m.Index(indexName1).AutoMap()); + } + + var exists2 = await _client.Indices.ExistsAsync(indexName2); + if (!exists2.Exists) + { + await _client.Indices.CreateAsync(indexName2); + await _client.MapAsync(m => m.Index(indexName2).AutoMap()); + } + + var exists3 = await _client.Indices.ExistsAsync(indexName3); + if (!exists3.Exists) + { + await _client.Indices.CreateAsync(indexName3); + await _client.MapAsync(m => m.Index(indexName3).AutoMap()); + } + + var exists4 = await _client.Indices.ExistsAsync(indexName4); + if (!exists4.Exists) + { + await _client.Indices.CreateAsync(indexName4); + await _client.MapAsync(m => m.Index(indexName4).AutoMap()); + } + + var r = await _client.Indices.BulkAliasAsync(b => + b.Add(a => a.Alias(GlobalConst.TransportWarningDetailsIndex).Index(indexName1)) + .Add(a => a.Alias(GlobalConst.ReturnWarningDetailsIndex).Index(indexName2)) + .Add(a => a.Alias(GlobalConst.CompleteWarningDetailsIndex).Index(indexName3)) + .Add(a => a.Alias(GlobalConst.ReturnMessageRecordIndex).Index(indexName4)) + ); + + } +} diff --git a/src/SJZY.Track.Repository/Repositories/WarningRuleRepository.cs b/src/SJZY.Track.Repository/Repositories/WarningRuleRepository.cs new file mode 100644 index 00000000..495e2a74 --- /dev/null +++ b/src/SJZY.Track.Repository/Repositories/WarningRuleRepository.cs @@ -0,0 +1,217 @@ +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Complete; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; +using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Extensions; + +namespace SJZY.Track.Repository.Repositories; + +public sealed class WarningRuleRepository : BasicRepository, IWarningRuleRepository, ITransientDependency +{ + #region 规则主体 + + public async Task CheckNameDuplicateAsync(int id, int subsystemId, int customId, string name) + { + var entity = await Context.Queryable() + .Where(x => x.SubSystemId == subsystemId && + x.CustomId == customId + && string.Equals(x.Name, name)) + .FirstAsync(); + + // 如果是新增,则要求不能重复添加 subsystemId+customId+name三者都相同的 + if (id == 0) + return entity != null; + + // 如果是修改,则要求查出来的和要修改的是同一个,否则视为重复 + return entity.Id != id; + } + + public async Task UpsertReturnIdAsync(WarningRuleEntity entity) + { + if (entity.Id == 0) + { + return await Context.Insertable(entity) + .ExecuteReturnIdentityAsync(); + } + + var isUpdated = await Context.Updateable(entity) + .ExecuteCommandHasChangeAsync(); + + // 指定了id更新失败 + if (!isUpdated) + { + throw new Exception($"{entity}更新失败"); + } + + return entity.Id; + } + + public async Task UpdateVersionAsync(WarningRuleEntity entity) + { + var version = VersionGenerate.GenVersion(); + await Context.Updateable() + .Where(x => x.Id == entity.Id) + .SetColumns(x => x.Version, version) + .ExecuteCommandHasChangeAsync(); + return version; + } + + public Task> GetWarningRuleListAsync(int? subsystemId, string? keyword, bool? isEnabled, int? warningType) + { + return Context.Queryable() + .WhereIF(subsystemId.HasValue, x => x.SubSystemId == subsystemId!.Value) + // todo: 客户/kpi名称 改成客户下拉 + // .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.Name.Contains(keyword!) || x.CustomName.Contains(keyword!)) + .WhereIF(isEnabled.HasValue, x => x.IsEnabled == isEnabled!.Value) + .WhereIF(warningType.HasValue, x => x.WarningType == warningType!.Value) + .ToListAsync(); + } + + public async Task GetWarningRuleForEnabledAsync(int subSystemId, int customId, WarningRuleType warningType) + { + var warningRules = await Context.Queryable() + .Where(x => x.SubSystemId == subSystemId) + .Where(x => x.CustomId == customId) + .Where(x => x.WarningType == (int) warningType) + .ToListAsync(); + + return warningRules.FirstOrDefault(); + } + + public async Task GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null) + { + var result = await Context.Queryable() + .Where(x => x.Id == id) + .WhereIF(warningType.HasValue, x => x.WarningType == (int) warningType!.Value) + .ToListAsync(); + return result.FirstOrDefault(); + } + + #endregion + + #region 运输时效预警规则 + + public Task> GetTransportSceneAsync(int warningId) + { + return Context.Queryable() + .Where(x => x.WarningId == warningId) + .ToListAsync(); + } + + public async Task GetTransportSceneAsync(int warningId, int sceneId) + { + var result = await Context.Queryable() + .Where(x => x.WarningId == warningId && x.Id == sceneId) + .ToListAsync(); + return result.FirstOrDefault(); + } + + public Task> GetTransportSceneRuleItemsAsync(int warningId, int sceneId) + { + return Context.Queryable() + .Where(x => x.WarningId == warningId && x.TransportWarningSceneId == sceneId) + .ToListAsync(); + } + + public async Task UpsertTransportSceneReturnIdAsync(TransportWarningSceneEntity entity) + { + if (entity.Id == 0) + { + return await Context.Insertable(entity) + .ExecuteReturnIdentityAsync(); + } + + var isUpdated = await Context.Updateable(entity) + .ExecuteCommandHasChangeAsync(); + + // 指定了id更新失败 + if (!isUpdated) + { + throw new Exception($"{entity}更新失败"); + } + + return entity.Id; + } + + public async Task BulkUpdateTransportSceneRuleItemsAsync(List entities) + { + return await Context.Updateable(entities) + .ExecuteCommandAsync(); + } + + public Task BulkInsertTransportSceneRuleItemsAsync(List entities) + { + return Context.Insertable(entities) + .ExecuteCommandAsync(); + } + + public Task DeleteTransportSceneRuleItemsAsync(HashSet ruleItemIds) + { + return Context.Deleteable() + .Where(x => ruleItemIds.Contains(x.Id)) + .ExecuteCommandAsync(); + } + + #endregion + + #region 回传时效预警规则 + + public Task> GetReturnRuleItemsAsync(int warningId) + { + return Context.Queryable() + .Where(x => x.WarningId == warningId) + .ToListAsync(); + } + + public Task BulkInsertReturnRuleItemsAsync(List entities) + { + return Context.Insertable(entities) + .ExecuteCommandAsync(); + } + + public async Task BulkUpdateReturnRuleItemsAsync(List entities) + { + return await Context.Updateable(entities) + .ExecuteCommandAsync(); + } + + public Task DeleteReturnRuleItemsAsync(HashSet ruleItemIds) + { + return Context.Deleteable() + .Where(x => ruleItemIds.Contains(x.Id)) + .ExecuteCommandAsync(); + } + + #endregion + + #region 完整预警规则 + + public Task> GetCompleteRuleItemsAsync(int warningId) + { + return Context.Queryable() + .Where(x => x.WarningId == warningId) + .ToListAsync(); + } + + public async Task BulkUpdateCompleteRuleItemsAsync(List entities) + { + return await Context.Updateable(entities) + .ExecuteCommandAsync(); + } + + public Task BulkInsertCompleteRuleItemsAsync(List entities) + { + return Context.Insertable(entities) + .ExecuteCommandAsync(); + } + + public Task DeleteCompleteRuleItemsAsync(HashSet ruleItemIds) + { + return Context.Deleteable() + .Where(x => ruleItemIds.Contains(x.Id)) + .ExecuteCommandAsync(); + } + + #endregion +} diff --git a/src/SJZY.Track.Repository/Repositories/WarningTaskRepository.cs b/src/SJZY.Track.Repository/Repositories/WarningTaskRepository.cs new file mode 100644 index 00000000..10679eb6 --- /dev/null +++ b/src/SJZY.Track.Repository/Repositories/WarningTaskRepository.cs @@ -0,0 +1,159 @@ +using System.Linq.Expressions; +using SJZY.Track.Domain.Aggregates.WarningAggregate; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Return; +using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; + +namespace SJZY.Track.Repository.Repositories; + +public sealed class WarningTaskRepository : BasicRepository, IWarningTaskRepository, ITransientDependency +{ + //public ISqlSugarClient Context { get; set; } + + public WarningTaskRepository() + { + //Context = SqlsugarScopeBasic.GetContextSafely("default"); + } + + #region 预警通知任务 + + public Task> ScanMayNeedExecutedTaskAsync() + { + return Context.Queryable() + .Where(x => x.IsClosed == false) + .ToListAsync(); + } + + public Task> GetWarningNotifyTaskAsync(Expression> expression) + { + return Context.Queryable() + .Where(expression) + .ToListAsync(); + } + + public async Task AddWarningNotifyTaskAsync(WarningNotifyTaskEntity entity) + { + return await Context.Insertable(entity).ExecuteCommandAsync() > 0; + } + + public Task BulkInsertWarningNotifyTaskAsync(List entities) + { + return Context.Insertable(entities) + .ExecuteCommandAsync(); + } + + public Task CloseWarningNotifyTaskAsync(Expression> expression) + { + return Context.Updateable() + .Where(expression) + .SetColumns(x => x.IsClosed, true) + .ExecuteCommandAsync(); + } + + public Task CloseWarningNotifyTaskAsync(WarningNotifyTaskEntity entity) + { + if (entity.IsClosed) return Task.FromResult(false); + return Context.Updateable(entity) + .SetColumns(x => x.IsClosed, true) + .ExecuteCommandHasChangeAsync(); + } + + #endregion + + #region 回传预警_重试推送任务 + + public Task> ScanReturnWarningRetryPushTaskAsync() + { + return Context.Queryable() + .Where(x => x.IsClosed == false) + .ToListAsync(); + } + + public Task> GetReturnWarningRetryPushTaskAsync(Expression> expression) + { + return Context.Queryable() + .Where(expression) + .ToListAsync(); + } + + public async Task AddReturnWarningRetryPushTaskAsync(ReturnWarningRetryPushTaskEntity entity) + { + return await Context.Insertable(entity) + .ExecuteCommandAsync() > 0; + } + + public Task BulkInsertReturnWarningRetryPushTaskAsync(List entities) + { + return Context.Insertable(entities) + .ExecuteCommandAsync(); + } + + public Task CloseReturnWarningRetryPushTaskAsync(Expression> expression) + { + return Context.Updateable() + .Where(expression) + .SetColumns(x => x.IsClosed, true) + .ExecuteCommandAsync(); + } + + public Task CloseReturnWarningRetryPushTaskAsync(ReturnWarningRetryPushTaskEntity entity) + { + if (entity.IsClosed) return Task.FromResult(false); + return Context.Updateable(entity) + .SetColumns(x => x.IsClosed, true) + .ExecuteCommandHasChangeAsync(); + } + + #endregion + + #region 运输预警_预警明细生成任务 + + public Task> ScanTransportWarningDetailsGenerateTaskAsync() + { + return Context.Queryable() + .Where(x => x.IsClosed == false) + .ToListAsync(); + } + + public Task> GetTransportWarningDetailsGenerateTaskAsync(Expression> expression) + { + return Context.Queryable() + .Where(expression) + .ToListAsync(); + } + + public async Task AddTransportWarningDetailsGenerateTaskAsync(TransportWarningDetailsGenerateTaskEntity entity) + { + return await Context.Insertable(entity) + .ExecuteCommandAsync() > 0; + } + + public Task BulkInsertTransportWarningDetailsGenerateTaskAsync(List entities) + { + return Context.Insertable(entities) + .ExecuteCommandAsync(); + } + + public Task BulkUpdateTransportWarningDetailsGenerateTaskAsync(List entities) + { + return Context.Updateable(entities) + .ExecuteCommandAsync(); + } + + public Task CloseTransportWarningDetailsGenerateTaskAsync(Expression> expression) + { + return Context.Updateable() + .Where(expression) + .SetColumns(x => x.IsClosed, true) + .ExecuteCommandAsync(); + } + + public Task CloseTransportWarningDetailsGenerateTaskAsync(TransportWarningDetailsGenerateTaskEntity entity) + { + if (entity.IsClosed) return Task.FromResult(false); + return Context.Updateable(entity) + .SetColumns(x => x.IsClosed, true) + .ExecuteCommandHasChangeAsync(); + } + + #endregion +}