236 lines
7.8 KiB
C#
236 lines
7.8 KiB
C#
using FreeRedis;
|
||
using SJZY.Expand.ABP.Core;
|
||
using SJZY.Track.Application.Contract.Services;
|
||
using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
|
||
using SJZY.Track.Domain.Consts;
|
||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||
using SJZY.Track.Domain.Enums;
|
||
using Volo.Abp.DependencyInjection;
|
||
|
||
namespace SJZY.Track.Application.Services.ForewarningServices;
|
||
|
||
/// <summary>
|
||
/// 用于处理预警通知
|
||
/// </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)
|
||
{
|
||
_ruleFactorCache = ruleFactorCache;
|
||
_notifyService = notifyService;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建预警通知
|
||
/// </summary>
|
||
/// <param name="order">创建预警通知需要的订单信息</param>
|
||
/// <returns>创建的通知总量,0表示没有规则</returns>
|
||
public async Task<int> CreateForewarningNotify(ForewarningOrderModel order)
|
||
{
|
||
var rules = GetRule(order);
|
||
if (rules == null || rules.Count == 0)
|
||
return 0;
|
||
|
||
return await ExecuteRule(rules, order.Id);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除预警通知
|
||
/// </summary>
|
||
/// <param name="order">订单信息</param>
|
||
/// <returns>清理的行数</returns>
|
||
public async Task<int> DeleteForewarningNotify(ForewarningOrderModel 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;
|
||
}
|
||
|
||
public void SendNotification(int intervalMinute)
|
||
{
|
||
var currentTimestamp = GetTimestamp(DateTime.Now);
|
||
var notifyMembers =
|
||
RedisHelper.Client.ZRangeByScore(GlobalConst.ForewarningNotifyKeyPrefix, 0, currentTimestamp);
|
||
if (notifyMembers == null || notifyMembers.Length == 0)
|
||
return;
|
||
|
||
var notifyValues = notifyMembers.Select(val => new RuleNotifyValue(val));
|
||
foreach (var notifyValue in notifyValues)
|
||
{
|
||
if (!_rules.TryGetValue(notifyValue.RuleId, out var rule))
|
||
continue;
|
||
|
||
//todo 发送通知
|
||
_notifyService.SendNotification("");
|
||
}
|
||
}
|
||
|
||
public void ClearCache()
|
||
{
|
||
_ruleGroupDict = null;
|
||
_factorDict = null;
|
||
_rules = null;
|
||
}
|
||
|
||
private List<string> DeleteNotifyValue(ForewarningRuleItem ruleItem, int orderId)
|
||
{
|
||
var members = new List<string>();
|
||
for (var index = 0; index < ruleItem.Count; index++)
|
||
{
|
||
var notifyValue = NotifyValue(ruleItem, orderId, index);
|
||
members.Add(notifyValue);
|
||
}
|
||
|
||
return members;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取规则
|
||
/// </summary>
|
||
/// <param name="rule"></param>
|
||
/// <returns></returns>
|
||
private List<ForewarningRuleItem> GetRule(ForewarningOrderModel order)
|
||
{
|
||
if (!_factorDict.TryGetValue(GetFactorKey(order.CustomerId, order.SystemId), 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.Track && val.TrackEnd >= order.Track);
|
||
result.AddRange(allRules);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
private string NotifyValue(ForewarningRuleItem ruleItem, int orderId, int sort)
|
||
{
|
||
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("未知的规则类型");
|
||
}
|
||
}
|
||
} |