add 关联补轨迹,add 批量删除轨迹

This commit is contained in:
李鹏军 2024-03-08 18:45:34 +08:00
parent 2e0a1f3e4d
commit 76fcd29ca7
16 changed files with 221 additions and 31 deletions

View File

@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
public class BatchDeleteTrackInput
{
/// <summary>
/// 订单类型,必填
/// </summary>
[Required]
[Range(1, int.MaxValue)]
public int OrderType { get; set; }
/// <summary>
/// 订单号,必填
/// </summary>
[Required]
public List<string> OrderNos { get; set; } = new();
/// <summary>
/// 轨迹code,必填
/// </summary>
[Required]
public List<string> TrackCodes { get; set; } = new();
}

View File

@ -1,4 +1,6 @@
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
using System.ComponentModel.DataAnnotations;
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
public class DeleteOrderInput : OrderNoInfo
{
@ -9,5 +11,6 @@ public class DeleteTrackInput : OrderNoInfo
/// <summary>
/// 轨迹code
/// </summary>
[Required]
public List<string> TrackCodes { get; set; } = new();
}

View File

@ -22,7 +22,11 @@ public class LinkOrderInput
[Required]
public string OrderNo { get; set; }
[Required] public int LinkOrderType { get; set; }
/// <summary>
/// 关联单类型
/// </summary>
[Required]
public int LinkOrderType { get; set; }
/// <summary>
/// 关联的单号

View File

@ -0,0 +1,19 @@
namespace SJZY.Track.Application.Contract.Events;
public class OrderLinkAfterFillTrackEvent
{
/// <summary>
/// 关联订单id
/// </summary>
public int OrderId { get; set; }
/// <summary>
/// 子系统id
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// 关联订单
/// </summary>
public List<int> LinkedOrderIds { get; set; } = null!;
}

View File

@ -0,0 +1,61 @@
using DotNetCore.CAP;
using SJZY.Track.Application.Contract.Events;
using SJZY.Track.Domain.Aggregates.OrderAggregate;
using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate;
using Volo.Abp.DependencyInjection;
namespace SJZY.Track.Application.EventHandlers;
public class OrderLinkAfterFillTrackHandler : ITransientDependency, ICapSubscribe
{
private readonly ITrackDetailsRepository _trackDetailsRepository;
private readonly ICapPublisher _capPublisher;
private readonly IOrderRepository _orderRepository;
public OrderLinkAfterFillTrackHandler(ITrackDetailsRepository trackDetailsRepository, ICapPublisher capPublisher,
IOrderRepository orderRepository)
{
_trackDetailsRepository = trackDetailsRepository;
_capPublisher = capPublisher;
_orderRepository = orderRepository;
}
[CapSubscribe(GlobalConst.OrderLinkAfterFillTrack)]
public async Task ConsumerAsync(OrderLinkAfterFillTrackEvent @event)
{
var tracks = await _trackDetailsRepository.GetListAsync(@event.OrderId);
foreach (var linkOrderId in @event.LinkedOrderIds)
{
var order = await _orderRepository.GetFirstAsync(x =>
x.Id == linkOrderId, x => new
{
x.OrderType, x.OrderNo, x.CustomId, x.IsPushCompleted, x.SubSystemId
});
if (order == default) continue;
// 补轨迹
foreach (var track in tracks)
{
var fillTrackEvent = new TrackUpsertEvent
{
OrderType = order.OrderType,
OrderNo = order.OrderNo,
TrackCode = track.TrackCode,
TrackPlace = track.TrackPlace,
TrackTime = track.TrackTime,
TrackTimeZone = track.TrackTimeZone,
OrderId = linkOrderId,
CustomId = order.CustomId,
SubSystemId = order.SubSystemId,
IsPushCompleted = order.IsPushCompleted,
UpSubSystemId = @event.SubSystemId,
};
await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, fillTrackEvent);
}
}
}
}

View File

