This commit is contained in:
xiaolipro 2024-09-07 18:14:17 +08:00
parent 8bf76305fe
commit b39d84655d
38 changed files with 621 additions and 180 deletions

View File

@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=dels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SJZY/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -20,7 +20,7 @@ public class EditWarningOutput
public int CustomerId { get; set; }
/// <summary>
/// 通知列表
/// 轨迹状态列表
/// </summary>
public List<WarningNotifyWithIdDto> TrackStatusSpans { get; set; } = new();

View File

@ -29,6 +29,11 @@ public class WarningNotifyDto
/// </summary>
public List<int> NotifyUserIds { get; set; }
/// <summary>
/// 通知人
/// </summary>
public List<string> NotifyUsers { get; set; }
/// <summary>
/// 通知方式
/// </summary>

View File

@ -10,7 +10,7 @@ public class WarningNotifyCacheEntry
public WarningNotifyCacheEntry(string notifyValue)
{
var values = notifyValue.Split(':');
var values = notifyValue.Split('_');
RuleId = int.Parse(values[0]);
OrderId = int.Parse(values[1]);
}
@ -27,6 +27,6 @@ public class WarningNotifyCacheEntry
public override string ToString()
{
return $"{RuleId}:{OrderId}";
return $"{RuleId}_{OrderId}";
}
}

View File

@ -1,9 +1,9 @@
using DotNetCore.CAP;
using SJZY.Expand.ABP.Core.Logger;
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;
using SJZY.Track.Domain.Queries;
using Volo.Abp.DependencyInjection;
namespace SJZY.Track.Application.CAP.EventHandlers;
@ -12,17 +12,12 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
{
private readonly IForewarningNotifyService _forewarningNotifyService;
private readonly IOrderRepository _orderRepository;
private readonly IProvideQueries _provideQueries;
private readonly ITrackPullCacheService _trackPullCacheService;
public TrackChangeAfterUpdateOrderEventHandler(IOrderRepository orderRepository,
IForewarningNotifyService forewarningNotifyService, ITrackPullCacheService trackPullCacheService,
IProvideQueries provideQueries)
IForewarningNotifyService forewarningNotifyService)
{
_orderRepository = orderRepository;
_forewarningNotifyService = forewarningNotifyService;
_trackPullCacheService = trackPullCacheService;
_provideQueries = provideQueries;
}
[CapSubscribe(GlobalConst.TrackChangeAfterUpdateOrderSubscribe)]
@ -30,7 +25,11 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
{
var orders = await GetOrdersAsync(@event.OrderIds);
if (orders.Count == 0) return;
if (orders.Count == 0)
{
LogHelper.Warn("没有找到订单信息,无法更新轨迹信息");
return;
}
var updateOrders = new List<OrderEntity>();
foreach (var order in orders)
@ -46,7 +45,7 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
}
// 执行轨迹兜底时 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
if (@event.DeficiencyTagValue > 0 && @event.DeficiencyTagValue <= 3)
if (@event.DeficiencyTagValue is > 0 and <= 3)
{
order.DeficiencyTag = @event.DeficiencyTagValue;
}

View File

