diff --git a/.gitignore b/.gitignore index d53fbc8a..96708268 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ ## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. +## files generated by popular Visual Studio add-ons.. # User-specific files *.suo @@ -189,3 +189,4 @@ UpgradeLog*.htm FakesAssemblies/ logs/ /.vs +.idea/ diff --git a/src/SJZY.Track.Api/SjzyTrackApiModule.cs b/src/SJZY.Track.Api/SjzyTrackApiModule.cs index 13d9633a..a9cd95ae 100644 --- a/src/SJZY.Track.Api/SjzyTrackApiModule.cs +++ b/src/SJZY.Track.Api/SjzyTrackApiModule.cs @@ -9,6 +9,7 @@ using SJZY.Expand.ABP.Core.Prometheus; using SJZY.Expand.ABP.Core.Redis; using SJZY.Expand.ABP.Core.Swagger; using SJZY.Track.Application; +using SJZY.Track.Application.HostServices; using SJZY.Track.Repository; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; @@ -62,6 +63,7 @@ namespace SJZY.Track.Api Configure(options => { options.IsEnabled = false; }); context.Services.AddRedis(); + context.Services.AddHostedService(); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/src/SJZY.Track.Api/appsettings.json b/src/SJZY.Track.Api/appsettings.json index d69cb0c1..6f7fc2b9 100644 --- a/src/SJZY.Track.Api/appsettings.json +++ b/src/SJZY.Track.Api/appsettings.json @@ -5,7 +5,7 @@ "Apollo": { "AppId": "SJZY.Track.Configuration", "Env": "DEV", - "Namespace": "Test", + "Namespace": "DEV", "MetaServer": "http://apollo-service-test-apollo-configservice.apollo.svc.cluster.local:8080", "ConfigServer": [ "http://apollo-service-test-apollo-configservice.apollo.svc.cluster.local:8080" ] } diff --git a/src/SJZY.Track.Application.Contract/Dtos/Common/IdWithNameDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Common/IdWithNameDto.cs new file mode 100644 index 00000000..7027b635 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Common/IdWithNameDto.cs @@ -0,0 +1,8 @@ +namespace SJZY.Track.Application.Contract.Dtos.Common; + +public class IdWithNameDto +{ + public int Id { get; set; } + + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/AreaCacheEntry.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/AreaCacheEntry.cs new file mode 100644 index 00000000..1e87ff17 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/AreaCacheEntry.cs @@ -0,0 +1,29 @@ +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class AreaCacheEntry +{ + /// + /// 国家或地区 + /// + public string Country { get; set; } = null!; + + /// + /// 省份 + /// + public string? Province { get; set; } + + /// + /// 城市 + /// + public string? City { get; set; } + + /// + /// 开始邮编 + /// + public string? LeftPostcode { get; set; } + + /// + /// 截止邮编 + /// + public string? RightPostcode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ClearRulCacheEvent.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ClearRulCacheEvent.cs new file mode 100644 index 00000000..e875e4b8 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ClearRulCacheEvent.cs @@ -0,0 +1,9 @@ +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + + +public class ClearRulCacheEvent +{ + public List Items { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/CreateWarningInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/CreateWarningInput.cs new file mode 100644 index 00000000..0179e744 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/CreateWarningInput.cs @@ -0,0 +1,42 @@ +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class CreateWarningInput +{ + /// + /// 模板名称 + /// + public string Name { get; set; } = null!; + + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 客户id + /// + public int CustomerId { get; set; } + + /// + /// 通知方式 + /// + public NotifyMode NotifyMode { get; set; } + + /// + /// 通知模板 + /// + public string NotifyTemplate { get; set; } = null!; + + /// + /// 轨迹状态跨度列表 + /// + public List TrackStatusSpans { get; set; } = new(); + + /// + /// 预警因素列表。 + /// + public List WarningFactors { get; set; } = new(); +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/EditWarningOutput.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/EditWarningOutput.cs new file mode 100644 index 00000000..f8c65d67 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/EditWarningOutput.cs @@ -0,0 +1,58 @@ +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class EditWarningOutput +{ + /// + /// 模板名称 + /// + public string Name { get; set; } = null!; + + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 客户id + /// + public int CustomerId { get; set; } + + /// + /// 通知方式 + /// + public NotifyMode NotifyMode { get; set; } + + /// + /// 通知模板 + /// + public string NotifyTemplate { get; set; } = null!; + + /// + /// 通知列表 + /// + public List TrackStatusSpans { get; set; } = new(); + + /// + /// 预警因素列表。 + /// + public List WarningFactors { get; set; } = new(); +} + +public class WarningNotifyWithIdDto: WarningNitifyDto +{ + /// + /// 通知id + /// + public int Id { get; set; } +} + +public class WarningFactorWithIdDto : WarningFactorDto +{ + /// + /// 因素id + /// + public int Id { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ForewarningRuleCacheEntry.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ForewarningRuleCacheEntry.cs new file mode 100644 index 00000000..6a00ddb7 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ForewarningRuleCacheEntry.cs @@ -0,0 +1,69 @@ +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class ForewarningRuleCacheEntry +{ + /// + /// 自增主键 + /// + public int Id { get; set; } + + /// + /// 预警id + /// + public int WarningId { get; set; } + + /// + /// 时效(小时) e.g. 24 + /// + public float TotalTimeSpan { get; set; } + + /// + /// 提醒的时间(小时) e.g. 9 10 11.. + /// + public float WarnTimeSpan { get; set; } + + /// + /// 提醒次数 e.g. 3 2 1 + /// + public int WarnCount { get; set; } + + /// + /// 起始轨迹状态id + /// + public int StartTrackStatusId { get; set; } + + /// + /// 终止轨迹状态id + /// + public int EndTrackStatusId { get; set; } + + /// + /// 货物类型 + /// + public string? CargoType { get; set; } + + /// + /// 月份 + /// + public List? Months { get; set; } + + /// + /// 区域 + /// + public List? Areas { get; set; } + + /// + /// 是否中转 + /// + public bool? IsTransit { get; set; } + + /// + /// 起运港口 + /// + public string? LeavePort { get; set; } + + /// + /// 产品编码 + /// + public string? ProductCode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ModifyTimeSpanInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ModifyTimeSpanInput.cs new file mode 100644 index 00000000..016106ec --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ModifyTimeSpanInput.cs @@ -0,0 +1,17 @@ +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class ModifyTimeSpanInput +{ + /// + /// 预警id + /// + public int WarningId { get; set; } + + /// + /// 规则详情中的时效相关信息 + /// + public List Items { get; set; } = new(); +} + diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/UpdateWarningInput.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/UpdateWarningInput.cs new file mode 100644 index 00000000..0d00a8d3 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/UpdateWarningInput.cs @@ -0,0 +1,45 @@ +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class UpdateWarningInput +{ + /// + /// 预警id + /// + public int Id { get; set; } + + /// + /// 模板名称 + /// + public string Name { get; set; } = null!; + + /// + /// 通知方式 + /// + public NotifyMode NotifyMode { get; set; } + + /// + /// 通知模板 + /// + public string NotifyTemplate { get; set; } = null!; + + /// + /// 通知列表 + /// + public List Notifies { get; set; } = new(); +} + +public class NotifyDto +{ + /// + /// 通知id + /// + public int Id { get; set; } + + /// + /// 通知人id + /// + public int NotifyUserId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningDto.cs new file mode 100644 index 00000000..f911768c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningDto.cs @@ -0,0 +1,17 @@ +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class WarningDto +{ + /// + /// 当前轨迹状态 + /// + public int TrackStatusId { get; set; } + + public int SubSystemId { get; set; } + + public int CustomerId { get; set; } + + public List Orders { get; set; } = new(); +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningNotifyCacheEntry.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningNotifyCacheEntry.cs new file mode 100644 index 00000000..61913c6d --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningNotifyCacheEntry.cs @@ -0,0 +1,32 @@ +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class WarningNotifyCacheEntry +{ + /// + /// 规则id + /// + public int RuleId { get; set; } + + /// + /// 订单id + /// + public int OrderId { get; set; } + + public WarningNotifyCacheEntry(int ruleId, int orderId) + { + RuleId = ruleId; + OrderId = orderId; + } + + public WarningNotifyCacheEntry(string notifyValue) + { + var values = notifyValue.Split(':'); + RuleId = int.Parse(values[0]); + OrderId = int.Parse(values[1]); + } + + public override string ToString() + { + return $"{RuleId}:{OrderId}"; + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningOrderDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningOrderDto.cs new file mode 100644 index 00000000..035175cc --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningOrderDto.cs @@ -0,0 +1,47 @@ +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +public class WarningOrderDto +{ + /// + /// 订单id + /// + public int Id { get; set; } + + /// + /// 轨迹发生时间 + /// + public DateTime TrackTime { get; set; } + + public string? CargoType { get; set; } + + public int Month { get; set; } + + public bool? IsTransit { get; set; } + + /// + /// 起运港 + /// + public string? LeavePort { get; set; } + + public string? ProductCode { get; set; } + + /// + /// 目的城市 + /// + public string ArrivalCity { get; set; } = null!; + + /// + /// 目的国家 + /// + public string ArrivalCountry { get; set; } = null!; + + /// + /// 到达邮编 + /// + public string? ArrivalPostCode { get; set; } + + /// + /// 到达州省 + /// + public string? ArrivalProvince { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningTimeSpanDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningTimeSpanDto.cs new file mode 100644 index 00000000..02c3e20c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningTimeSpanDto.cs @@ -0,0 +1,27 @@ +namespace SJZY.Track.Application.Contract.Dtos.Forewarning; + +/// +/// 预警规则时效 +/// +public class WarningTimeSpanDto +{ + /// + /// 预警规则id + /// + public int Id { get; set; } + + /// + /// 时效 + /// + public float TotalTimeSpan { get; set; } + + /// + /// 提醒时效 + /// + public float WarnTimeSpan { get; set; } + + /// + /// 提醒次数 + /// + public int WarnCount { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs index ebb47f17..14e2fdc3 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs @@ -51,4 +51,19 @@ public class UpdateOrderInput : OrderNoInfo /// 订单航班信息 /// public List FlightInfos { get; set; } = new(); + + /// + /// 货物类型 + /// + public string? CargoType { get; set; } + + /// + /// 是否转运 + /// + public bool? IsTransfer { get; set; } + + /// + /// 产品编码 + /// + public string? ProductCode { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackNode/TrackNodeInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackNode/TrackNodeInput.cs new file mode 100644 index 00000000..d487c033 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackNode/TrackNodeInput.cs @@ -0,0 +1,29 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackNode; + +public class EditTrackNodeInput +{ + /// + /// 操作类型 + /// + public OptionType Options { get; set; } + + /// + /// 轨迹节点id,创建不用填 + /// + public long Id { get; set; } + + /// + /// 系统id + /// + public int? SubSystemId { get; set; } + + /// + /// 节点名称 + /// + public string? NodeName { get; set; } + + /// + /// 节点英文名称 + /// + public string? NodeEnName { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullConfigInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullConfigInput.cs index f60beeb4..3a6b51dc 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullConfigInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullConfigInput.cs @@ -70,7 +70,11 @@ public class EditTrackPullConfigInput public string? OrderPath { get; set; } /// - /// 轨迹明细路径 /// public string? TrackDetailsPath { get; set; } + + /// + /// 轨迹内容code + /// + public string? TrackContentCode { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs index 1342b38b..24882e20 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs @@ -52,8 +52,14 @@ public class EditTrackPullContentInput /// /// 轨迹时区GMT /// + [Obsolete] public double? TrackTimeZone { get; set; } + /// + /// 兜底轨迹时区发生地 + /// + public int? TrackTimePlaceValue { get; set; } + /// /// 是否完结 /// diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs index 771b328c..a7f2c500 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs @@ -37,13 +37,13 @@ public class TrackPullContentConfigDto /// public int? TrackPlaceValue { get; set; } - /// - /// 轨迹时区GMT值 - /// - public float? TrackTimeZoneValue { get; set; } - /// /// 订单是否完成(由轨迹刷新配置决定) /// public bool IsCompleted { get; set; } + + /// + /// 轨迹时区发生地参数值 + /// + public int? TrackZonePlaceValue { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullParamConfigDto.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullParamConfigDto.cs index 79af5185..c4f65d0b 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullParamConfigDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullParamConfigDto.cs @@ -46,4 +46,9 @@ public class TrackPullParamConfigDto /// 轨迹明细数组路径 /// public string TrackDetailsPath { get; set; } = null!; + + /// + /// 轨迹内容代码 + /// + public string? TrackContentCode { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPush/EditTrackPushConfigInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPush/EditTrackPushConfigInput.cs index b4ed8f6e..bb4b0066 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackPush/EditTrackPushConfigInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPush/EditTrackPushConfigInput.cs @@ -1,4 +1,6 @@ -namespace SJZY.Track.Application.Contract.Dtos.TrackPush; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Contract.Dtos.TrackPush; public class EditTrackPushConfigInput { @@ -48,4 +50,19 @@ public class EditTrackPushConfigInput /// 是否全量推送 /// public bool? IsAllPush { get; set; } + + /// + /// 时间格式枚举 + /// + public DateTimeFormatEnum DateTimeFormatType { get; set; } + + /// + /// 日期时间格式模板 + /// + public string? DateTimeFormat { get; set; } + + /// + /// 时区格式格式模板 + /// + public string? TimezoneFormat { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushContentConfigDto.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushContentConfigDto.cs index c2bcf867..8984a769 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushContentConfigDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushContentConfigDto.cs @@ -1,4 +1,6 @@ -namespace SJZY.Track.Application.Contract.Dtos.TrackPush; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Contract.Dtos.TrackPush; public class TrackPushContentConfigDto { @@ -71,4 +73,19 @@ public class TrackPushContentConfigDto /// 扩展字段参数值 /// public string? ExpandFieldsValue { get; set; } + + /// + /// 时间格式枚举 + /// + public DateTimeFormatEnum DateTimeFormatType { get; set; } + + /// + /// 日期时间格式模板 + /// + public string? DateTimeFormat { get; set; } + + /// + /// 时区格式格式模板 + /// + public string? TimezoneFormat { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushInput.cs index bc1d10bc..28a18876 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPush/TrackPushInput.cs @@ -21,7 +21,7 @@ public class TrackUpdateInput public class TrackItem { public string Location { get; set; } = null!; - public DateTime Date { get; set; } + public string Date { get; set; } public double TimeZone { get; set; } public string? Description { get; set; } public string? DescriptionCn { get; set; } diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackStatus/GetTrackStatusDetailDto.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackStatus/GetTrackStatusDetailDto.cs index fe39c418..b77a4a41 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackStatus/GetTrackStatusDetailDto.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackStatus/GetTrackStatusDetailDto.cs @@ -36,5 +36,10 @@ /// 轨迹内容英文 /// public string EntrackStatus { get; set; } + + /// + /// 节点名称 + /// + public string? NodeName { get; set; } } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackStatus/QueryTrackNodeRequest.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackStatus/QueryTrackNodeRequest.cs new file mode 100644 index 00000000..f7903f71 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackStatus/QueryTrackNodeRequest.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackStatus; + +public class QueryTrackNodeRequest:PageInfoInput +{ + /// + /// 关键字,节点中文和英文 + /// + public string? KeyWord { get; set; } + + /// + /// 子系统id + /// + public long? SystemId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Events/ModifyWarningEvent.cs b/src/SJZY.Track.Application.Contract/Events/ModifyWarningEvent.cs new file mode 100644 index 00000000..265f00c2 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Events/ModifyWarningEvent.cs @@ -0,0 +1,23 @@ +namespace SJZY.Track.Application.Contract.Events; + +/// +/// redis 的发布订阅事件 +/// +public class ModifyWarningEvent:RedisEvent +{ + public string Key { get; set; } +} + +public class RedisEvent : IRedisEvent +{ + public string EventType { get; set; } +} + + +/// +/// 简单的事件接口,后续可以根据需要扩展 +/// +public interface IRedisEvent +{ + string EventType { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Events/OrderIdentifier.cs b/src/SJZY.Track.Application.Contract/Events/OrderIdentifier.cs index 158e64b1..49e79b04 100644 --- a/src/SJZY.Track.Application.Contract/Events/OrderIdentifier.cs +++ b/src/SJZY.Track.Application.Contract/Events/OrderIdentifier.cs @@ -5,6 +5,4 @@ public struct OrderIdentifier public int OrderId { get; set; } public string OrderNo { get; set; } public int OrderType { get; set; } - - public int CustomId { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs b/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs index a59c31c8..2eb42c61 100644 --- a/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs +++ b/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs @@ -2,6 +2,16 @@ public class TrackChangeAfterUpdateOrderEvent { + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 客户id + /// + public int CustomId { get; set; } + /// /// 订单Id /// @@ -22,6 +32,11 @@ public class TrackChangeAfterUpdateOrderEvent /// public string TrackEnContent { get; set; } = null!; + /// + /// 轨迹状态Id + /// + public int TrackStatusId { get; set; } + /// /// 轨迹发生地 /// @@ -36,4 +51,14 @@ public class TrackChangeAfterUpdateOrderEvent /// 轨迹时区 /// public float TrackTimeZone { get; set; } + + /// + /// 轨迹内容代码 + /// + public string? TrackCode { get; set; } + + /// + /// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种 + /// + public int DeficiencyTagValue { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs b/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs index 905060ea..2884b824 100644 --- a/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs +++ b/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs @@ -3,9 +3,14 @@ public class TrackUpsertEvent { /// - /// 子系统id(操作的订单都是相同子系统下的) + /// 子系统id /// public int SubSystemId { get; set; } + + /// + /// 客户id + /// + public int CustomId { get; set; } /// /// 订单id @@ -48,4 +53,9 @@ public class TrackUpsertEvent public List Visited { get; set; } = new(); public TrackRefreshStruct TrackRefreshData { get; set; } + + /// + /// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种 + /// + public int DeficiencyTagValue { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/SJZY.Track.Application.Contract.csproj b/src/SJZY.Track.Application.Contract/SJZY.Track.Application.Contract.csproj index 7a7f7902..f2e2a7a3 100644 --- a/src/SJZY.Track.Application.Contract/SJZY.Track.Application.Contract.csproj +++ b/src/SJZY.Track.Application.Contract/SJZY.Track.Application.Contract.csproj @@ -12,5 +12,8 @@ + + + diff --git a/src/SJZY.Track.Application.Contract/Services/IForewarningCacheService.cs b/src/SJZY.Track.Application.Contract/Services/IForewarningCacheService.cs new file mode 100644 index 00000000..08d8fafb --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Services/IForewarningCacheService.cs @@ -0,0 +1,42 @@ +using SJZY.Track.Application.Contract.Dtos.Forewarning; + +namespace SJZY.Track.Application.Contract.Services; + +public interface IForewarningCacheService +{ + /// + /// 获取预警规则,如果没有则返回null + /// + /// 子系统id + /// 客户id + /// + Task?> GetRulesAsync(int subSystemId, int customerId); + + /// + /// 删除规则 + /// + /// + /// + /// + Task DeleteRulesAsync(int subSystemId, int customerId); + + /// + /// 重置本地缓存 + /// + /// + Task Clear(ClearRulCacheEvent @event); + + /// + /// 清除redis 缓存 + /// + /// + Task ClearRedisByAreaId(int areaId); + + /// + /// 清除redis 缓存 + /// + /// 子系统id + /// 客户id + /// + Task ClearRedisBySystemIdAndCustomer(int subSystemId, int customerId); +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Services/IForewarningNotifyService.cs b/src/SJZY.Track.Application.Contract/Services/IForewarningNotifyService.cs new file mode 100644 index 00000000..914535ae --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Services/IForewarningNotifyService.cs @@ -0,0 +1,23 @@ +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Application.Contract.Services; + +/// +/// 预警通知服务接口定义。 +/// 提供创建、删除预警通知和发送通知的功能。 +/// +public interface IForewarningNotifyService +{ + /// + /// 创建预警通知。 + /// + /// 预警订单模型。 + /// 创建操作的结果,以整数形式返回。 + Task WarningCalculateAsync(WarningDto order); + + /// + /// 发送通知。 + /// + Task SendNotificationAsync(); +} \ No newline at end of file diff --git a/src/SJZY.Track.Application.Contract/Services/IKpiAreaCacheService.cs b/src/SJZY.Track.Application.Contract/Services/IKpiAreaCacheService.cs new file mode 100644 index 00000000..5713a2d8 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Services/IKpiAreaCacheService.cs @@ -0,0 +1,6 @@ +namespace SJZY.Track.Application.Contract.Services; + +public interface IKpiAreaCacheService +{ + +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/AutoMapper/ForewarningMapperProfile.cs b/src/SJZY.Track.Application/AutoMapper/ForewarningMapperProfile.cs new file mode 100644 index 00000000..6a48362a --- /dev/null +++ b/src/SJZY.Track.Application/AutoMapper/ForewarningMapperProfile.cs @@ -0,0 +1,39 @@ +using AutoMapper; +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Domain.Aggregates.ForewarningAggregate; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Dtos.Forewarning; +using Volo.Abp.AutoMapper; + +namespace SJZY.Track.Application.AutoMapper; + +public class ForewarningMapperProfile :Profile +{ + public ForewarningMapperProfile() + { + CreateMap(MemberList.None) + .Ignore(x => x.WarningFactors); + CreateMap(MemberList.None); + CreateMap(MemberList.None); + CreateMap(MemberList.None) + .ForMember(dest => dest.CountryCode, opt => opt.MapFrom(src => src.CountryCode)) + .ForMember(dest => dest.CountryName, opt => opt.MapFrom(src => src.CountryName)) + .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province)) + .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City)) + .ForMember(dest => dest.LeftPostcode, opt => opt.MapFrom(src => src.LeftPostCode)) + .ForMember(dest => dest.RightPostcode, opt => opt.MapFrom(src => src.RightPostCode)) + .ForMember(dest => dest.AreaId, opt => opt.MapFrom(src => src.AreaId)); + + CreateMap(MemberList.None) + .ForMember(dest => dest.SubSystemId, opt => opt.MapFrom(src => src.SystemId)) + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)); + + CreateMap(MemberList.None) + .ForMember(dest => dest.CountryCode, opt => opt.MapFrom(src => src.CountryCode)) + .ForMember(dest => dest.CountryName, opt => opt.MapFrom(src => src.CountryName)) + .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province)) + .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City)) + .ForMember(dest => dest.LeftPostcode, opt => opt.MapFrom(src => src.LeftPostCode)) + .ForMember(dest => dest.RightPostcode, opt => opt.MapFrom(src => src.RightPostCode)); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/AutoMapper/TrackNodeMapperProfile.cs b/src/SJZY.Track.Application/AutoMapper/TrackNodeMapperProfile.cs new file mode 100644 index 00000000..b2226591 --- /dev/null +++ b/src/SJZY.Track.Application/AutoMapper/TrackNodeMapperProfile.cs @@ -0,0 +1,20 @@ +using AutoMapper; +using SJZY.Track.Application.Contract.Dtos.TrackNode; +using SJZY.Track.Application.Contract.Dtos.TrackStatus; +using SJZY.Track.Domain.Aggregates.TrackNodeAggregate; +using SJZY.Track.Domain.Dtos.TrackNode; + +namespace SJZY.Track.Application.AutoMapper; + +public class TrackNodeMapperProfile :Profile +{ + public TrackNodeMapperProfile() + { + CreateMap(); + CreateMap(MemberList.None) + .ForMember(val => val.NodeName, opt => opt.MapFrom(src => src.NodeName)) + .ForMember(val => val.NodeEnName, opt => opt.MapFrom(src => src.NodeEnName)) + .ForMember(val => val.SubSystemId, opt => opt.MapFrom(src => src.SubSystemId)); + + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/AutoMapper/TrackPullAutoMapperProfile.cs b/src/SJZY.Track.Application/AutoMapper/TrackPullAutoMapperProfile.cs index c2409eea..8ca802aa 100644 --- a/src/SJZY.Track.Application/AutoMapper/TrackPullAutoMapperProfile.cs +++ b/src/SJZY.Track.Application/AutoMapper/TrackPullAutoMapperProfile.cs @@ -15,6 +15,7 @@ public class TrackPullAutoMapperProfile : Profile .ForMember(dest => dest.IsPush, opt => opt.MapFrom(source => false)); CreateMap(MemberList.None) .ForMember(dest => dest.TrackKeyword, opt => opt.MapFrom(src => string.Join(',', src.TrackKeywords))) + .ForMember(dest => dest.TrackZonePlaceValue, opt => opt.MapFrom(src => string.Join(',', src.TrackTimePlaceValue))) .ForMember(dest => dest.TrackKeywordReverse, opt => opt.MapFrom(src => string.Join(',', src.TrackKeywordReverses))); CreateMap(MemberList.None); CreateMap(MemberList.None); diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs index c8f184ae..85d0ef57 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs @@ -1,8 +1,11 @@ using DotNetCore.CAP; +using SJZY.Track.Application.Contract.Dtos.Forewarning; using SJZY.Track.Application.Contract.Events; +using SJZY.Track.Application.Contract.Services; using SJZY.Track.Domain.Aggregates.OrderAggregate; -using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Order; +using SJZY.Track.Domain.Queries; using Volo.Abp.DependencyInjection; namespace SJZY.Track.Application.CAP.EventHandlers; @@ -10,33 +13,28 @@ namespace SJZY.Track.Application.CAP.EventHandlers; public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICapSubscribe { private readonly IOrderRepository _orderRepository; - private readonly ITrackDetailsRepository _trackDetailsRepository; - + private readonly IForewarningNotifyService _forewarningNotifyService; + private readonly ITrackPullCacheService _trackPullCacheService; + private readonly IProvideQueries _provideQueries; + public TrackChangeAfterUpdateOrderEventHandler(IOrderRepository orderRepository, - ITrackDetailsRepository trackDetailsRepository) + IForewarningNotifyService forewarningNotifyService, ITrackPullCacheService trackPullCacheService, IProvideQueries provideQueries) { _orderRepository = orderRepository; - _trackDetailsRepository = trackDetailsRepository; + _forewarningNotifyService = forewarningNotifyService; + _trackPullCacheService = trackPullCacheService; + _provideQueries = provideQueries; } [CapSubscribe(GlobalConst.TrackChangeAfterUpdateOrderSubscribe)] public async Task ConsumerAsync(TrackChangeAfterUpdateOrderEvent @event) { - var orders = await _orderRepository.GetListAsync(x => @event.OrderIds.Contains(x.Id), - x => new OrderEntity - { - Id = x.Id, - IsCompleted = x.IsCompleted, - LatestTrackContent = x.LatestTrackContent, - LatestTrackEnContent = x.LatestTrackEnContent, - LatestTrackPlace = x.LatestTrackPlace, - LatestTrackTime = x.LatestTrackTime, - LatestTrackTimeZone = x.LatestTrackTimeZone, - }); + var orders = await GetOrdersAsync(@event.OrderIds); if (orders.Count == 0) return; var updateOrders = new List(); + var providerDic= await GetProviderDic(@event.OrderIds); foreach (var order in orders) { if (order.LatestTrackContent != @event.TrackContent) @@ -49,11 +47,28 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa } } + // 执行轨迹兜底时 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种 + if (@event.DeficiencyTagValue > 0 && @event.DeficiencyTagValue<=3) + { + order.DeficiencyTag = @event.DeficiencyTagValue; + } + if (!order.IsCompleted) { order.IsCompleted = @event.IsCompleted; } + if (!string.IsNullOrEmpty(@event.TrackCode) && providerDic.TryGetValue(order.Id, out var provider)) + { + var config = await _trackPullCacheService.GetPullParamWithContentConfigAsync(provider.SubSystemId, provider.ServiceProviderId); + + if (!string.IsNullOrEmpty(config?.ParamConfig.TrackContentCode) && config.ParamConfig.TrackContentCode!.ToLower() == @event.TrackCode.ToLower()) + { + // 触发拉取节点 + order.IsTriggerPull = 1; + } + } + order.LatestTrackContent = @event.TrackContent; order.LatestTrackEnContent = @event.TrackEnContent; order.LatestTrackPlace = @event.TrackPlace; @@ -65,7 +80,75 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa await _orderRepository.UpdateRangeAsync(updateOrders, x => new { x.IsCompleted, x.LatestTrackContent, x.LatestTrackEnContent, x.LatestTrackPlace, x.LatestTrackTime, - x.LatestTrackTimeZone + x.LatestTrackTimeZone,x.IsTriggerPull,x.DeficiencyTag + }); + + // 预警计算 + await _forewarningNotifyService.WarningCalculateAsync(BuiltWarningDto(orders, @event)); + } + + private async Task> GetOrdersAsync(int[] orderIds) + { + return await _orderRepository.GetListAsync(order => orderIds.Contains(order.Id), order => new OrderEntity + { + Id = order.Id, + + // 轨迹 + IsCompleted = order.IsCompleted, + LatestTrackContent = order.LatestTrackContent, + LatestTrackEnContent = order.LatestTrackEnContent, + LatestTrackPlace = order.LatestTrackPlace, + LatestTrackTime = order.LatestTrackTime, + LatestTrackTimeZone = order.LatestTrackTimeZone, + + // 因素 + CargoType = order.CargoType, + CreateTime = order.CreateTime, + ArrivalCountry = order.ArrivalCountry, + ArrivalProvince = order.ArrivalProvince, + ArrivalCity = order.ArrivalCity, + ArrivalPostCode = order.ArrivalPostCode, + IsTransit = order.IsTransit, + LeavePort = order.LeavePort, + ProductCode = order.ProductCode }); } + + private WarningDto BuiltWarningDto(List orders, TrackChangeAfterUpdateOrderEvent @event) + { + return new WarningDto + { + TrackStatusId = @event.TrackStatusId, + SubSystemId = @event.SubSystemId, + CustomerId = @event.CustomId, + Orders = orders.Select(order => new WarningOrderDto + { + Id = order.Id, + TrackTime = @event.TrackTime, + + CargoType = order.CargoType, + Month = order.CreateTime.Month, + IsTransit = order.IsTransit, + LeavePort = order.LeavePort, + ProductCode = order.ProductCode, + + ArrivalCountry = order.ArrivalCountry, + ArrivalProvince = order.ArrivalProvince, + ArrivalCity = order.ArrivalCity, + ArrivalPostCode = order.ArrivalPostCode, + }).ToList() + }; + } + + /// + /// 获取服务商字典 + /// + /// + /// + private async Task> GetProviderDic(int[] orderIds) + { + var providerList = await _provideQueries.FindOrderWithServiceProviderAsync(orderIds); + return providerList.GroupBy(val => val.OrderId) + .ToDictionary(val => val.Key, val => val.First()); + } } \ No newline at end of file diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushCompletedEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushCompletedEventHandler.cs index 4658aa44..b63c4e52 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushCompletedEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushCompletedEventHandler.cs @@ -26,7 +26,7 @@ public class TrackPushCompletedEventHandler : ITransientDependency, ICapSubscrib [CapSubscribe(GlobalConst.TrackPushCompletedSubscribe, Group = GlobalConst.TrackPushSubscribe)] public async Task ConsumerAsync(InterfaceRelayForwardReplayEvent @event) { - string logStr = null; + string? logStr = null; var optionType = Base64Helper.DeSerialize(@event.Attach); if (optionType == OptionType.新增.ToString()) { diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs index 734246eb..369221c7 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackPushEventHandler.cs @@ -1,4 +1,5 @@ -using DotNetCore.CAP; +using System.Globalization; +using DotNetCore.CAP; using Newtonsoft.Json; using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.InterfaceRelay.Application.Contracts; @@ -23,8 +24,8 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe private readonly ITrackPushCacheService _trackPushCacheService; private readonly ITrackDetailsRepository _trackDetailsRepository; private readonly ITrackPushRecordsRepository _trackPushRecordsRepository; - private List _pushConfigs; - private List _orders; + private List _pushConfigs = null!; + private List _orders = null!; public TrackPushEventHandler(IHttpClientProxy httpClientProxy, IOrderRepository orderRepository, ITrackPushCacheService trackPushCacheService, @@ -86,7 +87,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe if (config == null) continue; if (!config.IsAutoPush) continue; if (config.OrderType != order.OrderType) continue; - + var log = new TrackPushRecordIndex { OptionType = @event.OptionType.ToString(), @@ -112,7 +113,6 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe var tracks = await _trackDetailsRepository.GetListAsync(@event.OrderIds, @event.TrackCode); tracks = tracks.DistinctBy(x => x.OrderId).ToList(); // 防止重复推送 - if (tracks.Count == 0) return; // 一个能推的轨迹都不存在 // tips:人工推送只要能推就推了,不会校验已推和顺序问题,因为可能是更新 @@ -202,12 +202,14 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe private TrackUpdateInput BuiltUpdateItem(TrackPushContentConfigDto config, TrackPushRecordIndex log) { + var trackDatetime = log.TrackTime.AddHours(config.TrackTimeZone ?? log.TrackTimeZone - log.TrackTimeZone); + var trackDateStr = DateTimeFormat(trackDatetime,config.TrackTimeZone ?? log.TrackTimeZone,config); + var track = new TrackItem { // 推真实的发生地和发生时间(时间是计算后的) Location = log.TrackPlace, - Date = log.TrackTime.AddHours( - config.TrackTimeZone ?? log.TrackTimeZone - log.TrackTimeZone), + Date = trackDateStr, TimeZone = config.TrackTimeZone ?? log.TrackTimeZone, Description = config.TrackEnContentValue, DescriptionCn = config.TrackContentValue, @@ -228,12 +230,14 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe private async Task BuiltPushItem(TrackPushContentConfigDto config, TrackPushRecordIndex log) { + var trackDatetime = log.TrackTime.AddHours(config.TrackTimeZone ?? log.TrackTimeZone - log.TrackTimeZone); + var trackDateStr = DateTimeFormat(trackDatetime,config.TrackTimeZone ?? log.TrackTimeZone,config); + var track = new TrackItem { // 推真实的发生地和发生时间(时间是计算后的) Location = log.TrackPlace, - Date = log.TrackTime.AddHours( - config.TrackTimeZone ?? log.TrackTimeZone - log.TrackTimeZone), + Date = trackDateStr, TimeZone = config.TrackTimeZone ?? log.TrackTimeZone, Description = config.TrackEnContentValue, DescriptionCn = config.TrackContentValue, @@ -287,4 +291,28 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe return matched != null && matched.TrackCode == trackCode ? matched : default; } + + /// + /// 转换推送时间格式 + /// + /// + /// + /// + /// + private string DateTimeFormat(DateTime dateTime,double timezone, TrackPushContentConfigDto config) + { + if (config.DateTimeFormatType == DateTimeFormatEnum.Timestamp) + return DateTimeHelper.ConvertToUnixTimestamp(dateTime).ToString(); + + if (config.DateTimeFormatType == DateTimeFormatEnum.DateTimeAndTimezone + && !string.IsNullOrEmpty(config.DateTimeFormat) + && !string.IsNullOrEmpty(config.TimezoneFormat)) + { + var zoneStr = DateTimeHelper.TimezoneFormat(timezone, config.TimezoneFormat); + var datetimeStr = DateTimeHelper.FormatDateTime(dateTime, config.DateTimeFormat); + return $"{datetimeStr} {zoneStr}"; + } + + return dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffK"); + } } \ No newline at end of file diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs index f982343e..8411bf76 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs @@ -12,6 +12,7 @@ using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Consts; using SJZY.Track.Domain.Enums; +using SJZY.Track.Domain.Queries; using Volo.Abp.DependencyInjection; namespace SJZY.Track.Application.CAP.EventHandlers; @@ -22,15 +23,17 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe private readonly ICapPublisher _capPublisher; private readonly ITrackPullCacheService _trackPullCacheService; private readonly ITrackDetailsRepository _trackDetailsRepository; + private readonly ITimezoneQueries _timezoneQueries; public TrackRefreshEventHandler(IOrderRepository orderRepository, ICapPublisher capPublisher, ITrackPullCacheService trackPullCacheService, - ITrackDetailsRepository trackDetailsRepository) + ITrackDetailsRepository trackDetailsRepository, ITimezoneQueries timezoneQueries) { _orderRepository = orderRepository; _capPublisher = capPublisher; _trackPullCacheService = trackPullCacheService; _trackDetailsRepository = trackDetailsRepository; + _timezoneQueries = timezoneQueries; } [CapSubscribe(GlobalConst.TrackRefreshSubscribe, Group = GlobalConst.TrackRefreshSubscribe)] @@ -172,21 +175,12 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe var trackPlace = paramConfig.TrackPlaceName == null ? null : trackToken[paramConfig.TrackPlaceName]?.Value(); - if (trackPlace.IsNullOrWhiteSpace()) + var hasPlaceBottomLine = false; //执行了兜底但是没有兜底住,需要记录 + if (trackPlace.IsNullOrWhiteSpace() && matchedContentConfig?.TrackPlaceValue != null && matchedContentConfig.TrackPlaceValue.HasValue) { - Expression> resultExpression = - (TrackLocationValue?)matchedContentConfig?.TrackPlaceValue switch - { - TrackLocationValue.ArrivalPort => x => x.ArrivalPort, - TrackLocationValue.ArriveCity => x => x.ArrivalCity, - TrackLocationValue.ArrivalCountry => x => x.ArrivalCountry, - TrackLocationValue.LeavePort => x => x.LeavePort, - TrackLocationValue.LeaveCity => x => x.LeaveCity, - TrackLocationValue.LeaveCountry => x => x.LeaveCountry, - _ => x => x.LeaveCountry - }; - trackPlace = await _orderRepository.GetFirstAsync(x => - x.Id == context.OrderId, resultExpression); + //兜底没配置的就不需要兜底,拉取的发生地没就没 + trackPlace = await GetPlaceByOrder(matchedContentConfig!.TrackPlaceValue.Value,context.OrderId); + hasPlaceBottomLine = string.IsNullOrWhiteSpace(trackPlace); } // 轨迹发生时间 @@ -198,6 +192,17 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe ? null : trackToken[paramConfig.TrackTimeZoneName]?.Value(); + //兜底时区 + var hasTimezoneBottomLine = false; //执行了兜底但是没有兜底住,需要记录 + if (!trackTimeZone.HasValue && matchedContentConfig?.TrackZonePlaceValue != null && matchedContentConfig.TrackZonePlaceValue.HasValue) + { + //兜底没配置的就不需要兜底,拉取的发生地没就没 + var timezone= await GetTimezone(matchedContentConfig,context.OrderId); + + trackTimeZone = timezone; + hasTimezoneBottomLine = !timezone.HasValue; + } + if (matchedContentConfig is null) { var notMappedEvent = new TrackInsertForNotMappedEvent @@ -209,7 +214,7 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe OriginTrackData = JsonConvert.SerializeObject(trackToken), TrackPlace = trackPlace ?? GlobalConst.EmptyString, TrackTime = trackTime ?? DateTime.Now, - TrackTimeZone = trackTimeZone ?? matchedContentConfig?.TrackTimeZoneValue ?? 8 + TrackTimeZone = trackTimeZone ?? 8 }; if (paramConfig.TrackContentName != null) { @@ -220,6 +225,16 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe return; } + int deficiencyTagValue = 0; + // 触发了兜底,但是没有兜底住,对订单进行标记 + if (hasPlaceBottomLine || hasTimezoneBottomLine) + { + //1表示时区,2表示发生地,3表示两种 + var placeTagValue = hasPlaceBottomLine ? 2 : 0; + var timezoneTagValue = hasTimezoneBottomLine ? 1 : 0; + deficiencyTagValue = placeTagValue + timezoneTagValue; + } + // 新增/更新轨迹 var trackUpsertEvent = new TrackUpsertEvent { @@ -230,9 +245,9 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe OrderId = context.OrderId, OrderNo = context.OrderNo, OrderType = context.OrderType, - CustomId = context.CustomId } }, + TrackRefreshData = new TrackRefreshStruct { ServiceProviderName = context.ServiceProviderName, @@ -246,11 +261,61 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe TrackCode = matchedContentConfig.TrackContentCode, TrackPlace = trackPlace ?? GlobalConst.EmptyString, TrackTime = trackTime ?? DateTime.Now, - TrackTimeZone = trackTimeZone ?? matchedContentConfig.TrackTimeZoneValue ?? 8 + TrackTimeZone = trackTimeZone ?? 8, + DeficiencyTagValue = deficiencyTagValue }; trackUpsertEvent.IsAdd = !await _trackDetailsRepository.AnyByCodeAsync(context.OrderId, matchedContentConfig.TrackContentCode); await _capPublisher.PublishAsync(GlobalConst.TrackUpsertForRefreshSubscribe, trackUpsertEvent); } + + /// + /// 根据订单id获取兜底位置 + /// + /// + /// + /// + private Task GetPlaceByOrder(int trackValue,int orderId) + { + Expression> resultExpression = + (TrackLocationValue)trackValue switch + { + TrackLocationValue.ArrivalPort => x => x.ArrivalPort, + TrackLocationValue.ArriveCity => x => x.ArrivalCity, + TrackLocationValue.ArrivalCountry => x => x.ArrivalCountry, + TrackLocationValue.LeavePort => x => x.LeavePort, + TrackLocationValue.LeaveCity => x => x.LeaveCity, + TrackLocationValue.LeaveCountry => x => x.LeaveCountry, + _ => x => x.LeaveCountry + }; + return _orderRepository.GetFirstAsync(x => x.Id == orderId, resultExpression); + } + + /// + /// 获取时区 + /// + /// + /// + /// + private async Task GetTimezone(TrackPullContentConfigDto matchedContentConfig,int orderId ) + { + var placeOrCode = await GetPlaceByOrder(matchedContentConfig!.TrackZonePlaceValue.Value,orderId); + string timezone = null; + if (!string.IsNullOrEmpty(placeOrCode)) + { + + if (matchedContentConfig!.TrackZonePlaceValue.Value == (int)TrackLocationValue.ArrivalPort + || matchedContentConfig!.TrackZonePlaceValue.Value == (int)TrackLocationValue.LeavePort) + { + timezone = await _timezoneQueries.GetPortTimezone(placeOrCode); + return DateTimeHelper.ConvertTimeZoneToFloat(timezone); + } + + timezone = await _timezoneQueries.GetCityTimezone(placeOrCode); + return DateTimeHelper.ConvertTimeZoneToFloat(timezone); + } + + return null; + } } \ No newline at end of file diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs index 46443ffb..703900c5 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs @@ -2,11 +2,14 @@ using SJZY.Expand.ABP.Core.Logger; using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Track.Application.Contract.Events; +using SJZY.Track.Application.Contract.Services; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Order; using SJZY.Track.Domain.Queries; using Volo.Abp.DependencyInjection; +using OrderIdentifier = SJZY.Track.Application.Contract.Events.OrderIdentifier; namespace SJZY.Track.Application.CAP.EventHandlers; @@ -53,6 +56,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe var nextEvent = @event.DeepCopy(); nextEvent.Visited.AddRange(orderIds); nextEvent.SubSystemId = group.Key.SubSystemId; + nextEvent.CustomId = group.Key.CustomId; nextEvent.TrackRefreshData = new TrackRefreshStruct { ServiceProviderName = @event.TrackRefreshData.ServiceProviderName, @@ -83,7 +87,6 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe OrderId = x.OrderId, OrderNo = x.OrderNo, OrderType = x.OrderType, - CustomId = x.CustomId }).ToList(); var idx = 0; while (idx < identifiers.Count) @@ -100,9 +103,13 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } } } - + private async Task HandleTrackAsync(TrackUpsertEvent @event, List canNextOrderIds) { + var content = await _provideQueries.GetTrackContentAsync(@event.SubSystemId, @event.TrackCode); + + if (content == null) return; + var orderIds = @event.OrderIdentifiers.Select(x => x.OrderId).ToArray(); var tracks = await _trackDetailsRepository.GetListAsync(orderIds, @event.TrackCode, true); @@ -110,7 +117,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe var trackContents = await _provideQueries.MatchTrackContentAsync(newOrderIds, @event.TrackCode); var insertTracks = new List(); - + foreach (var order in @event.OrderIdentifiers) { var track = tracks.FirstOrDefault(x => x.OrderId == order.OrderId); @@ -170,46 +177,44 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe if (canNextOrderIds.Count == 0) return; - var content = await _provideQueries.GetTrackContentAsync(@event.SubSystemId, @event.TrackCode); - // 批量add to es await _trackDetailsRepository.AddManyAsync(insertTracks); - + // 触发订单主表更新事件(异步) await _capPublisher.PublishAsync(GlobalConst.TrackChangeAfterUpdateOrderSubscribe, new TrackChangeAfterUpdateOrderEvent { + SubSystemId = @event.SubSystemId, + CustomId = @event.CustomId, OrderIds = canNextOrderIds.ToArray(), IsCompleted = @event.TrackRefreshData.IsCompleted, TrackContent = content.TrackContent, TrackEnContent = content.TrackEnContent, + TrackStatusId = content.TrackStatusId, TrackPlace = @event.TrackPlace, TrackTime = @event.TrackTime, - TrackTimeZone = @event.TrackTimeZone + TrackTimeZone = @event.TrackTimeZone, + TrackCode = @event.TrackCode, + DeficiencyTagValue = @event.DeficiencyTagValue }); // 触发轨迹推送事件(异步) - foreach (var group in @event.OrderIdentifiers - .Where(x => x.OrderId.IsIn(canNextOrderIds)) - .GroupBy(x => x.CustomId)) + var trackPushEvent = new TrackPushEvent { - var trackPushEvent = new TrackPushEvent + OptionType = @event.IsAdd ? OptionType.新增 : OptionType.修改, + SubSystemId = @event.SubSystemId, + CustomId = @event.CustomId, + OrderIds = canNextOrderIds.ToArray(), + TrackCode = @event.TrackCode, + SystemPushInfo = new SystemPushStruct { - OptionType = @event.IsAdd ? OptionType.新增 : OptionType.修改, - SubSystemId = @event.SubSystemId, - CustomId = group.Key, - OrderIds = group.Select(x => x.OrderId).ToArray(), - TrackCode = @event.TrackCode, - SystemPushInfo = new SystemPushStruct - { - TrackStatus = content.TrackStatus, - TrackContent = content.TrackContent, - TrackPlace = @event.TrackPlace, - TrackTime = @event.TrackTime, - TrackTimeZone = @event.TrackTimeZone - } - }; - await _capPublisher.PublishAsync(GlobalConst.TrackPushSubscribe, trackPushEvent); - } + TrackStatus = content.TrackStatus, + TrackContent = content.TrackContent, + TrackPlace = @event.TrackPlace, + TrackTime = @event.TrackTime, + TrackTimeZone = @event.TrackTimeZone + } + }; + await _capPublisher.PublishAsync(GlobalConst.TrackPushSubscribe, trackPushEvent); } } \ No newline at end of file diff --git a/src/SJZY.Track.Application/Helpers/DateTimeHelper.cs b/src/SJZY.Track.Application/Helpers/DateTimeHelper.cs new file mode 100644 index 00000000..afc41d27 --- /dev/null +++ b/src/SJZY.Track.Application/Helpers/DateTimeHelper.cs @@ -0,0 +1,60 @@ +using SJZY.OTel; + +namespace SJZY.Track.Application.Helpers; + +public static class DateTimeHelper +{ + /// + /// convert to a Unix timestamp from a DateTime object. + /// + /// The DateTime object to convert to a Unix timestamp. + /// The Unix timestamp as a long. + public static long ConvertToUnixTimestamp(DateTime dateTime) + { + return new DateTimeOffset(dateTime).ToUnixTimeSeconds(); + } + + /// + /// Formats a DateTime object into a specified format. + /// + /// The DateTime object to format. + /// The format string. + /// The formatted date string. + public static string FormatDateTime(DateTime dateTime, string format) + { + return dateTime.ToString(format); + } + + /// + /// 时区格式化 + /// + /// + /// + /// + public static string TimezoneFormat(double timezone, string format) + { + //时区是整数丢掉浮点数部分 + return format.Replace("hh", ((int)timezone).ToString()); + } + + /// + /// 转换主数据中心类似GMT-09:00这种的时区为浮点数 + /// + /// 分区 + /// + /// + public static float? ConvertTimeZoneToFloat(string timeZone) + { + if (string.IsNullOrEmpty(timeZone)|| !timeZone.StartsWith("GMT")) + { + return null; + } + + var sign = timeZone[3] == '+' ? 1 : -1; + var parts = timeZone.Substring(4).Split(':'); + var hours = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + + return sign * (hours + minutes / 60f); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Helpers/ForewarningHelper.cs b/src/SJZY.Track.Application/Helpers/ForewarningHelper.cs new file mode 100644 index 00000000..a1a0e3bc --- /dev/null +++ b/src/SJZY.Track.Application/Helpers/ForewarningHelper.cs @@ -0,0 +1,243 @@ +using System.Dynamic; +using Nest; +using Newtonsoft.Json.Linq; +using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Track.Application.Contract.Dtos.Common; +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Domain.Aggregates.ForewarningAggregate; +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Application.Helpers; + +public class ForewarningHelper +{ + /// + /// 递推分裂预警规则列表 + /// + /// + /// + /// + public static List CalcRuleList(List statusSpans, + List factors) + { + if (statusSpans.Count == 0 || factors.Count == 0) + { + throw new BusinessException(message: "轨迹状态区间或因素列表为空"); + } + + var len = statusSpans.Count * factors.Select(f => f.FactorValue.Count).Aggregate((pre, cur) => pre * cur); + if (len == 0) + { + throw new BusinessException(message: "轨迹状态区间或因素列表为空"); + } + + var rules = Enumerable.Range(0, len).Select(_ => new WarningRuleEntity()).ToList(); + + var repeat = len / statusSpans.Count; + + FillStatus(statusSpans, rules, repeat); + + foreach (var factor in factors) + { + var factorValueCount = factor.FactorValue.Count; + + // repeat梯度下降,与times成反比关系 + repeat /= factorValueCount; + var times = len / repeat / factorValueCount; + FillFactor(factor, rules, repeat, times); + } + + + return rules; + } + + private static void FillStatus(List statusSpans, List rules, int repeat) + { + var row = 0; + foreach (var span in statusSpans) + { + for (var i = 0; i < repeat; i++, row++) + { + rules[row].StartTrackStatusId = span.StartTrackStatusId; + rules[row].StartTrackStatus = span.StartTrackStatus; + rules[row].EndTrackStatusId = span.EndTrackStatusId; + rules[row].EndTrackStatus = span.EndTrackStatus; + } + } + } + + private static void FillFactor(WarningFactorDto factor, List rules, int repeat, int times) + { + var row = 0; + for (var i = 0; i < times; i++) + { + foreach (var value in factor.FactorValue) + { + var strValue = value.ToString(); + if (strValue.IsNullOrEmpty()) + { + throw new BusinessException(message: "因素值不能为空"); + } + + for (var j = 0; j < repeat; j++, row++) + { + switch (factor.FactorType) + { + case RuleFactorType.CargoType: + if (value.Type is not JTokenType.String) + { + throw new BusinessException(message: "货物类型只接收字符串"); + } + + rules[row].CargoType = strValue; + break; + case RuleFactorType.Months: + foreach (var item in strValue.Split("/")) + { + if (!int.TryParse(item, out var month) || month < 1 || month > 12) + { + throw new BusinessException(message: "请输入合法的月份,多个月份请以'/'连接"); + } + } + + rules[row].Months = strValue; + break; + case RuleFactorType.Area: + if (value.Type is not JTokenType.Integer) + { + throw new BusinessException(message: "分区请传id"); + } + + rules[row].AreaId = int.Parse(strValue); + break; + case RuleFactorType.IsTransit: + if (value.Type is not JTokenType.Boolean) + { + throw new BusinessException(message: "是否中转只接收bool类型"); + } + + rules[row].IsTransit = bool.Parse(strValue); + break; + case RuleFactorType.LeavePort: + if (value.Type is not JTokenType.String) + { + throw new BusinessException(message: "起运港只接收字符串"); + } + + rules[row].LeavePort = strValue; + break; + case RuleFactorType.ProductCode: + if (value.Type is not JTokenType.String) + { + throw new BusinessException(message: "产品代码只接收字符串"); + } + + rules[row].ProductCode = strValue; + break; + default: + throw new BusinessException(message: $"不支持的因素:{factor.FactorType}"); + } + } + } + } + } + + public static List MapFactorEntities(List warningFactors) + { + return warningFactors.Select(x => new WarningFactorEntity + { + Type = x.FactorType, + Value = x.FactorValue.ToJson() + }).ToList(); + } + + public static List MapNotifyEntities(CreateWarningInput input) + { + return input.TrackStatusSpans.Select(x => new WarningNotifyEntity + { + StartTrackStatusId = x.StartTrackStatusId, + EndTrackStatusId = x.EndTrackStatusId, + NotifyMode = input.NotifyMode, + NotifyTemplate = input.NotifyTemplate, + NotifyUserId = x.NotifyUserId + }).ToList(); + } + + public static EditWarningOutput MapWarningSummary(WarningEntity warning, List statusList) + { + var statusDic = statusList.ToDictionary(x => x.Id, x => x.Name); + return new EditWarningOutput + { + Name = warning.Name, + SubSystemId = warning.SubSystemId, + CustomerId = warning.CustomerId, + NotifyMode = warning.WarningNotifies.First().NotifyMode, + NotifyTemplate = warning.WarningNotifies.First().NotifyTemplate, + TrackStatusSpans = warning.WarningNotifies.Select(x => new WarningNotifyWithIdDto + { + Id = x.Id, + StartTrackStatusId = x.StartTrackStatusId, + StartTrackStatus = statusDic.TryGetValue(x.StartTrackStatusId, out var start) ? start : "", + EndTrackStatusId = x.EndTrackStatusId, + EndTrackStatus = statusDic.TryGetValue(x.EndTrackStatusId, out var end) ? end : "", + NotifyUserId = x.NotifyUserId, + }).ToList(), + WarningFactors = warning.WarningFactors.Select(x => new WarningFactorWithIdDto + { + Id = x.Id, + FactorType = x.Type, + FactorValue = JArray.Parse(x.Value), + }).ToList() + }; + } + + public static List DynamicCalcRuleList(List rules, List factors) + { + var dynamicList = new List(); + + foreach (var rule in rules) + { + dynamic dynamicRule = new ExpandoObject(); + var dynamicRuleDict = (IDictionary)dynamicRule; + + dynamicRuleDict[nameof(Id)] = rule.Id; + dynamicRuleDict[nameof(rule.StartTrackStatus)] = rule.StartTrackStatus; + dynamicRuleDict[nameof(rule.EndTrackStatus)] = rule.EndTrackStatus; + + foreach (var factor in factors) + { + switch (factor) + { + case RuleFactorType.CargoType: + dynamicRuleDict[nameof(rule.CargoType)] = rule.CargoType; + break; + case RuleFactorType.Months: + dynamicRuleDict[nameof(rule.Months)] = rule.Months; + break; + case RuleFactorType.Area: + dynamicRuleDict[nameof(rule.Area)] = rule.Area; + break; + case RuleFactorType.IsTransit: + dynamicRuleDict[nameof(rule.IsTransit)] = rule.IsTransit; + break; + case RuleFactorType.LeavePort: + dynamicRuleDict[nameof(rule.LeavePort)] = rule.LeavePort; + break; + case RuleFactorType.ProductCode: + dynamicRuleDict[nameof(rule.ProductCode)] = rule.ProductCode; + break; + default: + throw new BusinessException(message: $"不支持的因素:{factor}"); + } + } + + dynamicRuleDict[nameof(rule.TotalTimeSpan)] = rule.TotalTimeSpan; + dynamicRuleDict[nameof(rule.WarnTimeSpan)] = rule.WarnTimeSpan; + dynamicRuleDict[nameof(rule.WarnCount)] = rule.WarnCount; + dynamicList.Add(dynamicRule); + } + + return dynamicList; + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/HostServices/RedisHostService.cs b/src/SJZY.Track.Application/HostServices/RedisHostService.cs new file mode 100644 index 00000000..0a7237e8 --- /dev/null +++ b/src/SJZY.Track.Application/HostServices/RedisHostService.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Hosting; +using SJZY.Expand.ABP.Core; +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Application.Contract.Events; +using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.UserCenter.SharedModel.Dtos.PHP.User; + +namespace SJZY.Track.Application.HostServices; + +public class RedisHostService:IHostedService +{ + readonly IForewarningCacheService _forewarningCacheService; + + public RedisHostService(IForewarningCacheService forewarningCacheService) + { + _forewarningCacheService = forewarningCacheService; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + // 订阅频道 + RedisHelper.Client.Subscribe(GetKey(), Subscribe); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + RedisHelper.Client.UnSubscribe(GetKey()); + return Task.CompletedTask; + } + + private void Subscribe(string message,object value) + { + if (value is string) + { + // 暂时只处理一个事件,不做反序列化 + //var instance = Newtonsoft.Json.JsonConvert.DeserializeObject(value as string); + var instance = Newtonsoft.Json.JsonConvert.DeserializeObject(value as string); + if(instance!=null) + _forewarningCacheService.Clear(instance); + } + } + + private string GetKey() + { + return $"{GlobalConst.PublishNotifyChannelKey}"; + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/SJZY.Track.Application.csproj b/src/SJZY.Track.Application/SJZY.Track.Application.csproj index 378f50c7..011dd0d7 100644 --- a/src/SJZY.Track.Application/SJZY.Track.Application.csproj +++ b/src/SJZY.Track.Application/SJZY.Track.Application.csproj @@ -29,4 +29,8 @@ + + + + diff --git a/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningCacheService.cs b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningCacheService.cs new file mode 100644 index 00000000..7c3b3b00 --- /dev/null +++ b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningCacheService.cs @@ -0,0 +1,223 @@ +using System.Collections.Concurrent; +using SJZY.Expand.ABP.Core; +using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.Queries; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.Services.ForewarningServices; + +public class ForewarningCacheService : IForewarningCacheService, ISingletonDependency +{ + readonly IKpiAreaRepository _kpiAreaRepository; + private readonly IWarningQueries _warningQueries; + + private const string GlobalWaningLockKey = "GlobalWaningLockKey_GlobalLock"; + + private readonly ConcurrentDictionary> _rules = new(); + private readonly ConcurrentDictionary> _areas = new(); + object _lock = new object(); + public ForewarningCacheService(IKpiAreaRepository kpiAreaRepository, IWarningQueries warningQueries) + { + _kpiAreaRepository = kpiAreaRepository; + _warningQueries = warningQueries; + } + + string WarningKey(int subSystemId, int customerId) => + $"{GlobalConst.TrackWarningKeyPrefix}:{subSystemId}:{customerId}"; + + string AreaKey(int areaId) => $"{GlobalConst.TrackAreaKeyPrefix}:{areaId}"; + + public async Task?> GetRulesAsync(int subSystemId, int customerId) + { + var key = WarningKey(subSystemId, customerId); + if( _rules.TryGetValue(key,out var rules)) + { + return rules; + } + + return await GetRules(subSystemId, customerId); + } + + private List WriteRules(List rules) + { + List result = new List(); + foreach (var item in rules) + { + List? areaList = null; + if (item.AreaId.HasValue) + { + areaList = GetAreaInfoByAreaId(item.AreaId!.Value); + } + + var entry = new ForewarningRuleCacheEntry + { + Id = item.Id, + WarningId = item.WarningId, + TotalTimeSpan = item.TotalTimeSpan, + WarnTimeSpan = item.WarnTimeSpan, + WarnCount = item.WarnCount, + StartTrackStatusId = item.StartTrackStatusId, + EndTrackStatusId = item.EndTrackStatusId, + CargoType = item.CargoType, + Months = string.IsNullOrEmpty(item.Months) + ? new List() + : item.Months.Split('/').Select(int.Parse).ToList(), + IsTransit = item.IsTransit, + Areas = areaList, + ProductCode = item.ProductCode, + LeavePort = item.LeavePort + }; + + result.Add(entry); + } + + return result; + } + + private async Task?> GetRules(int systemId, int customerId) + { + var key = WarningKey(systemId, customerId); + var result = RedisHelper.Client.Get>(key); + if (result != null) + { + // 非线程安全,需要加锁 + lock (_lock) + { + if (_rules.TryGetValue(key, out var ruleResult)) + { + return ruleResult; + } + + ruleResult = WriteRules(result); + _rules.TryAdd(key,ruleResult); + return ruleResult; + } + } + + // 非线程安全,需要加锁 + using (RedisHelper.Client.Lock(GlobalWaningLockKey, 20)) + { + if (_rules.TryGetValue(key, out var ruleResult)) return ruleResult; + + var warnings = await _warningQueries.GetWarningRule(systemId, customerId); + if (warnings == null || warnings.Count == 0) + { + RedisHelper.Client.Set(key, new List(), TimeSpan.FromSeconds(20)); + return new List(); + } + + RedisHelper.Client.Set(key, warnings); + ruleResult = WriteRules(warnings); + _rules.TryAdd(key, ruleResult); + return ruleResult; + } + } + + public Task DeleteRulesAsync(int subSystemId, int customerId) + { + _rules.Remove(WarningKey(subSystemId, customerId),out _); + + return Task.CompletedTask; + } + + private List? GetAreaInfoByAreaId(int areaId) + { + if (_areas.TryGetValue(areaId, out var areaInfos)) return areaInfos; + + var areas = GetAreas(areaId); + if (areas == null || areas.Count == 0) return null; + + areaInfos = areas.Select(KpiAreaInfoToEntry).ToList(); + _areas.TryAdd(areaId, areaInfos); + return areaInfos; + } + + AreaCacheEntry KpiAreaInfoToEntry(KpiAreaInfo model) + { + return new AreaCacheEntry + { + Country = model.Country, + Province = model.Province, + City = model.City, + LeftPostcode = model.LeftPostcode, + RightPostcode = model.RightPostcode + }; + } + + /// + /// 获取区域 + /// + /// + private List? GetAreas(int areaId) + { + var key = AreaKey(areaId); + var result = RedisHelper.Client.Get>(key); + if (result == null) + { + result = RedisHelper.Client.Get>(key); + if (result != null) return new List(); + + var kpiArea = _kpiAreaRepository.GetAreaInfosByAreaId(areaId); + if (kpiArea == null) + { + RedisHelper.Client.Set(key, new List(), TimeSpan.FromSeconds(20)); + return new List(); + } + + RedisHelper.Client.Set(key, kpiArea); + return kpiArea; + } + + return result; + } + + public Task Clear(ClearRulCacheEvent @event) + { + foreach (var item in @event.Items) + { + DeleteRulesAsync(item.SystemId, item.CustomerId); + } + return Task.CompletedTask; + } + + public async Task ClearRedisBySystemIdAndCustomer(int subSystemId,int customerId) + { + await RedisHelper.Client.DelAsync(WarningKey(subSystemId, customerId)); + var eventModel = new ClearRulCacheEvent + { + Items = new List() + { + new RuleAreaBaseInfo(subSystemId, customerId) + } + }; + + await PublishEvent(eventModel); + } + + public async Task ClearRedisByAreaId(int areaId) + { + var baseInfos = await _warningQueries.GetWarningRuleByAreaId(areaId); + if(baseInfos==null||baseInfos.Count==0) return; + baseInfos = baseInfos.DistinctBy(val => WarningKey(val.SystemId, val.CustomerId)).ToList(); + + var willDelKeys = baseInfos.Select(val => WarningKey(val.SystemId, val.CustomerId)).ToArray(); + await RedisHelper.Client.DelAsync(willDelKeys); + await RedisHelper.Client.DelAsync(AreaKey(areaId)); + var eventModel = new ClearRulCacheEvent + { + Items = baseInfos + }; + + await PublishEvent(eventModel); + } + + private async Task PublishEvent(ClearRulCacheEvent @event) + { + await RedisHelper.Client.PublishAsync(GlobalConst.PublishNotifyChannelKey, @event.ToJson()); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs new file mode 100644 index 00000000..efaaa7d0 --- /dev/null +++ b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs @@ -0,0 +1,188 @@ +using System.Text; +using FreeRedis; +using SJZY.Expand.ABP.Core; +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Queries; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.Services.ForewarningServices; + +/// +/// 用于处理预警通知 +/// +public class ForewarningNotifyService : IForewarningNotifyService, ITransientDependency +{ + private readonly IForewarningCacheService _cacheService; + private readonly IWarningQueries _warningQueries; + + public ForewarningNotifyService(IForewarningCacheService cacheService, IWarningQueries warningQueries) + { + _cacheService = cacheService; + _warningQueries = warningQueries; + } + + /// + /// 创建预警通知 + /// + /// 创建预警通知需要的订单信息 + /// 创建的通知总量,0表示没有规则 + public async Task WarningCalculateAsync(WarningDto dto) + { + if (dto.Orders.Count == 0) return; + + var rules = await _cacheService.GetRulesAsync(dto.SubSystemId, dto.CustomerId); + if (rules == null || rules.Count == 0) return; + + var adds = new List<(int ruleId, int orderId, int cnt, long ts)>(); + var dels = new List<(int ruleId, int orderId)>(); + foreach (var order in dto.Orders) + { + MatchRule( + rules.Where(rule => rule.StartTrackStatusId == dto.TrackStatusId).ToList(), + order + ).ForEach(rule => + { + var timestamp = GetTimestamp(order.TrackTime.AddMinutes(rule.WarnTimeSpan)); + adds.Add((rule.Id, order.Id, rule.WarnCount, timestamp)); + }); + + MatchRule( + rules.Where(rule => rule.EndTrackStatusId == dto.TrackStatusId).ToList(), + order + ).ForEach(rule => { dels.Add((rule.Id, order.Id)); }); + } + + var tran = RedisHelper.Client.Multi(); + + // 维护预警队列 + foreach (var kvp in adds.ToDictionary(x => $"{x.ruleId}_{x.orderId}", x => x.cnt)) + { + await tran.HSetNxAsync(GlobalConst.ForewarningNotifyCntKey, kvp.Key, kvp.Value); // nx: not exist + } + + await tran.ZAddAsync(GlobalConst.ForewarningNotifyKey, adds.Select(x => + { + var entry = new WarningNotifyCacheEntry(x.ruleId, x.orderId); + return new ZMember(entry.ToString(), x.ts); // update timestamp + }).ToArray()); + + // 移出预警队列 + await tran.HDelAsync(GlobalConst.ForewarningNotifyCntKey, + dels.Select(x => $"{x.ruleId}_{x.orderId}").ToArray()); + await tran.ZRemAsync(GlobalConst.ForewarningNotifyKey, + dels.Select(x => $"{x.ruleId}:{x.orderId}").ToArray()); + var res = tran.Exec(); + + if (res == null) throw new BusinessException(message: "轨迹预警计算失败"); + } + + private static List MatchRule(List rules, + WarningOrderDto order) + { + var res = new List(); + foreach (var rule in rules) + { + if (rule.IsTransit != null) + { + if (order.IsTransit != rule.IsTransit) continue; + } + + if (!rule.CargoType.IsNullOrEmpty()) + { + if (order.CargoType != rule.CargoType) continue; + } + + if (!rule.LeavePort.IsNullOrEmpty()) + { + if (order.LeavePort != rule.LeavePort) continue; + } + + if (!rule.ProductCode.IsNullOrEmpty()) + { + if (order.ProductCode != rule.ProductCode) continue; + } + + if (!rule.Months.IsNullOrEmpty()) + { + if (!order.Month.IsIn(rule.Months)) continue; + } + + if (!rule.Areas.IsNullOrEmpty()) + { + var inArea = false; + foreach (var area in rule.Areas!.Where(area => order.ArrivalCountry == area.Country)) + { + if (!area.LeftPostcode.IsNullOrEmpty() && !area.RightPostcode.IsNullOrEmpty()) + { + // tips: 参考小包的邮编匹配,字典序 + if (string.CompareOrdinal(order.ArrivalPostCode, area.LeftPostcode) < 0 || + string.CompareOrdinal(order.ArrivalPostCode, area.RightPostcode) > 0) continue; + } + + if (!area.City.IsNullOrEmpty() && order.ArrivalCity != area.City) continue; + if (!area.Province.IsNullOrEmpty() && order.ArrivalProvince != area.Province) continue; + inArea = true; + break; + } + + if (!inArea) continue; + } + + // 命中规则 + res.Add(rule); + } + + return res; + } + + public async Task SendNotificationAsync() + { + var max = GetTimestamp(DateTime.Now); + var set = await RedisHelper.Client.ZRangeByScoreAsync(GlobalConst.ForewarningNotifyKey, 0, max); + + if (set.IsNullOrEmpty()) return; + + var entries = set.Select(x => new WarningNotifyCacheEntry(x)).ToList(); + var ruleIds = entries.Select(x => x.RuleId).ToList(); + var notifyRules = await _warningQueries.GetWarningNotifyRulesAsync(ruleIds); + + /* + * +预警名称:字节全程时效 +预警规则: +{起飞-落地+普货+12345678+美国 时效9,预警时效7,次数3} 命中订单总数:124,详情点击https://middlebusiness.pet.shijizhongyun.com/trajectoryCenter/trajectoryOrder +{清关-提货+普货+12345678+美国 时效4,预警时效3,次数1} 命中订单总数:3250,详情点击https://middlebusiness.pet.shijizhongyun.com/trajectoryCenter/trajectoryOrder +预警名称:字节全程时效 +预警规则: +{起飞-落地+普货+12345678+美国 时效9,预警时效7,次数3} 命中订单总数:124,详情点击https://middlebusiness.pet.shijizhongyun.com/trajectoryCenter/trajectoryOrder +{清关-提货+普货+12345678+美国 时效4,预警时效3,次数1} 命中订单总数:3250,详情点击https://middlebusiness.pet.shijizhongyun.com/trajectoryCenter/trajectoryOrder + */ + foreach (var group in notifyRules.GroupBy(x => new { x.NotifyMode, x.NotifyUserId })) + { + var notifyContent = new StringBuilder(); + + foreach (var warningGroup in group.GroupBy(x => x.WarningName)) + { + notifyContent.AppendLine($"预警名称:{warningGroup.Key}"); + notifyContent.AppendLine($"预警规则:"); + + foreach (var rule in warningGroup.ToList()) + { + var cnt = entries.Count(x => x.RuleId == rule.RuleId); + if (cnt > 0) + { + notifyContent.AppendLine( + $"{rule.ToString()} 命中订单总数:{cnt},详情点击https://middlebusiness.pet.shijizhongyun.com/trajectoryCenter/trajectoryOrder"); + } + } + } + } + } + + private static long GetTimestamp(DateTime dateTime) + { + return new DateTimeOffset(dateTime).ToUnixTimeSeconds(); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningService.cs b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningService.cs new file mode 100644 index 00000000..0c7be38f --- /dev/null +++ b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningService.cs @@ -0,0 +1,203 @@ +using System.ComponentModel.DataAnnotations; +using SJZY.Track.Application.Contract.Dtos.Common; +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Application.Helpers; +using SJZY.Track.Domain.Aggregates.ForewarningAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.Dtos.TrackStatus; +using SJZY.Track.Domain.Queries; + +namespace SJZY.Track.Application.Services.ForewarningServices; + +[ApiExplorerSettings(GroupName = "预警服务")] +[Route("/ForewarningService")] +public class ForewarningService : ApplicationService +{ + private readonly IWarningRepository _warningRepository; + private readonly IForewarningCacheService _forewarningCacheService; + + public ForewarningService(IWarningRepository warningRepository, IForewarningCacheService forewarningCacheService) + { + _warningRepository = warningRepository; + _forewarningCacheService = forewarningCacheService; + } + + /// + /// 获取预警分页列表 + /// + /// + /// + [HttpPost("GetWarningPageList")] + public async Task> GetWarningPageList(GetWarningPageInputVo inputVo) + { + if (!inputVo.KeyWord.IsNullOrEmpty()) inputVo.KeyWord = inputVo.KeyWord!.Trim(); + return await _warningRepository.GetWarningPageListAsync(inputVo); + } + + /// + /// 创建预警 + /// + /// + /// + [HttpPost("CreateWarning")] + public async Task CreateWarning(CreateWarningInput input) + { + var warning = ObjectMapper.Map(input); + + if (await _warningRepository.IsAnyAsync(x => + x.SubSystemId == warning.SubSystemId && x.CustomerId == warning.CustomerId && x.Name == warning.Name)) + { + throw new BusinessException(message: "该子系统下的客户已经存在一个同名预警: " + warning.Name); + } + + warning.WarningRules = ForewarningHelper.CalcRuleList(input.TrackStatusSpans, input.WarningFactors); + warning.FactorTypeSort = input.WarningFactors.Select(x => x.FactorType).JoinAsString(","); + + warning.WarningFactors = ForewarningHelper.MapFactorEntities(input.WarningFactors); + warning.WarningNotifies = ForewarningHelper.MapNotifyEntities(input); + await _warningRepository.CreateForewarningAsync(warning); + return true; + } + + /// + /// 编辑预警 + /// + /// + /// + /// + [HttpPost("EditWarning")] + public async Task EditWarning(int warningId, + [FromServices] IBasicQuery statusQuery) + { + var warning = await _warningRepository.GetByIdAsync(warningId); + + if (warning == null) + { + throw new BusinessException(message: "预警不存在"); + } + + var statusList = await statusQuery.GetListAsync(x => x.SubSystemId == warning.SubSystemId, x => + new IdWithNameDto + { + Id = x.Id, + Name = x.Status + }); + + return ForewarningHelper.MapWarningSummary(warning, statusList); + } + + /// + /// 更新预警 + /// + /// + /// + [HttpPost("UpdateWarning")] + public async Task UpdateWarning(UpdateWarningInput input) + { + var warning = await _warningRepository.GetFirstAsync(x => x.Id == input.Id); + if (warning == null) + { + throw new BusinessException(message: "预警不存在"); + } + + if (await _warningRepository.IsAnyAsync(x => + x.SubSystemId == warning.SubSystemId && x.CustomerId == warning.CustomerId && x.Id != warning.Id && + x.Name == input.Name)) + { + throw new BusinessException(message: "该子系统下的客户已经存在一个同名预警: " + warning.Name); + } + + warning.SetName(input.Name); + + warning.WarningNotifies = input.Notifies.Select(x => new WarningNotifyEntity + { + Id = x.Id, + NotifyMode = input.NotifyMode, + NotifyTemplate = input.NotifyTemplate, + NotifyUserId = x.NotifyUserId + }).ToList(); + + await _warningRepository.UpdateForewarningAsync(warning); + + return true; + } + + /// + /// 启用or禁用预警 + /// + /// 预警id + /// 是否启用 + /// + [HttpPost("TurnWarning")] + public async Task TurnWarning(int warningId, bool isEnable) + { + var warning = await _warningRepository.GetFirstAsync(x => x.Id == warningId); + if (warning == null) + { + throw new BusinessException(message: "预警不存在"); + } + + if (isEnable) + { + if (await _warningRepository.IsAnyAsync(x => + x.SubSystemId == warning.SubSystemId && x.CustomerId == warning.CustomerId && x.IsEnable)) + { + throw new BusinessException(message: "该子系统下的客户已经存在一个启用的预警,请先禁用"); + } + } + + warning.SetEnable(isEnable); + + await _warningRepository.TurnWarningAsync(warning); + + // 清除缓存 + await _forewarningCacheService.DeleteRulesAsync(warning.SubSystemId, warning.CustomerId); + + return true; + } + + /// + /// 查询预警规则列表 + /// + /// 预警规则id + /// + /// + [HttpGet("GetWarningRuleList")] + public async Task> GetWarningRuleListAsync([Required] int warningId, + [FromServices] IBasicQuery factorQuery) + { + var factors = await factorQuery.GetListAsync(x => x.WarningId == warningId, x => x.Type); + var rules = await _warningRepository.GetWarningRuleListAsync(warningId); + + var dynamicList = ForewarningHelper.DynamicCalcRuleList(rules, factors); + return dynamicList; + } + + /// + /// 修改预警规则时效 + /// + /// + /// + /// + [HttpPost("ModifyTimeSpans")] + public async Task ModifyTimeSpans(ModifyTimeSpanInput input) + { + if (input.Items.IsNullOrEmpty()) return false; + + var warning = await _warningRepository.GetFirstAsync(x => x.Id == input.WarningId); + if (warning == null) + { + throw new BusinessException(message: "预警不存在"); + } + + var warningRules = ObjectMapper.Map, List>(input.Items); + + await _warningRepository.ModifyTimeSpansAsync(warningRules); + + // 清除缓存 + await _forewarningCacheService.DeleteRulesAsync(warning.SubSystemId, warning.CustomerId); + return true; + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Services/ForewarningServices/KpiAreaCacheService.cs b/src/SJZY.Track.Application/Services/ForewarningServices/KpiAreaCacheService.cs new file mode 100644 index 00000000..4728dcf6 --- /dev/null +++ b/src/SJZY.Track.Application/Services/ForewarningServices/KpiAreaCacheService.cs @@ -0,0 +1,29 @@ +using SJZY.Expand.ABP.Core; +using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Forewarning; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.Services.ForewarningServices; + +public class KpiAreaCacheService:IKpiAreaCacheService,ITransientDependency +{ + IKpiAreaRepository _kpiAreaRepository; + const string GlobalKpiAreaLockKey = "KpiAreaCacheService_GlobalLock"; + + public KpiAreaCacheService(IKpiAreaRepository kpiAreaRepository) + { + _kpiAreaRepository = kpiAreaRepository; + } + + private string GetKey(int systemId, string country) + { + return $"{GlobalConst.KpiAreaKeyPrefix}:{systemId}:{country}"; + } + + public Task GetKpiArea(int systemId, string country) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Services/ForewarningServices/KpiAreaService.cs b/src/SJZY.Track.Application/Services/ForewarningServices/KpiAreaService.cs new file mode 100644 index 00000000..354e177d --- /dev/null +++ b/src/SJZY.Track.Application/Services/ForewarningServices/KpiAreaService.cs @@ -0,0 +1,141 @@ +using SJZY.Expand.ABP.Core; +using SJZY.Track.Application.Contract.Services; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Application.Services.ForewarningServices; + +[ApiExplorerSettings(GroupName = "预警服务")] +[Route("/ForewarningService")] +public class KpiAreaService : ApplicationService +{ + private readonly IKpiAreaRepository _ruleAreaRepository; + private readonly IForewarningCacheService _forewarningCacheService; + + public KpiAreaService(IKpiAreaRepository ruleAreaRepository, IForewarningCacheService forewarningCacheService) + { + _ruleAreaRepository = ruleAreaRepository; + _forewarningCacheService = forewarningCacheService; + } + + /// + /// 创建分区 + /// + /// + /// 分区id + [HttpPost("KpiArea/CreateKpiArea")] + public async ValueTask CreateKpiArea(CreateRuleAreaRequest input) + { + var ruleAreaEntity = ObjectMapper.Map(input); + var areaId = await _ruleAreaRepository.CreateAreaAsync(ruleAreaEntity); + return new UnifyResultDto(true, areaId, "创建成功"); + } + + /// + /// 添加区域信息 + /// + /// + /// 区域id + [HttpPost("KpiArea/AddAreaInfo")] + public async ValueTask AddAreaInfoAsync(AddRuleAreaInfoRequest input) + { + var ruleAreaEntity = ObjectMapper.Map(input); + var ruleInfoId = await _ruleAreaRepository.AddAreaInfoAsync(ruleAreaEntity); + await _forewarningCacheService.ClearRedisByAreaId(input.AreaId); + return new UnifyResultDto(true, ruleInfoId, "添加成功"); + } + + /// + /// 删除分区 + /// + /// 分区id + /// + [HttpDelete("KpiArea/DeleteKpiArea")] + public async ValueTask DeleteKpiArea(int id) + { + await _ruleAreaRepository.DeleteArea(id); + await _forewarningCacheService.ClearRedisByAreaId(id); + return new UnifyResultDto(true, "删除成功"); + } + + /// + /// 删除分区信息 + /// + /// 分区信息id + /// + [HttpDelete("KpiArea/DeleteKpiAreaInfo")] + public async ValueTask DeleteKpiAreaInfo(int id) + { + var info=await _ruleAreaRepository.GetAreaInfoById(id); + if (info == null) return new UnifyResultDto(false,"未找到该区域信息"); + await _ruleAreaRepository.DeleteAreaInfo(id); + await _forewarningCacheService.ClearRedisByAreaId(info.AreaId); + return new UnifyResultDto(true, "删除成功"); + } + + /// + /// 更新区域信息 + /// + /// + /// + [HttpPost("KpiArea/ModifyKpiArea")] + public async Task ModifyKpiArea(UpdateRuleAreaRequest input) + { + if (input.Id <= 0) + return new UnifyResultDto(false, "id不能为空"); + + var ruleArea = await _ruleAreaRepository.GetFirstAsync(val => val.Id == input.Id); + if (ruleArea == null) + return new UnifyResultDto(false, "未找到该区域"); + + ruleArea.Name = input.Name; + await _ruleAreaRepository.UpdateAsync(ruleArea); + return new UnifyResultDto(true, "更新成功"); + } + + /// + /// 更新区域信息 + /// + /// + /// + [HttpPost("KpiArea/ModifyKpiAreaInfo")] + public async Task ModifyKpiAreaInfo(UpdateRuleAreaInfoRequest input) + { + if (input.Id <= 0) + return new UnifyResultDto(false, "id不能为空"); + + var areaInfo = await _ruleAreaRepository.GetAreaInfoById(input.Id); + if (areaInfo == null) + return new UnifyResultDto(false, "未找到该区域信息"); + + var ruleAreaEntity = ObjectMapper.Map(input, areaInfo); + await _ruleAreaRepository.UpdateAreaInfoAsync(ruleAreaEntity); + + await _forewarningCacheService.ClearRedisByAreaId(areaInfo.AreaId); + await RedisHelper.Client.PublishAsync(GlobalConst.PublishNotifyChannelKey, "clear local cache"); + return new UnifyResultDto(true, "更新成功"); + } + + /// + /// 查询分区列表 + /// + /// + /// + [HttpPost("KpiArea/GetKpiAreas")] + public async Task> GetKpiAreas(QueryAreaRequest input) + { + return await _ruleAreaRepository.GetAreas(input); + } + + /// + /// 查询分区列表 + /// + /// + /// + [HttpPost("KpiArea/GetKpiAreaInfos")] + public async Task> GetKpiAreaInfos(QueryAreaInfoRequest query) + { + return await _ruleAreaRepository.GetAreaInfos(query); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Services/ProvideServices/OrderService.Link.cs b/src/SJZY.Track.Application/Services/ProvideServices/OrderService.Link.cs index e9554da2..1e6cb204 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/OrderService.Link.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/OrderService.Link.cs @@ -101,7 +101,6 @@ public partial class OrderService OrderId = x.Id, OrderNo = x.OrderNo, OrderType = x.OrderType, - CustomId = x.CustomId }).ToArray(), TrackCode = track.TrackCode, TrackPlace = track.TrackPlace, diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs index 41a91a72..fe78e2dd 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackPushService.cs @@ -1,5 +1,6 @@ using DotNetCore.CAP; using Microsoft.AspNetCore.Authorization; +using SJZY.Track.Application.CAP.EventHandlers; using SJZY.Track.Application.Contract.Dtos.ProvideServices; using SJZY.Track.Application.Contract.Events; using SJZY.Track.Domain.Aggregates.OrderAggregate; @@ -31,6 +32,7 @@ public class TrackPushService : ApplicationService _capPublisher = capPublisher; _trackPushRecordsRepository = trackPushRecordsRepository; _requestCurrentUserService = requestCurrentUserService; + } /// @@ -90,9 +92,23 @@ public class TrackPushService : ApplicationService [HttpPost("Track/PushWarnForTask")] public async Task PushWarnForTaskAsync() { - // todo:订单数据上来后一次性update是否会引发长时间X独占? - var count = await _orderRepository.PushWarnOrdersAsync(); - return $"本次预警扫描了:{count}个订单"; + int total = 0, pageSize = 1000,waitTime = 1000; + var dateTimes = Enumerable.Range(1, 12).Select(val => DateTime.Now.AddHours(-val)).ToList(); + + foreach (var dateTime in dateTimes) + { + var rows = 0; + var pageindex = 1; + do + { + rows = await _orderRepository.PushWarnOrdersAsync(pageSize,pageindex,dateTime); + total+=rows; + pageindex++; + await Task.Delay(waitTime); + }while (rows!= pageSize); + } + + return $"本次预警扫描了:{total}个订单"; } /// diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs index f3d63ca2..3d76bd98 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs @@ -73,7 +73,6 @@ public partial class TrackService : ApplicationService, ITrackService OrderId = order.Id, OrderNo = order.OrderNo, OrderType = order.OrderType, - CustomId = order.CustomId } }; @event.OperationMan = name; @@ -111,18 +110,19 @@ public partial class TrackService : ApplicationService, ITrackService var name = userInfo.Data.UserName; - foreach (var group in orders.GroupBy(x => x.SubSystemId)) + foreach (var group in orders.GroupBy(x => new { x.SubSystemId, x.CustomId })) { // 新增/更新轨迹 var @event = new TrackUpsertEvent { - SubSystemId = group.Key, + SubSystemId = group.Key.SubSystemId, + CustomId = group.Key.CustomId, + OrderIdentifiers = group.Select(order => new OrderIdentifier { OrderId = order.Id, OrderNo = order.OrderNo, OrderType = input.OrderType, - CustomId = order.CustomId }).ToArray(), IsAdd = input.IsAdd, TrackCode = input.TrackCode, diff --git a/src/SJZY.Track.Application/Services/TrackNode/TrackNodeService.cs b/src/SJZY.Track.Application/Services/TrackNode/TrackNodeService.cs new file mode 100644 index 00000000..c9bd2637 --- /dev/null +++ b/src/SJZY.Track.Application/Services/TrackNode/TrackNodeService.cs @@ -0,0 +1,156 @@ +using SJZY.Track.Application.Contract.Dtos.TrackNode; +using SJZY.Track.Application.Contract.Dtos.TrackStatus; +using SJZY.Track.Domain.Aggregates.TrackNodeAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; +using SJZY.Track.Domain.Dtos.TrackNode; +using Volo.Abp.DependencyInjection; + +namespace SJZY.Track.Application.Services.TrackNode +{ + /// + /// 轨迹节点服务 + /// + [ApiExplorerSettings(GroupName = "轨迹节点")] + [Route("/Track/Node")] + public class TrackNodeService : ApplicationService, ITransientDependency + { + private readonly ITrackStatusRepository _trackStatusRepository; + private readonly ITrackNodeRepository _trackNodeRepository; + + public TrackNodeService( + ITrackNodeRepository trackNodeRepository, ITrackStatusRepository trackStatusRepository) + { + this._trackNodeRepository = trackNodeRepository; + _trackStatusRepository = trackStatusRepository; + } + + /// + /// 轨迹节点编辑 + /// + /// + [HttpPost("Edit")] + public async Task EditAsync(EditTrackNodeInput input) + { + return input.Options switch + { + OptionType.新增 => await AddAsync(input), + OptionType.修改 => await UpdateAsync(input), + OptionType.删除 => await DeleteAsync(input), + _ => throw new BusinessException(message: "当前操作动作不合法") + }; + } + + /// + /// 获取分页轨迹节点 + /// + /// + /// + [HttpPost("GetList")] + public async Task> GetPageTrackNode(QueryTrackNodeRequest input) + { + var queryParam = ObjectMapper.Map(input); + var resultDto = await _trackNodeRepository.GetPageList(queryParam); + return resultDto; + } + + /// + /// 获取详情 + /// + /// 节点id + /// + [HttpPost("GetDetail")] + public async Task GetDetail(int nodeid) + { + var resultDto = await _trackNodeRepository.GetDetail(nodeid); + return new UnifyResultDto(true,resultDto); + } + + /// + /// 获取分页轨迹字典,下拉框用的 + /// + /// + /// + [HttpPost("GetTrackNodeOption")] + public async Task> GetTrackNodeOption(QueryTrackNodeRequest input) + { + var queryParam = ObjectMapper.Map(input); + var resultDto = await _trackNodeRepository.GetPageDicList(queryParam); + return resultDto; + } + + #region 私有方法 + + private async Task AddAsync(EditTrackNodeInput input) + { + #region 业务校验 + if(string.IsNullOrEmpty(input.NodeName)|| string.IsNullOrEmpty(input.NodeEnName) || !input.SubSystemId.HasValue) + { + return new UnifyResultDto("200", "NodeName,NodeEnName,SubSystemId 必填", false); + } + + var trackNodesEntitys = await _trackNodeRepository.GetListAsync(a => + a.SubSystemId == input.SubSystemId && (a.NodeName == input.NodeName || a.NodeEnName == input.NodeEnName)); + if (trackNodesEntitys.Any()) + { + var trackNodeName = trackNodesEntitys.FirstOrDefault(a => a.NodeName == input.NodeName); + if (trackNodeName != null) + { + throw new BusinessException(message: "该子系统下节点名称已存在"); + } + var trackNodeEnName = trackNodesEntitys.FirstOrDefault(a => a.NodeEnName == input.NodeEnName); + if (trackNodeEnName != null) + { + throw new BusinessException(message: "该子系统下节点英文名称已存在"); + } + } + #endregion + + var entity = ObjectMapper.Map(input); + var isTrue = await _trackNodeRepository.InsertAsync(entity); + + return new UnifyResultDto("200", "新增成功", isTrue); + } + + private async Task UpdateAsync(EditTrackNodeInput input) + { + #region 业务校验 + if(string.IsNullOrEmpty(input.NodeName)|| string.IsNullOrEmpty(input.NodeEnName) ) + { + return new UnifyResultDto("200", "NodeName,NodeEnName 必填", false); + } + + var trackNodesEntitys = await _trackNodeRepository.GetFirstAsync(a => a.Id == input.Id); + if (trackNodesEntitys == null) + { + return new UnifyResultDto("200", "没有找到数据,更新失败", false); + } + + #endregion + + trackNodesEntitys.NodeEnName = input.NodeEnName; + trackNodesEntitys.NodeName = input.NodeName; + var isTrue = await _trackNodeRepository.UpdateAsync(trackNodesEntitys); + + return new UnifyResultDto("200", "修改成功", isTrue); + } + + private async Task DeleteAsync(EditTrackNodeInput input) + { + if(input.Id <=0 || !input.SubSystemId.HasValue) + { + return new UnifyResultDto("200", "SubSystemId 必填", false); + } + + var trackStatus = await _trackStatusRepository.GetFirstAsync(val => val.Node == input.Id && val.SubSystemId == input.SubSystemId); + if(trackStatus!=null) + { + return new UnifyResultDto("200", "删除失败,该节点已被使用", false); + } + + var rows = await _trackNodeRepository.DeleteById(input.Id); + return new UnifyResultDto("200", "删除成功", rows>0); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Services/TrackPush/TrackPushCacheService.cs b/src/SJZY.Track.Application/Services/TrackPush/TrackPushCacheService.cs index dfda8279..31e38468 100644 --- a/src/SJZY.Track.Application/Services/TrackPush/TrackPushCacheService.cs +++ b/src/SJZY.Track.Application/Services/TrackPush/TrackPushCacheService.cs @@ -48,7 +48,7 @@ public class TrackPushCacheService : ITrackPushCacheService, ITransientDependenc var trackPushConfig = await _trackPushConfigRepository.GetFirstAsync( x => x.SubSystemId == subSystemId && x.CustomId == customId, x => new { - x.Id, x.IsAutoPush, x.IsAllPush, x.IsCanDelete, x.IsCanUpdate, x.SubSystemId, x.CustomId + x.Id, x.IsAutoPush, x.IsAllPush, x.IsCanDelete, x.IsCanUpdate, x.SubSystemId, x.CustomId,x.DateTimeFormatType,x.DateTimeFormat,x.TimezoneFormat }); if (trackPushConfig == null) { @@ -68,6 +68,9 @@ public class TrackPushCacheService : ITrackPushCacheService, ITransientDependenc IsCanUpdate = trackPushConfig.IsCanUpdate, IsAllPush = trackPushConfig.IsAllPush, IsCanDelete = trackPushConfig.IsCanDelete, + DateTimeFormatType = trackPushConfig.DateTimeFormatType, + DateTimeFormat = trackPushConfig.DateTimeFormat, + TimezoneFormat = trackPushConfig.TimezoneFormat, ServiceProviderCode = item.ServiceProviderCode, OrderType = item.OrderType, TrackCode = item.TrackCode, diff --git a/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs b/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs index 10394daa..0dfe7b1e 100644 --- a/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs +++ b/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs @@ -99,7 +99,6 @@ namespace SJZY.Track.Application options.SucceedMessageExpiredAfter = configuration.GetValue("CAP:SucceedMessageExpiredAfter"); options.FallbackWindowLookbackSeconds = configuration.GetValue("CAP:FallbackWindowLookbackSeconds"); - options.EnableSubscriberParallelExecute = true; options.UseDashboard(dashboardOptions => { diff --git a/src/SJZY.Track.Application/Validations/Forewarning/CreateRuleFactorValidation.cs b/src/SJZY.Track.Application/Validations/Forewarning/CreateRuleFactorValidation.cs new file mode 100644 index 00000000..40f58dd3 --- /dev/null +++ b/src/SJZY.Track.Application/Validations/Forewarning/CreateRuleFactorValidation.cs @@ -0,0 +1,19 @@ +using FluentValidation; +using SJZY.Track.Application.Contract.Dtos.Forewarning; +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Application.Validations.Forewarning; + +public class CreateRuleFactorValidation : AbstractValidator +{ + public CreateRuleFactorValidation() + { + RuleFor(x => x.WarningFactors).NotNull().WithMessage("因素列表不能为空"); + RuleFor(x => x.TrackStatusSpans).NotNull().WithMessage("轨迹状态列表不能为空"); + + RuleFor(x => x.NotifyTemplate).NotEmpty().WithMessage("通知模板必填") + .MaximumLength(256).WithMessage("通知模板长度不能超过256"); + + // todo: more validation + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Validations/Forewarning/FactorItemDtoValidator.cs b/src/SJZY.Track.Application/Validations/Forewarning/FactorItemDtoValidator.cs new file mode 100644 index 00000000..368e5d63 --- /dev/null +++ b/src/SJZY.Track.Application/Validations/Forewarning/FactorItemDtoValidator.cs @@ -0,0 +1,6 @@ +namespace SJZY.Track.Application.Validations.Forewarning; + +public class FactorItemDtoValidator +{ + +} \ No newline at end of file diff --git a/src/SJZY.Track.Application/Validations/Forewarning/RuleTrackStateDtoValidator.cs b/src/SJZY.Track.Application/Validations/Forewarning/RuleTrackStateDtoValidator.cs new file mode 100644 index 00000000..cf61bc45 --- /dev/null +++ b/src/SJZY.Track.Application/Validations/Forewarning/RuleTrackStateDtoValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Application.Validations.Forewarning; + +public class RuleTrackStateDtoValidator : AbstractValidator +{ + public RuleTrackStateDtoValidator() + { + RuleFor(x => x.StartTrackStatusId).GreaterThan(0).WithMessage("起始轨迹状态id必须大于0"); + RuleFor(x => x.EndTrackStatusId).GreaterThan(0).WithMessage("终止轨迹状态id必须大于0"); + RuleFor(x => x.NotifyUserId).NotEmpty().WithMessage("通知对象不能为空"); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/FactorItemModel.cs b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/FactorItemModel.cs new file mode 100644 index 00000000..7df7f7f2 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/FactorItemModel.cs @@ -0,0 +1,16 @@ +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +public class FactorItemModel +{ + /// + /// Gets or sets the sort order. + /// + public int Sort { get; set; } + + /// + /// Gets or sets the type of the rule factor. + /// + public RuleFactorType FactorType { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/IWarningRepository.cs b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/IWarningRepository.cs new file mode 100644 index 00000000..efa73bbd --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/IWarningRepository.cs @@ -0,0 +1,62 @@ +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +public interface IWarningRepository : IBasicRepository +{ + /// + /// 创建预警,include factors, notifies, rules + /// + /// + /// + Task CreateForewarningAsync(WarningEntity warning); + + /// + /// 复制预警 + /// + /// + /// + Task CopyForewarning(int warningId); + + /// + /// 获取预警分页列表 + /// + /// + /// + Task> GetWarningPageListAsync(GetWarningPageInputVo input); + + /// + /// 获取预警规则列表 + /// + /// 预警id + /// + Task> GetWarningRuleListAsync(int warningId); + + /// + /// 修改预警 + /// + /// + /// + Task UpdateForewarningAsync(WarningEntity warning); + + /// + /// 修改预警时效 + /// + /// + /// + Task ModifyTimeSpansAsync(List inputItems); + + /// + /// 启用预警 + /// + /// + /// + Task TurnWarningAsync(WarningEntity warning); + + /// + /// 获取预警,include factors, notifies + /// + /// + /// + Task GetByIdAsync(int warningId); +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/RuleAreaEntity.cs b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/RuleAreaEntity.cs new file mode 100644 index 00000000..c6325956 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/RuleAreaEntity.cs @@ -0,0 +1,6 @@ +namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +public class RuleAreaEntity +{ + +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningEntity.cs b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningEntity.cs new file mode 100644 index 00000000..4d8cea7e --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningEntity.cs @@ -0,0 +1,77 @@ +using SJZY.Track.Domain.Enums; +using Volo.Abp; + +namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +[SugarTable("t_warning")] +public class WarningEntity: IFullAuditedBasicEntitys, IAggregateRoot +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 子系统id + /// + [SugarColumn(ColumnName = "SubSystemId")] + public int SubSystemId { get; set; } + + /// + /// 客户id + /// + [SugarColumn(ColumnName = "CustomerId")] + public int CustomerId { get; set; } + + /// + /// 规则模板名称,KPI名称 + /// + [SugarColumn(ColumnName = "Name")] + public string Name { get; set; } = null!; + + /// + /// 因素类型排序 + /// + [SugarColumn(ColumnName = "FactorTypeSort")] + public string FactorTypeSort { get; set; } = null!; + + /// + /// 是否启用,默认否 + /// + [SugarColumn(ColumnName = "IsEnable")] + public bool IsEnable { get; set; } + + /// + /// 预警规则 + /// + [SugarColumn(IsIgnore = true)] + public List WarningRules { get; set; } = new(); + + /// + /// 预警因素 + /// + [SugarColumn(IsIgnore = true)] + public List WarningFactors { get; set; } = new(); + + /// + /// 预警通知 + /// + [SugarColumn(IsIgnore = true)] + public List WarningNotifies { get; set; } = new(); + + public void SetName(string name) + { + Name = name; + } + + public void SetEnable(bool isEnable) + { + if (IsEnable == isEnable) + { + throw new BusinessException(message: $"预警已经是{(isEnable ? "启用" : "禁用")}状态"); + } + + IsEnable = isEnable; + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningFactorEntity.cs b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningFactorEntity.cs new file mode 100644 index 00000000..9c342518 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningFactorEntity.cs @@ -0,0 +1,31 @@ +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +[SugarTable("t_warning_factor")] +public class WarningFactorEntity : IFullAuditedBasicEntitys, IEntity +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 预警id + /// + [SugarColumn(ColumnName = "WarningId")] + public int WarningId { get; set; } + + /// + /// 因素类型(1货物类型,2月份,3分区,4是否中转,5起运港口,6销售产品) + /// + [SugarColumn(ColumnName = "Type")] + public RuleFactorType Type { get; set; } + + /// + /// 预警因素值(json数组字符串) + /// + [SugarColumn(ColumnName = "Value")] + public string Value { get; set; } = null!; +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningNotifyEntity.cs b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningNotifyEntity.cs new file mode 100644 index 00000000..1b846469 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningNotifyEntity.cs @@ -0,0 +1,49 @@ +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +[SugarTable("t_warning_notify")] +public class WarningNotifyEntity : IFullAuditedBasicEntitys, IEntity +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 预警id + /// + [SugarColumn(ColumnName = "WarningId")] + public int WarningId { get; set; } + + /// + /// 起始轨迹状态id + /// + [SugarColumn(ColumnName = "StartTrackStatusId", IsNullable = false)] + public int StartTrackStatusId { get; set; } + + /// + /// 终止轨迹状态id + /// + [SugarColumn(ColumnName = "EndTrackStatusId", IsNullable = false)] + public int EndTrackStatusId { get; set; } + + /// + /// 通知方式 + /// + [SugarColumn(ColumnName = "NotifyMode")] + public NotifyMode NotifyMode { get; set; } + + /// + /// 通知模板 + /// + [SugarColumn(ColumnName = "NotifyTemplate", Length = 256)] + public string NotifyTemplate { get; set; } = null!; + + /// + /// 通知人 + /// + [SugarColumn(ColumnName = "NotifyUserId")] + public int NotifyUserId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningRuleEntity.cs b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningRuleEntity.cs new file mode 100644 index 00000000..56a95930 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/ForewarningAggregate/WarningRuleEntity.cs @@ -0,0 +1,95 @@ +namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +[SugarTable("t_warning_rule")] +public class WarningRuleEntity : IFullAuditedBasicEntitys, IEntity +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 预警id + /// + [SugarColumn(ColumnName = "WarningId")] + public int WarningId { get; set; } + + /// + /// 时效(小时) + /// + [SugarColumn(ColumnName = "TotalTimeSpan")] + public float TotalTimeSpan { get; set; } + + /// + /// 提醒的时间(小时) + /// + [SugarColumn(ColumnName = "WarnTimeSpan")] + public float WarnTimeSpan { get; set; } + + /// + /// 提醒次数 + /// + [SugarColumn(ColumnName = "WarnCount")] + public int WarnCount { get; set; } + + /// + /// 起始轨迹状态id + /// + [SugarColumn(ColumnName = "StartTrackStatusId")] + public int StartTrackStatusId { get; set; } + + /// + /// 起始轨迹状态(冗余) + /// + [SugarColumn(ColumnName = "StartTrackStatusId")] + public string StartTrackStatus { get; set; } = null!; + + /// + /// 终止轨迹状态id + /// + [SugarColumn(ColumnName = "EndTrackStatusId")] + public int EndTrackStatusId { get; set; } + + /// + /// 终止轨迹状态(冗余) + /// + [SugarColumn(ColumnName = "EndTrackStatus")] + public string EndTrackStatus { get; set; } = null!; + + /// + /// 货物类型 + /// + [SugarColumn(ColumnName = "CargoType", Length = 64)] + public string? CargoType { get; set; } + + /// + /// 月份 + /// + [SugarColumn(ColumnName = "Months", Length = 25)] + public string? Months { get; set; } + + /// + /// 区域 + /// + [SugarColumn(ColumnName = "AreaId")] + public int? AreaId { get; set; } + + /// + /// 是否中转 + /// + [SugarColumn(ColumnName = "IsTransit")] + public bool? IsTransit { get; set; } + + /// + /// 起运港口 + /// + [SugarColumn(ColumnName = "LeavePort")] + public string? LeavePort { get; set; } + + /// + /// 产品编码 + /// + [SugarColumn(ColumnName = "ProductCode")] + public string? ProductCode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate.cs b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate.cs new file mode 100644 index 00000000..353b4ebc --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate.cs @@ -0,0 +1,6 @@ +namespace SJZY.Track.Domain.Aggregates; + +public class KPIAreaAggregate +{ + +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/IKpiAreaRepository.cs b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/IKpiAreaRepository.cs new file mode 100644 index 00000000..3b3a00b0 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/IKpiAreaRepository.cs @@ -0,0 +1,68 @@ +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Domain.Aggregates.KPIAreaAggregate; + +public interface IKpiAreaRepository : IBasicRepository +{ + /// + /// 创建分区 + /// + /// + /// + ValueTask CreateAreaAsync(RuleAreaEntity entity); + + /// + /// 添加分区信息 + /// + /// + /// + ValueTask AddAreaInfoAsync(RuleAreaInfoEntity entity); + + /// + /// 删除分区信息 + /// + /// 分区信息id + /// + Task DeleteAreaInfo(int areaInfoId); + + /// + /// 删除分区 + /// + /// 分区id + /// + ValueTask DeleteArea(int areaId); + + /// + /// 更新区域信息 + /// + /// + /// + Task UpdateAreaInfoAsync(RuleAreaInfoEntity entity); + + /// + /// 获得分区 + /// + /// + /// + Task> GetAreas(QueryAreaRequest query); + + /// + /// 获取分区信息 + /// + /// + /// + Task> GetAreaInfos(QueryAreaInfoRequest query); + + /// + /// 获取分区信息通过id + /// + /// + /// + Task GetAreaInfoById(int id); + + /// + /// 获取所有区域信息 + /// + /// + List? GetAreaInfosByAreaId(int areaId); +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/RuleAreaEntity.cs b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/RuleAreaEntity.cs new file mode 100644 index 00000000..5d43a11a --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/RuleAreaEntity.cs @@ -0,0 +1,50 @@ +namespace SJZY.Track.Domain.Aggregates.KPIAreaAggregate; + +[SugarTable("t_area", TableDescription = "KPI分区主表")] +public class RuleAreaEntity : IAggregateRoot +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")] + public int Id { get; set; } + + /// + /// 子系统id + /// + [SugarColumn(ColumnName = "SubSystemId", ColumnDescription = "子系统id")] + public int SubSystemId { get; set; } + + /// + /// 分区名称KPI名称 + /// + [SugarColumn(ColumnName = "Name", Length = 64, ColumnDescription = "分区名称KPI名称")] + public string Name { get; set; } + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")] + public int CreateUserId { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")] + public DateTime CreateTime { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "UpdateUserId", ColumnDescription = "更新人")] + public int UpdateUserId { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间")] + public DateTime UpdateTime { get; set; } + + [SugarColumn(IsIgnore = true)] + public List AreaInfos { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/RuleAreaInfoEntity.cs b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/RuleAreaInfoEntity.cs new file mode 100644 index 00000000..b1afa3c6 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate/RuleAreaInfoEntity.cs @@ -0,0 +1,77 @@ +namespace SJZY.Track.Domain.Aggregates.KPIAreaAggregate; + +[SugarTable("t_area_details", TableDescription = "kpi 区域子表")] +public class RuleAreaInfoEntity : IEntity +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")] + public int Id { get; set; } + + /// + /// t_rule_area 表id + /// + [SugarColumn(ColumnName = "AreaId", ColumnDescription = "t_rule_area 表id")] + public int AreaId { get; set; } + + /// + /// 国家或地区二字码 + /// + [SugarColumn(ColumnName = "CountryCode", Length = 128, ColumnDescription = "国家或地区二字码")] + public string CountryCode { get; set; } + + /// + /// 国家或地区名称 + /// + [SugarColumn(ColumnName = "CountryName", Length = 128, ColumnDescription = "国家或地区名称")] + public string CountryName { get; set; } + + /// + /// 州或者省份 + /// + [SugarColumn(ColumnName = "Province", Length = 128, IsNullable = true, ColumnDescription = "州或者省份")] + public string Province { get; set; } + + /// + /// 城市 + /// + [SugarColumn(ColumnName = "City", Length = 128, IsNullable = true, ColumnDescription = "城市")] + public string City { get; set; } + + /// + /// 起始邮编 + /// + [SugarColumn(ColumnName = "LeftPostcode", Length = 10, IsNullable = true, ColumnDescription = "起始邮编")] + public string LeftPostcode { get; set; } + + /// + /// 截止邮编 + /// + [SugarColumn(ColumnName = "RightPostcode", Length = 10, IsNullable = true, ColumnDescription = "截止邮编")] + public string RightPostcode { get; set; } + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")] + public int CreateUserId { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")] + public DateTime CreateTime { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "UpdateUserId", IsNullable = false, ColumnDescription = "更新人")] + public int UpdateUserId { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "UpdateTime", IsNullable = false, ColumnDescription = "更新时间")] + public DateTime UpdateTime { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs index 78cdd5df..7f7c6d8f 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs @@ -29,7 +29,21 @@ public interface IOrderRepository : IBasicRepository /// 更新推送预警单 /// /// - Task PushWarnOrdersAsync(); + Task PushWarnOrdersAsync(int pageSize,DateTime createTime); + + /// + /// 更新推送预警单 + /// + /// + Task PushWarnOrdersAsync(int pageSize, int pageindex, DateTime createTime); + + /// + /// 更新缺失标记,时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种 + /// + /// 订单id + /// 值 + /// + Task UpdateBatchIdsAsync(List ids, int newValue); #region 订单关联 diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs index e0f19389..51e8d0e6 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs @@ -131,7 +131,49 @@ public class OrderEntity : IFullAuditedBasicEntitys, IAggregateRoot /// [SugarColumn(ColumnName = "LeavePort")] public string? LeavePort { get; set; } - + + /// + /// 货物类型 + /// + [SugarColumn(ColumnName = "CargoType")] + public string? CargoType { get; set; } + + /// + /// 是否中转 + /// + [SugarColumn(ColumnName = "IsTransit")] + public bool? IsTransit { get; set; } + + /// + /// 产品编码 + /// + [SugarColumn(ColumnName = "ProductCode")] + public string? ProductCode { get; set; } + + /// + /// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种 + /// + [SugarColumn(ColumnName = "DeficiencyTag")] + public int? DeficiencyTag { get; set; } + + /// + /// 到达邮编 + /// + [SugarColumn(ColumnName = "ArrivalPostCode")] + public string? ArrivalPostCode { get; set; } + + /// + /// 到达州省 + /// + [SugarColumn(ColumnName = "ArrivalProvince")] + public string? ArrivalProvince { get; set; } + + /// + /// 是否触发拉取,1表示触发 + /// + [SugarColumn(ColumnName = "IsTriggerPull")] + public short? IsTriggerPull { get; set; } + [SugarColumn(IsIgnore = true)] public List OrderFlights { get; set; } [SugarColumn(IsIgnore = true)] public List OrderTemplates { get; set; } diff --git a/src/SJZY.Track.Domain/Aggregates/TrackNodeAggregate/ITrackNodeRepository.cs b/src/SJZY.Track.Domain/Aggregates/TrackNodeAggregate/ITrackNodeRepository.cs new file mode 100644 index 00000000..6ab6f473 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/TrackNodeAggregate/ITrackNodeRepository.cs @@ -0,0 +1,34 @@ +using SJZY.Track.Domain.Dtos.TrackNode; + +namespace SJZY.Track.Domain.Aggregates.TrackNodeAggregate; + +public interface ITrackNodeRepository:IBasicRepository +{ + /// + /// 分页查询 + /// + /// + /// + Task> GetPageList(TrackNodePageDto queryParam); + + /// + /// 分页查询字典 + /// + /// + /// + Task> GetPageDicList(TrackNodePageDto queryParam); + + /// + /// 删除 + /// + /// + /// + Task DeleteById(long inputId); + + /// + /// 获取详情 + /// + /// + /// + Task GetDetail(long id); +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/TrackNodeAggregate/TrackNodeEntity.cs b/src/SJZY.Track.Domain/Aggregates/TrackNodeAggregate/TrackNodeEntity.cs new file mode 100644 index 00000000..c17ef2c2 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/TrackNodeAggregate/TrackNodeEntity.cs @@ -0,0 +1,32 @@ +namespace SJZY.Track.Domain.Aggregates.TrackNodeAggregate; + +/// +/// 轨迹节点主表 +/// +[SugarTable("t_track_node", TableDescription = "轨迹节点主表")] +public class TrackNodeEntity : IFullAuditedBasicEntitys, IAggregateRoot +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")] + public long Id { get; set; } + + /// + /// 子系统id + /// + [SugarColumn(ColumnName = "SubSystemId", ColumnDescription = "子系统id")] + public int SubSystemId { get; set; } + + /// + /// 节点名称 + /// + [SugarColumn(ColumnName = "NodeName", Length = 32, ColumnDescription = "节点名称")] + public string? NodeName { get; set; } + + /// + /// 节点英文名称 + /// + [SugarColumn(ColumnName = "NodeEnName", Length = 32, ColumnDescription = "节点英文名称")] + public string? NodeEnName { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs b/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs index 5afef0fb..6e717032 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs @@ -71,4 +71,10 @@ public class TrackPullContentConfigEntity : IFullAuditedBasicEntitys, IEntity /// [SugarColumn(ColumnName = "IsCompleted")] public bool IsCompleted { get; set; } + + /// + /// 轨迹时区发生地参数值 + /// + [SugarColumn(ColumnName = "TrackZonePlaceValue")] + public int? TrackZonePlaceValue { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPushPullParamsConfigEntity.cs b/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPushPullParamsConfigEntity.cs index 59ea07e1..9687307f 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPushPullParamsConfigEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPushPullParamsConfigEntity.cs @@ -89,4 +89,10 @@ public class TrackPushPullParamsConfigEntity : IFullAuditedBasicEntitys, IEntity /// [SugarColumn(ColumnName = "ExpandFieldsName")] public string? ExpandFieldsName { get; set; } + + /// + /// 轨迹内容代码 + /// + [SugarColumn(ColumnName = "TrackContentCode")] + public string? TrackContentCode { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/TrackPushAggregate/TrackPushConfigEntity.cs b/src/SJZY.Track.Domain/Aggregates/TrackPushAggregate/TrackPushConfigEntity.cs index 659e3281..8398a0bf 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackPushAggregate/TrackPushConfigEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackPushAggregate/TrackPushConfigEntity.cs @@ -1,4 +1,6 @@ -namespace SJZY.Track.Domain.Aggregates.TrackPushAggregate; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Domain.Aggregates.TrackPushAggregate; /// /// 轨迹推送配置表 @@ -53,4 +55,22 @@ public class TrackPushConfigEntity : IFullAuditedBasicEntitys, IAggregateRoot /// [SugarColumn(ColumnName = "IsAllPush")] public bool IsAllPush { get; set; } + + /// + /// 日期时间格式类型 + /// + [SugarColumn(ColumnName = "DateTimeFormatType")] + public DateTimeFormatEnum DateTimeFormatType { get; set; } + + /// + /// 日期时间格式模板 + /// + [SugarColumn(ColumnName = "DateTimeFormat")] + public string? DateTimeFormat { get; set; } + + /// + /// 时区格式格式模板 + /// + [SugarColumn(ColumnName = "TimezoneFormat")] + public string? TimezoneFormat { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Aggregates/TrackStatusAggregate/TrackStatusEntity.cs b/src/SJZY.Track.Domain/Aggregates/TrackStatusAggregate/TrackStatusEntity.cs index dd98a21d..0b200c1c 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackStatusAggregate/TrackStatusEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackStatusAggregate/TrackStatusEntity.cs @@ -25,7 +25,7 @@ public class TrackStatusEntity : IFullAuditedBasicEntitys, IAggregateRoot public string EnStatus { get; set; } = null!; /// - /// 所属节点(主数据枚举ServiceNode) + /// 所属节点(来自表 t_track_node) 之前是枚举,后续调整为表 /// [SugarColumn(ColumnName = "Node")] public int Node { get; set; } diff --git a/src/SJZY.Track.Domain/Consts/GlobalConst.ES.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.ES.cs index 3dd246c4..baf25867 100644 --- a/src/SJZY.Track.Domain/Consts/GlobalConst.ES.cs +++ b/src/SJZY.Track.Domain/Consts/GlobalConst.ES.cs @@ -11,4 +11,9 @@ public partial class GlobalConst /// 轨迹推送记录 es索引名称(模板) /// public const string TrackPushRecordsIndex = "sjzy_track_push_records"; + + /// + /// 预警规则 es索引名称(模板) + /// + public const string RuleFactorIndex = "sjzy_rule_factor"; } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs index e2ca18ae..06972eff 100644 --- a/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs +++ b/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs @@ -21,4 +21,44 @@ public partial class GlobalConst /// 轨迹拉取配置信息redis key前缀 /// public const string TrackPullContentKeyPrefix = "TrackPullContent"; + + /// + /// 预警通知redis key + /// + public const string ForewarningNotifyKey = "ForewarningNotify"; + + /// + /// 预警通知剩余次数redis key + /// + public const string ForewarningNotifyCntKey = "ForewarningNotifyCnt"; + + /// + /// kpi区域redis key前缀 + /// + public const string KpiAreaKeyPrefix = "KpiArea"; + + /// + /// 城市时区redis key前缀 + /// + public const string CityTimezoneKeyPrefix = "CityTimezone"; + + /// + /// 港口时区redis key前缀 + /// + public const string PortTimezoneKeyPrefix = "PortTimezone"; + + /// + /// 发布订阅通知频道Key + /// + public const string PublishNotifyChannelKey = "PublishNotifyChannel"; + + /// + /// 轨迹区域reids key + /// + public const string TrackAreaKeyPrefix = "TrackArea"; + + /// + /// 轨迹预警数据redis key前缀 + /// + public const string TrackWarningKeyPrefix = "TrackWarning"; } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs b/src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs new file mode 100644 index 00000000..63cd5dad --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Dtos.Common; + +public class CityTimezoneDto +{ + /// + /// 城市英文名称 + /// + public string? CityEnName { get; set; } + + /// + /// 时区 + /// + public string Timezone { get; set; } +} diff --git a/src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs b/src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs new file mode 100644 index 00000000..896981cb --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Dtos.Common; + +public class PortTimezoneDto +{ + /// + /// 城市英文名称 + /// + public string? PortCode { get; set; } + + /// + /// 时区 + /// + public string Timezone { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/AddRuleAreaInfoRequest.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/AddRuleAreaInfoRequest.cs new file mode 100644 index 00000000..2ce1878c --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/AddRuleAreaInfoRequest.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class AddRuleAreaInfoRequest +{ + /// + /// 国家二字码,必填项。 + /// + [Required] + public string CountryCode { get; set; } + + /// + /// 国家名称 + /// + [Required] + public string CountryName { get; set; } + + /// + /// 省份名称,可为空。 + /// + public string? Province { get; set; } + + /// + /// 城市名称,可为空。 + /// + public string? City { get; set; } + + /// + /// 左侧邮政编码,可为空。 + /// + public string? LeftPostCode { get; set; } + + /// + /// 右侧邮政编码,可为空。 + /// + public string? RightPostCode { get; set; } + + /// + /// 区域ID,必填项。 + /// + [Required] + public int AreaId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/AreaDto.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/AreaDto.cs new file mode 100644 index 00000000..2ed7cc9f --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/AreaDto.cs @@ -0,0 +1,29 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class AreaDto +{ + /// + /// 区域id + /// + public int Id { get; set; } + + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 系统名称 + /// + public string SystemName { get; set; } + + /// + /// 区域名称 + /// + public string Name { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/AreaInfoDto.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/AreaInfoDto.cs new file mode 100644 index 00000000..d78454b6 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/AreaInfoDto.cs @@ -0,0 +1,45 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class RuleAreaInfoDto +{ + /// + /// 分区信息id + /// + public int Id { get; set; } + + /// + /// 分区id + /// + public int AreaId { get; set; } + + /// + /// 国家或地区 二字码 + /// + public string CountryCode { get; set; } + + /// + /// 国家名称 + /// + public string CountryName { get; set; } + + /// + /// 国家id + /// + public int CountryId { get; set; } + + /// + /// 省份 + /// + public string Province { get; set; } + + /// + /// 城市 + /// + public string City { get; set; } + + /// + /// 开始邮编 + /// + public string LeftPostcode { get; set; } + public string RightPostcode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/CreateRuleAreaRequest.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/CreateRuleAreaRequest.cs new file mode 100644 index 00000000..a3801625 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/CreateRuleAreaRequest.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class CreateRuleAreaRequest +{ + /// + /// 系统id + /// + [Required] + public int SystemId { get; set; } + + /// + /// 名称 + /// + [Required] + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningNotifiesVo.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningNotifiesVo.cs new file mode 100644 index 00000000..54ecfdb5 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningNotifiesVo.cs @@ -0,0 +1,131 @@ +using System.Text; +using NUglify.Helpers; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class GetWarningNotifiesVo +{ + /// + /// 规则 id + /// + public int RuleId { get; set; } + + /// + /// 预警名称 + /// + public string WarningName { get; set; } = null!; + + /// + /// 时效(小时) + /// + public float TotalTimeSpan { get; set; } + + /// + /// 提醒的时间(小时) + /// + public float WarnTimeSpan { get; set; } + + /// + /// 提醒次数 + /// + public int WarnCount { get; set; } + + /// + /// 起始轨迹状态(冗余) + /// + public string StartTrackStatus { get; set; } = null!; + + /// + /// 终止轨迹状态(冗余) + /// + public string EndTrackStatus { get; set; } = null!; + + /// + /// 货物类型 + /// + public string? CargoType { get; set; } + + /// + /// 月份 + /// + public string? Months { get; set; } + + /// + /// 区域 + /// + public string? AreaName { get; set; } + + /// + /// 是否中转 + /// + public bool? IsTransit { get; set; } + + /// + /// 起运港口 + /// + public string? LeavePort { get; set; } + + /// + /// 产品编码 + /// + public string? ProductCode { get; set; } + + /// + /// 通知方式 + /// + public NotifyMode NotifyMode { get; set; } + + /// + /// 通知模板 + /// + public string NotifyTemplate { get; set; } = null!; + + /// + /// 通知人 + /// + public int NotifyUserId { get; set; } + + /// + /// 因素类型排序 + /// + public string FactorTypeSort { get; set; } = null!; + + + public override string ToString() + { + // {起飞-落地+普货+1/2/3/4+美国 时效9,预警时效7,次数3} + var res = new StringBuilder(); + res.Append('{'); + res.Append($"{StartTrackStatus}-{EndTrackStatus}"); + FactorTypeSort.Split(",").Select(Enum.Parse).ForEach(factorType => + { + switch (factorType) + { + case RuleFactorType.CargoType: + res.Append($"+{CargoType}"); + break; + case RuleFactorType.Months: + res.Append($"+{Months}"); + break; + case RuleFactorType.Area: + res.Append($"+{AreaName}"); + break; + case RuleFactorType.IsTransit: + res.Append($"+{(IsTransit?? false? "中转":"直飞")}"); + break; + case RuleFactorType.LeavePort: + res.Append($"+{LeavePort}"); + break; + case RuleFactorType.ProductCode: + res.Append($"+{ProductCode}"); + break; + default: + throw new ArgumentOutOfRangeException(nameof(factorType), factorType, null); + } + }); + res.Append($" 时效{TotalTimeSpan},预警时效{WarnTimeSpan},次数{WarnCount}}}"); + + return res.ToString(); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningPageInputVo.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningPageInputVo.cs new file mode 100644 index 00000000..08d0b0ea --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningPageInputVo.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class GetWarningPageInputVo: PageInfoInput +{ + /// + /// 子系统id + /// + public int? SystemId { get; set; } + + /// + /// 关键字(客户or名称) + /// + public string? KeyWord { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningRuleListInput.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningRuleListInput.cs new file mode 100644 index 00000000..72b951be --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningRuleListInput.cs @@ -0,0 +1,19 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class GetWarningRuleListInput: PageInfoInput +{ + /// + /// kpi 预警规则模板id + /// + public int RuleId { get; set; } + + /// + /// 排序方式,不填不排序 + /// + public string? SortField { get; set; } + + /// + /// 排序方式 + /// + public bool IsAsc { get; set; } = false; +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/IForewarningFactorValue.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/IForewarningFactorValue.cs new file mode 100644 index 00000000..11dae2ce --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/IForewarningFactorValue.cs @@ -0,0 +1,16 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public interface IForewarningFactorValue +{ + public string? CargoType { get; set; } + + public int? Month { get; set; } + + public int? Area { get; set; } + + public bool? IsTransit { get; set; } + + public string? LeavePort { get; set; } + + public string? ProductCode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/KpiAreaInfo.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/KpiAreaInfo.cs new file mode 100644 index 00000000..eafc4d09 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/KpiAreaInfo.cs @@ -0,0 +1,31 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class KpiAreaInfo +{ + public int AreaId { get; set; } + + /// + /// 国家或地区 + /// + public string Country { get; set; } = null!; + + /// + /// 省份 + /// + public string? Province { get; set; } + + /// + /// 城市 + /// + public string? City { get; set; } + + /// + /// 开始邮编 + /// + public string? LeftPostcode { get; set; } + + /// + /// 截止邮编 + /// + public string? RightPostcode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaInfoRequest.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaInfoRequest.cs new file mode 100644 index 00000000..896f47a7 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaInfoRequest.cs @@ -0,0 +1,9 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class QueryAreaInfoRequest: PageInfoInput +{ + /// + /// 区域id + /// + public int AreaId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaRequest.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaRequest.cs new file mode 100644 index 00000000..31befffd --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaRequest.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class QueryAreaRequest:PageInfoInput +{ + /// + /// 分区名称 + /// + public string? Name { get; set; } + + /// + /// 子系统id + /// + public int? SystemId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/RuleAreaBaseInfo.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/RuleAreaBaseInfo.cs new file mode 100644 index 00000000..a8fef8d3 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/RuleAreaBaseInfo.cs @@ -0,0 +1,3 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public record RuleAreaBaseInfo(int SystemId, int CustomerId); \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/RuleCollectRequest.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/RuleCollectRequest.cs new file mode 100644 index 00000000..b4b1fadd --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/RuleCollectRequest.cs @@ -0,0 +1,7 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class RuleCollectRequest +{ + public string Name { get; set; } + +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/RuleFactor.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/RuleFactor.cs new file mode 100644 index 00000000..add12997 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/RuleFactor.cs @@ -0,0 +1,37 @@ +using SJZY.Track.Domain.Aggregates.ForewarningAggregate; + +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class RuleFactor +{ + public int RuleId { get; set; } + + public int SystemId { get; set; } + + public int CustomerId { get; set; } + + /// + /// Gets or sets the start track value. + /// + public int Start { get; set; } + + /// + /// Gets or sets the end track value. + /// + public int End { get; set; } + + /// + /// Gets or sets the user to notify. + /// + public string NotifyUser { get; set; } + + /// + /// Gets or sets the notification template. + /// + public string NotifyTemplate { get; set; } + + /// + /// 获取或设置因素项模型的列表。 + /// + public List FactorItems { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/UpdateRuleAreaInfoRequest.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/UpdateRuleAreaInfoRequest.cs new file mode 100644 index 00000000..8b1d4978 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/UpdateRuleAreaInfoRequest.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Domain.Dtos.Forewarning; + +/// +/// 更新规则区域信息请求 +/// +public class UpdateRuleAreaInfoRequest +{ + /// + /// 区域信息ID + /// + public int Id { get; set; } + + /// + /// 国家二字码,必填项。 + /// + [Required] + public string CountryCode { get; set; } + + /// + /// 国家名称,必填项。 + /// + [Required] + public string CountryName { get; set; } + + /// + /// 省份名称,可为空。 + /// + public string? Province { get; set; } + + /// + /// 城市名称,可为空。 + /// + public string? City { get; set; } + + /// + /// 左侧邮政编码,可为空。 + /// + public string? LeftPostCode { get; set; } + + /// + /// 右侧邮政编码,可为空。 + /// + public string? RightPostCode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/UpdateRuleAreaRequest.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/UpdateRuleAreaRequest.cs new file mode 100644 index 00000000..74ca3538 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/UpdateRuleAreaRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class UpdateRuleAreaRequest +{ + /// + /// 名称 + /// + [Required] + public string Name { get; set; } + + /// + /// 区域id + /// + public int Id { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/WarningFactorDto.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningFactorDto.cs new file mode 100644 index 00000000..3d4e93b9 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningFactorDto.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json.Linq; +using SJZY.Track.Domain.Enums; + +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class WarningFactorDto +{ + /// + /// 因素类型 + /// + public RuleFactorType FactorType { get; set; } + + /// + /// 因素值 + /// + public JArray FactorValue { get; set; } = new(); +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/WarningNitifyDto.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningNitifyDto.cs new file mode 100644 index 00000000..f831cfeb --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningNitifyDto.cs @@ -0,0 +1,29 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class WarningNitifyDto +{ + /// + /// 起始轨迹状态id + /// + public int StartTrackStatusId { get; set; } + + /// + /// 终止轨迹状态id + /// + public int EndTrackStatusId { get; set; } + + /// + /// 起始轨迹状态 + /// + public string StartTrackStatus { get; set; } = null!; + + /// + /// 终止轨迹状态 + /// + public string EndTrackStatus { get; set; } = null!; + + /// + /// 通知人id + /// + public int NotifyUserId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/WarningPageOutput.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningPageOutput.cs new file mode 100644 index 00000000..58408422 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningPageOutput.cs @@ -0,0 +1,29 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class WarningPageOutput +{ + /// + /// 预警id + /// + public int Id { get; set; } + + /// + /// 子系统 + /// + public string SubSystem { get; set; } = null!; + + /// + /// 客户 + /// + public string Custom { get; set; } = null!; + + /// + /// 规则模板名称,KPI名称 + /// + public string Name { get; set; } = null!; + + /// + /// 更新时间 + /// + public DateTime? UpdateTime { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/WarningRuleCacheDto.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningRuleCacheDto.cs new file mode 100644 index 00000000..43b6b25c --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningRuleCacheDto.cs @@ -0,0 +1,20 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class WarningRuleCacheDto +{ + public int Id { get; set; } + public int SystemId { get; set; } + public int CustomerId { get; set; } + public int WarningId { get; set; } + public float TotalTimeSpan { get; set; } + public float WarnTimeSpan { get; set; } + public int WarnCount { get; set; } + public int StartTrackStatusId { get; set; } + public int EndTrackStatusId { get; set; } + public string? CargoType { get; set; } + public string? Months { get; set; } + public int? AreaId { get; set; } + public bool? IsTransit { get; set; } + public string? LeavePort { get; set; } + public string? ProductCode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/WarningRuleVo.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningRuleVo.cs new file mode 100644 index 00000000..439393e1 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Forewarning/WarningRuleVo.cs @@ -0,0 +1,64 @@ +namespace SJZY.Track.Domain.Dtos.Forewarning; + +public class WarningRuleVo +{ + /// + /// 自增主键 + /// + public int Id { get; set; } + + /// + /// 时效(小时) + /// + public float TotalTimeSpan { get; set; } + + /// + /// 提醒的时间(小时) + /// + public float WarnTimeSpan { get; set; } + + /// + /// 提醒次数 + /// + public int WarnCount { get; set; } + + /// + /// 起始轨迹状态 + /// + public string StartTrackStatus { get; set; } = null!; + + /// + /// 终止轨迹状态 + /// + public string EndTrackStatus { get; set; } = null!; + + /// + /// 货物类型 + /// + public string? CargoType { get; set; } + + /// + /// 月份 + /// + public string? Months { get; set; } + + /// + /// 区域 + /// + public string? Area { get; set; } + + /// + /// 是否中转 + /// + public bool? IsTransit { get; set; } + + /// + /// 起运港口 + /// + public string? LeavePort { get; set; } + + /// + /// 销售产品 + /// + public string? ProductCode { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/Order/GetOrderDetailsInputVO.cs b/src/SJZY.Track.Domain/Dtos/Order/GetOrderDetailsInputVO.cs deleted file mode 100644 index 082b245a..00000000 --- a/src/SJZY.Track.Domain/Dtos/Order/GetOrderDetailsInputVO.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SJZY.Track.Domain.Dtos.Order -{ - public class GetOrderDetailsInputVO - { - - } - - public class TrackOrderDetailVO - { - - } -} diff --git a/src/SJZY.Track.Domain/Dtos/Order/OrderTransportDto.cs b/src/SJZY.Track.Domain/Dtos/Order/OrderTransportDto.cs new file mode 100644 index 00000000..d7d03691 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/Order/OrderTransportDto.cs @@ -0,0 +1,10 @@ +namespace SJZY.Track.Domain.Dtos.Order; + +public class OrderTransportDto +{ + public int OrderId { get; set; } + + public int ServiceProviderId { get; set; } + + public int SubSystemId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs b/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs index d44e75c8..0c0f13d5 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs @@ -2,6 +2,11 @@ public class TrackContentVO { + /// + /// 轨迹状态Id + /// + public int TrackStatusId { get; set; } + /// /// 轨迹状态 /// diff --git a/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodeDicDto.cs b/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodeDicDto.cs new file mode 100644 index 00000000..8402e103 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodeDicDto.cs @@ -0,0 +1,19 @@ +namespace SJZY.Track.Domain.Dtos.TrackNode; + +public class TrackNodeDicDto +{ + /// + /// 节点id + /// + public long Id { get; set; } + + /// + /// 名称 + /// + public string NodeName { get; set; } + + /// + /// 名称 + /// + public string EnName { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodeDto.cs b/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodeDto.cs new file mode 100644 index 00000000..401ec773 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodeDto.cs @@ -0,0 +1,61 @@ +namespace SJZY.Track.Domain.Dtos.TrackNode; + +/// +/// 轨迹节点主表 DTO +/// +public class TrackNodeDto +{ + /// + /// 自增主键 + /// + public long Id { get; set; } + + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 系统名称 + /// + public string? SystemName { get; set; } + + /// + /// 节点名称 + /// + public string? NodeName { get; set; } + + /// + /// 节点英文名称 + /// + public string? NodeEnName { get; set; } + + /// + /// 是否删除 + /// + public bool IsDeleted { get; set; } + + /// + /// 创建人id + /// + public int CreateUserId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 更新人 + /// + public int? UpdateUserId { get; set; } + + /// + /// 更新时间 + /// + public DateTime? UpdateTime { get; set; } + + public string? CreateUserName { get; set; } + + public string? UpdateUserName { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodePageDto.cs b/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodePageDto.cs new file mode 100644 index 00000000..059e92f2 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/TrackNode/TrackNodePageDto.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Dtos.TrackNode; + +public class TrackNodePageDto:PageInfoInput +{ + /// + /// 关键字,节点中文和英文 + /// + public string? KeyWord { get; set; } + + /// + /// 子系统id + /// + public long? SystemId { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullConfigDetailOutputVO.cs b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullConfigDetailOutputVO.cs index c52c081b..dbe0b308 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullConfigDetailOutputVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullConfigDetailOutputVO.cs @@ -78,4 +78,9 @@ public class GetTrackPullConfigDetailOutputVO /// 轨迹明细路径 /// public string TrackDetailsPath { get; set; } + + /// + /// 轨迹内容代码 + /// + public string? TrackContentCode { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentDetailOutputVO.cs b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentDetailOutputVO.cs index 05d748fe..ea5f1d27 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentDetailOutputVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentDetailOutputVO.cs @@ -68,4 +68,9 @@ public class GetTrackPullContentDetailOutputVO /// 是否完结 /// public bool IsCompleted { get; set; } + + /// + /// 轨迹时区兜底地址值 + /// + public int? TrackTimePlaceValue { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs index 047431d9..8fad0074 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs @@ -36,6 +36,11 @@ public class GetTrackPullContentListCacheVO /// 轨迹发生地参数值(主数据枚举) /// public int? TrackPlaceValue { get; set; } + + /// + /// 兜底时区轨迹发生地参数值(TrackLocationValue) + /// + public int? TrackTimePlaceValue { get; set; } /// /// 轨迹时区GMT @@ -46,4 +51,9 @@ public class GetTrackPullContentListCacheVO /// 订单是否完成(由轨迹刷新配置决定) /// public bool IsCompleted { get; set; } + + /// + /// 轨迹时区发生地参数值 + /// + public int? TrackZonePlaceValue { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Dtos/TrackStatus/GetTrackStatusDetailVO.cs b/src/SJZY.Track.Domain/Dtos/TrackStatus/GetTrackStatusDetailVO.cs index 66de23a9..76c4ac32 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackStatus/GetTrackStatusDetailVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackStatus/GetTrackStatusDetailVO.cs @@ -36,4 +36,9 @@ public class GetTrackStatusDetailVO /// 轨迹内容英文 /// public string EntrackStatus { get; set; } + + /// + /// 节点名称 + /// + public string? NodeName { get; set; } } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Enums/DateTimeFormatEnum.cs b/src/SJZY.Track.Domain/Enums/DateTimeFormatEnum.cs new file mode 100644 index 00000000..f7d68b5e --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/DateTimeFormatEnum.cs @@ -0,0 +1,22 @@ +namespace SJZY.Track.Domain.Enums; + +/// +/// 时间样式枚举 +/// +public enum DateTimeFormatEnum +{ + /// + /// 默认 + /// + None = 0, + + /// + /// 时间戳 + /// + Timestamp=1, + + /// + /// 时间加时区 + /// + DateTimeAndTimezone =2 +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Enums/NotifyMode.cs b/src/SJZY.Track.Domain/Enums/NotifyMode.cs new file mode 100644 index 00000000..f5821903 --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/NotifyMode.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Enums; + +public enum NotifyMode +{ + /// + /// 站内信 + /// + SiteInternal = 1, + + /// + /// 飞书 + /// + FeiShu = 2 +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Enums/RuleFactorType.cs b/src/SJZY.Track.Domain/Enums/RuleFactorType.cs new file mode 100644 index 00000000..6dd01ace --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/RuleFactorType.cs @@ -0,0 +1,38 @@ +namespace SJZY.Track.Domain.Enums; + + +/// +/// 枚举表示不同类型的规则因素。 +/// +public enum RuleFactorType +{ + /// + /// 表示货物类型。 + /// + CargoType = 1, + + /// + /// 表示月份。 + /// + Months, + + /// + /// 表示分区或区域。 + /// + Area, + + /// + /// 指示货物是否中转。 + /// + IsTransit, + + /// + /// 表示起运港口。 + /// + LeavePort, + + /// + /// 表示销售产品。 + /// + ProductCode +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Enums/TimezoneSourceEnum.cs b/src/SJZY.Track.Domain/Enums/TimezoneSourceEnum.cs new file mode 100644 index 00000000..70aca24a --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/TimezoneSourceEnum.cs @@ -0,0 +1,37 @@ +namespace SJZY.Track.Domain.Enums; + +/// +/// 推送轨迹时区来源 +/// +public enum TimezoneSourceEnum +{ + /// + /// 起运城市 + /// + DeliverCity = 1, + + /// + /// 接收城市 + /// + ReceiveCity = 2, + + /// + /// 起运港口 + /// + DeliverPort = 3, + + /// + /// 接收港口 + /// + ReceivePort = 4, + + /// + /// 起运国家 + /// + DeliverCountry = 5, + + /// + /// 接收国家 + /// + ReceiveCountry = 6 +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Queries/IProvideQueries.cs b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs index 34e99ae9..6392d05e 100644 --- a/src/SJZY.Track.Domain/Queries/IProvideQueries.cs +++ b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs @@ -67,5 +67,12 @@ public interface IProvideQueries Task> FindOrderWithServiceProviderAsync(int serviceProvider, int pageIndex, int pageSize = 1000); - Task GetTrackContentAsync(int subSystemId, string trackCode); + Task GetTrackContentAsync(int subSystemId, string trackCode); + + /// + /// 查找订单和服务商信息 + /// + /// 订单id + /// 订单和服务商信息 + Task> FindOrderWithServiceProviderAsync(int[] orderIds); } \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Queries/ITimezoneQueries.cs b/src/SJZY.Track.Domain/Queries/ITimezoneQueries.cs new file mode 100644 index 00000000..14e33748 --- /dev/null +++ b/src/SJZY.Track.Domain/Queries/ITimezoneQueries.cs @@ -0,0 +1,20 @@ +using SJZY.Track.Domain.Dtos.Common; + +namespace SJZY.Track.Domain.Queries; + +public interface ITimezoneQueries +{ + /// + /// 通过城市英文名称获取时区 + /// + /// + /// + Task GetCityTimezone(string cityEnName); + + /// + /// 通过港口三字码获取时区 + /// + /// + /// + Task GetPortTimezone(string airCode); +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/Queries/IWarningQueries.cs b/src/SJZY.Track.Domain/Queries/IWarningQueries.cs new file mode 100644 index 00000000..b3652bf6 --- /dev/null +++ b/src/SJZY.Track.Domain/Queries/IWarningQueries.cs @@ -0,0 +1,27 @@ +using SJZY.Track.Domain.Dtos.Forewarning; + +namespace SJZY.Track.Domain.Queries; + +public interface IWarningQueries +{ + /// + /// 获取预警通知数据 + /// + /// + /// + Task> GetWarningNotifyRulesAsync(List ruleIds); + + + /// + /// 获取预警规则列表 + /// + /// + Task?> GetWarningRule(int systemId, int customerId); + + /// + /// 获取预警规则基础信息 + /// + /// + /// + Task?> GetWarningRuleByAreaId(int areaId); +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/SJZY.Track.Domain.csproj b/src/SJZY.Track.Domain/SJZY.Track.Domain.csproj index d086d460..ea3705c5 100644 --- a/src/SJZY.Track.Domain/SJZY.Track.Domain.csproj +++ b/src/SJZY.Track.Domain/SJZY.Track.Domain.csproj @@ -11,5 +11,10 @@ + + + + + diff --git a/src/SJZY.Track.Domain/VEntities/VAirportEntity.cs b/src/SJZY.Track.Domain/VEntities/VAirportEntity.cs new file mode 100644 index 00000000..ba69e854 --- /dev/null +++ b/src/SJZY.Track.Domain/VEntities/VAirportEntity.cs @@ -0,0 +1,121 @@ +using SqlSugar; + +namespace SJZY.Track.Domain.VEntities; + +/// +/// 空港 +/// +[SugarTable("t_airport", "空港")] +public class VAirportEntity : IFullAuditedBasicEntitys +{ + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 空运航线id + /// + [SugarColumn(ColumnName = "AirRouteId")] + public int AirRouteId { get; set; } + + /// + /// 城市id + /// + [SugarColumn(ColumnName = "CityId")] + public int CityId { get; set; } + + /// + /// 主港口,空港id + /// + [SugarColumn(ColumnName = "MId", IsNullable = true)] + public int? MId { get; set; } + + /// + /// 中文名 + /// + [SugarColumn(ColumnName = "CnName")] + public string CnName { get; set; } + + /// + /// 英文名 + /// + [SugarColumn(ColumnName = "EnName")] + public string EnName { get; set; } + + /// + /// 代码 + /// + [SugarColumn(ColumnName = "Code", IsNullable = true)] + public string Code { get; set; } + + /// + /// 海关代码 + /// + [SugarColumn(ColumnName = "CustomsCode", IsNullable = true)] + public string CustomsCode { get; set; } + + /// + /// 海关名称 + /// + [SugarColumn(ColumnName = "CustomsName", IsNullable = true)] + public string CustomsName { get; set; } + + /// + /// 离境口岸代码 + /// + [SugarColumn(ColumnName = "DeparturePortCode", IsNullable = true)] + public string DeparturePortCode { get; set; } + + /// + /// 离境口岸名称 + /// + [SugarColumn(ColumnName = "DeparturePortName", IsNullable = true)] + public string DeparturePortName { get; set; } + + /// + /// 国内排序 + /// + [SugarColumn(ColumnName = "DomcSort")] + public int DomcSort { get; set; } + + /// + /// 国外排序 + /// + [SugarColumn(ColumnName = "OverSort")] + public int OverSort { get; set; } + + /// + /// 备注 + /// + [SugarColumn(ColumnName = "Remark", IsNullable = true)] + public string Remark { get; set; } + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "CreateUserId", IsNullable = true)] + public int? CreateUserId { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "CreateTime", IsNullable = false, DefaultValue = "CURRENT_TIMESTAMP")] + public DateTime CreateTime { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "UpdateUserId", IsNullable = true)] + public int? UpdateUserId { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "UpdateTime", IsNullable = true)] + public DateTime? UpdateTime { get; set; } + + /// + /// 是否删除 + /// + [SugarColumn(ColumnName = "IsDeleted")] + public bool IsDeleted { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/VEntities/VCityEntity.cs b/src/SJZY.Track.Domain/VEntities/VCityEntity.cs new file mode 100644 index 00000000..ad914504 --- /dev/null +++ b/src/SJZY.Track.Domain/VEntities/VCityEntity.cs @@ -0,0 +1,140 @@ +using SqlSugar; +using System; + +namespace SJZY.Track.Domain.VEntities; + +[SugarTable("t_city")] +public class VCityEntity : IFullAuditedBasicEntitys +{ + /// + /// 主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 国家id + /// + [SugarColumn(ColumnName = "CountryId")] + public int CountryId { get; set; } + + /// + /// 省份id + /// + [SugarColumn(ColumnName = "ProvinceId", IsNullable = true)] + public int? ProvinceId { get; set; } + + /// + /// 中文名 + /// + [SugarColumn(ColumnName = "CnName")] + public string CnName { get; set; } + + /// + /// 英文名 + /// + [SugarColumn(ColumnName = "EnName")] + public string EnName { get; set; } + + /// + /// 联合国code + /// + [SugarColumn(ColumnName = "UnCode", IsNullable = true)] + public string UnCode { get; set; } + + /// + /// 所区分组(1华南,2华东,3华北,4 西南,5华中) + /// + [SugarColumn(ColumnName = "CityType", IsNullable = true)] + public int? CityType { get; set; } + + /// + /// 时区 + /// + [SugarColumn(ColumnName = "TimeZone", IsNullable = true)] + public string TimeZone { get; set; } + + /// + /// 排序 + /// + [SugarColumn(ColumnName = "Sort")] + public int Sort { get; set; } + + /// + /// 备注 + /// + [SugarColumn(ColumnName = "Remark", IsNullable = true)] + public string Remark { get; set; } + + /// + /// 国家统计局统计用区划代码 + /// + [SugarColumn(ColumnName = "StatisticalZoningCode", IsNullable = true)] + public string StatisticalZoningCode { get; set; } + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "CreateUserId", IsNullable = true)] + public int? CreateUserId { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "CreateTime", IsNullable = false, DefaultValue = "CURRENT_TIMESTAMP")] + public DateTime CreateTime { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "UpdateUserId", IsNullable = true)] + public int? UpdateUserId { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "UpdateTime", IsNullable = true)] + public DateTime? UpdateTime { get; set; } + + /// + /// 是否删 + /// + [SugarColumn(ColumnName = "IsDeleted")] + public bool IsDeleted { get; set; } + + /// + /// 维护时区 + /// + [SugarColumn(ColumnName = "MaintainTimeZone", IsNullable = true)] + public decimal? MaintainTimeZone { get; set; } + + /// + /// 经度 + /// + [SugarColumn(ColumnName = "Longitude", IsNullable = true)] + public string Longitude { get; set; } + + /// + /// 纬度 + /// + [SugarColumn(ColumnName = "Latitude", IsNullable = true)] + public string Latitude { get; set; } + + /// + /// 海拔 + /// + [SugarColumn(ColumnName = "Elevation", IsNullable = true)] + public string Elevation { get; set; } + + /// + /// 语言 + /// + [SugarColumn(ColumnName = "Language", IsNullable = true)] + public string Language { get; set; } + + /// + /// timezoneURL + /// + [SugarColumn(ColumnName = "TimezoneURL", IsNullable = true)] + public string TimezoneURL { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/VEntities/VCountryEntity.cs b/src/SJZY.Track.Domain/VEntities/VCountryEntity.cs new file mode 100644 index 00000000..46310904 --- /dev/null +++ b/src/SJZY.Track.Domain/VEntities/VCountryEntity.cs @@ -0,0 +1,110 @@ +using SqlSugar; +using System; + +namespace SJZY.Track.Domain.VEntities; + +[SugarTable("v_country")] +public class VCountryEntity : IFullAuditedBasicEntitys +{ + /// + /// 主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 区域id + /// + [SugarColumn(ColumnName = "AreaId")] + public int AreaId { get; set; } + + /// + /// 币种id + /// + [SugarColumn(ColumnName = "CurrencyId", IsNullable = true)] + public int? CurrencyId { get; set; } + + /// + /// 中文名 + /// + [SugarColumn(ColumnName = "CnName")] + public string CnName { get; set; } + + /// + /// 英文名 + /// + [SugarColumn(ColumnName = "EnName")] + public string EnName { get; set; } + + /// + /// 二字代码 + /// + [SugarColumn(ColumnName = "Digit2Code")] + public string Digit2Code { get; set; } + + /// + /// 三字代码 + /// + [SugarColumn(ColumnName = "Digit3Code", IsNullable = true)] + public string Digit3Code { get; set; } + + /// + /// 企业代码类型 + /// + [SugarColumn(ColumnName = "EnterpriseCodeType", IsNullable = true)] + public string EnterpriseCodeType { get; set; } + + /// + /// 区号 + /// + [SugarColumn(ColumnName = "CountryCode", IsNullable = true)] + public int? CountryCode { get; set; } + + /// + /// 时差 + /// + [SugarColumn(ColumnName = "JetLag")] + public int JetLag { get; set; } + + /// + /// 城市名 + /// + [SugarColumn(ColumnName = "CountryName", IsNullable = true)] + public string CountryName { get; set; } + + /// + /// 是否不可使用 + /// + [SugarColumn(ColumnName = "IsUnusable", IsNullable = false)] + public bool IsUnusable { get; set; } + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "CreateUserId", IsNullable = true)] + public int? CreateUserId { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "CreateTime", IsNullable = false, DefaultValue = "CURRENT_TIMESTAMP")] + public DateTime CreateTime { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "UpdateUserId", IsNullable = true)] + public int? UpdateUserId { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "UpdateTime", IsNullable = true)] + public DateTime? UpdateTime { get; set; } + + /// + /// 是否删除 + /// + [SugarColumn(ColumnName = "IsDeleted", IsNullable = false)] + public bool IsDeleted { get; set; } +} \ No newline at end of file diff --git a/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs b/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs index 51bea216..1743a9fd 100644 --- a/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs +++ b/src/SJZY.Track.Domain/VEntities/VExternalSubjectEntity.cs @@ -1,4 +1,4 @@ -namespace SJZY.Track.Repository.VData.Entitys; +namespace SJZY.Track.Domain.VEntities; /// /// 外部主体 diff --git a/src/SJZY.Track.Repository/DataBaseContext.cs b/src/SJZY.Track.Repository/DataBaseContext.cs index 88539836..afc9be0c 100644 --- a/src/SJZY.Track.Repository/DataBaseContext.cs +++ b/src/SJZY.Track.Repository/DataBaseContext.cs @@ -20,21 +20,20 @@ public class DataBaseContext //新增 if (entityInfo.OperationType == DataFilterType.InsertByObject) { + var time = DateTime.Now; + var userId = CurrentUserRequest.CurrentUser.UserId; + if (entityInfo.PropertyName == nameof(ICreateBaicEntity.CreateTime)) - entityInfo.SetValue(DateTime.Now); - if (entityInfo.PropertyName == nameof(ICreateBaicEntity.CreateUserId)) + entityInfo.SetValue(time); + else if (entityInfo.PropertyName == nameof(IUpdateBasicEntity.UpdateTime)) + entityInfo.SetValue(time); + else if (entityInfo.PropertyName == nameof(ICreateBaicEntity.CreateUserId)) { - if (oldValue == null || (int)oldValue == 0) - { - try - { - entityInfo.SetValue(CurrentUserRequest.CurrentUser?.UserId); - } - catch - { - } - } - //entityInfo.SetValue(CurrentUserRequest.CurrentUser.UserId); + entityInfo.SetValue(userId); + } + else if (entityInfo.PropertyName == nameof(IUpdateBasicEntity.UpdateUserId)) + { + entityInfo.SetValue(userId); } } //修改 @@ -44,17 +43,7 @@ public class DataBaseContext entityInfo.SetValue(DateTime.Now); else if (entityInfo.PropertyName == nameof(IUpdateBasicEntity.UpdateUserId)) { - if (oldValue == null || (int)oldValue == 1) - { - try - { - entityInfo.SetValue(CurrentUserRequest.CurrentUser.UserId); - } - catch - { - } - } - //entityInfo.SetValue(CurrentUserRequest.CurrentUser.UserId); + entityInfo.SetValue(CurrentUserRequest.CurrentUser.UserId); } //删除 else if (entityInfo.PropertyName == nameof(IDeleteBasicEntity.IsDeleted)) diff --git a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs index f2a9f25c..60043689 100644 --- a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs +++ b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs @@ -122,7 +122,7 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, return await Context.Queryable() .RightJoin((a, b) => a.Id == b.OrderId) .LeftJoin((a, b, c) => b.ServiceProviderId == c.Id) - .Where((a, b) => b.ServiceProviderId == serviceProvider && !a.IsCompleted) + .Where((a, b) => b.ServiceProviderId == serviceProvider && !a.IsCompleted && a.IsTriggerPull==1) .Select((a, b, c) => new OrderWithServiceProviderVO { OrderNo = a.OrderNo, @@ -136,14 +136,29 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, .Take(pageSize) .ToListAsync(); } + + public async Task> FindOrderWithServiceProviderAsync(int[] orderIds) + { + return await Context.Queryable() + .InnerJoin((a, b) => a.Id == b.OrderId) + .Where((a, b) => orderIds.Contains( a.Id) && !a.IsCompleted) + .Select((a, b) => new OrderTransportDto + { + OrderId = a.Id, + ServiceProviderId = b.ServiceProviderId, + SubSystemId = a.SubSystemId + }) + .ToListAsync(); + } - public async Task GetTrackContentAsync(int subSystemId, string trackCode) + public async Task GetTrackContentAsync(int subSystemId, string trackCode) { return await Context.Queryable() .LeftJoin((x, y) => x.TrackStatusId == y.Id) .Where(x => x.SubSystemId == subSystemId && x.Code == trackCode) .Select((x, y) => new TrackContentVO { + TrackStatusId = x.TrackStatusId, TrackStatus = y.Status, TrackContent = x.Content, TrackEnContent = x.EnContent diff --git a/src/SJZY.Track.Repository/Queries/TimezoneQueries.cs b/src/SJZY.Track.Repository/Queries/TimezoneQueries.cs new file mode 100644 index 00000000..cd2a0569 --- /dev/null +++ b/src/SJZY.Track.Repository/Queries/TimezoneQueries.cs @@ -0,0 +1,101 @@ +using System.Reflection.Metadata; +using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Dtos.Common; +using SJZY.Track.Domain.Queries; +using SJZY.Track.Domain.VEntities; + +namespace SJZY.Track.Repository.Queries; + +public class TimezoneQueries:ITimezoneQueries, ISugarRepository, ITransientDependency +{ + public ISqlSugarClient Context { get; set; } + + public TimezoneQueries() + { + Context = SqlsugarScopeBasic.GetContextSafely("default"); + } + + private string GetCityKey(string cityEnName) + { + return $"{GlobalConst.CityTimezoneKeyPrefix}:{cityEnName}"; + } + + private string GetPortKey(string airCode) + { + return $"{GlobalConst.PortTimezoneKeyPrefix}:{airCode}"; + } + + /// + /// 获取城市时区 + /// + /// 城市英文名称 + /// 时区 + public async Task GetCityTimezone(string cityEnName) + { + var key =GetCityKey(cityEnName); + var timezone = RedisHelper.Client.Get(key); + if (!string.IsNullOrEmpty(timezone)) + { + return timezone; + } + + var cityTimezone = await GetTimezoneByCityName(cityEnName); + if (cityTimezone == null!) + { + await RedisHelper.Client.SetAsync(key, string.Empty, TimeSpan.FromSeconds(20)); + return string.Empty; + } + + await RedisHelper.Client.SetAsync(key, cityTimezone.Timezone, TimeSpan.FromHours(6)); + return cityTimezone.Timezone; + } + + /// + /// 获取港口时区 + /// + /// 港口三字码 + /// 时区 + public async Task GetPortTimezone(string airCode) + { + var key = GetPortKey(airCode); + var timezone = RedisHelper.Client.Get(key); + if (!string.IsNullOrEmpty(timezone)) + { + return timezone; + } + + var portTimezone = await GetTimezoneByPortCode(airCode); + if (portTimezone == null!) + { + await RedisHelper.Client.SetAsync(key, string.Empty, TimeSpan.FromSeconds(20)); + return string.Empty; + } + + await RedisHelper.Client.SetAsync(key, portTimezone.Timezone, TimeSpan.FromHours(6)); + return portTimezone.Timezone; + } + + private Task GetTimezoneByCityName(string cityEnName) + { + return Context.Queryable().Where(val => !val.IsDeleted && val.EnName == cityEnName) + .Select(val=>new CityTimezoneDto() + { + CityEnName =val.EnName, + Timezone = val.TimeZone + }) + .FirstAsync(); + } + + private Task GetTimezoneByPortCode(string airCode) + { + return Context.Queryable() + .InnerJoin((city, airport) => city.Id == airport.CityId) + .Where((city,airport) => !airport.IsDeleted && !city.IsDeleted && airport.Code == airCode) + .Select((city,airport)=>new PortTimezoneDto() + { + PortCode = airport.Code, + Timezone = city.TimeZone + }) + .FirstAsync(); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/Queries/TrackOrderQueries.cs b/src/SJZY.Track.Repository/Queries/TrackOrderQueries.cs index 841a3555..669ed58c 100644 --- a/src/SJZY.Track.Repository/Queries/TrackOrderQueries.cs +++ b/src/SJZY.Track.Repository/Queries/TrackOrderQueries.cs @@ -4,7 +4,6 @@ using SJZY.Track.Domain.Dtos.Order; using SJZY.Track.Domain.Enums; using SJZY.Track.Domain.Queries; using SJZY.Track.Domain.VEntities; -using SJZY.Track.Repository.VData.Entitys; namespace SJZY.Track.Repository.Queries; diff --git a/src/SJZY.Track.Repository/Queries/WarningQueries.cs b/src/SJZY.Track.Repository/Queries/WarningQueries.cs new file mode 100644 index 00000000..9772f425 --- /dev/null +++ b/src/SJZY.Track.Repository/Queries/WarningQueries.cs @@ -0,0 +1,85 @@ +using SJZY.Track.Domain.Aggregates.ForewarningAggregate; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.Queries; + +namespace SJZY.Track.Repository.Queries; + +public class WarningQueries : ISugarRepository, IWarningQueries, + ITransientDependency +{ + public ISqlSugarClient Context { get; set; } + + public WarningQueries() + { + Context = SqlsugarScopeBasic.GetContextSafely("default"); + } + + public async Task> GetWarningNotifyRulesAsync(List ruleIds) + { + return await Context.Queryable() + .LeftJoin((w, wr) => w.Id == wr.WarningId) + .LeftJoin((w, wr, a) => wr.AreaId == a.Id) + .LeftJoin((w, wr, a, wn) => + wr.WarningId == wn.WarningId && wr.StartTrackStatusId == wn.StartTrackStatusId && + wr.EndTrackStatusId == wn.EndTrackStatusId) + .Filter(null, true) // 支持历史规则 + .Where((w, wr) => ruleIds.Contains(wr.Id)) + .Select((w, wr, a, wn) => new GetWarningNotifiesVo + { + RuleId = wr.Id, + WarningName = w.Name, + TotalTimeSpan = wr.TotalTimeSpan, + WarnTimeSpan = wr.WarnTimeSpan, + WarnCount = wr.WarnCount, + StartTrackStatus = wr.StartTrackStatus, + EndTrackStatus = wr.EndTrackStatus, + CargoType = wr.CargoType, + Months = wr.Months, + AreaName = a.Name, + IsTransit = wr.IsTransit, + LeavePort = wr.LeavePort, + ProductCode = wr.ProductCode, + NotifyMode = wn.NotifyMode, + NotifyTemplate = wn.NotifyTemplate, + NotifyUserId = wn.NotifyUserId, + FactorTypeSort = w.FactorTypeSort + }) + .ToListAsync(); + } + + public Task?> GetWarningRule(int systemId, int customerId) + { + return Context.Queryable() + .InnerJoin((a, b) => a.WarningId == b.Id) + .Where((a, b) => b.IsEnable && b.SubSystemId == systemId && b.CustomerId == customerId) + .Select((a, b) => new WarningRuleCacheDto + { + Id = a.Id, + WarningId = a.WarningId, + TotalTimeSpan = a.TotalTimeSpan, + WarnTimeSpan = a.WarnTimeSpan, + WarnCount = a.WarnCount, + StartTrackStatusId = a.StartTrackStatusId, + EndTrackStatusId = a.EndTrackStatusId, + CargoType = a.CargoType, + Months = a.Months, + AreaId = a.AreaId, + IsTransit = a.IsTransit, + LeavePort = a.LeavePort, + ProductCode = a.ProductCode, + CustomerId = b.CustomerId, + SystemId = b.SubSystemId + }) + .ToListAsync(); + } + + public Task?> GetWarningRuleByAreaId(int areaId) + { + return Context.Queryable() + .InnerJoin((a, b) => a.WarningId == b.Id) + .Where((a, b) => b.IsEnable && a.AreaId == areaId) + .Select((a, b) => new RuleAreaBaseInfo(b.CustomerId, b.SubSystemId)) + .ToListAsync(); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/Repositories/KpiAreaInfoRepository.cs b/src/SJZY.Track.Repository/Repositories/KpiAreaInfoRepository.cs new file mode 100644 index 00000000..de1e628d --- /dev/null +++ b/src/SJZY.Track.Repository/Repositories/KpiAreaInfoRepository.cs @@ -0,0 +1,6 @@ +namespace SJZY.Track.Repository.Repositories; + +public class KpiAreaInfoRepository +{ + +} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/Repositories/KpiAreaRepository.cs b/src/SJZY.Track.Repository/Repositories/KpiAreaRepository.cs new file mode 100644 index 00000000..3eb4a25d --- /dev/null +++ b/src/SJZY.Track.Repository/Repositories/KpiAreaRepository.cs @@ -0,0 +1,113 @@ +using SJZY.Track.Domain; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.VEntities; + +namespace SJZY.Track.Repository.Repositories; + +public class KpiAreaRepository : BasicRepository, IKpiAreaRepository, ITransientDependency +{ + public async ValueTask CreateAreaAsync(RuleAreaEntity entity) + { + return await InsertReturnIdentityAsync(entity); + } + + public async ValueTask AddAreaInfoAsync(RuleAreaInfoEntity entity) + { + return await Context.Insertable(entity).ExecuteReturnIdentityAsync(); + } + + public async ValueTask DeleteArea(int areaId) + { + using var transaction = TransactionHelper.GetReadCommitted(); + await DeleteByIdAsync(areaId); + await Context.Deleteable().Where(x => x.AreaId == areaId).ExecuteCommandAsync(); + transaction.Complete(); + } + + public Task DeleteAreaInfo(int areaInfoId) + { + return Context.Deleteable().Where(val => val.Id == areaInfoId).ExecuteCommandAsync(); + } + + public Task UpdateAreaInfoAsync(RuleAreaInfoEntity entity) + { + return Context.Updateable(entity).ExecuteCommandAsync(); + } + + /// + /// 获取分区 + /// + /// + /// + public async Task> GetAreas(QueryAreaRequest query) + { + return await Context.Queryable() + .InnerJoin((area,sys)=>area.SubSystemId==sys.Id) + .WhereIF(!string.IsNullOrEmpty(query.Name),(area,sys)=>area.Name.Contains(query.Name)) + .WhereIF(query.SystemId.HasValue,(area,sys)=>area.SubSystemId == query.SystemId.Value) + .OrderBy((area,sys) => area.UpdateTime, OrderByType.Desc) + .Select((area,sys) => new AreaDto() + { + Id = area.Id, + Name = area.Name, + SystemName = sys.CnName, + SubSystemId = area.SubSystemId, + UpdateTime = area.CreateTime + }) + .ToPagedListAsync(query.PageIndex, query.PageSize); + } + + /// + /// 获得分区信息 + /// + /// + /// + public async Task> GetAreaInfos(QueryAreaInfoRequest query) + { + return await Context.Queryable() + .InnerJoin((x,c)=>x.CountryCode==c.Digit2Code) + .Where(x => x.AreaId == query.AreaId) + .OrderBy(x => x.UpdateTime, OrderByType.Desc) + .Select((x,c)=> new RuleAreaInfoDto() + { + Id = x.Id, + AreaId = x.AreaId, + City = x.City, + CountryId = c.Id, + CountryCode = x.CountryCode, + CountryName = x.CountryName, + LeftPostcode = x.LeftPostcode, + RightPostcode = x.RightPostcode, + Province = x.Province + }) + .ToPagedListAsync(query.PageIndex, query.PageSize); + } + + public Task GetAreaInfoById(int id) + { + return Context.Queryable() + .FirstAsync(val => val.Id == id)!; + } + + /// + /// 获取所有区域信息 + /// + /// + public List GetAreaInfosByAreaId(int areaId) + { + return Context.Queryable() + .InnerJoin((r,i)=>r.AreaId==i.Id) + .Where((r,i)=>r.AreaId==areaId) + .Select((r,i)=>new KpiAreaInfo + { + AreaId = r.AreaId, + Country = r.CountryCode, + LeftPostcode = r.LeftPostcode, + City = r.City, + Province = r.Province, + RightPostcode = r.RightPostcode + }) + .ToList(); + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs index 85faff5d..ef481d06 100644 --- a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs @@ -1,5 +1,7 @@ using SJZY.Track.Domain; using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Aggregates.TrackPushAggregate; +using SJZY.Track.Domain.Consts; using SJZY.Track.Domain.Dtos.Order; namespace SJZY.Track.Repository.Repositories; @@ -19,6 +21,13 @@ public class OrderRepository : BasicRepository, IOrderRepository, I tran.Complete(); } + public Task UpdateBatchIdsAsync(List ids, int newValue) + { + var idsString = string.Join(",", ids); + return Context.Ado.ExecuteCommandAsync( + $"UPDATE `t_order` SET `DeficiencyTag` = @newValue ,UpdateTime= current_timestamp WHERE `Id` IN ({idsString})", new { newValue }); + } + public async Task UpdateOrderAsync(OrderEntity order) { using var tran = TransactionHelper.GetReadCommitted(); @@ -53,7 +62,7 @@ public class OrderRepository : BasicRepository, IOrderRepository, I tran.Complete(); } - public async Task PushWarnOrdersAsync() + public async Task PushWarnOrdersAsync(int pageSize, DateTime createTime) { return await Context.Ado.ExecuteCommandAsync(@"UPDATE `t_track_push_config` `a` LEFT JOIN `t_order` `b` ON `a`.`SubSystemId` = `b`.`SubSystemId` AND `a`.`CustomId` = `b`.`CustomId` @@ -61,8 +70,52 @@ SET `b`.`QuestionType` = CASE WHEN `b`.`LatestPushTime` IS NULL THEN 2 WHEN `b`.`LatestPushTime` < DATE_SUB(NOW(), INTERVAL 12 HOUR) THEN 2 ELSE 0 END -WHERE `b`.`IsCompleted` = 0 AND `b`.`QuestionType` != 1 AND `b`.`CreateTime` < DATE_SUB(NOW(), INTERVAL 12 HOUR) -AND `a`.`IsDeleted` = 0 AND `b`.`IsDeleted` = 0;"); +WHERE `b`.`IsCompleted` = 0 AND `b`.`QuestionType` != 1 AND `b`.`CreateTime` < @createTime +AND `a`.`IsDeleted` = 0 AND `b`.`IsDeleted` = 0 limit @pageSize", new { pageSize,createTime }); + } + + /// + /// 更新推送预警单 + /// + /// + /// + /// + /// + public async Task PushWarnOrdersAsync(int pageSize,int pageindex, DateTime createTime) + { + var ordersToUpdate = await Context.Queryable() + .LeftJoin((b, a) => a.SubSystemId == b.SubSystemId && a.CustomId == b.CustomId) + .Where((b, a) => + !b.IsCompleted + && b.QuestionType != 1 + && b.CreateTime < createTime + && !a.IsDeleted + && !b.IsDeleted) + .Select((b, a) => b) + .ToPageListAsync(pageindex,pageSize); + + var latestPushTimeIds = new LinkedList(); + var questionZeroIds = new LinkedList(); + foreach (var item in ordersToUpdate) + { + var questionType = item.LatestPushTime == null + || item.LatestPushTime < DateTime.Now.AddHours(-12) ? 2 : 0; + if(item.QuestionType== questionType) continue; + if(questionType==2) latestPushTimeIds.AddLast(item.Id); + else questionZeroIds.AddLast(item.Id); + } + + int rows = 0; + if(latestPushTimeIds.Count>0)rows+= UpdateOrderQuestionType(2, latestPushTimeIds); + if(questionZeroIds.Count>0)rows+= UpdateOrderQuestionType(0, questionZeroIds); + return rows; + } + + private int UpdateOrderQuestionType(int questionType, IEnumerable ids) + { + var idsString = string.Join(",", ids); + return Context.Ado.ExecuteCommand( + $"UPDATE `t_order` SET `QuestionType` = @questionType ,UpdateTime= current_timestamp WHERE `Id` IN ({idsString})", new { questionType }); } #region 订单关联 @@ -91,7 +144,7 @@ AND `a`.`IsDeleted` = 0 AND `b`.`IsDeleted` = 0;"); public async Task UnLinkByIdsAsync(List linkIds) { - await Context.Deleteable(linkIds).ExecuteCommandAsync(); + await Context.Deleteable(linkIds).ExecuteCommandAsync(); } #endregion diff --git a/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs index 4f798cb2..33c278d9 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackDetailsRepository.cs @@ -1,4 +1,5 @@ using Nest; +using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Expand.ElasticSearch.Services; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Consts; @@ -175,7 +176,7 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract,ITrackNodeRepository,IScopedDependency +{ + public async Task> GetPageList(TrackNodePageDto queryParam) + { + var result = await Context.Queryable() + .LeftJoin((t,u1)=>t.CreateUserId==u1.Id) + .LeftJoin((t,u1,u2)=>t.UpdateUserId==u2.Id) + .LeftJoin((t,u1,u2,u3)=>t.SubSystemId==u3.Id) + .Where((t,u1,u2)=>!t.IsDeleted&&!u1.IsDeleted&&!u2.IsDeleted) + .WhereIF(!string.IsNullOrEmpty(queryParam.KeyWord), (t,u1,u2)=>t.NodeName!.Contains(queryParam.KeyWord)||t.NodeEnName!.Contains(queryParam.KeyWord)) + .WhereIF(queryParam.SystemId.HasValue,(t,u1,u2)=>t.SubSystemId == queryParam.SystemId.Value) + .Select((t, u1, u2,u3) =>new TrackNodeDto() + { + Id = t.Id, + NodeName = t.NodeName, + NodeEnName = t.NodeEnName, + SubSystemId = t.SubSystemId, + UpdateTime = t.UpdateTime, + CreateTime = t.CreateTime, + UpdateUserId = t.UpdateUserId, + UpdateUserName = u2.Name, + CreateUserName = u1.Name, + SystemName = u3.CnName + }).ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize); + + return result; + } + + public async Task> GetPageDicList(TrackNodePageDto queryParam) + { + var result = await Context.Queryable() + .Where((t)=>!t.IsDeleted) + .WhereIF(!string.IsNullOrEmpty(queryParam.KeyWord), (t)=>t.NodeName!.Contains(queryParam.KeyWord)||t.NodeEnName!.Contains(queryParam.KeyWord)) + .WhereIF(queryParam.SystemId.HasValue,(t)=>t.SubSystemId == queryParam.SystemId.Value) + .Select(t =>new TrackNodeDicDto() + { + Id = t.Id, + NodeName = t.NodeName, + EnName = t.NodeEnName, + }).ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize); + + return result; + } + + public Task DeleteById(long inputId) + { + return Context.Deleteable().Where(val=>val.Id==inputId).ExecuteCommandAsync(); + } + + public async Task GetDetail(long id) + { + var result = await Context.Queryable() + .LeftJoin((t, u1) => t.CreateUserId == u1.Id) + .LeftJoin((t, u1, u2) => t.UpdateUserId == u2.Id) + .LeftJoin((t, u1, u2, u3) => t.SubSystemId == u3.Id) + .Where((t, u1, u2) => !t.IsDeleted && !u1.IsDeleted && !u2.IsDeleted&& t.Id==id) + .Select((t, u1, u2, u3) => new TrackNodeDto() + { + Id = t.Id, + NodeName = t.NodeName, + NodeEnName = t.NodeEnName, + SubSystemId = t.SubSystemId, + UpdateTime = t.UpdateTime, + CreateTime = t.CreateTime, + UpdateUserId = t.UpdateUserId, + UpdateUserName = u2.Name, + CreateUserName = u1.Name, + SystemName = u3.CnName + }).FirstAsync(); + return result; + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/Repositories/TrackPullConfigRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackPullConfigRepository.cs index b3267e8a..2c313138 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackPullConfigRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackPullConfigRepository.cs @@ -72,7 +72,8 @@ public class TrackPullConfigRepository : BasicRepository, { Id = trackPull.Id, SubSystem = system.CnName, - ServiceProvider = service.Name + ServiceProvider = service.Name, + TrackContentCode = paramsConfig.TrackContentCode }, true) .SingleAsync(); } @@ -86,6 +87,7 @@ public class TrackPullConfigRepository : BasicRepository, { Id = pullContent.Id, Code = trackContent.Code + "-" + trackContent.Content, + TrackTimePlaceValue = pullContent.TrackZonePlaceValue, }, true) .Mapper(x => { diff --git a/src/SJZY.Track.Repository/Repositories/TrackPushConfigRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackPushConfigRepository.cs index c7e21820..f073f286 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackPushConfigRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackPushConfigRepository.cs @@ -6,7 +6,6 @@ using SJZY.Track.Domain.Dtos.Common; using SJZY.Track.Domain.Dtos.TrackPush; using SJZY.Track.Domain.Enums; using SJZY.Track.Domain.VEntities; -using SJZY.Track.Repository.VData.Entitys; namespace SJZY.Track.Repository.Repositories; diff --git a/src/SJZY.Track.Repository/Repositories/TrackStatusRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackStatusRepository.cs index a4abd5b5..7ea0fb26 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackStatusRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackStatusRepository.cs @@ -1,3 +1,4 @@ +using SJZY.Track.Domain.Aggregates.TrackNodeAggregate; using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; using SJZY.Track.Domain.Dtos.TrackStatus; using SJZY.Track.Domain.VEntities; @@ -14,9 +15,10 @@ public class TrackStatusRepository : BasicRepository, ITrackS public async Task GetDetailAsync(int id) { var detail = await Context.Queryable() - .LeftJoin((a, b) => a.SubSystemId == b.Id&&b.IsDeleted==false) - .Where((a, b) => a.Id == id) - .Select((a, b) => new GetTrackStatusDetailVO + .InnerJoin((a,t)=>a.Node==t.Id) + .LeftJoin((a, t,b) => a.SubSystemId == b.Id&&b.IsDeleted==false) + .Where((a,t, b) => a.Id == id) + .Select((a, t,b) => new GetTrackStatusDetailVO { Id = a.Id, SubSystemId = a.SubSystemId, @@ -24,7 +26,8 @@ public class TrackStatusRepository : BasicRepository, ITrackS Code = a.Code, EntrackStatus = a.EnStatus, NodeId = a.Node, - TrackStatus = a.Status + TrackStatus = a.Status, + NodeName = t.NodeName }) .FirstAsync(); return detail; diff --git a/src/SJZY.Track.Repository/Repositories/WarningRepository.cs b/src/SJZY.Track.Repository/Repositories/WarningRepository.cs new file mode 100644 index 00000000..fe25f1d7 --- /dev/null +++ b/src/SJZY.Track.Repository/Repositories/WarningRepository.cs @@ -0,0 +1,112 @@ +using SJZY.Track.Domain; +using SJZY.Track.Domain.Aggregates.ForewarningAggregate; +using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; +using SJZY.Track.Domain.Dtos.Forewarning; +using SJZY.Track.Domain.VEntities; + +namespace SJZY.Track.Repository.Repositories; + +public class WarningRepository : BasicRepository, IWarningRepository, ITransientDependency +{ + public async Task CreateForewarningAsync(WarningEntity warning) + { + using var tran = TransactionHelper.GetReadCommitted(); + + var ruleId = await InsertReturnIdentityAsync(warning); + warning.WarningNotifies.ForEach(x => x.WarningId = ruleId); + warning.WarningFactors.ForEach(x => x.WarningId = ruleId); + warning.WarningRules.ForEach(x => x.WarningId = ruleId); + + await Context.Insertable(warning.WarningFactors).ExecuteCommandAsync(); + await Context.Insertable(warning.WarningNotifies).ExecuteCommandAsync(); + await Context.Insertable(warning.WarningRules).ExecuteCommandAsync(); + tran.Complete(); + return ruleId; + } + + public Task CopyForewarning(int warningId) + { + throw new NotImplementedException(); + } + + public Task> GetWarningPageListAsync(GetWarningPageInputVo input) + { + var query = Context.Queryable() + .LeftJoin((a, b) => a.SubSystemId == b.Id) + .LeftJoin((a, b, c) => a.CustomerId == c.Id) + .WhereIF(input.SystemId.HasValue, (a, b) => a.SubSystemId == input.SystemId) + .WhereIF(!string.IsNullOrEmpty(input.KeyWord), + (a, b, c) => a.Name.Contains(input.KeyWord!) || c.Name.Contains(input.KeyWord!)) + .OrderBy(a => a.UpdateTime, OrderByType.Desc); + + return query + .Select((a, b, c) => new WarningPageOutput + { + Id = a.Id, + SubSystem = b.CnName, + Custom = c.Name, + Name = a.Name, + UpdateTime = a.UpdateTime + }) + .ToPagedListAsync(input.PageIndex, input.PageSize); + } + + public async Task> GetWarningRuleListAsync(int warningId) + { + return await Context.Queryable() + .LeftJoin((a, b) => a.StartTrackStatusId == b.Id) + .LeftJoin((a, b, c) => a.EndTrackStatusId == c.Id) + .LeftJoin((a, b, c, d) => a.AreaId == d.Id) + .Where(a => a.WarningId == warningId) + .Select((a, b, c, d) => new WarningRuleVo + { + StartTrackStatus = b.Status, + EndTrackStatus = c.Status, + Area = d.Name + }, true) + .ToListAsync(); + } + + public async Task UpdateForewarningAsync(WarningEntity warning) + { + using var tran = TransactionHelper.GetReadCommitted(); + + await Context.Updateable(warning).ExecuteCommandAsync(); + await Context.Updateable(warning.WarningNotifies) + .UpdateColumns(x => new { x.NotifyMode, x.NotifyTemplate, x.NotifyUserId, x.UpdateTime, x.UpdateUserId }) + .ExecuteCommandAsync(); + + tran.Complete(); + } + + public async Task ModifyTimeSpansAsync(List rules) + { + return await Context.Updateable(rules) + .UpdateColumns(x => new { x.TotalTimeSpan, x.WarnTimeSpan, x.WarnCount, x.UpdateTime, x.UpdateUserId }) + .ExecuteCommandAsync(); + } + + public async Task TurnWarningAsync(WarningEntity warning) + { + return await Context.Updateable(warning) + .UpdateColumns(x => new { x.IsEnable, x.UpdateTime, x.UpdateUserId }) + .ExecuteCommandAsync() > 0; + } + + public async Task GetByIdAsync(int warningId) + { + var warning = await GetFirstAsync(x => x.Id == warningId); + + if (warning == null) return null; + + warning.WarningFactors = await Context.Queryable() + .Where(x => x.WarningId == warningId) + .ToListAsync(); + warning.WarningNotifies = await Context.Queryable() + .Where(x => x.WarningId == warningId) + .ToListAsync(); + + return warning; + } +} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/SJZY.Track.Repository.csproj b/src/SJZY.Track.Repository/SJZY.Track.Repository.csproj index 35617f6a..1c38bf3e 100644 --- a/src/SJZY.Track.Repository/SJZY.Track.Repository.csproj +++ b/src/SJZY.Track.Repository/SJZY.Track.Repository.csproj @@ -17,4 +17,16 @@ + + + + + + + + + + + + diff --git a/src/SJZY.Track.Repository/TrackPushRecord/Entitys/TrackPushRecordEntity.cs b/src/SJZY.Track.Repository/TrackPushRecord/Entitys/TrackPushRecordEntity.cs deleted file mode 100644 index b3c2ff5b..00000000 --- a/src/SJZY.Track.Repository/TrackPushRecord/Entitys/TrackPushRecordEntity.cs +++ /dev/null @@ -1,59 +0,0 @@ -using SJZY.Track.Domain.SeedWork; - -namespace SJZY.Track.Repository.TrackPushRecord.Entitys -{ - /// - /// 轨迹推送记录 - /// - [SugarTable("t_track_push_record")] - public class TrackPushRecordEntity : IAggregateRoot - { - /// - /// 自增主键 - /// - [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] - public int Id { get; set; } - - /// - /// 创建时间(推送时间) - /// - [SugarColumn(ColumnName = "CreateTime")] - public DateTime CreateTime { get; set; } - - /// - /// 创建人(推送人) - /// - [SugarColumn(ColumnName = "CreateUserId")] - public int CreateUserId { get; set; } - - /// - /// 是否自动推送 - /// - [SugarColumn(ColumnName = "IsAutoPush")] - public bool IsAutoPush { get; set; } - - /// - /// 推送内容 - /// - [SugarColumn(ColumnName = "PushContent")] - public string PushContent { get; set; } = null!; - - /// - /// 接口通请求id - /// - [SugarColumn(ColumnName = "RequestId")] - public string RequestId { get; set; } = null!; - - /// - /// 服务商id - /// - [SugarColumn(ColumnName = "ServiceProviderId")] - public int ServiceProviderId { get; set; } - - /// - /// 推送轨迹总数 - /// - [SugarColumn(ColumnName = "TrackTotal")] - public int TrackTotal { get; set; } - } -} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/TrackPushRecord/Services/ITrackPushRecordRepository.cs b/src/SJZY.Track.Repository/TrackPushRecord/Services/ITrackPushRecordRepository.cs deleted file mode 100644 index 686a3402..00000000 --- a/src/SJZY.Track.Repository/TrackPushRecord/Services/ITrackPushRecordRepository.cs +++ /dev/null @@ -1,8 +0,0 @@ -using SJZY.Track.Repository.TrackPushRecord.Entitys; - -namespace SJZY.Track.Repository.TrackPushRecord.Services -{ - public interface ITrackPushRecordRepository : ISimpleClient - { - } -} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/TrackPushRecord/Services/TrackPushRecordRepository.cs b/src/SJZY.Track.Repository/TrackPushRecord/Services/TrackPushRecordRepository.cs deleted file mode 100644 index e8e16ab9..00000000 --- a/src/SJZY.Track.Repository/TrackPushRecord/Services/TrackPushRecordRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using SJZY.Track.Repository.Repositories; -using SJZY.Track.Repository.TrackPushRecord.Entitys; - -namespace SJZY.Track.Repository.TrackPushRecord.Services -{ - public class TrackPushRecordRepository : BasicRepository, ITrackPushRecordRepository, IScopedDependency - { - - } -} diff --git a/src/SJZY.Track.Repository/TrackPushRecordDetails/Entitys/TrackPushRecordDetailsEntity.cs b/src/SJZY.Track.Repository/TrackPushRecordDetails/Entitys/TrackPushRecordDetailsEntity.cs deleted file mode 100644 index 64208102..00000000 --- a/src/SJZY.Track.Repository/TrackPushRecordDetails/Entitys/TrackPushRecordDetailsEntity.cs +++ /dev/null @@ -1,47 +0,0 @@ -using SJZY.Track.Domain.SeedWork; - -namespace SJZY.Track.Repository.TrackPushRecordDetails.Entitys -{ - /// - /// 轨迹推送明细表 - /// - [SugarTable("t_track_push_record_details")] - public class TrackPushRecordDetailsEntity : IAggregateRoot - { - /// - /// 自增主键 - /// - [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] - public int Id { get; set; } - - /// - /// 创建时间(消费完成时间) - /// - [SugarColumn(ColumnName = "CreateTime")] - public DateTime CreateTime { get; set; } - - /// - /// 异常信息 - /// - [SugarColumn(ColumnName = "ErrorMessage")] - public string ErrorMessage { get; set; } - - /// - /// 是否成功 - /// - [SugarColumn(ColumnName = "IsSuccess")] - public bool IsSuccess { get; set; } - - /// - /// 订单id - /// - [SugarColumn(ColumnName = "OrderId")] - public int OrderId { get; set; } - - /// - /// 推送记录id - /// - [SugarColumn(ColumnName = "PushRecordId")] - public int PushRecordId { get; set; } - } -} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/TrackPushRecordDetails/Services/ITrackPushRecordDetailsRepository.cs b/src/SJZY.Track.Repository/TrackPushRecordDetails/Services/ITrackPushRecordDetailsRepository.cs deleted file mode 100644 index f84fafa7..00000000 --- a/src/SJZY.Track.Repository/TrackPushRecordDetails/Services/ITrackPushRecordDetailsRepository.cs +++ /dev/null @@ -1,8 +0,0 @@ -using SJZY.Track.Repository.TrackPushRecordDetails.Entitys; - -namespace SJZY.Track.Repository.TrackPushRecordDetails.Services -{ - public interface ITrackPushRecordDetailsRepository : ISimpleClient - { - } -} \ No newline at end of file diff --git a/src/SJZY.Track.Repository/TrackPushRecordDetails/Services/TrackPushRecordDetailsRepository.cs b/src/SJZY.Track.Repository/TrackPushRecordDetails/Services/TrackPushRecordDetailsRepository.cs deleted file mode 100644 index 3a8c025b..00000000 --- a/src/SJZY.Track.Repository/TrackPushRecordDetails/Services/TrackPushRecordDetailsRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using SJZY.Track.Repository.Repositories; -using SJZY.Track.Repository.TrackPushRecordDetails.Entitys; - -namespace SJZY.Track.Repository.TrackPushRecordDetails.Services -{ - public class TrackPushRecordDetailsRepository : BasicRepository, ITrackPushRecordDetailsRepository, IScopedDependency - { - - } -}