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; /// /// 轨迹订单服务 /// [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 轨迹订单列表 /// /// 获取分页的轨迹订单 /// /// /// [HttpPost("GetTrackOrderList")] public async Task> GetTrackOrderPagedList(GetPageTrackOrderInput input) { var vo = ObjectMapper.Map(input); var pageList = await _orderRepository.GetTrackOrderPagedList(vo); return pageList; } #endregion /// /// 更新订单状态 /// /// /// /// [HttpPost("UpdateOrderStatus")] public async Task 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; } /// /// 获取轨迹推送记录 /// /// /// 订单id [HttpGet("GetTrackPushRecords")] public async Task> GetTrackPushRecordsAsync(int orderId) { var records = await _trackPushRecordEsRepository.GetTrackPushRecordsAsync(orderId); var res = ObjectMapper.Map, List>(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; } /// /// 根据订单id获取轨迹明细列表 /// /// /// /// /// /// [HttpPost("GetDetails")] public async Task> GetTracksAsync(int? orderId, string? orderNo, string? appKey, SortMethod sortMethod = SortMethod.ByCreateTimeDesc) { Expression>? 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(); } var list = ObjectMapper.Map, List>(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; } /// /// 按时间轴获取轨迹内容 /// /// /// [HttpPost("GetDetailTimeline")] public async Task> GetDetailTimeline(GetDetailTimelineInput input) { var res = new List(); 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() : 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"; } } /// /// 根据轨迹id删除单条轨迹 /// /// 轨迹id /// [HttpPost("DeleteTrackById")] public async Task 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; } /// /// 根据订单id查询订单操作记录 /// /// 订单id /// [HttpGet("GetOrderOperationsById")] public async Task> 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(); await handler.HandleOrderDeletedOrCompletedAsync(order); } catch (Exception ex) { Log.Error(ex.ToString()); } finally { await scope.DisposeAsync(); } }); } }