@ -14,15 +14,16 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
private readonly ICapPublisher _capPublisher;
private readonly IOrderRepository _orderRepository;
private readonly IProvideQueries _provideQueries;
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
public TrackDeleteEventHandler(IProvideQueries provideQueries,
IOrderRepository orderRepository, ICapPublisher capPublisher, ITrackDetailsRepository trackDetailsRepository)
IOrderRepository orderRepository, ICapPublisher capPublisher,
ITrackDetailsEsRepository trackDetailsEsRepository)
{
_provideQueries = provideQueries;
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_trackDetailsRepository = trackDetailsRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
}
[CapSubscribe(GlobalConst.TrackDeleteSubscribe, Group = GlobalConst.TrackDeleteSubscribe)]
@ -104,9 +105,9 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
private async Task DeleteTrackAsync(TrackDeleteEvent @event)
{
// 删除轨迹(删的是同子系统下的单,所以code是统一的)
var tracks = await _trackDetailsRepository.GetLatestListAsync(@event.OrderIds, @event.TrackCodes);
var tracks = await _trackDetailsEsRepository.GetLatestListAsync(@event.OrderIds, @event.TrackCodes);
await _trackDetailsRepository.DeleteTrackAsync(@event.OrderIds, @event.TrackCodes);
await _trackDetailsEsRepository.DeleteTrackAsync(@event.OrderIds, @event.TrackCodes);
var orders = await _orderRepository.GetListAsync(x => @event.OrderIds.Contains(x.Id),
x => new OrderEntity

View File

@ -13,12 +13,12 @@ namespace SJZY.Track.Application.CAP.EventHandlers;
public class TrackPushCompletedEventHandler : ITransientDependency, ICapSubscribe
{
private readonly IOrderRepository _orderRepository;
private readonly ITrackPushRecordsRepository _trackPushRecordsRepository;
private readonly ITrackPushRecordEsRepository _trackPushRecordEsRepository;
public TrackPushCompletedEventHandler(ITrackPushRecordsRepository trackPushRecordsRepository,
public TrackPushCompletedEventHandler(ITrackPushRecordEsRepository trackPushRecordEsRepository,
IOrderRepository orderRepository)
{
_trackPushRecordsRepository = trackPushRecordsRepository;
_trackPushRecordEsRepository = trackPushRecordEsRepository;
_orderRepository = orderRepository;
}
@ -45,7 +45,7 @@ public class TrackPushCompletedEventHandler : ITransientDependency, ICapSubscrib
log.RequestId = @event.RequestId;
// 往es 写推送日志
await _trackPushRecordsRepository.AddAsync(log);
await _trackPushRecordEsRepository.AddAsync(log);
// if (!log.IsSuccess)
// {

View File

@ -20,21 +20,21 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
{
private readonly IHttpClientProxy<IInterfaceForwardServiceV2> _httpClientProxy;
private readonly IOrderRepository _orderRepository;
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
private readonly ITrackPushCacheService _trackPushCacheService;
private readonly ITrackPushRecordsRepository _trackPushRecordsRepository;
private readonly ITrackPushRecordEsRepository _trackPushRecordEsRepository;
private List<OrderEntity> _orders = null!;
private List<TrackPushContentConfigDto> _pushConfigs = null!;
public TrackPushEventHandler(IHttpClientProxy<IInterfaceForwardServiceV2> httpClientProxy,
IOrderRepository orderRepository, ITrackPushCacheService trackPushCacheService,
ITrackDetailsRepository trackDetailsRepository, ITrackPushRecordsRepository trackPushRecordsRepository)
ITrackDetailsEsRepository trackDetailsEsRepository, ITrackPushRecordEsRepository trackPushRecordEsRepository)
{
_httpClientProxy = httpClientProxy;
_orderRepository = orderRepository;
_trackPushCacheService = trackPushCacheService;
_trackDetailsRepository = trackDetailsRepository;
_trackPushRecordsRepository = trackPushRecordsRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
_trackPushRecordEsRepository = trackPushRecordEsRepository;
}
[CapSubscribe(GlobalConst.TrackPushSubscribe, Group = GlobalConst.TrackPushSubscribe)]
@ -113,7 +113,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
config = _pushConfigs.FirstOrDefault(x => x.TrackCode == @event.TrackCode);
if (config == null) return;
var tracks = await _trackDetailsRepository.GetListAsync(@event.OrderIds, @event.TrackCode);
var tracks = await _trackDetailsEsRepository.GetListAsync(@event.OrderIds, @event.TrackCode);
tracks = tracks.DistinctBy(x => x.OrderId).ToList(); // 防止重复推送
if (tracks.Count == 0) return; // 一个能推的轨迹都不存在
@ -256,7 +256,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
if (config.IsAllPush)
{
var history = await _trackPushRecordsRepository.GetLatestTrackPushRecordAsync(log.OrderId);
var history = await _trackPushRecordEsRepository.GetLatestTrackPushRecordAsync(log.OrderId);
if (history != default)
{
body = JsonConvert.DeserializeObject<TrackPushInput>(history.PushContent);

View File

@ -15,33 +15,33 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
private readonly ICapPublisher _capPublisher;
private readonly IOrderRepository _orderRepository;
private readonly IProvideQueries _provideQueries;
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
public TrackUpsertEventHandler(IOrderRepository orderRepository, ICapPublisher capPublisher,
IProvideQueries provideQueries, ITrackDetailsRepository trackDetailsRepository)
IProvideQueries provideQueries, ITrackDetailsEsRepository trackDetailsEsRepository)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_provideQueries = provideQueries;
_trackDetailsRepository = trackDetailsRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
}
[CapSubscribe(GlobalConst.TrackUpsertSubscribe, Group = GlobalConst.TrackUpsertSubscribe)]
[CapSubscribe(GlobalConst.TrackUpsertForRefreshSubscribe, Group = GlobalConst.TrackUpsertForRefreshSubscribe)]
public async Task ConsumerAsync(TrackUpsertEvent @event)
{
var orderIds = @event.OrderIdentifiers.Select(x => x.OrderId).ToArray();
var dup = @event.Visited.Intersect(orderIds).ToArray();
if (dup.Any())
{
LogHelper.Warn($"{(@event.IsAdd ? "添加" : "修改")}轨迹时,发现循环依赖:{dup.JoinAsString(",")}", true);
return;
}
if (!CanDelete(@event)) return;
var canNextOrderIds = new List<int>();
await HandleTrackAsync(@event, canNextOrderIds);
if (canNextOrderIds.Count == 0) return;
// 根据订单关联关系异步向下刷(异步)
await DownByOrderLink(@event, canNextOrderIds);
}
private async Task DownByOrderLink(TrackUpsertEvent @event, List<int> canNextOrderIds)
{
// 根据订单关联关系异步向下刷(异步)
var linkedOrders = await _orderRepository.GetLinkedOrdersAsync(canNextOrderIds.ToArray());
foreach (var order in @event.OrderIdentifiers)
@ -55,7 +55,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
}))
{
var nextEvent = @event.DeepCopy();
nextEvent.Visited.AddRange(orderIds);
nextEvent.Visited.AddRange(canNextOrderIds);
nextEvent.SubSystemId = group.Key.SubSystemId;
nextEvent.CustomId = group.Key.CustomId;
nextEvent.TrackRefreshData = new TrackRefreshStruct
@ -103,6 +103,27 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
}
}
private bool CanDelete(TrackUpsertEvent @event)
{
// 系统创建的轨迹不允许修改
if (@event.Visited.IsNullOrEmpty() && @event is { OperationMan: GlobalConst.SystemUser, IsAdd: false })
{
LogHelper.Warn($"系统创建的轨迹不允许被修改:{@event}", true);
return false;
}
var orderIds = @event.OrderIdentifiers.Select(x => x.OrderId).ToArray();
var dup = @event.Visited.Intersect(orderIds).ToArray();
if (dup.Any())
{
LogHelper.Warn($"{(@event.IsAdd ? "添加" : "修改")}轨迹时,发现循环依赖:{dup.JoinAsString(",")}", true);
return false;
}
return true;
}
private async Task HandleTrackAsync(TrackUpsertEvent @event, List<int> canNextOrderIds)
{
var trackContent = await _provideQueries.GetTrackContentAsync(@event.SubSystemId, @event.TrackCode);
@ -117,7 +138,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
var orderIds = @event.OrderIdentifiers.Select(x => x.OrderId).ToArray();
// important:轨迹code相同,时间、发生地不同的,可能存在多条!!
var tracks = await _trackDetailsRepository.GetListAsync(orderIds, @event.TrackCode, true);
var tracks = await _trackDetailsEsRepository.GetListAsync(orderIds, @event.TrackCode, true);
var insertTracks = new List<TrackDetailsIndex>();
@ -167,7 +188,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
track.UpdateUser = @event.OperationMan ?? GlobalConst.SystemUser;
track.UpdateTime = DateTime.Now;
await _trackDetailsRepository.UpdateAsync(track);
await _trackDetailsEsRepository.UpdateAsync(track);
}
canNextOrderIds.Add(order.OrderId);
@ -176,7 +197,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
if (canNextOrderIds.Count == 0) return;
// 批量add to es
await _trackDetailsRepository.AddManyAsync(insertTracks);
await _trackDetailsEsRepository.AddManyAsync(insertTracks);
// 触发订单主表更新事件(异步)
await _capPublisher.PublishAsync(GlobalConst.TrackChangeAfterUpdateOrderSubscribe,

View File

@ -28,18 +28,18 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler
private readonly ILogger<TrackRefreshEventHandler> _logger;
private readonly IOrderRepository _orderRepository;
private readonly ITimezoneQueries _timezoneQueries;
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
private readonly ITrackPullCacheService _trackPullCacheService;
public TrackRefreshEventHandler(IOrderRepository orderRepository,
ICapPublisher capPublisher, ITrackPullCacheService trackPullCacheService,
ITrackDetailsRepository trackDetailsRepository, ITimezoneQueries timezoneQueries,
ITrackDetailsEsRepository trackDetailsEsRepository, ITimezoneQueries timezoneQueries,
ILogger<TrackRefreshEventHandler> logger)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_trackPullCacheService = trackPullCacheService;
_trackDetailsRepository = trackDetailsRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
_timezoneQueries = timezoneQueries;
_logger = logger;
}
@ -152,7 +152,7 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler
{
var originTrackDataHash = StringHelper.GetHash(JsonConvert.SerializeObject(trackToken));
// 如果已经存在一模一样的报文了说明处理过了,直接return
if (await _trackDetailsRepository.AnyAsync(context.OrderId, originTrackDataHash))
if (await _trackDetailsEsRepository.AnyAsync(context.OrderId, originTrackDataHash))
{
return;
}
@ -327,7 +327,7 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler
? track.TrackTime
: track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone));
await _trackDetailsRepository.AddAsync(track);
await _trackDetailsEsRepository.AddAsync(track);
}
/// <summary>

