Remove debug APIs and transport scan Hangfire; move Upsert to Order/Upsert*; keep original response shapes and put result+reason only in exception messages. Co-authored-by: Cursor <cursoragent@cursor.com>
326 lines
12 KiB
C#
326 lines
12 KiB
C#
using System.Linq.Expressions;
|
|
using Fake.Application.Dtos;
|
|
using TrackCenter.Application.Dtos.ProvideServices;
|
|
using TrackCenter.Application.Dtos.TrackOrder;
|
|
using TrackCenter.Application.Helpers;
|
|
using TrackCenter.Application.Helpers.OSS;
|
|
using TrackCenter.Application.Services.Warning.Handlers;
|
|
using TrackCenter.Domain.Aggregates.OrderAggregate;
|
|
using TrackCenter.Domain.Aggregates.TrackContentAggregate;
|
|
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
|
|
using TrackCenter.Domain.Aggregates.TrackPushAggregate;
|
|
using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
|
|
using TrackCenter.Domain.Dtos.Order;
|
|
using TrackCenter.Repository.Aggregates.OrderAggregate;
|
|
|
|
namespace TrackCenter.Application.Services.TrackOrders;
|
|
|
|
/// <summary>
|
|
/// 轨迹订单服务
|
|
/// </summary>
|
|
[ApiExplorerSettings(GroupName = "轨迹订单")]
|
|
[Route("/Track/Order")]
|
|
public class TrackOrderService : ApplicationServiceBase
|
|
{
|
|
private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository;
|
|
private readonly IOrderRepository _orderRepository;
|
|
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
|
|
private readonly ITrackPushRecordEsRepository _trackPushRecordEsRepository;
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
public TrackOrderService(
|
|
IOrderRepository orderRepository,
|
|
ITrackDetailsEsRepository trackDetailsEsRepository,
|
|
ITrackPushRecordEsRepository trackPushRecordEsRepository,
|
|
IOrderOperationRecordEsRepository orderOperationRecordEsRepository,
|
|
IServiceProvider serviceProvider)
|
|
{
|
|
_orderRepository = orderRepository;
|
|
_trackDetailsEsRepository = trackDetailsEsRepository;
|
|
_trackPushRecordEsRepository = trackPushRecordEsRepository;
|
|
_orderOperationRecordEsRepository = orderOperationRecordEsRepository;
|
|
_serviceProvider = serviceProvider;
|
|
}
|
|
|
|
#region 轨迹订单列表
|
|
|
|
/// <summary>
|
|
/// 获取分页的轨迹订单
|
|
/// </summary>
|
|
/// <param name="input"></param>
|
|
/// <returns></returns>
|
|
[HttpPost("GetTrackOrderList")]
|
|
public async Task<PagedResult<TrackOrderVO>> GetTrackOrderPagedList(GetPageTrackOrderInput input)
|
|
{
|
|
var vo = ObjectMapper.Map<GetPageTrackOrderInput, GetPageTrackOrderInputVO>(input);
|
|
var pageList = await _orderRepository.GetTrackOrderPagedList(vo);
|
|
return pageList;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 更新订单状态
|
|
/// </summary>
|
|
/// <param name="input"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="BusinessException"></exception>
|
|
[HttpPost("UpdateOrderStatus")]
|
|
public async Task<bool> UpdateOrderStatus(UpdateOrderStatusInput input)
|
|
{
|
|
var order = await _orderRepository.GetFirstAsync(x => x.Id == input.OrderId);
|
|
|
|
if (order == null) throw new BusinessException(message: "订单不存在");
|
|
|
|
var oldIsCompleted = order.IsCompleted;
|
|
if (oldIsCompleted == input.IsCompleted)
|
|
{
|
|
throw new BusinessException(message: "订单状态未发生变化");
|
|
}
|
|
|
|
order.IsCompleted = input.IsCompleted;
|
|
var b = await _orderRepository.UpdateColumnAsync(order, x => new
|
|
{
|
|
x.IsCompleted
|
|
});
|
|
|
|
// 预警计算
|
|
if (order.IsCompleted)
|
|
WarningCalc_OrderDeletedOrCompleted(order);
|
|
|
|
return b;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取轨迹推送记录
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <param name="orderId">订单id</param>
|
|
[HttpGet("GetTrackPushRecords")]
|
|
public async Task<List<TrackPushRecordOutput>> GetTrackPushRecordsAsync(int orderId)
|
|
{
|
|
var records = await _trackPushRecordEsRepository.GetTrackPushRecordsAsync(orderId);
|
|
|
|
var res = ObjectMapper.Map<List<TrackPushRecordIndex>, List<TrackPushRecordOutput>>(records);
|
|
|
|
res.ForEach(x =>
|
|
{
|
|
int hours = (int) x.TrackTimeZone; // 提取整数部分作为小时
|
|
int minutes = (int) ((x.TrackTimeZone - hours) * 60); // 将小数部分乘以 60 转换为分钟
|
|
x.TrackGTMTimeZone =
|
|
$"GMT {(x.TrackTimeZone >= 0 ? "+" : "-")}{new TimeSpan(hours, minutes, 0).ToString().RemovePostFix(":00")}";
|
|
});
|
|
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据订单id获取轨迹明细列表
|
|
/// </summary>
|
|
/// <param name="orderId"></param>
|
|
/// <param name="orderNo"></param>
|
|
/// <param name="appKey"></param>
|
|
/// <param name="sortMethod"></param>
|
|
/// <returns></returns>
|
|
[HttpPost("GetDetails")]
|
|
public async Task<List<TrackOrderDetailDto>> GetTracksAsync(int? orderId, string? orderNo, string? appKey, SortMethod sortMethod = SortMethod.ByCreateTimeDesc)
|
|
{
|
|
Expression<Func<OrderEntity, bool>>? whereExpression = null;
|
|
//根据订单id查询
|
|
if (orderId != null && orderId > 0)
|
|
whereExpression = m => m.Id == orderId;
|
|
|
|
//根据订单号+系统id查询
|
|
if (!string.IsNullOrEmpty(orderNo) && !string.IsNullOrEmpty(appKey))
|
|
whereExpression = m => m.OrderNo == orderNo && m.AppKey == appKey;
|
|
|
|
if (whereExpression == default)
|
|
throw new BusinessException(message: "请求参数异常");
|
|
|
|
var order = await _orderRepository.GetFirstAsync(whereExpression);
|
|
if (order == default)
|
|
{
|
|
throw new BusinessException(message: "订单不存在");
|
|
}
|
|
|
|
var trackDetails = await _trackDetailsEsRepository.GetListAsync(order.Id);
|
|
if (trackDetails.IsNullOrEmpty())
|
|
{
|
|
return new List<TrackOrderDetailDto>();
|
|
}
|
|
|
|
var list = ObjectMapper.Map<List<TrackDetailsIndex>, List<TrackOrderDetailDto>>(trackDetails);
|
|
|
|
// 计算时区
|
|
list.ForEach(a =>
|
|
{
|
|
if (a.TrackTimeZoneValue == 0f)
|
|
{
|
|
a.TrackTimeZone = "GMT +00:00";
|
|
}
|
|
else if (Math.Abs(a.TrackTimeZoneValue - GlobalConst.NoneTrackTimeZone) < 1)
|
|
{
|
|
a.TrackTimeZone = "--";
|
|
}
|
|
else
|
|
{
|
|
// 将小数形式的小时转换为时区偏移量
|
|
var hours = (int) a.TrackTimeZoneValue; // 提取整数部分作为小时
|
|
var minutes = (int) ((a.TrackTimeZoneValue - hours) * 60); // 将小数部分乘以 60 转换为分钟
|
|
var offset = new TimeSpan(hours, minutes, 0);
|
|
var timeZoneStr = offset.ToString();
|
|
var flag = a.TrackTimeZoneValue > 0f ? "+" : "";
|
|
a.TrackTimeZone = $"GMT {flag}{timeZoneStr.RemovePostFix(":00")}";
|
|
}
|
|
});
|
|
|
|
// 排序
|
|
switch (sortMethod)
|
|
{
|
|
case SortMethod.ByTrackTime:
|
|
list.Sort((a, b) => DateTimeHelper.ConvertUtc0Compare(a.TrackTime, a.TrackTimeZoneValue, b.TrackTime, b.TrackTimeZoneValue));
|
|
break;
|
|
case SortMethod.ByTrackTimeDesc:
|
|
list.Sort((a, b) => DateTimeHelper.ConvertUtc0Compare(b.TrackTime, b.TrackTimeZoneValue, a.TrackTime, a.TrackTimeZoneValue));
|
|
break;
|
|
case SortMethod.ByCreateTime:
|
|
list.Sort((a, b) => a.CreateTime.CompareTo(b.CreateTime));
|
|
break;
|
|
case SortMethod.ByCreateTimeDesc:
|
|
list.Sort((a, b) => b.CreateTime.CompareTo(a.CreateTime));
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(sortMethod), sortMethod, null);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 按时间轴获取轨迹内容
|
|
/// </summary>
|
|
/// <param name="input"></param>
|
|
/// <returns></returns>
|
|
[HttpPost("GetDetailTimeline")]
|
|
public async Task<List<GetDetailTimelineOutput>> GetDetailTimeline(GetDetailTimelineInput input)
|
|
{
|
|
var res = new List<GetDetailTimelineOutput>();
|
|
|
|
var order = await _orderRepository.GetFirstAsync(m => m.OrderNo == input.OrderNumber);
|
|
if (order == null)
|
|
throw new BusinessException("订单不存在");
|
|
|
|
var trackDetails = await _trackDetailsEsRepository.GetListAsync(order.Id);
|
|
//时间轴不显示轨迹内容为空的轨迹,进行过滤
|
|
trackDetails = trackDetails.Where(x => !x.TrackContent.IsNullOrWhiteSpace() && x.TrackContent != GlobalConst.EmptyString).ToList();
|
|
|
|
foreach (var item in trackDetails)
|
|
{
|
|
//时间和时区任意一个为空则使用创建时间做为排序参照值
|
|
var timeTick = ToUnixTimestamp(Math.Abs(item.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
|
|
? item.CreateTime
|
|
: item.TrackTime);
|
|
res.Add(new GetDetailTimelineOutput()
|
|
{
|
|
IsHighlight = true,
|
|
IsShowHover = true,
|
|
Remark = item.Remarks == null || item.Remarks == "--" ? "" : item.Remarks,
|
|
TrackTimeZone = $"GMT{(item.TrackTimeZone >= 0 ? "+" : "-")}{GetTimeZoneStr(item.TrackTimeZone)}",
|
|
TrackTime = timeTick,
|
|
TrackPlace = item.TrackPlace,
|
|
EnContent = item.TrackEnContent,
|
|
Files = item.Files == null
|
|
? new List<TrackFileItem>()
|
|
: item.Files!.Select(m => new TrackFileItem()
|
|
{
|
|
OssKey = m.OssKey,
|
|
FileName = m.FileName,
|
|
Url = m.OssKey.GetUrlByKey(),
|
|
}).ToList(),
|
|
Content = item.TrackContent,
|
|
TrackCode = item.TrackCode,
|
|
TrackPlaceNation = item.TrackPlaceNation,
|
|
TrackPlaceProvince = item.TrackPlaceProvince,
|
|
TrackPlaceCity = item.TrackPlaceCity,
|
|
});
|
|
}
|
|
|
|
// 按轨迹发生时间正序排序
|
|
res = res.OrderBy(m => m.TrackTime).ToList();
|
|
|
|
return res;
|
|
}
|
|
|
|
public static long ToUnixTimestamp(DateTime time)
|
|
{
|
|
var sTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Utc, TimeZoneInfo.Local);
|
|
return (long) (time - sTime).TotalMilliseconds;
|
|
}
|
|
|
|
private static string GetTimeZoneStr(float timeZone)
|
|
{
|
|
var time = timeZone.ToString().Split('.');
|
|
if (time.Length > 1 && time[1] != "0") //如果时区有小数点
|
|
{
|
|
return time[0] + ":30";
|
|
}
|
|
else
|
|
{
|
|
return time[0] + ":00";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据轨迹id删除单条轨迹
|
|
/// </summary>
|
|
/// <param name="trackId">轨迹id</param>
|
|
/// <returns></returns>
|
|
[HttpPost("DeleteTrackById")]
|
|
public async Task<bool> DeleteTrackByIdAsync(string trackId)
|
|
{
|
|
var track = await _trackDetailsEsRepository.GetByIdAsync(trackId);
|
|
|
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
|
if (track == null)
|
|
{
|
|
throw new BusinessException(message: "轨迹不存在!");
|
|
}
|
|
|
|
await _trackDetailsEsRepository.DeleteTrackByIdAsync(trackId, track.IndexName);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据订单id查询订单操作记录
|
|
/// </summary>
|
|
/// <param name="orderId">订单id</param>
|
|
/// <returns></returns>
|
|
[HttpGet("GetOrderOperationsById")]
|
|
public async Task<List<OrderOperationRecordIndex>> GetOrderOperationsByIdAsync(int orderId)
|
|
{
|
|
return await _orderOperationRecordEsRepository.GetOrderOperationsByIdAsync(orderId);
|
|
}
|
|
|
|
private void WarningCalc_OrderDeletedOrCompleted(OrderEntity order)
|
|
{
|
|
_ = Task.Run(async () =>
|
|
{
|
|
var scope = _serviceProvider.CreateAsyncScope();
|
|
try
|
|
{
|
|
var handler = scope.ServiceProvider.GetRequiredService<IOrderWarningHandler>();
|
|
await handler.HandleOrderDeletedOrCompletedAsync(order);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex.ToString());
|
|
}
|
|
finally
|
|
{
|
|
await scope.DisposeAsync();
|
|
}
|
|
});
|
|
}
|
|
}
|