From 94778ef228482c74aad6fcd071dfc0da47abfc98 Mon Sep 17 00:00:00 2001
From: xiaolipro <2357729423@qq.com>
Date: Mon, 29 Jul 2024 18:10:48 +0800
Subject: [PATCH] =?UTF-8?q?add=EF=BC=9A=E9=A2=84=E8=AD=A6=E8=AE=A1?=
=?UTF-8?q?=E7=AE=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Forewarning/ForewarningRuleCacheEntry.cs | 2 +-
.../Dtos/Forewarning/WarningDto.cs | 17 ++
.../Forewarning/WarningNotifyCacheEntry.cs | 32 ++++
.../Dtos/Forewarning/WarningOrderDto.cs | 13 +-
.../Services/IForewarningNotifyService.cs | 10 +-
...TrackChangeAfterUpdateOrderEventHandler.cs | 60 +++----
.../ForewarningNotifyService.cs | 150 +++++++++++-------
.../Consts/GlobalConst.Redis.cs | 5 +-
.../Dtos/Forewarning/ForewarningRuleItem.cs | 26 ---
.../Dtos/Forewarning/RuleNotifyValue.cs | 40 -----
10 files changed, 181 insertions(+), 174 deletions(-)
create mode 100644 src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningDto.cs
create mode 100644 src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningNotifyCacheEntry.cs
rename src/{SJZY.Track.Domain => SJZY.Track.Application.Contract}/Dtos/Forewarning/WarningOrderDto.cs (74%)
delete mode 100644 src/SJZY.Track.Domain/Dtos/Forewarning/ForewarningRuleItem.cs
delete mode 100644 src/SJZY.Track.Domain/Dtos/Forewarning/RuleNotifyValue.cs
diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ForewarningRuleCacheEntry.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ForewarningRuleCacheEntry.cs
index 09ab3e1d..71d402b6 100644
--- a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ForewarningRuleCacheEntry.cs
+++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/ForewarningRuleCacheEntry.cs
@@ -50,7 +50,7 @@ public class ForewarningRuleCacheEntry
///
/// 区域
///
- public List? AreaId { get; set; }
+ public AreaCacheEntry? Area { get; set; }
///
/// 是否中转
diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningDto.cs
new file mode 100644
index 00000000..f911768c
--- /dev/null
+++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningDto.cs
@@ -0,0 +1,17 @@
+using SJZY.Track.Domain.Dtos.Forewarning;
+
+namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
+
+public class WarningDto
+{
+ ///
+ /// 当前轨迹状态
+ ///
+ public int TrackStatusId { get; set; }
+
+ public int SubSystemId { get; set; }
+
+ public int CustomerId { get; set; }
+
+ public List Orders { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningNotifyCacheEntry.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningNotifyCacheEntry.cs
new file mode 100644
index 00000000..71112bb8
--- /dev/null
+++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningNotifyCacheEntry.cs
@@ -0,0 +1,32 @@
+namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
+
+public class WarningNotifyCacheEntry
+{
+ ///
+ /// 订单id
+ ///
+ public int OrderId { get; set; }
+
+ ///
+ /// 规则id
+ ///
+ 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}";
+ }
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/WarningOrderDto.cs b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningOrderDto.cs
similarity index 74%
rename from src/SJZY.Track.Domain/Dtos/Forewarning/WarningOrderDto.cs
rename to src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningOrderDto.cs
index 4cdc5a91..c9a395b9 100644
--- a/src/SJZY.Track.Domain/Dtos/Forewarning/WarningOrderDto.cs
+++ b/src/SJZY.Track.Application.Contract/Dtos/Forewarning/WarningOrderDto.cs
@@ -1,4 +1,4 @@
-namespace SJZY.Track.Domain.Dtos.Forewarning;
+namespace SJZY.Track.Application.Contract.Dtos.Forewarning;
public class WarningOrderDto
{
@@ -7,18 +7,9 @@ public class WarningOrderDto
///
public int Id { get; set; }
- ///
- /// 当前轨迹状态
- ///
- public int TrackStatusId { get; set; }
-
- public int SubSystemId { get; set; }
-
- public int CustomerId { get; set; }
-
public string? CargoType { get; set; }
- public int? Month { get; set; }
+ public int Month { get; set; }
public bool? IsTransit { get; set; }
diff --git a/src/SJZY.Track.Application.Contract/Services/IForewarningNotifyService.cs b/src/SJZY.Track.Application.Contract/Services/IForewarningNotifyService.cs
index 8830f610..2709a84c 100644
--- a/src/SJZY.Track.Application.Contract/Services/IForewarningNotifyService.cs
+++ b/src/SJZY.Track.Application.Contract/Services/IForewarningNotifyService.cs
@@ -1,3 +1,4 @@
+using SJZY.Track.Application.Contract.Dtos.Forewarning;
using SJZY.Track.Domain.Dtos.Forewarning;
namespace SJZY.Track.Application.Contract.Services;
@@ -13,14 +14,7 @@ public interface IForewarningNotifyService
///
/// 预警订单模型。
/// 创建操作的结果,以整数形式返回。
- Task WarningCalculateAsync(List order);
-
- ///
- /// 删除预警通知。
- ///
- /// 预警订单模型。
- /// 删除操作的结果,以整数形式返回。
- Task DeleteForewarningNotify(WarningOrderDto order);
+ Task WarningCalculateAsync(WarningDto order);
///
/// 发送通知。
diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs
index 52f1d535..3e96a3b5 100644
--- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs
+++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs
@@ -1,4 +1,5 @@
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;
@@ -28,7 +29,7 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
if (orders.Count == 0) return;
var updateOrders = new List();
-
+
foreach (var order in orders)
{
if (order.LatestTrackContent != @event.TrackContent)
@@ -65,11 +66,34 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
x.IsCompleted, x.LatestTrackContent, x.LatestTrackEnContent, x.LatestTrackPlace, x.LatestTrackTime,
x.LatestTrackTimeZone
});
-
+
// 预警计算
- var warningOrders = orders.Select(order => MapWarningOrderDto(order, @event)).ToList();
-
- _forewarningNotifyService.WarningCalculateAsync(warningOrders);
+ await _forewarningNotifyService.WarningCalculateAsync( BuiltWarningDto(orders, @event));
+ }
+
+ private WarningDto BuiltWarningDto(List orders, TrackChangeAfterUpdateOrderEvent @event)
+ {
+ return new WarningDto
+ {
+ TrackStatusId = @event.TrackStatusId,
+ SubSystemId = @event.SubSystemId,
+ CustomerId = @event.CustomId,
+ Orders = orders.Select(order => new WarningOrderDto
+ {
+ Id = order.Id,
+
+ 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)
@@ -77,7 +101,7 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
return new OrderEntity
{
Id = order.Id,
-
+
// 轨迹
IsCompleted = order.IsCompleted,
LatestTrackContent = order.LatestTrackContent,
@@ -85,7 +109,7 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
LatestTrackPlace = order.LatestTrackPlace,
LatestTrackTime = order.LatestTrackTime,
LatestTrackTimeZone = order.LatestTrackTimeZone,
-
+
// 因素
CargoType = order.CargoType,
CreateTime = order.CreateTime,
@@ -98,26 +122,4 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
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,
- };
- }
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs
index f438a9cd..75f44fec 100644
--- a/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs
+++ b/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs
@@ -1,8 +1,8 @@
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.Dtos.Forewarning;
using Volo.Abp.DependencyInjection;
namespace SJZY.Track.Application.Services.ForewarningServices;
@@ -18,84 +18,122 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
{
_cacheService = cacheService;
}
+
///
/// 创建预警通知
///
- /// 创建预警通知需要的订单信息
+ /// 创建预警通知需要的订单信息
/// 创建的通知总量,0表示没有规则
- public async Task WarningCalculateAsync(List 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();
+ var members = new List();
+ 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 DeleteForewarningNotify(WarningOrderDto order)
+ private static List MatchRule(List rules,
+ WarningOrderDto order)
{
- throw new NotImplementedException();
+ var res = new List();
+ foreach (var rule in rules)
+ {
+ if (rule.IsTransit != null)
+ {
+ if (order.IsTransit != rule.IsTransit) continue;
+ }
+
+ if (!rule.CargoType.IsNullOrEmpty())
+ {
+ if (order.CargoType != rule.CargoType) continue;
+ }
+
+ if (!rule.LeavePort.IsNullOrEmpty())
+ {
+ if (order.LeavePort != rule.LeavePort) continue;
+ }
+
+ if (!rule.ProductCode.IsNullOrEmpty())
+ {
+ if (order.ProductCode != rule.ProductCode) continue;
+ }
+
+ if (!rule.Months.IsNullOrEmpty())
+ {
+ if (!order.Month.IsIn(rule.Months)) continue;
+ }
+
+ if (rule.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)
{
var currentTimestamp = GetTimestamp(DateTime.Now);
var notifyMembers =
- RedisHelper.Client.ZRangeByScore(GlobalConst.ForewarningNotifyKeyPrefix, 0, currentTimestamp);
+ RedisHelper.Client.ZRangeByScore(GlobalConst.ForewarningNotifyKey, 0, currentTimestamp);
if (notifyMembers == null || notifyMembers.Length == 0)
return;
- var notifyValues = notifyMembers.Select(val => new RuleNotifyValue(val));
+ var notifyValues = notifyMembers.Select(val => new WarningNotifyCacheEntry(val));
foreach (var notifyValue in notifyValues)
{
}
}
- private List DeleteNotifyValue(ForewarningRuleItem ruleItem, int orderId)
- {
- var members = new List();
- for (var index = 0; index < ruleItem.Count; index++)
- {
- var notifyValue = NotifyValue(ruleItem, orderId, index);
- members.Add(notifyValue);
- }
-
- return members;
- }
-
- private async ValueTask ExecuteRule(List rules, int orderId)
- {
- var members = new List();
- 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 ExecuteRule(ForewarningRuleItem ruleItem, int orderId)
- {
- var members = new List();
- 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)
+ private static long GetTimestamp(DateTime dateTime)
{
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();
- }
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs
index 376af72f..9b29d958 100644
--- a/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs
+++ b/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs
@@ -22,11 +22,10 @@ public partial class GlobalConst
///
public const string TrackPullContentKeyPrefix = "TrackPullContent";
-
///
- /// 预警通知redis key前缀
+ /// 预警通知redis key
///
- public const string ForewarningNotifyKeyPrefix = "ForewarningNotity";
+ public const string ForewarningNotifyKey = "ForewarningNotify";
///
/// kpi区域redis key前缀
diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/ForewarningRuleItem.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/ForewarningRuleItem.cs
deleted file mode 100644
index 9e03f6c2..00000000
--- a/src/SJZY.Track.Domain/Dtos/Forewarning/ForewarningRuleItem.cs
+++ /dev/null
@@ -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; }
-}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Dtos/Forewarning/RuleNotifyValue.cs b/src/SJZY.Track.Domain/Dtos/Forewarning/RuleNotifyValue.cs
deleted file mode 100644
index eda39cc7..00000000
--- a/src/SJZY.Track.Domain/Dtos/Forewarning/RuleNotifyValue.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace SJZY.Track.Domain.Dtos.Forewarning;
-
-public class RuleNotifyValue
-{
- public int RuleId { get; set; }
-
- public int OrderId { get; set; }
-
- ///
- /// 第几次通知
- ///
- public int Count { get; set; }
-
- ///
- /// 结束轨迹状态
- ///
- 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}";
- }
-}
\ No newline at end of file