View File

@ -36,18 +36,12 @@ public class ForewarningCacheService : IForewarningCacheService, ISingletonDepen
return await GetRules(subSystemId, customerId);
}
public Task DeleteRulesAsync(int subSystemId, int customerId)
{
_rules.Remove(WarningKey(subSystemId, customerId), out _);
return Task.CompletedTask;
}
public Task Clear(ClearRulCacheEvent @event)
{
foreach (var item in @event.Items)
{
DeleteRulesAsync(item.SystemId, item.CustomerId);
var redisKey = WarningKey(item.SystemId, item.CustomerId);
_rules.Remove(redisKey, out _);
}
return Task.CompletedTask;
@ -91,7 +85,7 @@ public class ForewarningCacheService : IForewarningCacheService, ISingletonDepen
private List<ForewarningRuleCacheEntry> WriteRules(List<WarningRuleCacheDto> rules)
{
List<ForewarningRuleCacheEntry> result = new List<ForewarningRuleCacheEntry>();
var result = new List<ForewarningRuleCacheEntry>();
foreach (var item in rules)
{
List<AreaCacheEntry>? areaList = null;
@ -153,11 +147,11 @@ public class ForewarningCacheService : IForewarningCacheService, ISingletonDepen
var warnings = await _warningQueries.GetWarningRule(systemId, customerId);
if (warnings == null || warnings.Count == 0)
{
RedisHelper.Client.Set(key, new List<WarningRuleCacheDto>(), TimeSpan.FromSeconds(20));
await RedisHelper.Client.SetAsync(key, new List<WarningRuleCacheDto>(), TimeSpan.FromSeconds(20));
return new List<ForewarningRuleCacheEntry>();
}
RedisHelper.Client.Set(key, warnings);
await RedisHelper.Client.SetAsync(key, warnings);
ruleResult = WriteRules(warnings);
_rules.TryAdd(key, ruleResult);
return ruleResult;
@ -169,7 +163,7 @@ public class ForewarningCacheService : IForewarningCacheService, ISingletonDepen
if (_areas.TryGetValue(areaId, out var areaInfos)) return areaInfos;
var areas = GetAreas(areaId);
if (areas == null || areas.Count == 0) return null;
if (areas.Count == 0) return null;
areaInfos = areas.Select(KpiAreaInfoToEntry).ToList();
_areas.TryAdd(areaId, areaInfos);
@ -192,7 +186,7 @@ public class ForewarningCacheService : IForewarningCacheService, ISingletonDepen
/// 获取区域
/// </summary>
/// <returns></returns>
private List<KpiAreaInfo>? GetAreas(int areaId)
private List<KpiAreaInfo> GetAreas(int areaId)
{
var key = AreaKey(areaId);
var result = RedisHelper.Client.Get<List<KpiAreaInfo>>(key);

View File

@ -1,11 +1,13 @@
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;
@ -17,18 +19,20 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
{
private readonly IForewarningCacheService _cacheService;
private readonly IWarningQueries _warningQueries;
private readonly IWarningRecordEsRepository _warningRecordEsRepository;
public ForewarningNotifyService(IForewarningCacheService cacheService, IWarningQueries warningQueries)
public ForewarningNotifyService(IForewarningCacheService cacheService, IWarningQueries warningQueries,
IWarningRecordEsRepository warningRecordEsRepository)
{
_cacheService = cacheService;
_warningQueries = warningQueries;
_warningRecordEsRepository = warningRecordEsRepository;
}
/// <summary>
/// 创建预警通知
/// </summary>
/// <param name="dto">创建预警通知需要的订单信息</param>
/// <returns>创建的通知总量,0表示没有规则</returns>
public async Task WarningCalculateAsync(WarningDto dto)
{
if (dto.Orders.Count == 0) return;
@ -36,19 +40,30 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
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 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 timestamp = GetTimestamp(order.TrackTime.AddMinutes(rule.WarnTimeSpan));
adds.Add((rule.Id, order.Id, rule.WarnCount, timestamp));
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
@ -57,74 +72,123 @@ public class ForewarningNotifyService : IForewarningNotifyService, ITransientDep
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
return new ZMember(entry.ToString(), x.ts);
}).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();
// 右区间移出预警队列
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); // 标记处理完成
}
if (res == null) throw new BusinessException(message: "轨迹预警计算失败");
tran.Exec();
}
public async Task SendNotificationAsync()
{
var max = GetTimestamp(DateTime.Now);
var set = await RedisHelper.Client.ZRangeByScoreAsync(GlobalConst.ForewarningNotifyKey, 0, max);
var timestamp = GetTimestamp(DateTime.Now);
var set = await RedisHelper.Client.ZRangeByScoreAsync(GlobalConst.ForewarningNotifyKey, 0, timestamp);
if (set.IsNullOrEmpty()) return;
if (set.IsNullOrEmpty())
{
LogHelper.Info("没有运输时效需要预警");
return;
}
var entries = set.Select(x => new WarningNotifyCacheEntry(x)).ToList();
var ruleIds = entries.Select(x => x.RuleId).ToList();
var ruleIds = entries.Select(x => x.RuleId).Distinct().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
var userRules = new Dictionary<int, List<GetWarningNotifiesVo>>();
foreach (var rule in notifyRules)
{
x.NotifyMode,
x.NotifyUserId
}))
{
var notifyContent = new StringBuilder();
foreach (var warningGroup in group.GroupBy(x => x.WarningName))
foreach (var userId in rule.NotifyUserIds)
{
notifyContent.AppendLine($"预警名称:{warningGroup.Key}");
notifyContent.AppendLine($"预警规则:");
foreach (var rule in warningGroup.ToList())
if (!userRules.ContainsKey(userId))
{
var cnt = entries.Count(x => x.RuleId == rule.RuleId);
if (cnt > 0)
{
notifyContent.AppendLine(
$"{rule.ToString()} 命中订单总数:{cnt},详情点击https://middlebusiness.pet.shijizhongyun.com/trajectoryCenter/trajectoryOrder");
}
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,

View File

@ -22,6 +22,7 @@ public class ForewarningService : ApplicationService
_forewarningCacheService = forewarningCacheService;
}
/// <summary>
/// 获取预警分页列表
/// </summary>
@ -150,7 +151,7 @@ public class ForewarningService : ApplicationService
await _warningRepository.TurnWarningAsync(warning);
// 清除缓存
await _forewarningCacheService.DeleteRulesAsync(warning.SubSystemId, warning.CustomerId);
await _forewarningCacheService.ClearRedisBySystemIdAndCustomer(warning.SubSystemId, warning.CustomerId);
return true;
}
@ -194,7 +195,7 @@ public class ForewarningService : ApplicationService
await _warningRepository.ModifyTimeSpansAsync(warningRules);
// 清除缓存
await _forewarningCacheService.DeleteRulesAsync(warning.SubSystemId, warning.CustomerId);
await _forewarningCacheService.ClearRedisBySystemIdAndCustomer(warning.SubSystemId, warning.CustomerId);
return true;
}
}

