This commit is contained in:
xiaolipro 2024-07-29 11:55:36 +08:00
parent 1513bb4964
commit 6b211d8f7f
9 changed files with 187 additions and 203 deletions

View File

@ -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; }
}

View File

@ -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>? AreaId { get; set; }
/// <summary>
/// 是否中转
/// </summary>
public bool? IsTransit { get; set; }
/// <summary>
/// 起运港口
/// </summary>
public string? LeavePort { get; set; }
/// <summary>
/// 产品编码
/// </summary>
public string? ProductCode { get; set; }
}

View File

@ -0,0 +1,8 @@
using SJZY.Track.Application.Contract.Dtos.Forewarning;
namespace SJZY.Track.Application.Contract.Services;
public interface IForewarningCacheService
{
Task<List<ForewarningRuleCacheEntry>> GetRulesAsync(int subSystemId);
}

View File

@ -13,14 +13,14 @@ public interface IForewarningNotifyService
/// </summary>
/// <param name="order">预警订单模型。</param>
/// <returns>创建操作的结果,以整数形式返回。</returns>
Task WarningCalculateAsync(List<ForewarningOrderModel> order);
Task WarningCalculateAsync(List<WarningOrderDto> order);
/// <summary>
/// 删除预警通知。
/// </summary>
/// <param name="order">预警订单模型。</param>
/// <returns>删除操作的结果,以整数形式返回。</returns>
Task<int> DeleteForewarningNotify(ForewarningOrderModel order);
Task<int> DeleteForewarningNotify(WarningOrderDto order);
/// <summary>
/// 发送通知。

View File

@ -1,32 +0,0 @@
using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
using SJZY.Track.Domain.Dtos.Forewarning;
namespace SJZY.Track.Application.Contract.Services;
/// <summary>
/// 定义了与规则因子缓存相关的服务接口。
/// </summary>
public interface IRuleFactorCacheService
{
/// <summary>
/// 获取所有因子收集的规则。
/// </summary>
/// <returns>规则数组。</returns>
Task<ForewarningRuleItem[]> GetAllFactorCollect();
/// <summary>
/// 获取所有规则因子。
/// </summary>
/// <returns>规则因子实体列表。</returns>
Task<List<RuleFactor>> GetAllRuleFactor();
/// <summary>
/// 清除因子缓存。
/// </summary>
void ClearFactorCache();
/// <summary>
/// 清除规则缓存。
/// </summary>
void ClearRuleCache();
}

View File

@ -67,67 +67,59 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
});
// 预警计算
var warningOrders = orders.Select(order => new ForewarningOrderModel
{
Id = order.Id,
TrackStatusId = @event.TrackStatusId,
SubSystemId = order.SubSystemId,
CustomerId = order.CustomId,
CargoType = order.CargoType,
Month = order.CreateTime.Month,
Area = DetermineAreaBasedOnOrderDetails(order),
IsTransit = order.IsTransit,
LeavePort = order.LeavePort,
ProductCode = order.ProductCode
}).ToList();
var warningOrders = orders.Select(order => MapWarningOrderDto(order, @event.TrackStatusId)).ToList();
_forewarningNotifyService.WarningCalculateAsync(warningOrders);
}
private OrderEntity MapOrderSummary(OrderEntity x)
private OrderEntity MapOrderSummary(OrderEntity order)
{
return new OrderEntity
{
Id = x.Id,
SubSystemId = x.SubSystemId,
CustomId = x.CustomId,
Id = order.Id,
SubSystemId = order.SubSystemId,
CustomId = order.CustomId,
// 轨迹
IsCompleted = x.IsCompleted,
LatestTrackContent = x.LatestTrackContent,
LatestTrackEnContent = x.LatestTrackEnContent,
LatestTrackPlace = x.LatestTrackPlace,
LatestTrackTime = x.LatestTrackTime,
LatestTrackTimeZone = x.LatestTrackTimeZone,
IsCompleted = order.IsCompleted,
LatestTrackContent = order.LatestTrackContent,
LatestTrackEnContent = order.LatestTrackEnContent,
LatestTrackPlace = order.LatestTrackPlace,
LatestTrackTime = order.LatestTrackTime,
LatestTrackTimeZone = order.LatestTrackTimeZone,
// 因素
CargoType = x.CargoType,
CreateTime = x.CreateTime,
ArrivalCountry = x.ArrivalCountry,
ArrivalProvince = x.ArrivalProvince,
ArrivalCity = x.ArrivalCity,
ArrivalPostCode = x.ArrivalPostCode,
IsTransit = x.IsTransit,
LeavePort = x.LeavePort,
ProductCode = x.ProductCode
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
};
}
// Placeholder methods for deriving or converting values. You'll need to implement these based on your specific logic.
private int DetermineTrackBasedOnOrderDetails(OrderEntity order)
private WarningOrderDto MapWarningOrderDto(OrderEntity order, int trackStatusId)
{
// Implement logic to determine Track
return default;
}
private int? DetermineMonthBasedOnOrderDetails(OrderEntity order)
{
// Implement logic to determine Month
return default;
}
private int? DetermineAreaBasedOnOrderDetails(OrderEntity order)
{
// Implement logic to determine Area
return default;
return new WarningOrderDto
{
Id = order.Id,
TrackStatusId = trackStatusId,
SubSystemId = order.SubSystemId,
CustomerId = order.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,
};
}
}