@ -68,6 +68,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
var orderIsCompleted = false;
if (track == default)
{
//todo:可以优化
// 根据轨迹code在订单模板内容中寻找匹配的
var trackContent = await _provideQueries.MatchTrackContentAsync(@event.OrderId, @event.TrackCode);
if (trackContent == default)

View File

@ -1,4 +1,6 @@
using SJZY.Track.Application.Contract.Dtos.ProvideServices;
using DotNetCore.CAP;
using SJZY.Track.Application.Contract.Dtos.ProvideServices;
using SJZY.Track.Application.Contract.Events;
using SJZY.Track.Domain.Aggregates.OrderAggregate;
using SJZY.Track.Domain.Queries;
@ -13,11 +15,14 @@ public class OrderLinkService : ApplicationService
{
private readonly IOrderRepository _orderRepository;
private readonly IBasicQuery<OrderLinkEntity> _orderLinkQuery;
private readonly ICapPublisher _capPublisher;
public OrderLinkService(IBasicQuery<OrderLinkEntity> orderLinkQuery, IOrderRepository orderRepository)
public OrderLinkService(IBasicQuery<OrderLinkEntity> orderLinkQuery, IOrderRepository orderRepository,
ICapPublisher capPublisher)
{
_orderLinkQuery = orderLinkQuery;
_orderRepository = orderRepository;
_capPublisher = capPublisher;
}
/// <summary>
@ -28,14 +33,17 @@ public class OrderLinkService : ApplicationService
[HttpPost("Order/Link")]
public async Task<bool> LinkOrderAsync(LinkOrderInput input)
{
var orderId = await _orderRepository.GetFirstAsync(x =>
x.OrderType == input.OrderType && x.OrderNo == input.OrderNo, x => x.Id);
if (orderId == default)
var order = await _orderRepository.GetFirstAsync(x =>
x.OrderType == input.OrderType && x.OrderNo == input.OrderNo, x => new
{
x.Id, x.SubSystemId
});
if (order == default)
{
throw new BusinessException(message: $"订单:{input.OrderNo}不存在");
}
//todo:循环关联?
//tips:循环关联问题再消费时解决
if (input.OrderType == input.LinkOrderType)
{
throw new BusinessException(message: "不能同级关联");
@ -52,10 +60,23 @@ public class OrderLinkService : ApplicationService
if (!input.IsLink) return await UnLinkOrderAsync(input);
// 关联订单
return await DoLinkOrderAsync(input, orderId);
var linkedOrderIds = await DoLinkOrderAsync(input, order.Id);
// 补轨迹
if (linkedOrderIds.Count > 0)
{
await _capPublisher.PublishAsync(GlobalConst.OrderLinkAfterFillTrack, new OrderLinkAfterFillTrackEvent
{
OrderId = order.Id,
SubSystemId = order.SubSystemId,
LinkedOrderIds = linkedOrderIds,
});
}
return true;
}
private async Task<bool> DoLinkOrderAsync(LinkOrderInput input, int orderId)
private async Task<List<int>> DoLinkOrderAsync(LinkOrderInput input, int orderId)
{
// 获取已关联的订单
var linkOrderNos = await _orderLinkQuery.GetListAsync(x =>
@ -70,6 +91,8 @@ public class OrderLinkService : ApplicationService
x.OrderType == input.LinkOrderType && noLinkOrderNos.Contains(x.OrderNo),
x => new { x.Id, x.OrderNo });
if (canLinkOrders.Count == 0) return new List<int>();
await _orderRepository.LinkOrderAsync(canLinkOrders.Select(x => new OrderLinkEntity
{
OrderId = orderId,
@ -80,9 +103,7 @@ public class OrderLinkService : ApplicationService
LinkOrderType = input.LinkOrderType
}).ToList());
// todo:补轨迹
return true;
return canLinkOrders.Select(x => x.Id).ToList();
}
private async Task<bool> UnLinkOrderAsync(LinkOrderInput input)

View File

@ -65,14 +65,19 @@ public class TrackService : ApplicationService
var order = await _orderRepository.GetFirstAsync(x =>
x.OrderType == input.OrderType && x.OrderNo == input.OrderNo, x => new
{
x.Id, x.SubSystemId, x.IsPushCompleted, x.CustomId
x.Id, x.SubSystemId, x.IsCompleted
});
if (order == default)
{
throw new BusinessException(message: $"订单:{input.OrderNo}不存在");
}
var codes = await _provideQueries.GetCanDeleteTrackCodesAsync(order.Id, input.TrackCodes);
if (order.IsCompleted)
{
throw new BusinessException(message: $"订单:{input.OrderNo}已完成无法删除轨迹");
}
var codes = await _provideQueries.GetCanDeleteTrackCodesAsync(new List<int>(order.Id), input.TrackCodes);
if (codes.Count == 0)
{
@ -83,11 +88,45 @@ public class TrackService : ApplicationService
var @event = new TrackDeleteEvent
{
OrderId = order.Id,
TrackCodes = codes,
TrackCodes = codes.Select(x => x.TrackCode).ToList(),
SubSystemId = order.SubSystemId,
};
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event);
return true;
}
[HttpPost("Track/BatchDelete")]
public async Task<bool> BatchTrackAsync(BatchDeleteTrackInput input)
{
var orders = await _orderRepository.GetListAsync(x =>
x.OrderType == input.OrderType && input.OrderNos.Contains(x.OrderNo) && !x.IsPushCompleted
, x => new
{
x.Id, x.SubSystemId
});
var codes = await _provideQueries.GetCanDeleteTrackCodesAsync(
orders.Select(x => x.Id).ToList(), input.TrackCodes
);
if (codes.Count == 0)
{
throw new BusinessException(message: "没有一个轨迹代码是可删的");
}
// 删除轨迹
foreach (var group in codes.GroupBy(x => x.OrderId))
{
var @event = new TrackDeleteEvent
{
OrderId = group.Key,
TrackCodes = codes.Select(x => x.TrackCode).ToList(),
SubSystemId = orders.First(x => x.Id == group.Key).SubSystemId,
};
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event);
}
return true;
}
}

