track/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs
2024-08-05 15:43:04 +08:00

190 lines
7.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.Aggregates.ForewarningAggregate;
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;
/// <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: "轨迹预警计算失败");
}
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 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;
}
private static long GetTimestamp(DateTime dateTime)
{
return new DateTimeOffset(dateTime).ToUnixTimeSeconds();
}
}