View File

@ -0,0 +1,13 @@
using SJZY.Track.Application.Contract.Dtos.Forewarning;
using SJZY.Track.Application.Contract.Services;
using Volo.Abp.DependencyInjection;
namespace SJZY.Track.Application.Services.ForewarningServices;
public class ForewarningCacheService: IForewarningCacheService, ISingletonDependency
{
public Task<List<ForewarningRuleCacheEntry>> GetRulesAsync(int subSystemId)
{
throw new NotImplementedException();
}
}

View File

@ -14,15 +14,13 @@ namespace SJZY.Track.Application.Services.ForewarningServices;
/// </summary>
public class ForewarningNotifyService : IForewarningNotifyService, ITransientDependency
{
private IRuleFactorCacheService _ruleFactorCache;
IReadOnlyDictionary<string, List<ForewarningRuleItem>> _ruleGroupDict;
IReadOnlyDictionary<string, List<RuleFactor>> _factorDict;
private IReadOnlyDictionary<int, ForewarningRuleItem> _rules;
readonly INotifyService _notifyService;
public ForewarningNotifyService(IRuleFactorCacheService ruleFactorCache, INotifyService notifyService)
public ForewarningNotifyService(INotifyService notifyService)
{
_ruleFactorCache = ruleFactorCache;
_notifyService = notifyService;
}
@ -31,31 +29,14 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
/// </summary>
/// <param name="order">创建预警通知需要的订单信息</param>
/// <returns>创建的通知总量,0表示没有规则</returns>
public async Task WarningCalculateAsync(List<ForewarningOrderModel> order)
public async Task WarningCalculateAsync(List<WarningOrderDto> order)
{
throw new NotImplementedException();
}
/// <summary>
/// 清除预警通知
/// </summary>
/// <param name="order">订单信息</param>
/// <returns>清理的行数</returns>
public async Task<int> DeleteForewarningNotify(ForewarningOrderModel order)
public Task<int> DeleteForewarningNotify(WarningOrderDto order)
{
var rules = GetRule(order);
if (rules == null || rules.Count == 0)
return 0;
var willDeleteMembers = new List<string>();
foreach (var rule in rules)
{
var allRuleNotities = DeleteNotifyValue(rule, order.Id);
willDeleteMembers.AddRange(allRuleNotities);
}
await RedisHelper.Client.ZRemAsync(GlobalConst.ForewarningNotifyKeyPrefix, willDeleteMembers.ToArray());
return willDeleteMembers.Count;
throw new NotImplementedException();
}
public void SendNotification(int intervalMinute)
@ -96,33 +77,6 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
return members;
}
/// <summary>
/// 获取规则
/// </summary>
/// <param name="rule"></param>
/// <returns></returns>
private List<ForewarningRuleItem> GetRule(ForewarningOrderModel order)
{
if (!_factorDict.TryGetValue(GetFactorKey(order.CustomerId, order.SubSystemId), out var factors))
{
return null;
}
var keys = GetForewarningKeys(factors, order);
var result = new List<ForewarningRuleItem>();
foreach (var item in keys)
{
if (!_ruleGroupDict.TryGetValue(item, out var rules))
continue;
var allRules = rules.Where(val => val.TrackStart <= order.TrackStatusId && val.TrackEnd >= order.TrackStatusId);
result.AddRange(allRules);
}
return result;
}
private async ValueTask<int> ExecuteRule(List<ForewarningRuleItem> rules, int orderId)
{
var members = new List<ZMember>();
@ -162,71 +116,4 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
var result = new RuleNotifyValue(ruleItem.Id, orderId, sort, ruleItem.TrackEnd);
return result.ToString();
}
private List<string> GetForewarningKeys(List<RuleFactor> factors, IForewarningFactorValue rule)
{
var keys = new List<string>();
foreach (var factor in factors)
{
var key = GetRuleKey(rule, factor);
keys.Add(key);
}
return keys;
}
private async Task Init()
{
// todo:缓存设计有问题 重构 120s 10w 30w 3000k/s
var allFactorCollect = await _ruleFactorCache.GetAllFactorCollect();
var factors = await _ruleFactorCache.GetAllRuleFactor();
var factorsMap = factors.GroupBy(val => val.RuleId)
.ToDictionary(val => val.Key, val => val.First());
_rules = allFactorCollect.ToDictionary(val => val.Id);
_factorDict = factors.GroupBy(val => GetFactorKey(val.CustomerId, val.SystemId))
.ToDictionary(val => val.Key, val => val.ToList());
_ruleGroupDict = allFactorCollect.GroupBy(x => GetRuleKey(x, factorsMap[x.RuleId]))
.ToDictionary(val => val.Key, val => val.ToList());
}
private string GetFactorKey(int customerId, int systemId)
{
return $"{customerId}:{systemId}";
}
private string GetRuleKey(IForewarningFactorValue rule, RuleFactor factors)
{
var key = "";
foreach (var factorItem in factors.FactorItems)
{
key += GetFactorValue(factorItem, rule);
}
return key;
}
private string GetFactorValue(FactorItemModel factorItem, IForewarningFactorValue rule)
{
switch (factorItem.FactorType)
{
case RuleFactorType.Areas:
return rule.Area.HasValue ? rule.Area.Value.ToString() : throw new Exception("区域不能为空");
case RuleFactorType.CargoType:
return rule.CargoType;
case RuleFactorType.LeavePort:
return rule.LeavePort;
case RuleFactorType.Months:
return rule.Month.HasValue ? rule.Month.Value.ToString() : throw new Exception("月份不能为空");
case RuleFactorType.ProductCode:
return rule.ProductCode;
case RuleFactorType.IsTransit:
return rule.IsTransit.HasValue ? "1" : "0";
// Add additional cases for other RuleFactorTypes as needed
default:
throw new Exception("未知的规则类型");
}
}
}

View File

@ -1,6 +1,6 @@
namespace SJZY.Track.Domain.Dtos.Forewarning;
public class ForewarningOrderModel:IForewarningFactorValue
public class WarningOrderDto
{
/// <summary>
/// 订单id
@ -20,8 +20,6 @@ public class ForewarningOrderModel:IForewarningFactorValue
public int? Month { get; set; }
public int? Area { get; set; }
public bool? IsTransit { get; set; }
/// <summary>
@ -30,4 +28,24 @@ public class ForewarningOrderModel:IForewarningFactorValue
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; }
}