View File

@ -11,8 +11,8 @@ public interface ITrackDetailsRepository
Task<List<TrackDetailsIndex>> GetListAsync(int orderType, List<string> orderNos);
Task<(List<TrackDetailsIndex> list, long total)> GetListAsync(TrackDetailsQueryInput input);
Task<List<TrackDetailsIndex>?> GetListAsync(int orderId);
Task<List<TrackDetailsIndex>> GetListAsync(int orderId);
/// <summary>
/// 删除轨迹
/// </summary>
@ -20,5 +20,6 @@ public interface ITrackDetailsRepository
/// <param name="trackCodes"></param>
/// <returns></returns>
Task DeleteTrackAsync(int orderId, List<string> trackCodes);
Task<List<TrackDetailsIndex>?> GetListAsync(List<int> orderIds);
}

View File

@ -0,0 +1,7 @@
namespace SJZY.Track.Domain.Dtos.TrackTemplate;
public class CanDeleteTrackCodeVO
{
public int OrderId { get; set; }
public string TrackCode { get; set; }
}

View File

@ -11,14 +11,14 @@ public class GlobalConst
/// 轨迹新增/更新 mq订阅名称
/// </summary>
public const string TrackUpsertSubscribe = "SJZY.Track.TrackUpsert";
/// <summary>
/// 轨迹删除 mq订阅名称
/// </summary>
public const string TrackDeleteSubscribe = "SJZY.Track.TrackDelete";
/// <summary>
/// 接口通批量轨迹刷新订阅
/// 接口通批量轨迹刷新 mq订阅名称
/// </summary>
public const string InterfaceRelayTrackRefreshSubscribe = "SJZY.Track.TrackRefresh";
@ -27,6 +27,11 @@ public class GlobalConst
/// </summary>
public const string UpdateOrderTrackSubscribe = "SJZY.Track.UpdateOrderTrack";
/// <summary>
/// 关联订单后补轨迹 mq订阅名称
/// </summary>
public const string OrderLinkAfterFillTrack = "SJZY.Track.OrderLinkAfterFillTrack";
/// <summary>
/// 轨迹要推送的客户redis key前缀
/// </summary>

