track/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs
hardstylewyl 5edaa2f80c update: 新增/修改轨迹增加IsToMq参数,标记为true则轨迹操作后一定会推送
update: MQ广播异步改同步
update: 更新契约包版本7.0.10
2024-12-27 11:18:20 +08:00

490 lines
20 KiB
C#

using DotNetCore.CAP;
using SJZY.Track.Application.Contract.Dtos.ProvideServices;
using SJZY.Track.Application.Contract.Events;
using SJZY.Track.Application.Contract.Services.ProvideServices;
using SJZY.Track.Domain.Aggregates.OrderAggregate;
using SJZY.Track.Domain.Aggregates.TrackContentAggregate;
using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate;
using SJZY.Track.Domain.Enums;
using SJZY.UserCenter.HttpApi.Services;
using SJZY.UserCenter.HttpApi.Services.User;
using Volo.Abp.Http.Client.DynamicProxying;
namespace SJZY.Track.Application.Services.ProvideServices;
/// <summary>
/// 轨迹服务
/// </summary>
[ApiExplorerSettings(GroupName = "提供接口")]
[Route("/ProviderService")]
public partial class TrackService : ApplicationService, ITrackService
{
private readonly ICapPublisher _capPublisher;
private readonly IOrderRepository _orderRepository;
private readonly IHttpClientProxy<IUserBaseInfoService> _requestCurrentUserService;
private readonly ITrackContentRepository _trackContentRepository;
public TrackService(IOrderRepository orderRepository, ICapPublisher capPublisher,
ITrackDetailsEsRepository trackDetailsEsRepository, ITrackContentRepository trackContentRepository,
IHttpClientProxy<IUserBaseInfoService> requestCurrentUserService)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_trackContentRepository = trackContentRepository;
_requestCurrentUserService = requestCurrentUserService;
_trackDetailsEsRepository = trackDetailsEsRepository;
}
/// <summary>
/// 新增、更新轨迹(存在就更新)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Track/Upsert")]
public async Task<UnifyResultDto> UpsertTrackAsync(UpsertTrackInput input)
{
var order = await _orderRepository.GetFirstAsync(x =>
x.SubSystemId == input.SubSystemId && x.OrderType == input.OrderType && x.OrderNo == input.OrderNo,
x => new
{
x.Id,
x.CustomId
});
if (order == default)
{
throw new BusinessException(message: $"订单:{input.OrderNo}不存在");
}
if (!GlobalConst.SupportTrackSources.Contains(input.Source))
{
throw new BusinessException(message: "轨迹来源是必须的");
}
//要求时间在当前时间的前后100年之间内
bool ValidateTrackTime(DateTime? d) => (d != null && d < DateTime.Now.AddYears(-100) || d > DateTime.Now.AddYears(100));
if (ValidateTrackTime(input.TrackTime) || ValidateTrackTime(input.NewTrackTime) || ValidateTrackTime(input.EstimatedDeliveryDate))
{
throw new BusinessException(message: "轨迹时间不合法");
}
if ((input.EstimatedDeliveryDate.HasValue == false && input.DestinationTimeZone.HasValue) ||
(input.EstimatedDeliveryDate.HasValue && input.DestinationTimeZone.HasValue == false))
{
throw new BusinessException(message: "【预计签收时间】和【签收地时区】必须同时输入");
}
//当修改时,如果为来源为外部提供商则不允许修改
if (!input.IsAdd && input.Source == (int) TrackSourceEnum.ExternalServiceProvide)
{
throw new BusinessException(message: "来源为外部服务商轨迹不允许修改");
}
var trackContent = await _trackContentRepository.GetByCodeAsync(input.SubSystemId, input.TrackCode);
if (trackContent == default)
{
throw new BusinessException(message: $"轨迹代码:{input.TrackCode}轨迹内容不存在");
}
//指示是否为手动操作
var manualOperation = input.Source == (int) TrackSourceEnum.Artificial;
//不允许手动创建
if (manualOperation && input.IsAdd && !trackContent.IsAllowManuallyCreate)
{
throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动创建");
}
//不允许手动更新
if (manualOperation && !input.IsAdd && !trackContent.IsAllowManuallyUpdate)
{
throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动更新");
}
var tracks = (await _trackDetailsEsRepository.GetListAsync(new[] { order.Id }, input.TrackCode, true))
.Where(x => x.TrackTime == input.TrackTime)
.ToList();
//当为更新操作时发现更新的数目为0进行重试操作
if (!input.IsAdd && tracks.Count == 0)
{
var curRetryCount = 0;
var success = false;
while (!success && curRetryCount < GlobalConst.MaxUpdateTrackRetryCount)
{
try
{
tracks = (await _trackDetailsEsRepository.GetListAsync(new[] { order.Id }, input.TrackCode, true))
.Where(x => x.TrackTime == input.TrackTime)
.ToList();
if (tracks.Count > 0)
success = true;
await Task.Delay(300);
}
finally
{
curRetryCount++;
}
}
if (tracks.Count == 0)
{
throw new BusinessException(message: $"订单:{input.OrderNo},轨迹代码:{input.TrackCode}没有要更新的记录");
}
}
//更新轨迹,同一时间存在多条轨迹记录,理由:不清晰到底要修改哪一条或是全部修改
if (!input.IsAdd && tracks.Count > 1)
{
throw new BusinessException(message: $"订单:{input.OrderNo},轨迹代码:{input.TrackCode}存在多条记录,不允许修改");
}
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
//todo:保留之前的方式 后续可去除
userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync(input.OptionUserId);
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
//TODO: log || ex
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
var name = userInfo.Data.UserName;
// 新增/更新轨迹
var @event = ObjectMapper.Map<UpsertTrackInput, TrackUpsertEvent>(input);
@event.SubSystemId = input.SubSystemId;
@event.CustomId = order.CustomId;
@event.OrderIdentifiers = new[]
{
new OrderIdentifier
{
OrderId = order.Id,
OrderNo = input.OrderNo,
OrderType = input.OrderType,
}
};
@event.OperationMan = name;
await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, @event);
return new UnifyResultDto(true, true);
}
/// <summary>
/// 删除轨迹
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[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,
x => x.Id);
if (orderId == default)
{
throw new BusinessException(message: $"订单:{input.OrderNo}不存在");
}
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
//TODO: log || ex
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
var name = userInfo.Data.UserName;
//删除轨迹约束,只要其中有一条来源是外部的就立即返回错误
var tracks = await _trackDetailsEsRepository
.GetListAsync(new[] { orderId },
input.TrackIdentifiers.Select(x => x.TrackCode).ToList(),
TrackSourceEnum.ExternalServiceProvide);
//当要删除的轨迹标识出现交叉则不允许删除
var intersect = tracks.Select(x => $"{x.TrackCode}{x.TrackTime}")
.Intersect(input.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.TrackTime}")).ToList();
if (intersect.Count > 0)
{
throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除");
}
// 删除轨迹
var @event = new TrackDeleteEvent
{
OrderIds = new[] { orderId },
TrackIdentifiers = input.TrackIdentifiers,
SubSystemId = input.SubSystemId,
OperationMan = name
};
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event);
return new UnifyResultDto(true, true);
}
/// <summary>
/// 根据esid删除轨迹内容
/// </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)
return new UnifyResultDto(false, "未找到指定id下的轨迹内容");
if (tracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode()))
return new UnifyResultDto(false, "存在来源于外部服务商的轨迹,不允许删除");
if (tracks.GroupBy(m => m.SubSystemId).Count() > 1)
return new UnifyResultDto(false, "暂不支持跨子系统批量删除");
// 获取用户name
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
// 删除轨迹
var currentUser = CurrentUserRequest.CurrentUser;
var userName = userInfo.Data.UserName;
if (string.IsNullOrEmpty(userName))
userName = (await _orderRepository.GetUserById(currentUser.UserId))?.Name;
var @event = new TrackDeleteEvent
{
EsIds = input.TrackContentEsIds.ToArray(),
SubSystemId = tracks.First().SubSystemId,
OperationMan = userName,
OperationManId = currentUser.UserId
};
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event);
return new UnifyResultDto(true);
}
/// <summary>
/// 批量删除轨迹
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Track/BatchDelete")]
public async Task<UnifyResultDto> BatchDeleteTrackAsync(BatchDeleteTrackInput input)
{
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);
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 intersect = tracks.Select(x => $"{x.TrackCode}{x.TrackTime}")
.Intersect(input.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.TrackTime}")).ToList();
if (intersect.Count > 0)
{
throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除");
}
// 删除轨迹
var @event = new TrackDeleteEvent
{
OrderIds = orderIds.ToArray(),
TrackIdentifiers = input.TrackIdentifiers,
SubSystemId = input.SubSystemId,
OperationMan = userInfo.Data.UserName,
};
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event);
return new UnifyResultDto(true, true);
}
/// <summary>
/// 批量新增、更新轨迹
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Track/BatchUpsert")]
public async Task<UnifyResultDto> BatchUpsertTrackAsync(BatchUpsertTrackInput input)
{
input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空
var orders = await _orderRepository.GetListAsync(x =>
x.SubSystemId == input.SubSystemId && x.OrderType == input.OrderType &&
input.OrderNos.Contains(x.OrderNo),
x => new
{
x.Id,
x.OrderNo,
x.CustomId
});
if (orders.Count == 0)
{
throw new BusinessException(message: "订单一个都不存在");
}
if (!GlobalConst.SupportTrackSources.Contains(input.Source))
{
throw new BusinessException(message: "轨迹来源是必须的");
}
//当修改时,如果为来源为外部提供商则不允许修改
if (!input.IsAdd && input.Source == (int) TrackSourceEnum.ExternalServiceProvide)
{
throw new BusinessException(message: "来源为外部服务商轨迹不允许修改");
}
// 校验主体:批量新增只选取第一条订单进行校验
//【新增】存在指定轨迹的内容,进行更新覆盖(只处理一条的情况,并且决定权交给用户),不存在直接插入即可
//【更新】存在指定轨迹的内容(匹配到多条则不处理)
var order = orders.First();
//要求时间在当前时间的前后100年之间内
bool ValidateTrackTime(DateTime? d) => (d != null && d < DateTime.Now.AddYears(-100) || d > DateTime.Now.AddYears(100));
if (ValidateTrackTime(input.TrackTime) || ValidateTrackTime(input.NewTrackTime) || ValidateTrackTime(input.EstimatedDeliveryDate))
{
throw new BusinessException(message: "轨迹时间不合法");
}
if ((input.EstimatedDeliveryDate.HasValue == false && input.DestinationTimeZone.HasValue) ||
(input.EstimatedDeliveryDate.HasValue && input.DestinationTimeZone.HasValue == false))
{
throw new BusinessException(message: "【预计签收时间】和【签收地时区】必须同时输入");
}
var trackContent = await _trackContentRepository.GetByCodeAsync(input.SubSystemId, input.TrackCode);
if (trackContent == default)
{
throw new BusinessException(message: $"轨迹代码:{input.TrackCode}轨迹内容不存在");
}
//指示是否为手动操作
var manualOperation = input.Source == (int) TrackSourceEnum.Artificial;
//不允许手动创建
if (manualOperation && input.IsAdd && !trackContent.IsAllowManuallyCreate)
{
throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动创建");
}
//不允许手动更新
if (manualOperation && !input.IsAdd && !trackContent.IsAllowManuallyUpdate)
{
throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动更新");
}
var tracks = (await _trackDetailsEsRepository.GetListAsync(new[] { order.Id }, input.TrackCode, true))
.Where(x => x.TrackTime == input.TrackTime)
.ToList();
//当为更新操作时发现更新的数目为0进行重试操作
if (!input.IsAdd && tracks.Count == 0)
{
var curRetryCount = 0;
var success = false;
while (!success && curRetryCount < GlobalConst.MaxUpdateTrackRetryCount)
{
try
{
tracks = (await _trackDetailsEsRepository.GetListAsync(new[] { order.Id }, input.TrackCode, true))
.Where(x => x.TrackTime == input.TrackTime)
.ToList();
if (tracks.Count > 0)
success = true;
await Task.Delay(300);
}
finally
{
curRetryCount++;
}
}
if (tracks.Count == 0)
{
throw new BusinessException(message: $"订单:[{string.Join(",", input.OrderNos)}],轨迹代码:{input.TrackCode}没有要更新的记录");
}
}
//更新轨迹,同一时间存在多条轨迹记录,理由:不清晰到底要修改哪一条或是全部修改
if (!input.IsAdd && tracks.Count > 1)
{
throw new BusinessException(message: $"订单:{order.OrderNo},轨迹代码:{input.TrackCode}存在多条记录,不允许修改");
}
// 获取用户name
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
// 按客户新增/更新轨迹(为了推送轨迹时方便)
foreach (var group in orders.GroupBy(x => x.CustomId))
{
var @event = new TrackUpsertEvent
{
SubSystemId = input.SubSystemId,
CustomId = group.Key,
OrderIdentifiers = group.Select(o => new OrderIdentifier
{
OrderId = o.Id,
OrderNo = o.OrderNo,
OrderType = input.OrderType,
}).ToArray(),
IsAdd = input.IsAdd,
TrackCode = input.TrackCode,
TrackPlace = input.TrackPlace,
TrackTime = input.TrackTime,
NewTrackTime = input.NewTrackTime,
TrackTimeZone = input.TrackTimeZone,
OperationMan = userInfo.Data.UserName,
OperationManId = userInfo.Data.UserId,
IsVisibility = input.IsVisibility,
Source = input.Source,
Remarks = input.Remarks,
Files = input.Files,
Content = input.Content,
EnStatus = input.EnStatus,
TrackPlaceCity = input.TrackPlaceCity,
TrackPlaceCityId = input.TrackPlaceCityId,
TrackPlaceProvince = input.TrackPlaceProvince,
TrackPlaceProvinceId = input.TrackPlaceProvinceId,
TrackPlaceNation = input.TrackPlaceNation,
TrackPlaceNationId = input.TrackPlaceNationId,
TrackPlacePostcode = input.TrackPlacePostcode,
EstimatedDeliveryDate = input.EstimatedDeliveryDate,
DestinationTimeZone = input.DestinationTimeZone,
Extra = input.Extra,
ForcedPush = input.ForcedPush,
IsToMq = input.IsToMq,
};
await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, @event);
}
return new UnifyResultDto(true, true);
}
}