perf:优化轨迹删除为批量,优化空入参

This commit is contained in:
hardstylewyl 2025-04-27 11:17:59 +08:00
parent 87b9976ab2
commit a1ae86dcfb
7 changed files with 124 additions and 106 deletions

View File

@ -11,12 +11,12 @@ public class DeleteTrackInput : OrderNoInfo
/// <summary>
/// 轨迹标识符集合,为支持esid删除,所以非必填
/// </summary>
public List<TrackIdentifierDto>? TrackIdentifiers { get; set; } = new();
public List<TrackIdentifierDto> TrackIdentifiers { get; set; } = new();
/// <summary>
/// 轨迹ESId集合,非必填,如果有优先级最高
/// </summary>
public List<string>? EsIds { get; set; }
public List<string> EsIds { get; set; } = new();
/// <summary>
/// 重复的情况下,用什么字段比较

View File

@ -4,20 +4,20 @@ namespace SJZY.Track.Application.Contract.Events;
public class TrackDeleteEvent
{
/// <summary>
/// 根据esId定位轨迹,优先级最高
/// </summary>
public string[] EsIds { get; set; } = Array.Empty<string>();
/// <summary>
/// 子系统id
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// es中的id
/// </summary>
public string[]? EsIds { get; set; }
/// <summary>
/// 订单ids
/// </summary>
public int[] OrderIds { get; set; } = null!;
public int[] OrderIds { get; set; } = Array.Empty<int>();
/// <summary>
/// 重复的情况下,用什么字段比较
@ -37,7 +37,7 @@ public class TrackDeleteEvent
/// <summary>
/// 轨迹标识符
/// </summary>
public List<TrackIdentifierDto>? TrackIdentifiers { get; set; }
public List<TrackIdentifierDto> TrackIdentifiers { get; set; } = new();
/// <summary>
/// 操作人
@ -57,6 +57,6 @@ public class TrackDeleteEvent
public override string ToString()
{
return $"[TrackDelete by {OperationMan}] SubSystemId: {SubSystemId}, OrderIds: {string.Join(",",
OrderIds)}, TrackCodes: {string.Join(",", TrackIdentifiers.Select(x=>x.TrackCode))}";
OrderIds)}, TrackCodes: {string.Join(",", TrackIdentifiers.Select(x => x.TrackCode))}";
}
}

View File