View File

@ -9,7 +9,7 @@ public interface IBasicQuery<T> where T : IEntity
/// <returns></returns>
Task<T?> GetFirstAsync(Expression<Func<T, bool>> whereExpression);
Task<TResult> GetFirstAsync<TResult>(Expression<Func<T, bool>> whereExpression,
Task<TResult?> GetFirstAsync<TResult>(Expression<Func<T, bool>> whereExpression,
Expression<Func<T, TResult>> resultExpression);
/// <summary>

View File

@ -1,4 +1,3 @@
using SJZY.Track.Domain.Aggregates.TrackPullAggregate;
using SJZY.Track.Domain.Dtos.Order;
using SJZY.Track.Domain.Dtos.TrackDetails;
using SJZY.Track.Domain.Dtos.TrackTemplate;
@ -32,14 +31,14 @@ public interface IProvideQueries
/// <param name="code">轨迹代码</param>
/// <returns></returns>
Task<MatchTrackContentDto?> MatchTrackContentAsync(int orderId, string code);
/// <summary>
/// 获取可删除的订单轨迹代码
/// </summary>
/// <param name="orderId"></param>
/// <param name="orderIds"></param>
/// <param name="trackCodes"></param>
/// <returns></returns>
Task<List<string>> GetCanDeleteTrackCodesAsync(int orderId, List<string> trackCodes);
Task<List<CanDeleteTrackCodeVO>> GetCanDeleteTrackCodesAsync(List<int> orderIds, List<string> trackCodes);
/// <summary>
/// 根据子系统内容映射,得到映射后的轨迹code

View File

@ -13,7 +13,7 @@ public class BasicQuery<T> : SimpleClient<T>, IBasicQuery<T> where T : class, IE
Context = SqlsugarScopeBasic.GetContextSafely("default");
}
public async Task<TResult> GetFirstAsync<TResult>(Expression<Func<T, bool>> whereExpression,
public async Task<TResult?> GetFirstAsync<TResult>(Expression<Func<T, bool>> whereExpression,
Expression<Func<T, TResult>> resultExpression)
{
return await Context.Queryable<T>().Where(whereExpression).Select(resultExpression).FirstAsync();

View File

@ -80,14 +80,19 @@ public class ProvideQueries : ISugarRepository, IProvideQueries,
.FirstAsync();
}
public async Task<List<string>> GetCanDeleteTrackCodesAsync(int orderId, List<string> trackCodes)
public async Task<List<CanDeleteTrackCodeVO>> GetCanDeleteTrackCodesAsync(List<int> orderIds,
List<string> trackCodes)
{
return await Context.Queryable<OrderTemplateEntity>()
.LeftJoin<TrackTemplateContentEntity>((x, a) => x.TrackTemplateId == a.TrackTemplateId && a.IsCanDelete)
.LeftJoin<TrackContentEntity>((x, a, b) => a.TrackContentId == b.Id)
.Where((x, a, b) => x.OrderId == orderId
.Where((x, a, b) => orderIds.Contains(x.OrderId)
&& trackCodes.Contains(b.Code) && a.IsCanDelete)
.Select((x, a, b) => b.Code)
.Select((x, a, b) => new CanDeleteTrackCodeVO
{
OrderId = x.OrderId,
TrackCode = b.Code
})
.ToListAsync();
}

View File

@ -86,7 +86,7 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract<TrackDetail
return data.Documents.ToList();
}
public async Task<List<TrackDetailsIndex>?> GetListAsync(int orderId)
public async Task<List<TrackDetailsIndex>> GetListAsync(int orderId)
{
var data = await Context.SearchAsync<TrackDetailsIndex>(x => x
.Query(q =>
@ -95,7 +95,7 @@ internal class TrackDetailsRepository : ElasticSearchContextAbstract<TrackDetail
.Size(1000)
.Sort(s => s.Field(f => f.TrackTime, SortOrder.Descending)));
return data?.Documents?.ToList();
return data.Documents.ToList();
}
public async Task<List<TrackDetailsIndex>?> GetListAsync(List<int> orderIds)