track/src/SJZY.Track.Application/Services/ForewarningServices/ForewarningNotifyService.cs
2024-09-07 18:14:17 +08:00

258 lines
9.6 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.Expand.ABP.Core.Logger;
using SJZY.Track.Application.Contract.Dtos.Forewarning;
using SJZY.Track.Application.Contract.Services;
using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
using SJZY.Track.Domain.Dtos.Forewarning;
using SJZY.Track.Domain.Queries;
using SJZY.Track.Share.Enums;
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;
private readonly IWarningRecordEsRepository _warningRecordEsRepository;
public ForewarningNotifyService(IForewarningCacheService cacheService, IWarningQueries warningQueries,
IWarningRecordEsRepository warningRecordEsRepository)
{
_cacheService = cacheService;
_warningQueries = warningQueries;
_warningRecordEsRepository = warningRecordEsRepository;
}
/// <summary>
/// 创建预警通知
/// </summary>
/// <param name="dto">创建预警通知需要的订单信息</param>
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, 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 interval = (rule.TotalTimeSpan - rule.WarnTimeSpan) / rule.WarnCount;
var startTime = order.TrackTime.AddMinutes(rule.WarnTimeSpan);
for (var i = 0; i < rule.WarnCount; i++)
{
var timestamp = GetTimestamp(startTime);
startTime = startTime.AddMinutes(interval);
// 计算预警时间戳,作为排序依据
adds.Add((rule.Id, order.Id, timestamp));
}
});
// 删右区间
MatchRule(
rules.Where(rule => rule.EndTrackStatusId == dto.TrackStatusId).ToList(),
order
).ForEach(rule => { dels.Add((rule.Id, order.Id)); });
}
var tran = RedisHelper.Client.Multi();
// 左区间加入预警队列
await tran.ZAddAsync(GlobalConst.ForewarningNotifyKey, adds.Select(x =>
{
var entry = new WarningNotifyCacheEntry(x.ruleId, x.orderId);
return new ZMember(entry.ToString(), x.ts);
}).ToArray());
// 右区间移出预警队列
var right = dels.Select(x => new WarningNotifyCacheEntry(x.ruleId, x.orderId)).ToList();
if (!right.IsNullOrEmpty())
{
await tran.ZRemAsync(GlobalConst.ForewarningNotifyKey, right.Select(x => x.ToString()).ToArray());
await _warningRecordEsRepository.BulkUpdateAsync(dels); // 标记处理完成
}
tran.Exec();
}
public async Task SendNotificationAsync()
{
var timestamp = GetTimestamp(DateTime.Now);
var set = await RedisHelper.Client.ZRangeByScoreAsync(GlobalConst.ForewarningNotifyKey, 0, timestamp);
if (set.IsNullOrEmpty())
{
LogHelper.Info("没有运输时效需要预警");
return;
}
var entries = set.Select(x => new WarningNotifyCacheEntry(x)).ToList();
var ruleIds = entries.Select(x => x.RuleId).Distinct().ToList();
var notifyRules = await _warningQueries.GetWarningNotifyRulesAsync(ruleIds);
var userRules = new Dictionary<int, List<GetWarningNotifiesVo>>();
foreach (var rule in notifyRules)
{
foreach (var userId in rule.NotifyUserIds)
{
if (!userRules.ContainsKey(userId))
{
userRules[userId] = new List<GetWarningNotifiesVo>();
}
userRules[userId].Add(rule);
}
}
var warningRecords = new List<WarningRecordIndex>();
foreach (var entry in entries)
{
var rule = notifyRules.FirstOrDefault(x => x.RuleId == entry.RuleId);
if (rule == null) continue;
warningRecords.Add(new WarningRecordIndex
{
SubSystemId = rule.SubSystemId,
WarningName = rule.WarningName,
CustomName = rule.CustomName,
RuleId = entry.RuleId,
Rule = rule.ToString(),
OrderId = entry.OrderId,
WarningStatus = WarningStatus.UnProcessed,
NotifyUser = rule.NotifyUser,
CreateTime = DateTime.Now,
});
}
await _warningRecordEsRepository.BulkInsertAsync(warningRecords);
foreach (var userRule in userRules)
{
var siteInternalContent = BuildNotifyContent(userRule.Value, entries, NotifyMode.SiteInternal);
var feiShuContent = BuildNotifyContent(userRule.Value, entries, NotifyMode.FeiShu);
if (!string.IsNullOrEmpty(siteInternalContent))
{
// todo:发送站内通知
}
if (!string.IsNullOrEmpty(feiShuContent))
{
// todo:发送飞书通知
}
}
}
private string BuildNotifyContent(List<GetWarningNotifiesVo> rules, List<WarningNotifyCacheEntry> entries,
NotifyMode mode)
{
/* 以人的维度发送通知
*
预警名称:字节全程时效
预警规则:
{起飞-落地+普货+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
*/
var notifyContent = new StringBuilder();
foreach (var warningGroup in rules.Where(x => x.NotifyModes.Contains(mode))
.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);
notifyContent.AppendLine(
$"{rule.ToString()} 命中订单总数:{cnt},详情点击https://middlebusiness.pet.shijizhongyun.com/trajectoryCenter/trajectoryOrder");
}
}
return notifyContent.ToString();
}
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();
}
}