View File

@ -14,7 +14,7 @@ public partial class OrderService
{
private readonly ICapPublisher _capPublisher;
private readonly IBasicQuery<OrderLinkEntity> _orderLinkQuery;
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
/// <summary>
/// 关联、解除关联订单
@ -99,7 +99,7 @@ public partial class OrderService
LinkOrderType = input.LinkOrderType
}).ToList());
var tracks = await _trackDetailsRepository.GetListAsync(orderId);
var tracks = await _trackDetailsEsRepository.GetListAsync(orderId);
// 补轨迹(按子系统、按客户分,同样是为了方便轨迹upsert和push)
foreach (var group in existedOrders.GroupBy(x => new

View File

@ -16,16 +16,16 @@ namespace SJZY.Track.Application.Services.ProvideServices;
[Route("/ProviderService")]
public partial class OrderService : ApplicationService, IOrderService
{
private readonly IOrderOperationRecordRepository _orderOperationRecordRepository;
private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository;
private readonly IOrderRepository _orderRepository;
public OrderService(IOrderRepository orderRepository, IBasicQuery<OrderLinkEntity> orderLinkQuery,
ICapPublisher capPublisher, ITrackDetailsRepository trackDetailsRepository,
IOrderOperationRecordRepository orderOperationRecordRepository)
ICapPublisher capPublisher, ITrackDetailsEsRepository trackDetailsEsRepository,
IOrderOperationRecordEsRepository orderOperationRecordEsRepository)
{
_orderRepository = orderRepository;
_orderOperationRecordRepository = orderOperationRecordRepository;
_trackDetailsRepository = trackDetailsRepository;
_orderOperationRecordEsRepository = orderOperationRecordEsRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
_capPublisher = capPublisher;
_orderLinkQuery = orderLinkQuery;
}
@ -54,7 +54,7 @@ public partial class OrderService : ApplicationService, IOrderService
Content = input.ToJson(),
};
_ = _orderOperationRecordRepository.AddAsync(index);
_ = _orderOperationRecordEsRepository.AddAsync(index);
return new UnifyResultDto<IdBaseDto>(true, new IdBaseDto
{
@ -79,7 +79,7 @@ public partial class OrderService : ApplicationService, IOrderService
CreateUserId = CurrentUserRequest.CurrentUser.UserId,
CreateTime = DateTime.Now
};
_ = _orderOperationRecordRepository.AddAsync(indexForAdd);
_ = _orderOperationRecordEsRepository.AddAsync(indexForAdd);
return new UnifyResultDto<IdBaseDto>(true, new IdBaseDto

View File

@ -19,16 +19,16 @@ public class TrackPushService : ApplicationService
private readonly ICapPublisher _capPublisher;
private readonly IOrderRepository _orderRepository;
private readonly IHttpClientProxy<IRequestCurrentUserService> _requestCurrentUserService;
private readonly ITrackPushRecordsRepository _trackPushRecordsRepository;
private readonly ITrackPushRecordEsRepository _trackPushRecordEsRepository;
public TrackPushService(IOrderRepository orderRepository
, ICapPublisher capPublisher
, ITrackPushRecordsRepository trackPushRecordsRepository,
, ITrackPushRecordEsRepository trackPushRecordEsRepository,
IHttpClientProxy<IRequestCurrentUserService> requestCurrentUserService)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_trackPushRecordsRepository = trackPushRecordsRepository;
_trackPushRecordEsRepository = trackPushRecordEsRepository;
_requestCurrentUserService = requestCurrentUserService;
}
@ -115,6 +115,6 @@ public class TrackPushService : ApplicationService
throw new BusinessException(message: "订单不存在");
}
return await _trackPushRecordsRepository.GetTrackPushRecordsAsync(orderId);
return await _trackPushRecordEsRepository.GetTrackPushRecordsAsync(orderId);
}
}

