Merge branch 'refs/heads/S04轨迹预警' into test
This commit is contained in:
commit
ef102e9177
3
.gitignore
vendored
3
.gitignore
vendored
@ -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/
|
||||
|
||||
@ -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<AbpAuditingOptions>(options => { options.IsEnabled = false; });
|
||||
|
||||
context.Services.AddRedis();
|
||||
context.Services.AddHostedService<RedisHostService>();
|
||||
}
|
||||
|
||||
public override void OnApplicationInitialization(ApplicationInitializationContext context)
|
||||
|
||||
@ -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" ]
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Common;
|
||||
|
||||
public class IdWithNameDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
public class AreaCacheEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 国家或地区
|
||||
/// </summary>
|
||||
public string Country { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 省份
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始邮编
|
||||
/// </summary>
|
||||
public string? LeftPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 截止邮编
|
||||
/// </summary>
|
||||
public string? RightPostcode { get; set; }
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
|
||||
public class ClearRulCacheEvent
|
||||
{
|
||||
public List<RuleAreaBaseInfo> Items { get; set; }
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户id
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知方式
|
||||
/// </summary>
|
||||
public NotifyMode NotifyMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知模板
|
||||
/// </summary>
|
||||
public string NotifyTemplate { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹状态跨度列表
|
||||
/// </summary>
|
||||
public List<WarningNitifyDto> TrackStatusSpans { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 预警因素列表。
|
||||
/// </summary>
|
||||
public List<WarningFactorDto> WarningFactors { get; set; } = new();
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户id
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知方式
|
||||
/// </summary>
|
||||
public NotifyMode NotifyMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知模板
|
||||
/// </summary>
|
||||
public string NotifyTemplate { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 通知列表
|
||||
/// </summary>
|
||||
public List<WarningNotifyWithIdDto> TrackStatusSpans { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 预警因素列表。
|
||||
/// </summary>
|
||||
public List<WarningFactorWithIdDto> WarningFactors { get; set; } = new();
|
||||
}
|
||||
|
||||
public class WarningNotifyWithIdDto: WarningNitifyDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public class WarningFactorWithIdDto : WarningFactorDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 因素id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
public class ForewarningRuleCacheEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警id
|
||||
/// </summary>
|
||||
public int WarningId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时效(小时) e.g. 24
|
||||
/// </summary>
|
||||
public float TotalTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒的时间(小时) e.g. 9 10 11..
|
||||
/// </summary>
|
||||
public float WarnTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒次数 e.g. 3 2 1
|
||||
/// </summary>
|
||||
public int WarnCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始轨迹状态id
|
||||
/// </summary>
|
||||
public int StartTrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 终止轨迹状态id
|
||||
/// </summary>
|
||||
public int EndTrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型
|
||||
/// </summary>
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 月份
|
||||
/// </summary>
|
||||
public List<int>? Months { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域
|
||||
/// </summary>
|
||||
public List<AreaCacheEntry>? Areas { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中转
|
||||
/// </summary>
|
||||
public bool? IsTransit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起运港口
|
||||
/// </summary>
|
||||
public string? LeavePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码
|
||||
/// </summary>
|
||||
public string? ProductCode { get; set; }
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
public class ModifyTimeSpanInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 预警id
|
||||
/// </summary>
|
||||
public int WarningId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 规则详情中的时效相关信息
|
||||
/// </summary>
|
||||
public List<WarningTimeSpanDto> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 预警id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 通知方式
|
||||
/// </summary>
|
||||
public NotifyMode NotifyMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知模板
|
||||
/// </summary>
|
||||
public string NotifyTemplate { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 通知列表
|
||||
/// </summary>
|
||||
public List<NotifyDto> Notifies { get; set; } = new();
|
||||
}
|
||||
|
||||
public class NotifyDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知人id
|
||||
/// </summary>
|
||||
public int NotifyUserId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
public class WarningDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前轨迹状态
|
||||
/// </summary>
|
||||
public int TrackStatusId { get; set; }
|
||||
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
public List<WarningOrderDto> Orders { get; set; } = new();
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
public class WarningNotifyCacheEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 规则id
|
||||
/// </summary>
|
||||
public int RuleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单id
|
||||
/// </summary>
|
||||
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}";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
public class WarningOrderDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 订单id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生时间
|
||||
/// </summary>
|
||||
public DateTime TrackTime { get; set; }
|
||||
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
public int Month { get; set; }
|
||||
|
||||
public bool? IsTransit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起运港
|
||||
/// </summary>
|
||||
public string? LeavePort { get; set; }
|
||||
|
||||
public string? ProductCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目的城市
|
||||
/// </summary>
|
||||
public string ArrivalCity { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 目的国家
|
||||
/// </summary>
|
||||
public string ArrivalCountry { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 到达邮编
|
||||
/// </summary>
|
||||
public string? ArrivalPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 到达州省
|
||||
/// </summary>
|
||||
public string? ArrivalProvince { get; set; }
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则时效
|
||||
/// </summary>
|
||||
public class WarningTimeSpanDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 预警规则id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时效
|
||||
/// </summary>
|
||||
public float TotalTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒时效
|
||||
/// </summary>
|
||||
public float WarnTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒次数
|
||||
/// </summary>
|
||||
public int WarnCount { get; set; }
|
||||
}
|
||||
@ -51,4 +51,19 @@ public class UpdateOrderInput : OrderNoInfo
|
||||
/// 订单航班信息
|
||||
/// </summary>
|
||||
public List<OrderFlightInfo> FlightInfos { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型
|
||||
/// </summary>
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否转运
|
||||
/// </summary>
|
||||
public bool? IsTransfer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码
|
||||
/// </summary>
|
||||
public string? ProductCode { get; set; }
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.TrackNode;
|
||||
|
||||
public class EditTrackNodeInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作类型
|
||||
/// </summary>
|
||||
public OptionType Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹节点id,创建不用填
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 系统id
|
||||
/// </summary>
|
||||
public int? SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点名称
|
||||
/// </summary>
|
||||
public string? NodeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点英文名称
|
||||
/// </summary>
|
||||
public string? NodeEnName { get; set; }
|
||||
}
|
||||
@ -70,7 +70,11 @@ public class EditTrackPullConfigInput
|
||||
public string? OrderPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹明细路径
|
||||
/// </summary>
|
||||
public string? TrackDetailsPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容code
|
||||
/// </summary>
|
||||
public string? TrackContentCode { get; set; }
|
||||
}
|
||||
@ -52,8 +52,14 @@ public class EditTrackPullContentInput
|
||||
/// <summary>
|
||||
/// 轨迹时区GMT
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public double? TrackTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 兜底轨迹时区发生地
|
||||
/// </summary>
|
||||
public int? TrackTimePlaceValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否完结
|
||||
/// </summary>
|
||||
|
||||
@ -37,13 +37,13 @@ public class TrackPullContentConfigDto
|
||||
/// </summary>
|
||||
public int? TrackPlaceValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹时区GMT值
|
||||
/// </summary>
|
||||
public float? TrackTimeZoneValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单是否完成(由轨迹刷新配置决定)
|
||||
/// </summary>
|
||||
public bool IsCompleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹时区发生地参数值
|
||||
/// </summary>
|
||||
public int? TrackZonePlaceValue { get; set; }
|
||||
}
|
||||
@ -46,4 +46,9 @@ public class TrackPullParamConfigDto
|
||||
/// 轨迹明细数组路径
|
||||
/// </summary>
|
||||
public string TrackDetailsPath { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容代码
|
||||
/// </summary>
|
||||
public string? TrackContentCode { get; set; }
|
||||
}
|
||||
@ -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
|
||||
/// 是否全量推送
|
||||
/// </summary>
|
||||
public bool? IsAllPush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间格式枚举
|
||||
/// </summary>
|
||||
public DateTimeFormatEnum DateTimeFormatType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日期时间格式模板
|
||||
/// </summary>
|
||||
public string? DateTimeFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区格式格式模板
|
||||
/// </summary>
|
||||
public string? TimezoneFormat { get; set; }
|
||||
}
|
||||
@ -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
|
||||
/// 扩展字段参数值
|
||||
/// </summary>
|
||||
public string? ExpandFieldsValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间格式枚举
|
||||
/// </summary>
|
||||
public DateTimeFormatEnum DateTimeFormatType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日期时间格式模板
|
||||
/// </summary>
|
||||
public string? DateTimeFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区格式格式模板
|
||||
/// </summary>
|
||||
public string? TimezoneFormat { get; set; }
|
||||
}
|
||||
@ -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; }
|
||||
|
||||
@ -36,5 +36,10 @@
|
||||
/// 轨迹内容英文
|
||||
/// </summary>
|
||||
public string EntrackStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点名称
|
||||
/// </summary>
|
||||
public string? NodeName { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
namespace SJZY.Track.Application.Contract.Dtos.TrackStatus;
|
||||
|
||||
public class QueryTrackNodeRequest:PageInfoInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 关键字,节点中文和英文
|
||||
/// </summary>
|
||||
public string? KeyWord { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public long? SystemId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
namespace SJZY.Track.Application.Contract.Events;
|
||||
|
||||
/// <summary>
|
||||
/// redis 的发布订阅事件
|
||||
/// </summary>
|
||||
public class ModifyWarningEvent:RedisEvent
|
||||
{
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
public class RedisEvent : IRedisEvent
|
||||
{
|
||||
public string EventType { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 简单的事件接口,后续可以根据需要扩展
|
||||
/// </summary>
|
||||
public interface IRedisEvent
|
||||
{
|
||||
string EventType { get; set; }
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
@ -2,6 +2,16 @@
|
||||
|
||||
public class TrackChangeAfterUpdateOrderEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户id
|
||||
/// </summary>
|
||||
public int CustomId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单Id
|
||||
/// </summary>
|
||||
@ -22,6 +32,11 @@ public class TrackChangeAfterUpdateOrderEvent
|
||||
/// </summary>
|
||||
public string TrackEnContent { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹状态Id
|
||||
/// </summary>
|
||||
public int TrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地
|
||||
/// </summary>
|
||||
@ -36,4 +51,14 @@ public class TrackChangeAfterUpdateOrderEvent
|
||||
/// 轨迹时区
|
||||
/// </summary>
|
||||
public float TrackTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容代码
|
||||
/// </summary>
|
||||
public string? TrackCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
|
||||
/// </summary>
|
||||
public int DeficiencyTagValue { get; set; }
|
||||
}
|
||||
@ -3,9 +3,14 @@
|
||||
public class TrackUpsertEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 子系统id(操作的订单都是相同子系统下的)
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户id
|
||||
/// </summary>
|
||||
public int CustomId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单id
|
||||
@ -48,4 +53,9 @@ public class TrackUpsertEvent
|
||||
public List<int> Visited { get; set; } = new();
|
||||
|
||||
public TrackRefreshStruct TrackRefreshData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
|
||||
/// </summary>
|
||||
public int DeficiencyTagValue { get; set; }
|
||||
}
|
||||
@ -12,5 +12,8 @@
|
||||
<PackageReference Include="SJZY.Expand.ABP.Common" Version="7.0.11" />
|
||||
<PackageReference Include="Volo.Abp.Core" Version="7.0.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SJZY.Track.Domain\SJZY.Track.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
using SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Application.Contract.Services;
|
||||
|
||||
public interface IForewarningCacheService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取预警规则,如果没有则返回null
|
||||
/// </summary>
|
||||
/// <param name="subSystemId">子系统id</param>
|
||||
/// <param name="customerId">客户id</param>
|
||||
/// <returns></returns>
|
||||
Task<List<ForewarningRuleCacheEntry>?> GetRulesAsync(int subSystemId, int customerId);
|
||||
|
||||
/// <summary>
|
||||
/// 删除规则
|
||||
/// </summary>
|
||||
/// <param name="subSystemId"></param>
|
||||
/// <param name="customerId"></param>
|
||||
/// <returns></returns>
|
||||
Task DeleteRulesAsync(int subSystemId, int customerId);
|
||||
|
||||
/// <summary>
|
||||
/// 重置本地缓存
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task Clear(ClearRulCacheEvent @event);
|
||||
|
||||
/// <summary>
|
||||
/// 清除redis 缓存
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task ClearRedisByAreaId(int areaId);
|
||||
|
||||
/// <summary>
|
||||
/// 清除redis 缓存
|
||||
/// </summary>
|
||||
/// <param name="subSystemId">子系统id</param>
|
||||
/// <param name="customerId">客户id</param>
|
||||
/// <returns></returns>
|
||||
Task ClearRedisBySystemIdAndCustomer(int subSystemId, int customerId);
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
using SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Application.Contract.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 预警通知服务接口定义。
|
||||
/// 提供创建、删除预警通知和发送通知的功能。
|
||||
/// </summary>
|
||||
public interface IForewarningNotifyService
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建预警通知。
|
||||
/// </summary>
|
||||
/// <param name="order">预警订单模型。</param>
|
||||
/// <returns>创建操作的结果,以整数形式返回。</returns>
|
||||
Task WarningCalculateAsync(WarningDto order);
|
||||
|
||||
/// <summary>
|
||||
/// 发送通知。
|
||||
/// </summary>
|
||||
Task SendNotificationAsync();
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
namespace SJZY.Track.Application.Contract.Services;
|
||||
|
||||
public interface IKpiAreaCacheService
|
||||
{
|
||||
|
||||
}
|
||||
@ -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<CreateWarningInput, WarningEntity>(MemberList.None)
|
||||
.Ignore(x => x.WarningFactors);
|
||||
CreateMap<WarningTimeSpanDto, WarningRuleEntity>(MemberList.None);
|
||||
CreateMap<UpdateRuleAreaRequest, RuleAreaEntity>(MemberList.None);
|
||||
CreateMap<AddRuleAreaInfoRequest, RuleAreaInfoEntity>(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<CreateRuleAreaRequest, RuleAreaEntity>(MemberList.None)
|
||||
.ForMember(dest => dest.SubSystemId, opt => opt.MapFrom(src => src.SystemId))
|
||||
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name));
|
||||
|
||||
CreateMap<UpdateRuleAreaInfoRequest, RuleAreaInfoEntity>(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));
|
||||
}
|
||||
}
|
||||
@ -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<QueryTrackNodeRequest, TrackNodePageDto>();
|
||||
CreateMap<EditTrackNodeInput, TrackNodeEntity>(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));
|
||||
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ public class TrackPullAutoMapperProfile : Profile
|
||||
.ForMember(dest => dest.IsPush, opt => opt.MapFrom(source => false));
|
||||
CreateMap<EditTrackPullContentInput, TrackPullContentConfigEntity>(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<GetTrackPullConfigDetailOutputVO, TrackPullParamConfigDto>(MemberList.None);
|
||||
CreateMap<GetTrackPullContentListCacheVO, TrackPullContentConfigDto>(MemberList.None);
|
||||
|
||||
@ -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<OrderEntity>();
|
||||
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<List<OrderEntity>> 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<OrderEntity> 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()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务商字典
|
||||
/// </summary>
|
||||
/// <param name="orderIds"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<Dictionary<int, OrderTransportDto>> GetProviderDic(int[] orderIds)
|
||||
{
|
||||
var providerList = await _provideQueries.FindOrderWithServiceProviderAsync(orderIds);
|
||||
return providerList.GroupBy(val => val.OrderId)
|
||||
.ToDictionary(val => val.Key, val => val.First());
|
||||
}
|
||||
}
|
||||
@ -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<string>(@event.Attach);
|
||||
if (optionType == OptionType.新增.ToString())
|
||||
{
|
||||
|
||||
@ -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<TrackPushContentConfigDto> _pushConfigs;
|
||||
private List<OrderEntity> _orders;
|
||||
private List<TrackPushContentConfigDto> _pushConfigs = null!;
|
||||
private List<OrderEntity> _orders = null!;
|
||||
|
||||
public TrackPushEventHandler(IHttpClientProxy<IInterfaceForwardServiceV2> 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<TrackPushInput> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换推送时间格式
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <param name="timezone"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
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");
|
||||
}
|
||||
}
|
||||
@ -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<string>();
|
||||
if (trackPlace.IsNullOrWhiteSpace())
|
||||
var hasPlaceBottomLine = false; //执行了兜底但是没有兜底住,需要记录
|
||||
if (trackPlace.IsNullOrWhiteSpace() && matchedContentConfig?.TrackPlaceValue != null && matchedContentConfig.TrackPlaceValue.HasValue)
|
||||
{
|
||||
Expression<Func<OrderEntity, string?>> 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<float>();
|
||||
|
||||
//兜底时区
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据订单id获取兜底位置
|
||||
/// </summary>
|
||||
/// <param name="trackValue"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <returns></returns>
|
||||
private Task<string?> GetPlaceByOrder(int trackValue,int orderId)
|
||||
{
|
||||
Expression<Func<OrderEntity, string?>> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取时区
|
||||
/// </summary>
|
||||
/// <param name="matchedContentConfig"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<float?> 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;
|
||||
}
|
||||
}
|
||||
@ -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<int> 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<TrackDetailsIndex>();
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
60
src/SJZY.Track.Application/Helpers/DateTimeHelper.cs
Normal file
60
src/SJZY.Track.Application/Helpers/DateTimeHelper.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using SJZY.OTel;
|
||||
|
||||
namespace SJZY.Track.Application.Helpers;
|
||||
|
||||
public static class DateTimeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// convert to a Unix timestamp from a DateTime object.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The DateTime object to convert to a Unix timestamp.</param>
|
||||
/// <returns>The Unix timestamp as a long.</returns>
|
||||
public static long ConvertToUnixTimestamp(DateTime dateTime)
|
||||
{
|
||||
return new DateTimeOffset(dateTime).ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a DateTime object into a specified format.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The DateTime object to format.</param>
|
||||
/// <param name="format">The format string.</param>
|
||||
/// <returns>The formatted date string.</returns>
|
||||
public static string FormatDateTime(DateTime dateTime, string format)
|
||||
{
|
||||
return dateTime.ToString(format);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时区格式化
|
||||
/// </summary>
|
||||
/// <param name="timezone"></param>
|
||||
/// <param name="format"></param>
|
||||
/// <returns></returns>
|
||||
public static string TimezoneFormat(double timezone, string format)
|
||||
{
|
||||
//时区是整数丢掉浮点数部分
|
||||
return format.Replace("hh", ((int)timezone).ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换主数据中心类似GMT-09:00这种的时区为浮点数
|
||||
/// </summary>
|
||||
/// <param name="timeZone">分区</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
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);
|
||||
}
|
||||
}
|
||||
243
src/SJZY.Track.Application/Helpers/ForewarningHelper.cs
Normal file
243
src/SJZY.Track.Application/Helpers/ForewarningHelper.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 递推分裂预警规则列表
|
||||
/// </summary>
|
||||
/// <param name="statusSpans"></param>
|
||||
/// <param name="factors"></param>
|
||||
/// <returns></returns>
|
||||
public static List<WarningRuleEntity> CalcRuleList(List<WarningNitifyDto> statusSpans,
|
||||
List<WarningFactorDto> 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<WarningNitifyDto> statusSpans, List<WarningRuleEntity> 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<WarningRuleEntity> 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<WarningFactorEntity> MapFactorEntities(List<WarningFactorDto> warningFactors)
|
||||
{
|
||||
return warningFactors.Select(x => new WarningFactorEntity
|
||||
{
|
||||
Type = x.FactorType,
|
||||
Value = x.FactorValue.ToJson()
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public static List<WarningNotifyEntity> 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<IdWithNameDto> 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<dynamic> DynamicCalcRuleList(List<WarningRuleVo> rules, List<RuleFactorType> factors)
|
||||
{
|
||||
var dynamicList = new List<dynamic>();
|
||||
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
dynamic dynamicRule = new ExpandoObject();
|
||||
var dynamicRuleDict = (IDictionary<string, object?>)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;
|
||||
}
|
||||
}
|
||||
50
src/SJZY.Track.Application/HostServices/RedisHostService.cs
Normal file
50
src/SJZY.Track.Application/HostServices/RedisHostService.cs
Normal file
@ -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<ModifyWarningEvent>(value as string);
|
||||
var instance = Newtonsoft.Json.JsonConvert.DeserializeObject<ClearRulCacheEvent>(value as string);
|
||||
if(instance!=null)
|
||||
_forewarningCacheService.Clear(instance);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetKey()
|
||||
{
|
||||
return $"{GlobalConst.PublishNotifyChannelKey}";
|
||||
}
|
||||
}
|
||||
@ -29,4 +29,8 @@
|
||||
<ProjectReference Include="..\SJZY.Track.Domain\SJZY.Track.Domain.csproj" />
|
||||
<ProjectReference Include="..\SJZY.Track.Application.Contract\SJZY.Track.Application.Contract.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Validations\Forewarning\FactorItemDtoValidator.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -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<string, List<ForewarningRuleCacheEntry>> _rules = new();
|
||||
private readonly ConcurrentDictionary<int, List<AreaCacheEntry>> _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<List<ForewarningRuleCacheEntry>?> 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<ForewarningRuleCacheEntry> WriteRules(List<WarningRuleCacheDto> rules)
|
||||
{
|
||||
List<ForewarningRuleCacheEntry> result = new List<ForewarningRuleCacheEntry>();
|
||||
foreach (var item in rules)
|
||||
{
|
||||
List<AreaCacheEntry>? 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<int>()
|
||||
: 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<List<ForewarningRuleCacheEntry>?> GetRules(int systemId, int customerId)
|
||||
{
|
||||
var key = WarningKey(systemId, customerId);
|
||||
var result = RedisHelper.Client.Get<List<WarningRuleCacheDto>>(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<WarningRuleCacheDto>(), TimeSpan.FromSeconds(20));
|
||||
return new List<ForewarningRuleCacheEntry>();
|
||||
}
|
||||
|
||||
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<AreaCacheEntry>? 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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取区域
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private List<KpiAreaInfo>? GetAreas(int areaId)
|
||||
{
|
||||
var key = AreaKey(areaId);
|
||||
var result = RedisHelper.Client.Get<List<KpiAreaInfo>>(key);
|
||||
if (result == null)
|
||||
{
|
||||
result = RedisHelper.Client.Get<List<KpiAreaInfo>>(key);
|
||||
if (result != null) return new List<KpiAreaInfo>();
|
||||
|
||||
var kpiArea = _kpiAreaRepository.GetAreaInfosByAreaId(areaId);
|
||||
if (kpiArea == null)
|
||||
{
|
||||
RedisHelper.Client.Set(key, new List<KpiAreaInfo>(), TimeSpan.FromSeconds(20));
|
||||
return new List<KpiAreaInfo>();
|
||||
}
|
||||
|
||||
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<RuleAreaBaseInfo>()
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 用于处理预警通知
|
||||
/// </summary>
|
||||
public class ForewarningNotifyService : IForewarningNotifyService, ITransientDependency
|
||||
{
|
||||
private readonly IForewarningCacheService _cacheService;
|
||||
private readonly IWarningQueries _warningQueries;
|
||||
|
||||
public ForewarningNotifyService(IForewarningCacheService cacheService, IWarningQueries warningQueries)
|
||||
{
|
||||
_cacheService = cacheService;
|
||||
_warningQueries = warningQueries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建预警通知
|
||||
/// </summary>
|
||||
/// <param name="dto">创建预警通知需要的订单信息</param>
|
||||
/// <returns>创建的通知总量,0表示没有规则</returns>
|
||||
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<ForewarningRuleCacheEntry> MatchRule(List<ForewarningRuleCacheEntry> rules,
|
||||
WarningOrderDto order)
|
||||
{
|
||||
var res = new List<ForewarningRuleCacheEntry>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取预警分页列表
|
||||
/// </summary>
|
||||
/// <param name="inputVo"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("GetWarningPageList")]
|
||||
public async Task<UnifyPageResultDto<WarningPageOutput>> GetWarningPageList(GetWarningPageInputVo inputVo)
|
||||
{
|
||||
if (!inputVo.KeyWord.IsNullOrEmpty()) inputVo.KeyWord = inputVo.KeyWord!.Trim();
|
||||
return await _warningRepository.GetWarningPageListAsync(inputVo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建预警
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("CreateWarning")]
|
||||
public async Task<bool> CreateWarning(CreateWarningInput input)
|
||||
{
|
||||
var warning = ObjectMapper.Map<CreateWarningInput, WarningEntity>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑预警
|
||||
/// </summary>
|
||||
/// <param name="warningId"></param>
|
||||
/// <param name="statusQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("EditWarning")]
|
||||
public async Task<EditWarningOutput> EditWarning(int warningId,
|
||||
[FromServices] IBasicQuery<TrackStatusEntity> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新预警
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("UpdateWarning")]
|
||||
public async Task<bool> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用or禁用预警
|
||||
/// </summary>
|
||||
/// <param name="warningId">预警id</param>
|
||||
/// <param name="isEnable">是否启用</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("TurnWarning")]
|
||||
public async Task<bool> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询预警规则列表
|
||||
/// </summary>
|
||||
/// <param name="warningId">预警规则id</param>
|
||||
/// <param name="factorQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("GetWarningRuleList")]
|
||||
public async Task<List<dynamic>> GetWarningRuleListAsync([Required] int warningId,
|
||||
[FromServices] IBasicQuery<WarningFactorEntity> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改预警规则时效
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
[HttpPost("ModifyTimeSpans")]
|
||||
public async Task<bool> 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<WarningTimeSpanDto>, List<WarningRuleEntity>>(input.Items);
|
||||
|
||||
await _warningRepository.ModifyTimeSpansAsync(warningRules);
|
||||
|
||||
// 清除缓存
|
||||
await _forewarningCacheService.DeleteRulesAsync(warning.SubSystemId, warning.CustomerId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建分区
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns>分区id</returns>
|
||||
[HttpPost("KpiArea/CreateKpiArea")]
|
||||
public async ValueTask<UnifyResultDto> CreateKpiArea(CreateRuleAreaRequest input)
|
||||
{
|
||||
var ruleAreaEntity = ObjectMapper.Map<CreateRuleAreaRequest, RuleAreaEntity>(input);
|
||||
var areaId = await _ruleAreaRepository.CreateAreaAsync(ruleAreaEntity);
|
||||
return new UnifyResultDto(true, areaId, "创建成功");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加区域信息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns>区域id</returns>
|
||||
[HttpPost("KpiArea/AddAreaInfo")]
|
||||
public async ValueTask<UnifyResultDto> AddAreaInfoAsync(AddRuleAreaInfoRequest input)
|
||||
{
|
||||
var ruleAreaEntity = ObjectMapper.Map<AddRuleAreaInfoRequest, RuleAreaInfoEntity>(input);
|
||||
var ruleInfoId = await _ruleAreaRepository.AddAreaInfoAsync(ruleAreaEntity);
|
||||
await _forewarningCacheService.ClearRedisByAreaId(input.AreaId);
|
||||
return new UnifyResultDto(true, ruleInfoId, "添加成功");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区
|
||||
/// </summary>
|
||||
/// <param name="id">分区id</param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("KpiArea/DeleteKpiArea")]
|
||||
public async ValueTask<UnifyResultDto> DeleteKpiArea(int id)
|
||||
{
|
||||
await _ruleAreaRepository.DeleteArea(id);
|
||||
await _forewarningCacheService.ClearRedisByAreaId(id);
|
||||
return new UnifyResultDto(true, "删除成功");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区信息
|
||||
/// </summary>
|
||||
/// <param name="id">分区信息id</param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("KpiArea/DeleteKpiAreaInfo")]
|
||||
public async ValueTask<UnifyResultDto> 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, "删除成功");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新区域信息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("KpiArea/ModifyKpiArea")]
|
||||
public async Task<UnifyResultDto> 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, "更新成功");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新区域信息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("KpiArea/ModifyKpiAreaInfo")]
|
||||
public async Task<UnifyResultDto> 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, "更新成功");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询分区列表
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("KpiArea/GetKpiAreas")]
|
||||
public async Task<UnifyPageResultDto<AreaDto>> GetKpiAreas(QueryAreaRequest input)
|
||||
{
|
||||
return await _ruleAreaRepository.GetAreas(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询分区列表
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("KpiArea/GetKpiAreaInfos")]
|
||||
public async Task<UnifyPageResultDto<RuleAreaInfoDto>> GetKpiAreaInfos(QueryAreaInfoRequest query)
|
||||
{
|
||||
return await _ruleAreaRepository.GetAreaInfos(query);
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -90,9 +92,23 @@ public class TrackPushService : ApplicationService
|
||||
[HttpPost("Track/PushWarnForTask")]
|
||||
public async Task<string> 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}个订单";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 轨迹节点服务
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹节点编辑
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("Edit")]
|
||||
public async Task<UnifyResultDto> EditAsync(EditTrackNodeInput input)
|
||||
{
|
||||
return input.Options switch
|
||||
{
|
||||
OptionType.新增 => await AddAsync(input),
|
||||
OptionType.修改 => await UpdateAsync(input),
|
||||
OptionType.删除 => await DeleteAsync(input),
|
||||
_ => throw new BusinessException(message: "当前操作动作不合法")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取分页轨迹节点
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("GetList")]
|
||||
public async Task<UnifyPageResultDto<TrackNodeDto>> GetPageTrackNode(QueryTrackNodeRequest input)
|
||||
{
|
||||
var queryParam = ObjectMapper.Map<QueryTrackNodeRequest, TrackNodePageDto>(input);
|
||||
var resultDto = await _trackNodeRepository.GetPageList(queryParam);
|
||||
return resultDto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取详情
|
||||
/// </summary>
|
||||
/// <param name="nodeid">节点id</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("GetDetail")]
|
||||
public async Task<UnifyResultDto> GetDetail(int nodeid)
|
||||
{
|
||||
var resultDto = await _trackNodeRepository.GetDetail(nodeid);
|
||||
return new UnifyResultDto(true,resultDto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取分页轨迹字典,下拉框用的
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("GetTrackNodeOption")]
|
||||
public async Task<UnifyPageResultDto<TrackNodeDicDto>> GetTrackNodeOption(QueryTrackNodeRequest input)
|
||||
{
|
||||
var queryParam = ObjectMapper.Map<QueryTrackNodeRequest, TrackNodePageDto>(input);
|
||||
var resultDto = await _trackNodeRepository.GetPageDicList(queryParam);
|
||||
return resultDto;
|
||||
}
|
||||
|
||||
#region 私有方法
|
||||
|
||||
private async Task<UnifyResultDto> 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<EditTrackNodeInput, TrackNodeEntity>(input);
|
||||
var isTrue = await _trackNodeRepository.InsertAsync(entity);
|
||||
|
||||
return new UnifyResultDto("200", "新增成功", isTrue);
|
||||
}
|
||||
|
||||
private async Task<UnifyResultDto> 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<UnifyResultDto> 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
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -99,7 +99,6 @@ namespace SJZY.Track.Application
|
||||
options.SucceedMessageExpiredAfter = configuration.GetValue<int>("CAP:SucceedMessageExpiredAfter");
|
||||
options.FallbackWindowLookbackSeconds =
|
||||
configuration.GetValue<int>("CAP:FallbackWindowLookbackSeconds");
|
||||
options.EnableSubscriberParallelExecute = true;
|
||||
|
||||
options.UseDashboard(dashboardOptions =>
|
||||
{
|
||||
|
||||
@ -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<CreateWarningInput>
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
namespace SJZY.Track.Application.Validations.Forewarning;
|
||||
|
||||
public class FactorItemDtoValidator
|
||||
{
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
using FluentValidation;
|
||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Application.Validations.Forewarning;
|
||||
|
||||
public class RuleTrackStateDtoValidator : AbstractValidator<WarningNitifyDto>
|
||||
{
|
||||
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("通知对象不能为空");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
using SJZY.Track.Domain.Enums;
|
||||
|
||||
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
|
||||
|
||||
public class FactorItemModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order.
|
||||
/// </summary>
|
||||
public int Sort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the rule factor.
|
||||
/// </summary>
|
||||
public RuleFactorType FactorType { get; set; }
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
|
||||
|
||||
public interface IWarningRepository : IBasicRepository<WarningEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建预警,include factors, notifies, rules
|
||||
/// </summary>
|
||||
/// <param name="warning"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> CreateForewarningAsync(WarningEntity warning);
|
||||
|
||||
/// <summary>
|
||||
/// 复制预警
|
||||
/// </summary>
|
||||
/// <param name="warningId"></param>
|
||||
/// <returns></returns>
|
||||
Task CopyForewarning(int warningId);
|
||||
|
||||
/// <summary>
|
||||
/// 获取预警分页列表
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
Task<UnifyPageResultDto<WarningPageOutput>> GetWarningPageListAsync(GetWarningPageInputVo input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取预警规则列表
|
||||
/// </summary>
|
||||
/// <param name="warningId">预警id</param>
|
||||
/// <returns></returns>
|
||||
Task<List<WarningRuleVo>> GetWarningRuleListAsync(int warningId);
|
||||
|
||||
/// <summary>
|
||||
/// 修改预警
|
||||
/// </summary>
|
||||
/// <param name="warning"></param>
|
||||
/// <returns></returns>
|
||||
Task UpdateForewarningAsync(WarningEntity warning);
|
||||
|
||||
/// <summary>
|
||||
/// 修改预警时效
|
||||
/// </summary>
|
||||
/// <param name="inputItems"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> ModifyTimeSpansAsync(List<WarningRuleEntity> inputItems);
|
||||
|
||||
/// <summary>
|
||||
/// 启用预警
|
||||
/// </summary>
|
||||
/// <param name="warning"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> TurnWarningAsync(WarningEntity warning);
|
||||
|
||||
/// <summary>
|
||||
/// 获取预警,include factors, notifies
|
||||
/// </summary>
|
||||
/// <param name="warningId"></param>
|
||||
/// <returns></returns>
|
||||
Task<WarningEntity?> GetByIdAsync(int warningId);
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
|
||||
|
||||
public class RuleAreaEntity
|
||||
{
|
||||
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "SubSystemId")]
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CustomerId")]
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 规则模板名称,KPI名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Name")]
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 因素类型排序
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "FactorTypeSort")]
|
||||
public string FactorTypeSort { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用,默认否
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsEnable")]
|
||||
public bool IsEnable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public List<WarningRuleEntity> WarningRules { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 预警因素
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public List<WarningFactorEntity> WarningFactors { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 预警通知
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public List<WarningNotifyEntity> 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using SJZY.Track.Domain.Enums;
|
||||
|
||||
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
|
||||
|
||||
[SugarTable("t_warning_factor")]
|
||||
public class WarningFactorEntity : IFullAuditedBasicEntitys, IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "WarningId")]
|
||||
public int WarningId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 因素类型(1货物类型,2月份,3分区,4是否中转,5起运港口,6销售产品)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Type")]
|
||||
public RuleFactorType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警因素值(json数组字符串)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Value")]
|
||||
public string Value { get; set; } = null!;
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
using SJZY.Track.Domain.Enums;
|
||||
|
||||
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
|
||||
|
||||
[SugarTable("t_warning_notify")]
|
||||
public class WarningNotifyEntity : IFullAuditedBasicEntitys, IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "WarningId")]
|
||||
public int WarningId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始轨迹状态id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "StartTrackStatusId", IsNullable = false)]
|
||||
public int StartTrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 终止轨迹状态id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "EndTrackStatusId", IsNullable = false)]
|
||||
public int EndTrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知方式
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "NotifyMode")]
|
||||
public NotifyMode NotifyMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知模板
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "NotifyTemplate", Length = 256)]
|
||||
public string NotifyTemplate { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 通知人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "NotifyUserId")]
|
||||
public int NotifyUserId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
|
||||
|
||||
[SugarTable("t_warning_rule")]
|
||||
public class WarningRuleEntity : IFullAuditedBasicEntitys, IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "WarningId")]
|
||||
public int WarningId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时效(小时)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "TotalTimeSpan")]
|
||||
public float TotalTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒的时间(小时)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "WarnTimeSpan")]
|
||||
public float WarnTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒次数
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "WarnCount")]
|
||||
public int WarnCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始轨迹状态id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "StartTrackStatusId")]
|
||||
public int StartTrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始轨迹状态(冗余)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "StartTrackStatusId")]
|
||||
public string StartTrackStatus { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 终止轨迹状态id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "EndTrackStatusId")]
|
||||
public int EndTrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 终止轨迹状态(冗余)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "EndTrackStatus")]
|
||||
public string EndTrackStatus { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CargoType", Length = 64)]
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 月份
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Months", Length = 25)]
|
||||
public string? Months { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "AreaId")]
|
||||
public int? AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中转
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsTransit")]
|
||||
public bool? IsTransit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起运港口
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "LeavePort")]
|
||||
public string? LeavePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "ProductCode")]
|
||||
public string? ProductCode { get; set; }
|
||||
}
|
||||
6
src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate.cs
Normal file
6
src/SJZY.Track.Domain/Aggregates/KPIAreaAggregate.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace SJZY.Track.Domain.Aggregates;
|
||||
|
||||
public class KPIAreaAggregate
|
||||
{
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace SJZY.Track.Domain.Aggregates.KPIAreaAggregate;
|
||||
|
||||
public interface IKpiAreaRepository : IBasicRepository<RuleAreaEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建分区
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
ValueTask<int> CreateAreaAsync(RuleAreaEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// 添加分区信息
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
ValueTask<int> AddAreaInfoAsync(RuleAreaInfoEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区信息
|
||||
/// </summary>
|
||||
/// <param name="areaInfoId">分区信息id</param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteAreaInfo(int areaInfoId);
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区
|
||||
/// </summary>
|
||||
/// <param name="areaId">分区id</param>
|
||||
/// <returns></returns>
|
||||
ValueTask DeleteArea(int areaId);
|
||||
|
||||
/// <summary>
|
||||
/// 更新区域信息
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> UpdateAreaInfoAsync(RuleAreaInfoEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// 获得分区
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<UnifyPageResultDto<AreaDto>> GetAreas(QueryAreaRequest query);
|
||||
|
||||
/// <summary>
|
||||
/// 获取分区信息
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<UnifyPageResultDto<RuleAreaInfoDto>> GetAreaInfos(QueryAreaInfoRequest query);
|
||||
|
||||
/// <summary>
|
||||
/// 获取分区信息通过id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<RuleAreaInfoEntity?> GetAreaInfoById(int id);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有区域信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<KpiAreaInfo>? GetAreaInfosByAreaId(int areaId);
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
namespace SJZY.Track.Domain.Aggregates.KPIAreaAggregate;
|
||||
|
||||
[SugarTable("t_area", TableDescription = "KPI分区主表")]
|
||||
public class RuleAreaEntity : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "SubSystemId", ColumnDescription = "子系统id")]
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 分区名称KPI名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Name", Length = 64, ColumnDescription = "分区名称KPI名称")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")]
|
||||
public int CreateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateUserId", ColumnDescription = "更新人")]
|
||||
public int UpdateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间")]
|
||||
public DateTime UpdateTime { get; set; }
|
||||
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public List<RuleAreaInfoEntity> AreaInfos { get; set; }
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
namespace SJZY.Track.Domain.Aggregates.KPIAreaAggregate;
|
||||
|
||||
[SugarTable("t_area_details", TableDescription = "kpi 区域子表")]
|
||||
public class RuleAreaInfoEntity : IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// t_rule_area 表id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "AreaId", ColumnDescription = "t_rule_area 表id")]
|
||||
public int AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区二字码
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CountryCode", Length = 128, ColumnDescription = "国家或地区二字码")]
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CountryName", Length = 128, ColumnDescription = "国家或地区名称")]
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 州或者省份
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Province", Length = 128, IsNullable = true, ColumnDescription = "州或者省份")]
|
||||
public string Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "City", Length = 128, IsNullable = true, ColumnDescription = "城市")]
|
||||
public string City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始邮编
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "LeftPostcode", Length = 10, IsNullable = true, ColumnDescription = "起始邮编")]
|
||||
public string LeftPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 截止邮编
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "RightPostcode", Length = 10, IsNullable = true, ColumnDescription = "截止邮编")]
|
||||
public string RightPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")]
|
||||
public int CreateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateUserId", IsNullable = false, ColumnDescription = "更新人")]
|
||||
public int UpdateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateTime", IsNullable = false, ColumnDescription = "更新时间")]
|
||||
public DateTime UpdateTime { get; set; }
|
||||
}
|
||||
@ -29,7 +29,21 @@ public interface IOrderRepository : IBasicRepository<OrderEntity>
|
||||
/// 更新推送预警单
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<int> PushWarnOrdersAsync();
|
||||
Task<int> PushWarnOrdersAsync(int pageSize,DateTime createTime);
|
||||
|
||||
/// <summary>
|
||||
/// 更新推送预警单
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<int> PushWarnOrdersAsync(int pageSize, int pageindex, DateTime createTime);
|
||||
|
||||
/// <summary>
|
||||
/// 更新缺失标记,时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
|
||||
/// </summary>
|
||||
/// <param name="ids">订单id</param>
|
||||
/// <param name="newValue">值</param>
|
||||
/// <returns></returns>
|
||||
Task<int> UpdateBatchIdsAsync(List<int> ids, int newValue);
|
||||
|
||||
#region 订单关联
|
||||
|
||||
|
||||
@ -131,7 +131,49 @@ public class OrderEntity : IFullAuditedBasicEntitys, IAggregateRoot
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "LeavePort")]
|
||||
public string? LeavePort { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CargoType")]
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中转
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsTransit")]
|
||||
public bool? IsTransit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "ProductCode")]
|
||||
public string? ProductCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "DeficiencyTag")]
|
||||
public int? DeficiencyTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 到达邮编
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "ArrivalPostCode")]
|
||||
public string? ArrivalPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 到达州省
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "ArrivalProvince")]
|
||||
public string? ArrivalProvince { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否触发拉取,1表示触发
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsTriggerPull")]
|
||||
public short? IsTriggerPull { get; set; }
|
||||
|
||||
[SugarColumn(IsIgnore = true)] public List<OrderFlightEntity> OrderFlights { get; set; }
|
||||
[SugarColumn(IsIgnore = true)] public List<OrderTemplateEntity> OrderTemplates { get; set; }
|
||||
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
using SJZY.Track.Domain.Dtos.TrackNode;
|
||||
|
||||
namespace SJZY.Track.Domain.Aggregates.TrackNodeAggregate;
|
||||
|
||||
public interface ITrackNodeRepository:IBasicRepository<TrackNodeEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// 分页查询
|
||||
/// </summary>
|
||||
/// <param name="queryParam"></param>
|
||||
/// <returns></returns>
|
||||
Task<UnifyPageResultDto<TrackNodeDto>> GetPageList(TrackNodePageDto queryParam);
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询字典
|
||||
/// </summary>
|
||||
/// <param name="queryParam"></param>
|
||||
/// <returns></returns>
|
||||
Task<UnifyPageResultDto<TrackNodeDicDto>> GetPageDicList(TrackNodePageDto queryParam);
|
||||
|
||||
/// <summary>
|
||||
/// 删除
|
||||
/// </summary>
|
||||
/// <param name="inputId"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteById(long inputId);
|
||||
|
||||
/// <summary>
|
||||
/// 获取详情
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<TrackNodeDto> GetDetail(long id);
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
namespace SJZY.Track.Domain.Aggregates.TrackNodeAggregate;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹节点主表
|
||||
/// </summary>
|
||||
[SugarTable("t_track_node", TableDescription = "轨迹节点主表")]
|
||||
public class TrackNodeEntity : IFullAuditedBasicEntitys, IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")]
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "SubSystemId", ColumnDescription = "子系统id")]
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "NodeName", Length = 32, ColumnDescription = "节点名称")]
|
||||
public string? NodeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点英文名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "NodeEnName", Length = 32, ColumnDescription = "节点英文名称")]
|
||||
public string? NodeEnName { get; set; }
|
||||
}
|
||||
@ -71,4 +71,10 @@ public class TrackPullContentConfigEntity : IFullAuditedBasicEntitys, IEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsCompleted")]
|
||||
public bool IsCompleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹时区发生地参数值
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "TrackZonePlaceValue")]
|
||||
public int? TrackZonePlaceValue { get; set; }
|
||||
}
|
||||
@ -89,4 +89,10 @@ public class TrackPushPullParamsConfigEntity : IFullAuditedBasicEntitys, IEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "ExpandFieldsName")]
|
||||
public string? ExpandFieldsName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容代码
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "TrackContentCode")]
|
||||
public string? TrackContentCode { get; set; }
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
namespace SJZY.Track.Domain.Aggregates.TrackPushAggregate;
|
||||
using SJZY.Track.Domain.Enums;
|
||||
|
||||
namespace SJZY.Track.Domain.Aggregates.TrackPushAggregate;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹推送配置表
|
||||
@ -53,4 +55,22 @@ public class TrackPushConfigEntity : IFullAuditedBasicEntitys, IAggregateRoot
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsAllPush")]
|
||||
public bool IsAllPush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日期时间格式类型
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "DateTimeFormatType")]
|
||||
public DateTimeFormatEnum DateTimeFormatType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日期时间格式模板
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "DateTimeFormat")]
|
||||
public string? DateTimeFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区格式格式模板
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "TimezoneFormat")]
|
||||
public string? TimezoneFormat { get; set; }
|
||||
}
|
||||
@ -25,7 +25,7 @@ public class TrackStatusEntity : IFullAuditedBasicEntitys, IAggregateRoot
|
||||
public string EnStatus { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 所属节点(主数据枚举ServiceNode)
|
||||
/// 所属节点(来自表 t_track_node) 之前是枚举,后续调整为表
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Node")]
|
||||
public int Node { get; set; }
|
||||
|
||||
@ -11,4 +11,9 @@ public partial class GlobalConst
|
||||
/// 轨迹推送记录 es索引名称(模板)
|
||||
/// </summary>
|
||||
public const string TrackPushRecordsIndex = "sjzy_track_push_records";
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则 es索引名称(模板)
|
||||
/// </summary>
|
||||
public const string RuleFactorIndex = "sjzy_rule_factor";
|
||||
}
|
||||
@ -21,4 +21,44 @@ public partial class GlobalConst
|
||||
/// 轨迹拉取配置信息redis key前缀
|
||||
/// </summary>
|
||||
public const string TrackPullContentKeyPrefix = "TrackPullContent";
|
||||
|
||||
/// <summary>
|
||||
/// 预警通知redis key
|
||||
/// </summary>
|
||||
public const string ForewarningNotifyKey = "ForewarningNotify";
|
||||
|
||||
/// <summary>
|
||||
/// 预警通知剩余次数redis key
|
||||
/// </summary>
|
||||
public const string ForewarningNotifyCntKey = "ForewarningNotifyCnt";
|
||||
|
||||
/// <summary>
|
||||
/// kpi区域redis key前缀
|
||||
/// </summary>
|
||||
public const string KpiAreaKeyPrefix = "KpiArea";
|
||||
|
||||
/// <summary>
|
||||
/// 城市时区redis key前缀
|
||||
/// </summary>
|
||||
public const string CityTimezoneKeyPrefix = "CityTimezone";
|
||||
|
||||
/// <summary>
|
||||
/// 港口时区redis key前缀
|
||||
/// </summary>
|
||||
public const string PortTimezoneKeyPrefix = "PortTimezone";
|
||||
|
||||
/// <summary>
|
||||
/// 发布订阅通知频道Key
|
||||
/// </summary>
|
||||
public const string PublishNotifyChannelKey = "PublishNotifyChannel";
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹区域reids key
|
||||
/// </summary>
|
||||
public const string TrackAreaKeyPrefix = "TrackArea";
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹预警数据redis key前缀
|
||||
/// </summary>
|
||||
public const string TrackWarningKeyPrefix = "TrackWarning";
|
||||
}
|
||||
14
src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs
Normal file
14
src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Common;
|
||||
|
||||
public class CityTimezoneDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 城市英文名称
|
||||
/// </summary>
|
||||
public string? CityEnName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区
|
||||
/// </summary>
|
||||
public string Timezone { get; set; }
|
||||
}
|
||||
14
src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs
Normal file
14
src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Common;
|
||||
|
||||
public class PortTimezoneDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 城市英文名称
|
||||
/// </summary>
|
||||
public string? PortCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区
|
||||
/// </summary>
|
||||
public string Timezone { get; set; }
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class AddRuleAreaInfoRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 国家二字码,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 省份名称,可为空。
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市名称,可为空。
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 左侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? LeftPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 右侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? RightPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域ID,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int AreaId { get; set; }
|
||||
}
|
||||
29
src/SJZY.Track.Domain/Dtos/Forewarning/AreaDto.cs
Normal file
29
src/SJZY.Track.Domain/Dtos/Forewarning/AreaDto.cs
Normal file
@ -0,0 +1,29 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class AreaDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 区域id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public int SubSystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 系统名称
|
||||
/// </summary>
|
||||
public string SystemName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
public DateTime UpdateTime { get; set; }
|
||||
}
|
||||
45
src/SJZY.Track.Domain/Dtos/Forewarning/AreaInfoDto.cs
Normal file
45
src/SJZY.Track.Domain/Dtos/Forewarning/AreaInfoDto.cs
Normal file
@ -0,0 +1,45 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class RuleAreaInfoDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 分区信息id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 分区id
|
||||
/// </summary>
|
||||
public int AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区 二字码
|
||||
/// </summary>
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家名称
|
||||
/// </summary>
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家id
|
||||
/// </summary>
|
||||
public int CountryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 省份
|
||||
/// </summary>
|
||||
public string Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
public string City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始邮编
|
||||
/// </summary>
|
||||
public string LeftPostcode { get; set; }
|
||||
public string RightPostcode { get; set; }
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class CreateRuleAreaRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统id
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int SystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
131
src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningNotifiesVo.cs
Normal file
131
src/SJZY.Track.Domain/Dtos/Forewarning/GetWarningNotifiesVo.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using System.Text;
|
||||
using NUglify.Helpers;
|
||||
using SJZY.Track.Domain.Enums;
|
||||
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class GetWarningNotifiesVo
|
||||
{
|
||||
/// <summary>
|
||||
/// 规则 id
|
||||
/// </summary>
|
||||
public int RuleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警名称
|
||||
/// </summary>
|
||||
public string WarningName { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 时效(小时)
|
||||
/// </summary>
|
||||
public float TotalTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒的时间(小时)
|
||||
/// </summary>
|
||||
public float WarnTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒次数
|
||||
/// </summary>
|
||||
public int WarnCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始轨迹状态(冗余)
|
||||
/// </summary>
|
||||
public string StartTrackStatus { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 终止轨迹状态(冗余)
|
||||
/// </summary>
|
||||
public string EndTrackStatus { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型
|
||||
/// </summary>
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 月份
|
||||
/// </summary>
|
||||
public string? Months { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域
|
||||
/// </summary>
|
||||
public string? AreaName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中转
|
||||
/// </summary>
|
||||
public bool? IsTransit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起运港口
|
||||
/// </summary>
|
||||
public string? LeavePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码
|
||||
/// </summary>
|
||||
public string? ProductCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知方式
|
||||
/// </summary>
|
||||
public NotifyMode NotifyMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知模板
|
||||
/// </summary>
|
||||
public string NotifyTemplate { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 通知人
|
||||
/// </summary>
|
||||
public int NotifyUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 因素类型排序
|
||||
/// </summary>
|
||||
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<RuleFactorType>).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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class GetWarningPageInputVo: PageInfoInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public int? SystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关键字(客户or名称)
|
||||
/// </summary>
|
||||
public string? KeyWord { get; set; }
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class GetWarningRuleListInput: PageInfoInput
|
||||
{
|
||||
/// <summary>
|
||||
/// kpi 预警规则模板id
|
||||
/// </summary>
|
||||
public int RuleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序方式,不填不排序
|
||||
/// </summary>
|
||||
public string? SortField { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序方式
|
||||
/// </summary>
|
||||
public bool IsAsc { get; set; } = false;
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
31
src/SJZY.Track.Domain/Dtos/Forewarning/KpiAreaInfo.cs
Normal file
31
src/SJZY.Track.Domain/Dtos/Forewarning/KpiAreaInfo.cs
Normal file
@ -0,0 +1,31 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class KpiAreaInfo
|
||||
{
|
||||
public int AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区
|
||||
/// </summary>
|
||||
public string Country { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 省份
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始邮编
|
||||
/// </summary>
|
||||
public string? LeftPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 截止邮编
|
||||
/// </summary>
|
||||
public string? RightPostcode { get; set; }
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class QueryAreaInfoRequest: PageInfoInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 区域id
|
||||
/// </summary>
|
||||
public int AreaId { get; set; }
|
||||
}
|
||||
14
src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaRequest.cs
Normal file
14
src/SJZY.Track.Domain/Dtos/Forewarning/QueryAreaRequest.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class QueryAreaRequest:PageInfoInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 分区名称
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子系统id
|
||||
/// </summary>
|
||||
public int? SystemId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public record RuleAreaBaseInfo(int SystemId, int CustomerId);
|
||||
@ -0,0 +1,7 @@
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class RuleCollectRequest
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
}
|
||||
37
src/SJZY.Track.Domain/Dtos/Forewarning/RuleFactor.cs
Normal file
37
src/SJZY.Track.Domain/Dtos/Forewarning/RuleFactor.cs
Normal file
@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start track value.
|
||||
/// </summary>
|
||||
public int Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end track value.
|
||||
/// </summary>
|
||||
public int End { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user to notify.
|
||||
/// </summary>
|
||||
public string NotifyUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the notification template.
|
||||
/// </summary>
|
||||
public string NotifyTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置因素项模型的列表。
|
||||
/// </summary>
|
||||
public List<FactorItemModel> FactorItems { get; set; }
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
/// <summary>
|
||||
/// 更新规则区域信息请求
|
||||
/// </summary>
|
||||
public class UpdateRuleAreaInfoRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 区域信息ID
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家二字码,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家名称,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 省份名称,可为空。
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市名称,可为空。
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 左侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? LeftPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 右侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? RightPostCode { get; set; }
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class UpdateRuleAreaRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
}
|
||||
17
src/SJZY.Track.Domain/Dtos/Forewarning/WarningFactorDto.cs
Normal file
17
src/SJZY.Track.Domain/Dtos/Forewarning/WarningFactorDto.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SJZY.Track.Domain.Enums;
|
||||
|
||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
||||
|
||||
public class WarningFactorDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 因素类型
|
||||
/// </summary>
|
||||
public RuleFactorType FactorType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 因素值
|
||||
/// </summary>
|
||||
public JArray FactorValue { get; set; } = new();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user