Merge remote-tracking branch 'origin/S04轨迹预警' into S04轨迹预警
This commit is contained in:
commit
d00df6745b
@ -50,7 +50,7 @@ public class ForewarningRuleCacheEntry
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 区域
|
/// 区域
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<AreaCacheEntry>? AreaId { get; set; }
|
public AreaCacheEntry? Area { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否中转
|
/// 是否中转
|
||||||
|
|||||||
@ -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 OrderId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规则id
|
||||||
|
/// </summary>
|
||||||
|
public int RuleId { get; set; }
|
||||||
|
|
||||||
|
public WarningNotifyCacheEntry(int orderId, int ruleId)
|
||||||
|
{
|
||||||
|
RuleId = ruleId;
|
||||||
|
OrderId = orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WarningNotifyCacheEntry(string notifyValue)
|
||||||
|
{
|
||||||
|
var values = notifyValue.Split(':');
|
||||||
|
OrderId = int.Parse(values[0]);
|
||||||
|
RuleId = int.Parse(values[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{OrderId}:{RuleId}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||||
|
|
||||||
public class WarningOrderDto
|
public class WarningOrderDto
|
||||||
{
|
{
|
||||||
@ -7,18 +7,9 @@ public class WarningOrderDto
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前轨迹状态
|
|
||||||
/// </summary>
|
|
||||||
public int TrackStatusId { get; set; }
|
|
||||||
|
|
||||||
public int SubSystemId { get; set; }
|
|
||||||
|
|
||||||
public int CustomerId { get; set; }
|
|
||||||
|
|
||||||
public string? CargoType { get; set; }
|
public string? CargoType { get; set; }
|
||||||
|
|
||||||
public int? Month { get; set; }
|
public int Month { get; set; }
|
||||||
|
|
||||||
public bool? IsTransit { get; set; }
|
public bool? IsTransit { get; set; }
|
||||||
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
using SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
using SJZY.Track.Domain.Dtos.Forewarning;
|
||||||
|
|
||||||
namespace SJZY.Track.Application.Contract.Services;
|
namespace SJZY.Track.Application.Contract.Services;
|
||||||
@ -13,14 +14,7 @@ public interface IForewarningNotifyService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="order">预警订单模型。</param>
|
/// <param name="order">预警订单模型。</param>
|
||||||
/// <returns>创建操作的结果,以整数形式返回。</returns>
|
/// <returns>创建操作的结果,以整数形式返回。</returns>
|
||||||
Task WarningCalculateAsync(List<WarningOrderDto> order);
|
Task WarningCalculateAsync(WarningDto order);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 删除预警通知。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="order">预警订单模型。</param>
|
|
||||||
/// <returns>删除操作的结果,以整数形式返回。</returns>
|
|
||||||
Task<int> DeleteForewarningNotify(WarningOrderDto order);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送通知。
|
/// 发送通知。
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using DotNetCore.CAP;
|
using DotNetCore.CAP;
|
||||||
|
using SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||||
using SJZY.Track.Application.Contract.Events;
|
using SJZY.Track.Application.Contract.Events;
|
||||||
using SJZY.Track.Application.Contract.Services;
|
using SJZY.Track.Application.Contract.Services;
|
||||||
using SJZY.Track.Domain.Aggregates.OrderAggregate;
|
using SJZY.Track.Domain.Aggregates.OrderAggregate;
|
||||||
@ -67,9 +68,32 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 预警计算
|
// 预警计算
|
||||||
var warningOrders = orders.Select(order => MapWarningOrderDto(order, @event)).ToList();
|
await _forewarningNotifyService.WarningCalculateAsync( BuiltWarningDto(orders, @event));
|
||||||
|
}
|
||||||
|
|
||||||
_forewarningNotifyService.WarningCalculateAsync(warningOrders);
|
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,
|
||||||
|
|
||||||
|
CargoType = order.CargoType,
|
||||||
|
Month = order.CreateTime.Month,
|
||||||
|
IsTransit = order.IsTransit,
|
||||||
|
LeavePort = order.LeavePort,
|
||||||
|
ProductCode = order.ProductCode,
|
||||||
|
|
||||||
|
ArrivalCountry = order.ArrivalCountry,
|
||||||
|
ArrivalProvince = order.ArrivalProvince,
|
||||||
|
ArrivalCity = order.ArrivalCity,
|
||||||
|
ArrivalPostCode = order.ArrivalPostCode,
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private OrderEntity MapOrderSummary(OrderEntity order)
|
private OrderEntity MapOrderSummary(OrderEntity order)
|
||||||
@ -98,26 +122,4 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
|
|||||||
ProductCode = order.ProductCode
|
ProductCode = order.ProductCode
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private WarningOrderDto MapWarningOrderDto(OrderEntity order, TrackChangeAfterUpdateOrderEvent @event)
|
|
||||||
{
|
|
||||||
return new WarningOrderDto
|
|
||||||
{
|
|
||||||
Id = order.Id,
|
|
||||||
TrackStatusId = @event.TrackStatusId,
|
|
||||||
SubSystemId = @event.SubSystemId,
|
|
||||||
CustomerId = @event.CustomId,
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
using FreeRedis;
|
using FreeRedis;
|
||||||
using SJZY.Expand.ABP.Core;
|
using SJZY.Expand.ABP.Core;
|
||||||
|
using SJZY.Track.Application.Contract.Dtos.Forewarning;
|
||||||
using SJZY.Track.Application.Contract.Services;
|
using SJZY.Track.Application.Contract.Services;
|
||||||
using SJZY.Track.Domain.Consts;
|
using SJZY.Track.Domain.Consts;
|
||||||
using SJZY.Track.Domain.Dtos.Forewarning;
|
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
|
|
||||||
namespace SJZY.Track.Application.Services.ForewarningServices;
|
namespace SJZY.Track.Application.Services.ForewarningServices;
|
||||||
@ -18,84 +18,122 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
|
|||||||
{
|
{
|
||||||
_cacheService = cacheService;
|
_cacheService = cacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建预警通知
|
/// 创建预警通知
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="order">创建预警通知需要的订单信息</param>
|
/// <param name="dto">创建预警通知需要的订单信息</param>
|
||||||
/// <returns>创建的通知总量,0表示没有规则</returns>
|
/// <returns>创建的通知总量,0表示没有规则</returns>
|
||||||
public async Task WarningCalculateAsync(List<WarningOrderDto> order)
|
public async Task WarningCalculateAsync(WarningDto dto)
|
||||||
{
|
{
|
||||||
var rules = await _cacheService.GetRulesAsync(order[0].SubSystemId, order[0].CustomerId);
|
if (dto.Orders.Count == 0) return;
|
||||||
|
|
||||||
|
var rules = await _cacheService.GetRulesAsync(dto.SubSystemId, dto.CustomerId);
|
||||||
|
if (rules == null || rules.Count == 0) return;
|
||||||
|
|
||||||
|
var zMembers = new List<ZMember>();
|
||||||
|
var members = new List<string>();
|
||||||
|
foreach (var order in dto.Orders)
|
||||||
|
{
|
||||||
|
MatchRule(
|
||||||
|
rules.Where(rule => rule.StartTrackStatusId == dto.TrackStatusId).ToList(),
|
||||||
|
order
|
||||||
|
).ForEach(rule =>
|
||||||
|
{
|
||||||
|
var notifyTimestamp = GetTimestamp(DateTime.Now.AddMinutes(rule.WarnTimeSpan));
|
||||||
|
var entry = new WarningNotifyCacheEntry(rule.Id, order.Id);
|
||||||
|
var notifyValue = new ZMember(entry.ToString(), notifyTimestamp);
|
||||||
|
zMembers.Add(notifyValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
var endingRules = MatchRule(
|
||||||
|
rules.Where(rule => rule.EndTrackStatusId == dto.TrackStatusId).ToList(),
|
||||||
|
order
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var rule in endingRules)
|
||||||
|
{
|
||||||
|
var entry = new WarningNotifyCacheEntry(rule.Id, order.Id);
|
||||||
|
var member = entry.ToString();
|
||||||
|
// todo: pipeline optimize?
|
||||||
|
var score = await RedisHelper.Client.ZScoreAsync(GlobalConst.ForewarningNotifyKey, member);
|
||||||
|
// tips: score is null 说明发生ending时,没有匹配到规则,所以没有add到set,不需要删除
|
||||||
|
if (score != null && (long)score > GetTimestamp(DateTime.Now))
|
||||||
|
{
|
||||||
|
members.Add(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await RedisHelper.Client.ZAddAsync(GlobalConst.ForewarningNotifyKey, zMembers.ToArray());
|
||||||
|
await RedisHelper.Client.ZRemAsync(GlobalConst.ForewarningNotifyKey, members.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> DeleteForewarningNotify(WarningOrderDto order)
|
private static List<ForewarningRuleCacheEntry> MatchRule(List<ForewarningRuleCacheEntry> rules,
|
||||||
|
WarningOrderDto order)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
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.Area != null)
|
||||||
|
{
|
||||||
|
if (order.ArrivalCountry != rule.Area.Country) continue;
|
||||||
|
if (!rule.Area.LeftPostcode.IsNullOrEmpty() && !rule.Area.RightPostcode.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
// todo: 参考小包的邮编匹配
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rule.Area.City.IsNullOrEmpty() && order.ArrivalCity != rule.Area.City) continue;
|
||||||
|
if (!rule.Area.Province.IsNullOrEmpty() && order.ArrivalProvince != rule.Area.Province) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Add(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendNotification(int intervalMinute)
|
public void SendNotification(int intervalMinute)
|
||||||
{
|
{
|
||||||
var currentTimestamp = GetTimestamp(DateTime.Now);
|
var currentTimestamp = GetTimestamp(DateTime.Now);
|
||||||
var notifyMembers =
|
var notifyMembers =
|
||||||
RedisHelper.Client.ZRangeByScore(GlobalConst.ForewarningNotifyKeyPrefix, 0, currentTimestamp);
|
RedisHelper.Client.ZRangeByScore(GlobalConst.ForewarningNotifyKey, 0, currentTimestamp);
|
||||||
if (notifyMembers == null || notifyMembers.Length == 0)
|
if (notifyMembers == null || notifyMembers.Length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var notifyValues = notifyMembers.Select(val => new RuleNotifyValue(val));
|
var notifyValues = notifyMembers.Select(val => new WarningNotifyCacheEntry(val));
|
||||||
foreach (var notifyValue in notifyValues)
|
foreach (var notifyValue in notifyValues)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> DeleteNotifyValue(ForewarningRuleItem ruleItem, int orderId)
|
private static long GetTimestamp(DateTime dateTime)
|
||||||
{
|
|
||||||
var members = new List<string>();
|
|
||||||
for (var index = 0; index < ruleItem.Count; index++)
|
|
||||||
{
|
|
||||||
var notifyValue = NotifyValue(ruleItem, orderId, index);
|
|
||||||
members.Add(notifyValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ValueTask<int> ExecuteRule(List<ForewarningRuleItem> rules, int orderId)
|
|
||||||
{
|
|
||||||
var members = new List<ZMember>();
|
|
||||||
foreach (var rule in rules)
|
|
||||||
{
|
|
||||||
var notifys = ExecuteRule(rule, orderId);
|
|
||||||
members.AddRange(notifys);
|
|
||||||
}
|
|
||||||
|
|
||||||
await RedisHelper.Client.ZAddAsync(GlobalConst.ForewarningNotifyKeyPrefix, members.ToArray());
|
|
||||||
return members.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ZMember> ExecuteRule(ForewarningRuleItem ruleItem, int orderId)
|
|
||||||
{
|
|
||||||
var members = new List<ZMember>();
|
|
||||||
var intervalMinute = 60 * ((ruleItem.Timeliness - ruleItem.Remind) / ruleItem.Count); // 间隔多少分钟
|
|
||||||
|
|
||||||
for (var index = 0; index < ruleItem.Count; index++)
|
|
||||||
{
|
|
||||||
var notifyTime = (ruleItem.Remind * 60) + intervalMinute * index;
|
|
||||||
var notifyTimestamp = GetTimestamp(DateTime.Now.AddMinutes(notifyTime));
|
|
||||||
var notifyValue = new ZMember(NotifyValue(ruleItem, orderId, index), notifyTimestamp);
|
|
||||||
members.Add(notifyValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long GetTimestamp(DateTime dateTime)
|
|
||||||
{
|
{
|
||||||
return new DateTimeOffset(dateTime).ToUnixTimeSeconds();
|
return new DateTimeOffset(dateTime).ToUnixTimeSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string NotifyValue(ForewarningRuleItem ruleItem, int orderId, int sort)
|
|
||||||
{
|
|
||||||
var result = new RuleNotifyValue(ruleItem.Id, orderId, sort, ruleItem.TrackEnd);
|
|
||||||
return result.ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -22,11 +22,10 @@ public partial class GlobalConst
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string TrackPullContentKeyPrefix = "TrackPullContent";
|
public const string TrackPullContentKeyPrefix = "TrackPullContent";
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 预警通知redis key前缀
|
/// 预警通知redis key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ForewarningNotifyKeyPrefix = "ForewarningNotity";
|
public const string ForewarningNotifyKey = "ForewarningNotify";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// kpi区域redis key前缀
|
/// kpi区域redis key前缀
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
|
||||||
|
|
||||||
public record struct ForewarningRuleItem:IForewarningFactorValue
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public int CustomerId { get; set; }
|
|
||||||
|
|
||||||
public int SystemId { get; set; }
|
|
||||||
public int RuleId { get; set; }
|
|
||||||
public float Remind { get; set; }
|
|
||||||
public float Timeliness { get; set; }
|
|
||||||
public int Count { get; set; }
|
|
||||||
public int TrackStart { get; set; }
|
|
||||||
public int TrackEnd { get; set; }
|
|
||||||
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; }
|
|
||||||
|
|
||||||
public string Conect { get; set; }
|
|
||||||
|
|
||||||
public string Template { get; set; }
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
namespace SJZY.Track.Domain.Dtos.Forewarning;
|
|
||||||
|
|
||||||
public class RuleNotifyValue
|
|
||||||
{
|
|
||||||
public int RuleId { get; set; }
|
|
||||||
|
|
||||||
public int OrderId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 第几次通知
|
|
||||||
/// </summary>
|
|
||||||
public int Count { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 结束轨迹状态
|
|
||||||
/// </summary>
|
|
||||||
public int TrackEnd { get; set; }
|
|
||||||
|
|
||||||
public RuleNotifyValue(int ruleId, int orderId, int count, int trackEnd)
|
|
||||||
{
|
|
||||||
RuleId = ruleId;
|
|
||||||
OrderId = orderId;
|
|
||||||
Count = count;
|
|
||||||
TrackEnd = trackEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RuleNotifyValue(string notifyValue)
|
|
||||||
{
|
|
||||||
var values = notifyValue.Split(':');
|
|
||||||
RuleId = int.Parse(values[0]);
|
|
||||||
OrderId = int.Parse(values[1]);
|
|
||||||
Count = int.Parse(values[2]);
|
|
||||||
TrackEnd = int.Parse(values[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{RuleId}:{OrderId}:{Count}:{TrackEnd}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user