View File

@ -8,7 +8,7 @@ namespace SJZY.Track.Application.Services.ProvideServices;
/// </summary>
public partial class TrackService
{
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
/// <summary>
/// 查询轨迹
@ -22,7 +22,7 @@ public partial class TrackService
var res = new List<GetTrackOutput>();
input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空
var details = await _trackDetailsRepository.GetListAsync(input.SubSystemId, input.OrderType, input.OrderNos);
var details = await _trackDetailsEsRepository.GetListAsync(input.SubSystemId, input.OrderType, input.OrderNos);
// 是否对外
var trackCodes = details.Select(x => x.TrackCode)

View File

@ -23,14 +23,14 @@ public partial class TrackService : ApplicationService, ITrackService
private readonly ITrackContentRepository _trackContentRepository;
public TrackService(IOrderRepository orderRepository, ICapPublisher capPublisher,
ITrackDetailsRepository trackDetailsRepository, ITrackContentRepository trackContentRepository,
ITrackDetailsEsRepository trackDetailsEsRepository, ITrackContentRepository trackContentRepository,
IHttpClientProxy<IRequestCurrentUserService> requestCurrentUserService)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_trackContentRepository = trackContentRepository;
_requestCurrentUserService = requestCurrentUserService;
_trackDetailsRepository = trackDetailsRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
}
/// <summary>

View File

@ -0,0 +1,64 @@
using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
using SJZY.Track.Domain.Aggregates.OrderAggregate;
using SJZY.Track.Domain.Dtos.Forewarning;
namespace SJZY.Track.Application.Services.TrackOrders;
public class OrderWarningService
{
private readonly IOrderRepository _orderRepository;
private readonly IWarningRecordEsRepository _warningRecordEsRepository;
public OrderWarningService(IWarningRecordEsRepository warningRecordEsRepository, IOrderRepository orderRepository)
{
_warningRecordEsRepository = warningRecordEsRepository;
_orderRepository = orderRepository;
}
/// <summary>
/// 获取订单运输时效预警分页列表
/// </summary>
/// <param name="inputVo"></param>
/// <param name="warningRecordEsRepository"></param>
/// <returns></returns>
[HttpPost("GetOrderWarningPageList")]
public async Task<UnifyPageResultDto<GetOrderWarningPageOutput>> GetOrderWarningPageListAsync(
GetOrderWarningPageListVo inputVo)
{
var result = await _warningRecordEsRepository.GetOrderWarningPageListAsync(inputVo);
var orderIds = result.list.Select(x => x.OrderId).ToList();
var orders = await _orderRepository.GetListAsync(x => orderIds.Contains(x.Id), x => new
{
x.Id,
x.LatestTrackContent,
x.LatestTrackTime,
x.LatestTrackPlace,
x.OrderNo
});
var list = new List<GetOrderWarningPageOutput>();
result.list.ForEach(x =>
{
var item = new GetOrderWarningPageOutput
{
Rule = x.Rule,
WarningName = x.WarningName,
WarningStatus = x.WarningStatus,
CreateTime = x.CreateTime,
NotifyUser = x.NotifyUser,
CustomName = x.CustomName,
};
var order = orders.FirstOrDefault(o => o.Id == x.OrderId);
if (order == default) return;
item.OrderNo = order.OrderNo;
item.LatestTrackTime = order.LatestTrackTime;
item.LatestTrackPlace = order.LatestTrackPlace;
item.LatestTrackContent = order.LatestTrackContent;
});
return new UnifyPageResultDto<GetOrderWarningPageOutput>(list, inputVo.PageIndex, inputVo.PageSize,
(int) result.total);
}
}

View File

@ -12,24 +12,24 @@ namespace SJZY.Track.Application.Services.TrackOrders;
[Route("/Track/Order")]
public class TrackOrderService : ApplicationService
{
private readonly IOrderOperationRecordRepository _orderOperationRecordRepository;
private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository;
private readonly IOrderRepository _orderRepository;
private readonly ITrackContentRepository _trackContentRepository;
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ITrackPushRecordsRepository _trackPushRecordsRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
private readonly ITrackPushRecordEsRepository _trackPushRecordEsRepository;
public TrackOrderService(
IOrderRepository orderRepository,
ITrackDetailsRepository trackDetailsRepository,
ITrackDetailsEsRepository trackDetailsEsRepository,
ITrackContentRepository trackContentRepository,
ITrackPushRecordsRepository trackPushRecordsRepository,
IOrderOperationRecordRepository orderOperationRecordRepository)
ITrackPushRecordEsRepository trackPushRecordEsRepository,
IOrderOperationRecordEsRepository orderOperationRecordEsRepository)
{
_orderRepository = orderRepository;
_trackDetailsRepository = trackDetailsRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
_trackContentRepository = trackContentRepository;
_trackPushRecordsRepository = trackPushRecordsRepository;
_orderOperationRecordRepository = orderOperationRecordRepository;
_trackPushRecordEsRepository = trackPushRecordEsRepository;
_orderOperationRecordEsRepository = orderOperationRecordEsRepository;
}
#region 轨迹订单列表
@ -74,7 +74,7 @@ public class TrackOrderService : ApplicationService
[HttpGet("GetTrackPushRecords")]
public async Task<List<TrackPushRecordOutput>> GetTrackPushRecordsAsync(int orderId)
{
var records = await _trackPushRecordsRepository.GetTrackPushRecordsAsync(orderId);
var records = await _trackPushRecordEsRepository.GetTrackPushRecordsAsync(orderId);
var res = ObjectMapper.Map<List<TrackPushRecordIndex>, List<TrackPushRecordOutput>>(records);
@ -103,7 +103,7 @@ public class TrackOrderService : ApplicationService
throw new BusinessException(message: "订单不存在");
}
var trackDetails = await _trackDetailsRepository.GetListAsync(orderId);
var trackDetails = await _trackDetailsEsRepository.GetListAsync(orderId);
if (trackDetails.IsNullOrEmpty())
{
return new List<TrackOrderDetailDto>();
@ -162,7 +162,7 @@ public class TrackOrderService : ApplicationService
[HttpPost("DeleteTrackById")]
public async Task<bool> DeleteTrackByIdAsync(string trackId)
{
var track = await _trackDetailsRepository.GetByIdAsync(trackId);
var track = await _trackDetailsEsRepository.GetByIdAsync(trackId);
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (track == null)
@ -170,7 +170,7 @@ public class TrackOrderService : ApplicationService
throw new BusinessException(message: "轨迹不存在!");
}
await _trackDetailsRepository.DeleteTrackByIdAsync(trackId, track.IndexName);
await _trackDetailsEsRepository.DeleteTrackByIdAsync(trackId, track.IndexName);
return true;
}
@ -182,6 +182,6 @@ public class TrackOrderService : ApplicationService
[HttpGet("GetOrderOperationsById")]
public async Task<List<OrderOperationRecordIndex>> GetOrderOperationsByIdAsync(int orderId)
{
return await _orderOperationRecordRepository.GetOrderOperationsByIdAsync(orderId);
return await _orderOperationRecordEsRepository.GetOrderOperationsByIdAsync(orderId);
}
}