@ -26,11 +26,15 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
private readonly IBroadcastingService _broadcastingService;
private readonly IServiceProvider _serviceProvider;
public TrackDeleteEventHandler(IProvideQueries provideQueries,
IOrderRepository orderRepository, ICapPublisher capPublisher,
public TrackDeleteEventHandler(
IProvideQueries provideQueries,
IOrderRepository orderRepository,
ICapPublisher capPublisher,
ITrackDetailsEsRepository trackDetailsEsRepository,
IOrderOperationRecordEsRepository orderOperationRecordEsRepository,
IBroadcastingService broadcastingService, IServiceProvider serviceProvider)
IBroadcastingService broadcastingService,
IServiceProvider serviceProvider
)
{
_provideQueries = provideQueries;
_orderRepository = orderRepository;
@ -65,8 +69,7 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
var linkedOrders = await _orderRepository.GetLinkedOrdersAsync(@event.OrderIds);
foreach (var orderId in @event.OrderIds)
{
foreach (var group in linkedOrders.Where(x => x.OwnerOrderId == orderId)
.GroupBy(x => x.SubSystemId))
foreach (var group in linkedOrders.Where(x => x.OwnerOrderId == orderId).GroupBy(x => x.SubSystemId))
{
var nextEvent = @event.DeepCopy();
nextEvent.Visited.AddRange(@event.OrderIds);
@ -76,9 +79,11 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
// 不同子系统需要做内容映射
if (@event.SubSystemId != nextEvent.SubSystemId)
{
var codeMaps = await _provideQueries
.GetTrackCodeMapsAsync(@event.SubSystemId, nextEvent.SubSystemId,
@event.TrackIdentifiers.Select(x => x.TrackCode).ToArray());
var codeMaps = await _provideQueries.GetTrackCodeMapsAsync(
@event.SubSystemId,
nextEvent.SubSystemId,
@event.TrackIdentifiers.Select(x => x.TrackCode).ToArray()
);
if (codeMaps.Count == 0)
{
@ -133,7 +138,8 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
List<int> orderIds;
List<TrackDetailsIndex> tracks;
List<TrackDetailsIndex> deleteTracks;
if (@event.EsIds != null && @event.EsIds.Any()) //根据明确的esid进行删除
// 优先根据EsId查询轨迹
if (@event.EsIds.Length > 0) //根据明确的esid进行删除
{
deleteTracks = await _trackDetailsEsRepository.GetDataByIds(@event.EsIds);
orderIds = deleteTracks.Select(m => m.OrderId).Distinct().ToList();
@ -145,17 +151,14 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
}).ToList();
// 外部api获取的轨迹不允许删除
if (deleteTracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode()))
if (deleteTracks.Any(m => m.Source == (int) TrackSourceEnum.ExternalServiceProvide))
return deleteTracks;
//查出订单下的所有轨迹,以供后面回退使用
tracks = await _trackDetailsEsRepository.GetListAsync(@event.OrderIds);
// 删除轨迹
foreach (var item in deleteTracks)
{
await _trackDetailsEsRepository.DeleteTrackByIdAsync(item.Id, item.IndexName);
}
// 删除轨迹批量
await _trackDetailsEsRepository.DeleteTrackByIdsAsync(deleteTracks.Select(x => x.Id).ToHashSet());
//清理已经删除过的esid
@event.EsIds = Array.Empty<string>();
@ -202,10 +205,8 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe
if (deleteTracks.Count == 0) return deleteTracks;
foreach (var track in deleteTracks)
{
await _trackDetailsEsRepository.DeleteTrackByIdAsync(track.Id, track.IndexName);
}
// 删除轨迹批量
await _trackDetailsEsRepository.DeleteTrackByIdsAsync(deleteTracks.Select(x => x.Id).ToHashSet());
//从被删除轨迹寻找订单id,稍后会更新订单主表
orderIds = deleteTracks.Select(m => m.OrderId).Distinct().ToList();

View File

@ -20,11 +20,13 @@ public partial class OrderService : ApplicationService, IOrderService
private readonly IOrderRepository _orderRepository;
private readonly IServiceProvider _serviceProvider;
public OrderService(IOrderRepository orderRepository,
ICapPublisher capPublisher
, ITrackDetailsEsRepository trackDetailsEsRepository,
public OrderService(
IOrderRepository orderRepository,
ICapPublisher capPublisher,
ITrackDetailsEsRepository trackDetailsEsRepository,
IOrderOperationRecordEsRepository orderOperationRecordEsRepository,
IServiceProvider serviceProvider)
IServiceProvider serviceProvider
)
{
_orderRepository = orderRepository;
_orderOperationRecordEsRepository = orderOperationRecordEsRepository;

View File

@ -31,14 +31,16 @@ public partial class TrackService : ApplicationService, ITrackService
private readonly IHttpClientProxy<IUserBaseInfoService> _requestCurrentUserService;
private readonly ITrackContentRepository _trackContentRepository;
public TrackService(IOrderRepository orderRepository,
public TrackService(
IOrderRepository orderRepository,
ICapPublisher capPublisher,
ITrackDetailsEsRepository trackDetailsEsRepository,
ITrackContentRepository trackContentRepository,
IHttpClientProxy<IUserBaseInfoService> requestCurrentUserService,
ITrackTemplateRepository trackTemplateRepository,
IHttpContextAccessor contextAccessor,
IVSystemQueries vSystemQueries)
IVSystemQueries vSystemQueries
)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
@ -59,10 +61,12 @@ public partial class TrackService : ApplicationService, ITrackService
[HttpPost("Track/Delete")]
public async Task<UnifyResultDto> DeleteTrackAsync(DeleteTrackInput input)
{
var orderId = await _orderRepository.GetFirstAsync(
x => x.SubSystemId == input.SubSystemId && x.OrderType == input.OrderType && x.OrderNo == input.OrderNo,
var orderId = await _orderRepository.GetFirstAsync(x =>
x.SubSystemId == input.SubSystemId
&& x.OrderType == input.OrderType
&& x.OrderNo == input.OrderNo,
x => x.Id);
if (orderId == null)
if (orderId == 0)
{
throw new BusinessException(message: $"订单:{input.OrderNo}不存在");
}
@ -73,38 +77,29 @@ public partial class TrackService : ApplicationService, ITrackService
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
var name = userInfo.Data.UserName;
List<TrackDetailsIndex> tracks;
bool existsExternal;
var esIds = input.EsIds?.Distinct().ToArray();
//优先使用esId
if (esIds != null && esIds.Length > 0)
// 查询轨迹优先使用esId,tips:考虑删除事件处理直接通过EsId而不是再查询一遍?
List<TrackDetailsIndex>? tracks = null;
var esIds = input.EsIds.Distinct().ToArray();
if (esIds.Length > 0)
{
tracks = await _trackDetailsEsRepository.GetDataByIds(esIds);
existsExternal = tracks.Any(x => x.Source == (int) TrackSourceEnum.ExternalServiceProvide);
}
else
else if (input.TrackIdentifiers.Count > 0)
{
var trackCodes = input.TrackIdentifiers?.Select(x => x.TrackCode).ToList();
var trackIds = input.TrackIdentifiers?.Select(y => $"{y.TrackCode}{y.TrackTime}").ToHashSet();
//没有任何需要操作的轨迹
if (trackIds == null || trackIds.Count == 0)
{
throw new BusinessException(message: "没有任何轨迹要删除,请检查输入参数");
}
//删除轨迹约束,只要其中有一条来源是外部的就立即返回错误
// todo:漏洞,使用标识符(code+时间)删除缺少时区补充
var trackCodes = input.TrackIdentifiers.Select(x => x.TrackCode).ToList();
var trackIds = input.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.TrackTime}").ToHashSet();
tracks = await _trackDetailsEsRepository.GetListAsync(new[] { orderId }, trackCodes);
//检查删除的轨迹中是否包含外部轨迹
existsExternal = tracks
.Where(x => x.Source == (int) TrackSourceEnum.ExternalServiceProvide)
// todo:漏洞,如果是外部服务商轨迹(拉取得到的轨迹),没有扩展?
tracks = tracks
// 先通过code+时间初步过滤
.Where(y => trackIds.Contains($"{y.TrackCode}{y.TrackTime}"))
// 如果提供了比较字段,则进一步过滤,但要求这个code的所有轨迹都要包含Extra!
.WhereIf(!input.CompareFields.IsNullOrWhiteSpace(), y =>
{
if (string.IsNullOrWhiteSpace(y.Extra))
{
throw new BusinessException(message: "轨迹extra为空无法使用比较字段比较,请检查轨迹");
}
var extraDict = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(y.Extra);
if (extraDict!.TryGetValue(input.CompareFields!, out var compareValueJsonElement))
@ -112,47 +107,47 @@ public partial class TrackService : ApplicationService, ITrackService
return false;
})
.Select(x => $"{x.TrackCode}{x.TrackTime}")
.Any(x => trackIds.Contains(x));
.ToList();
}
if (existsExternal)
{
throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除");
}
if (tracks.Count == 0)
if (tracks == null || tracks.Count == 0)
{
throw new BusinessException(message: "没有任何轨迹要删除,请检查输入参数");
}
// 删除轨迹
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe,
new TrackDeleteEvent
{
EsIds = input.EsIds?.ToArray(),
OrderIds = new[] { orderId },
CompareFields = input.CompareFields,
CompareValue = input.CompareValue,
Source = input.Source,
TrackIdentifiers = input.TrackIdentifiers,
SubSystemId = input.SubSystemId,
OperationMan = name
});
//删除轨迹约束,只要其中有一条来源是外部的就立即返回错误
if (tracks.Any(x => x.Source == (int) TrackSourceEnum.ExternalServiceProvide))
{
throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除");
}
// 删除轨迹(异步)
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, new TrackDeleteEvent
{
EsIds = esIds,
OrderIds = new[] { orderId },
SubSystemId = input.SubSystemId,
CompareFields = input.CompareFields,
CompareValue = input.CompareValue,
Source = input.Source,
TrackIdentifiers = input.TrackIdentifiers,
OperationMan = userInfo.Data.UserName
});
return new UnifyResultDto(true, true);
}
/// <summary>
/// 根据esid删除轨迹内容
/// 根据esid删除轨迹内容(FBA专用)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("Track/BatchDeleteByEsId")]
public async Task<UnifyResultDto> BatchDeleteTrackByEsIdAsync(BatchDeleteTrackByEsIdInput input)
{
var tracks = await _trackDetailsEsRepository.GetDataByIds(input.TrackContentEsIds.ToArray());
if (tracks == null || tracks.Count == 0)
var esIds = input.TrackContentEsIds.ToArray();
var tracks = await _trackDetailsEsRepository.GetDataByIds(esIds);
if (tracks.Count == 0)
return new UnifyResultDto(false, "未找到指定id下的轨迹内容");
if (tracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode()))
@ -176,7 +171,7 @@ public partial class TrackService : ApplicationService, ITrackService
var @event = new TrackDeleteEvent
{
EsIds = input.TrackContentEsIds.ToArray(),
EsIds = esIds,
SubSystemId = tracks.First().SubSystemId,
Source = input.Source,
OperationMan = userName,
@ -198,36 +193,28 @@ public partial class TrackService : ApplicationService, ITrackService
{
input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空
var orderIds = await _orderRepository.GetListAsync(x =>
x.SubSystemId == input.SubSystemId && x.OrderType == input.OrderType &&
input.OrderNos.Contains(x.OrderNo)
, x => x.Id);
x.SubSystemId == input.SubSystemId
&& x.OrderType == input.OrderType
&& input.OrderNos.Contains(x.OrderNo),
x => x.Id);
if (orderIds.Count == 0)
{
throw new BusinessException(message: "订单一个都不存在");
}
// 获取用户name
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
//删除轨迹约束,只要其中有一条来源是外部的就立即返回错误
var tracks = await _trackDetailsEsRepository
.GetListAsync(orderIds.ToArray(),
input.TrackIdentifiers.Select(x => x.TrackCode).ToList(),
TrackSourceEnum.ExternalServiceProvide);
var tracks = await _trackDetailsEsRepository.GetListAsync(orderIds.ToArray(),
input.TrackIdentifiers.Select(x => x.TrackCode).ToList(),
TrackSourceEnum.ExternalServiceProvide
);
//当要删除的轨迹标识出现交叉则不允许删除
var intersect = tracks
.WhereIf(!input.CompareFields.IsNullOrWhiteSpace(), y =>
{
if (string.IsNullOrWhiteSpace(y.Extra))
{
throw new BusinessException(message: "轨迹extra为空无法使用比较字段比较,请检查轨迹");
}
var extraDict = TextJsonHelper.DeserializeToDictionary(y.Extra);
if (extraDict!.TryGetValue(input.CompareFields!, out var compareValueJsonElement))
@ -236,12 +223,20 @@ public partial class TrackService : ApplicationService, ITrackService
return false;
})
.Select(x => $"{x.TrackCode}{x.TrackTime}")
.Intersect(input.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.TrackTime}")).ToList();
.Intersect(input.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.TrackTime}"))
.ToList();
if (intersect.Count > 0)
{
throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除");
}
// 获取用户name
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
// 删除轨迹
var @event = new TrackDeleteEvent
{

View File

@ -41,6 +41,8 @@ public interface ITrackDetailsEsRepository : IElasticsearchClientService<TrackDe
Task DeleteTrackByIdAsync(string id, string indexName);
Task DeleteTrackByIdsAsync(HashSet<string> esIds);
Task<List<TrackDetailsIndex>> GetDataByIds(string[] ids);
Task<TrackDetailsIndex?> GetDetailAsync(string orderNo, string trackCode, int orderType, int subSystemId, DateTime trackTime);

View File

@ -691,7 +691,7 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
var r = await Context.SearchAsync<TrackDetailsIndex>(x => x
.Index(GlobalConst.TrackDetailsIndex)
.Version()
.Query(q => query)
.Query(_ => query)
.Size(SafelyLimit)
.Sort(s => s
.Field(f => f.ZeroTrackTime, SortOrder.Descending)
@ -774,16 +774,34 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
}
}
public async Task DeleteTrackByIdsAsync(HashSet<string> esIds)
{
var r = await Context.DeleteByQueryAsync<TrackDetailsIndex>(x => x
.Index(GlobalConst.TrackDetailsIndex)
.Query(q => q.Ids(i => i.Values(esIds)))
);
if (!r.IsValid)
{
throw new InvalidOperationException($"轨迹删除失败,ids:[{string.Join(", ", esIds)}],原因:{r.DebugInformation}。");
}
}
public async Task<List<TrackDetailsIndex>> GetDataByIds(string[] ids)
{
var res = await Context.SearchAsync<TrackDetailsIndex>(m => m
var r = await Context.SearchAsync<TrackDetailsIndex>(m => m
.Index(GlobalConst.TrackDetailsIndex)
.Version()
.Query(
q => q.Ids(i => i.Values(ids))
));
return res.ToIdAndVersionList();
if (!r.IsValid)
{
throw new InvalidOperationException($"轨迹查询失败,ids:[{string.Join(", ", ids)}],原因:{r.DebugInformation}。");
}
return r.ToIdAndVersionList();
}
public async Task<TrackDetailsIndex?> GetDetailAsync(string orderNo, string trackCode, int orderType, int subSystemId, DateTime trackTime)