View File

@ -12,14 +12,6 @@ public interface IForewarningCacheService
/// <returns></returns>
Task<List<ForewarningRuleCacheEntry>?> GetRulesAsync(int subSystemId, int customerId);
/// <summary>
/// 删除规则
/// </summary>
/// <param name="subSystemId"></param>
/// <param name="customerId"></param>
/// <returns></returns>
Task DeleteRulesAsync(int subSystemId, int customerId);
/// <summary>
/// 重置本地缓存
/// </summary>

View File

@ -0,0 +1,34 @@
using SJZY.Track.Domain.Dtos.Forewarning;
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
public interface IWarningRecordEsRepository
{
/// <summary>
/// 添加预警记录
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
Task AddAsync(WarningRecordIndex index);
/// <summary>
/// 批量添加预警记录
/// </summary>
/// <param name="warningRecords"></param>
/// <returns></returns>
Task BulkInsertAsync(List<WarningRecordIndex> warningRecords);
/// <summary>
/// 批量更新预警记录
/// </summary>
/// <param name="warningRecords"></param>
/// <returns></returns>
Task BulkUpdateAsync(List<(int ruleId, int orderId)> warningRecords);
/// <summary>
/// 获取订单运输时效预警分页列表
/// </summary>
/// <param name="inputVo"></param>
/// <returns></returns>
Task<(List<WarningRecordIndex> list, long total)> GetOrderWarningPageListAsync(GetOrderWarningPageListVo inputVo);
}

View File

@ -34,8 +34,14 @@ public class WarningNotifyEntity : IFullAuditedBasicEntitys, IEntity
public string NotifyMode { get; set; }
/// <summary>
/// 通知人,多个逗号分隔
/// 通知人id,多个逗号分隔
/// </summary>
[SugarColumn(ColumnName = "NotifyUserId")]
public string NotifyUserId { get; set; }
/// <summary>
/// 通知人,多个逗号分隔
/// </summary>
[SugarColumn(ColumnName = "NotifyUser")]
public string NotifyUser { get; set; }
}

View File

@ -7,6 +7,11 @@ namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
[ElasticsearchType(RelationName = GlobalConst.TrackWarningRecordIndex)]
public class WarningRecordIndex : IdBaseIndex
{
/// <summary>
/// 子系统
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// 索引名称
/// </summary>
@ -18,14 +23,14 @@ public class WarningRecordIndex : IdBaseIndex
public string WarningName { get; set; } = null!;
/// <summary>
/// 规则Id
/// 客户简称
/// </summary>
public int RuleId { get; set; }
public string CustomName { get; set; }
/// <summary>
/// 规则
/// 预警规则id
/// </summary>
public string Rule { get; set; } = null!;
public int RuleId { get; set; }
/// <summary>
/// 订单Id
@ -33,12 +38,22 @@ public class WarningRecordIndex : IdBaseIndex
public int OrderId { get; set; }
/// <summary>
/// 订单号
/// 预警规则
/// </summary>
public string OrderNo { get; set; } = null!;
public string Rule { get; set; } = null!;
/// <summary>
///
/// 预警状态
/// </summary>
public int CustomId { get; set; }
public WarningStatus WarningStatus { get; set; }
/// <summary>
/// 通知用户
/// </summary>
public string NotifyUser { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
}

View File

@ -0,0 +1,14 @@
namespace SJZY.Track.Domain.Aggregates.ForewarningAggregate;
public enum WarningStatus
{
/// <summary>
/// 待处理
/// </summary>
UnProcessed = 1,
/// <summary>
/// 已处理
/// </summary>
Processed = 2,
}

View File

@ -1,6 +1,6 @@
namespace SJZY.Track.Domain.Aggregates.OrderAggregate;
public interface IOrderOperationRecordRepository
public interface IOrderOperationRecordEsRepository
{
/// <summary>
/// 添加订单操作记录

View File

@ -2,7 +2,7 @@ using SJZY.Expand.ElasticSearch.Services;
namespace SJZY.Track.Domain.Aggregates.TrackDetailsAggregate;
public interface ITrackDetailsRepository : IElasticsearchClientService<TrackDetailsIndex>
public interface ITrackDetailsEsRepository : IElasticsearchClientService<TrackDetailsIndex>
{
Task<List<TrackDetailsIndex>> GetListAsync(int[] orderIds, string trackCode, bool forUpsert = false);

View File

@ -1,6 +1,6 @@
namespace SJZY.Track.Domain.Aggregates.TrackPushAggregate;
public interface ITrackPushRecordsRepository
public interface ITrackPushRecordEsRepository
{
/// <summary>
/// 添加推送记录

View File

@ -3,22 +3,22 @@ namespace SJZY.Track.Domain.Consts;
public partial class GlobalConst
{
/// <summary>
/// 轨迹明细 es索引名称(模板)
/// 轨迹明细 es索引名称(模板按天)
/// </summary>
public const string TrackIndex = "sjzy_track_details";
/// <summary>
/// 轨迹推送记录 es索引名称(模板)
/// 轨迹推送记录 es索引名称(模板按天)
/// </summary>
public const string TrackPushRecordsIndex = "sjzy_track_push_records";
/// <summary>
/// 预警记录 es索引名称(模板)
/// 运输时效预警记录 es索引名称(模板按天)
/// </summary>
public const string TrackWarningRecordIndex = "sjzy_track_warning_records";
/// <summary>
/// 订单操作记录 es索引名称(模板)
/// 订单操作记录 es索引名称(模板按天)
/// </summary>
public const string OrderOperationRecordIndex = "sjzy_track_order_operation_records";
}

View File

@ -0,0 +1,48 @@
using System.ComponentModel.DataAnnotations;
using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
namespace SJZY.Track.Domain.Dtos.Forewarning;
public class GetOrderWarningPageListVo : PageInfoInput
{
/// <summary>
/// 子系统id
/// </summary>
public int? SubSystemId { get; set; }
/// <summary>
/// 订单号
/// </summary>
[MaxLength(500)]
public List<string>? OrderNos { get; set; }
/// <summary>
/// 预警状态
/// </summary>
public WarningStatus? WarningStatus { get; set; }
/// <summary>
/// 客户简称
/// </summary>
public string? CustomName { get; set; }
/// <summary>
/// 预警名称
/// </summary>
public string? WarningName { get; set; }
/// <summary>
/// 通知用户
/// </summary>
public string? NotifyUser { get; set; }
/// <summary>
/// 创建时间开始
/// </summary>
public DateTime? StartTime { get; set; }
/// <summary>
/// 创建时间结束
/// </summary>
public DateTime? EndTime { get; set; }
}

View File

@ -0,0 +1,53 @@
using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
namespace SJZY.Track.Domain.Dtos.Forewarning;
public class GetOrderWarningPageOutput
{
/// <summary>
/// 预警名称
/// </summary>
public string WarningName { get; set; } = null!;
/// <summary>
/// 客户简称
/// </summary>
public string CustomName { get; set; }
/// <summary>
/// 预警规则
/// </summary>
public string Rule { get; set; } = null!;
/// <summary>
/// 预警状态
/// </summary>
public WarningStatus WarningStatus { get; set; }
/// <summary>
/// 通知用户
/// </summary>
public string NotifyUser { get; set; }
/// <summary>
/// 最新轨迹内容(异步维护)
/// </summary>
public string? LatestTrackContent { get; set; }
/// <summary>
/// 最新轨迹时间(异步维护)
/// </summary>
public DateTime? LatestTrackTime { get; set; }
/// <summary>
/// 最新轨迹发生地(异步维护)
/// </summary>
public string? LatestTrackPlace { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
public string OrderNo { get; set; }
}

View File

@ -6,6 +6,11 @@ namespace SJZY.Track.Domain.Dtos.Forewarning;
public class GetWarningNotifiesVo
{
/// <summary>
/// 子系统
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// 规则 id
/// </summary>
@ -16,6 +21,11 @@ public class GetWarningNotifiesVo
/// </summary>
public string WarningName { get; set; } = null!;
/// <summary>
/// 客户简称
/// </summary>
public string CustomName { get; set; }
/// <summary>
/// 时效(小时)
/// </summary>
@ -85,6 +95,8 @@ public class GetWarningNotifiesVo
public List<int> NotifyUserIds { get; set; }
public string NotifyUser { get; set; }
/// <summary>
/// 因素类型排序
/// </summary>

View File

@ -5,6 +5,12 @@ public class WarningRuleCacheDto
public int Id { get; set; }
public int SystemId { get; set; }
public int CustomerId { get; set; }
/// <summary>
/// 客户简称
/// </summary>
public string CustomName { get; set; }
public int WarningId { get; set; }
public float TotalTimeSpan { get; set; }
public float WarnTimeSpan { get; set; }

View File

@ -2,6 +2,7 @@ using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
using SJZY.Track.Domain.Aggregates.KPIAreaAggregate;
using SJZY.Track.Domain.Dtos.Forewarning;
using SJZY.Track.Domain.Queries;
using SJZY.Track.Domain.VEntities;
using SJZY.Track.Share.Enums;
namespace SJZY.Track.Repository.Queries;
@ -19,12 +20,15 @@ public class WarningQueries : ISugarRepository, IWarningQueries,
.LeftJoin<WarningNotifyEntity>((w, wr, a, wn) =>
wr.WarningId == wn.WarningId && wr.StartTrackStatusId == wn.StartTrackStatusId &&
wr.EndTrackStatusId == wn.EndTrackStatusId)
.LeftJoin<VExternalSubjectEntity>((w, wr, a, wn, cus) => w.CustomerId == cus.Id)
.Filter(null, true) // 支持历史规则
.Where((w, wr) => ruleIds.Contains(wr.Id))
.Select<GetWarningNotifiesVo>((w, wr, a, wn) => new GetWarningNotifiesVo
.Select<GetWarningNotifiesVo>((w, wr, a, wn, cus) => new GetWarningNotifiesVo
{
SubSystemId = w.SubSystemId,
RuleId = wr.Id,
WarningName = w.Name,
CustomName = cus.Name,
TotalTimeSpan = wr.TotalTimeSpan,
WarnTimeSpan = wr.WarnTimeSpan,
WarnCount = wr.WarnCount,
@ -37,6 +41,7 @@ public class WarningQueries : ISugarRepository, IWarningQueries,
LeavePort = wr.LeavePort,
ProductCode = wr.ProductCode,
NotifyMode = wn.NotifyMode,
NotifyUser = wn.NotifyUser,
NotifyUserId = wn.NotifyUserId,
FactorTypeSort = w.FactorTypeSort
})

View File

@ -7,11 +7,11 @@ using SJZY.Track.Domain.Consts;
namespace SJZY.Track.Repository.Repositories;
public class OrderOperationRecordRepository : ElasticSearchContextAbstract<TrackDetailsIndex>,
IOrderOperationRecordRepository,
public class OrderOperationRecordEsRepository : ElasticSearchContextAbstract<TrackDetailsIndex>,
IOrderOperationRecordEsRepository,
ITransientDependency
{
public OrderOperationRecordRepository(ConnectionFactory connectionFactory) : base(connectionFactory)
public OrderOperationRecordEsRepository(ConnectionFactory connectionFactory) : base(connectionFactory)
{
}
@ -26,7 +26,7 @@ public class OrderOperationRecordRepository : ElasticSearchContextAbstract<Track
if (!r.IsValid)
{
LogHelper.Error($"轨迹推送记录写入es失败,原因:{r.DebugInformation}。");
LogHelper.Error($"订单操作记录写入es失败,原因:{r.DebugInformation}。");
}
}

View File

@ -6,12 +6,12 @@ using SJZY.Track.Domain.Consts;
namespace SJZY.Track.Repository.Repositories;
internal class TrackDetailsRepository : ElasticSearchContextAbstract<TrackDetailsIndex>, ITrackDetailsRepository,
internal class DetailsEsRepository : ElasticSearchContextAbstract<TrackDetailsIndex>, ITrackDetailsEsRepository,
ITransientDependency
{
const int SafelyLimit = GlobalConst.SafelyLimit;
public TrackDetailsRepository(ConnectionFactory connectionFactory) : base(connectionFactory)
public DetailsEsRepository(ConnectionFactory connectionFactory) : base(connectionFactory)
{
}

View File

@ -7,10 +7,10 @@ using SJZY.Track.Domain.Consts;
namespace SJZY.Track.Repository.Repositories;
public class TrackPushRecordsRepository : ElasticSearchContextAbstract<TrackDetailsIndex>, ITrackPushRecordsRepository,
public class PushRecordEsRepository : ElasticSearchContextAbstract<TrackDetailsIndex>, ITrackPushRecordEsRepository,
ITransientDependency
{
public TrackPushRecordsRepository(ConnectionFactory connectionFactory) : base(connectionFactory)
public PushRecordEsRepository(ConnectionFactory connectionFactory) : base(connectionFactory)
{
}

View File

@ -0,0 +1,106 @@
using Nest;
using SJZY.Expand.ABP.Core.Logger;
using SJZY.Expand.ElasticSearch.Services;
using SJZY.Track.Domain.Aggregates.ForewarningAggregate;
using SJZY.Track.Domain.Consts;
using SJZY.Track.Domain.Dtos.Forewarning;
namespace SJZY.Track.Repository.Repositories;
public class WarningRecordEsRepository : ElasticSearchContextAbstract<WarningRecordIndex>, IWarningRecordEsRepository,
ITransientDependency
{
public WarningRecordEsRepository(ConnectionFactory connectionFactory) : base(connectionFactory)
{
}
public async Task AddAsync(WarningRecordIndex index)
{
index.IndexName = $"{GlobalConst.TrackWarningRecordIndex}-{DateTime.Now:yyyy.MM.dd}";
index.CreateTime = DateTime.Now;
// 按天写入es
var r = await Context.IndexAsync(index, request => request
.Index(index.IndexName)
);
if (!r.IsValid)
{
LogHelper.Error($"运输时效预警记录写入es失败,原因:{r.DebugInformation}。");
}
}
public async Task BulkInsertAsync(List<WarningRecordIndex> warningRecords)
{
if (!warningRecords.Any()) return;
// 按天写入es
var indexName = $"{GlobalConst.TrackIndex}-{DateTime.Now:yyyy.MM.dd}";
warningRecords.ForEach(x => x.IndexName = indexName);
var r = await Context.IndexManyAsync(warningRecords, indexName);
if (!r.IsValid)
{
throw new InvalidOperationException($"{warningRecords.Count}条运输时效预警记录新增失败,原因:{r.DebugInformation}。");
}
}
public async Task BulkUpdateAsync(List<(int ruleId, int orderId)> rules)
{
foreach (var rule in rules)
{
var r = await Context.UpdateByQueryAsync<WarningRecordIndex>(u =>
u.Query(q =>
q.Term(f => f.RuleId, rule.ruleId)
&& q.Term(f => f.OrderId, rule.orderId)
)
.Script(s => s.Source("ctx._source.warningStatus = params.warningStatus")
.Params(p => p.Add("warningStatus", WarningStatus.Processed))
)
);
if (!r.IsValid)
{
throw new InvalidOperationException(
$"运输时效预警记录更新失败,ruleId:{rule.ruleId},orderId:{rule.orderId},原因:{r.DebugInformation}。");
}
}
}
public async Task<(List<WarningRecordIndex> list, long total)> GetOrderWarningPageListAsync(
GetOrderWarningPageListVo input)
{
Func<QueryContainerDescriptor<WarningRecordIndex>, QueryContainer> query = q =>
(input.SubSystemId.HasValue ? q.Term(f => f.SubSystemId, input.SubSystemId) : null)
&& (!input.OrderNos.IsNullOrEmpty()
? q.Terms(t => t
.Field(f => f.OrderId.Suffix("keyword"))
.Terms(input.OrderNos))
: null)
&& (input.WarningStatus.HasValue ? q.Term(f => f.WarningStatus, input.WarningStatus) : null)
&& (!input.CustomName.IsNullOrWhiteSpace()
? q.Wildcard(w => w.CustomName, $"*{input.CustomName}*")
: null)
&& (!input.WarningName.IsNullOrWhiteSpace()
? q.Wildcard(w => w.WarningName, $"*{input.WarningName}*")
: null)
&& (!input.NotifyUser.IsNullOrWhiteSpace()
? q.Wildcard(w => w.NotifyUser, $"*{input.NotifyUser}*")
: null)
&& (input.StartTime.HasValue
? q.DateRange(r => r.Field(f => f.CreateTime).GreaterThanOrEquals(input.StartTime))
: null)
&& (input.EndTime.HasValue
? q.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.EndTime))
: null);
var data = await Context.SearchAsync<WarningRecordIndex>(x => x
.Source()
.Sort(s => s.Field(f => f.CreateTime, SortOrder.Descending))
.From((input.PageIndex - 1) * input.PageSize)
.Size(input.PageSize)
.TrackTotalHits()
);
return (data.Documents.ToList(), data.Total);
}
}