using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Track.Application.Contract.Dtos.Warning; using SJZY.Track.Application.Contract.Dtos.Warning.Transport; using SJZY.Track.Application.Helpers; using SJZY.Track.Application.Services.Warning.Handlers; using SJZY.Track.Application.Services.Warning.Model; using SJZY.Track.Domain.Aggregates.KPIAreaAggregate; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; using SJZY.Track.Domain.Aggregates.WarningAggregate; using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport; using SJZY.Track.Domain.Dtos.Warning; using SJZY.Track.Domain.Enums; using SJZY.Track.Domain.Extensions; using SJZY.Track.Domain.Queries; namespace SJZY.Track.Application.Services.Warning; /// /// 运输时效预警服务 /// [ApiExplorerSettings(GroupName = "预警服务")] [Route("Warning/Transport")] public class TransportWarningService : ApplicationService { private readonly IOrderRepository _orderRepository; private readonly IVExternalSubjectQueries _vExternalSubjectQueries; private readonly IVSystemQueries _vSystemQueries; private readonly IWarningRuleRepository _warningRuleRepository; private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; private readonly ITransportWarningHandler _transportWarningHandler; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; private readonly ITrackStatusRepository _trackStatusRepository; private readonly IKpiAreaRepository _kpiAreaRepository; public TransportWarningService(IWarningRuleRepository warningRuleRepository, IWarningDetailsEsRepository warningDetailsEsRepository, ITransportWarningHandler transportWarningHandler, ITrackDetailsEsRepository trackDetailsEsRepository, ITrackStatusRepository trackStatusRepository, IKpiAreaRepository kpiAreaRepository, IOrderRepository orderRepository, IVExternalSubjectQueries vExternalSubjectQueries, IVSystemQueries vSystemQueries) { _warningRuleRepository = warningRuleRepository; _warningDetailsEsRepository = warningDetailsEsRepository; _transportWarningHandler = transportWarningHandler; _trackDetailsEsRepository = trackDetailsEsRepository; _trackStatusRepository = trackStatusRepository; _kpiAreaRepository = kpiAreaRepository; _orderRepository = orderRepository; _vExternalSubjectQueries = vExternalSubjectQueries; _vSystemQueries = vSystemQueries; } /// /// 获取预警规则场景 /// /// /// [HttpPost("GetTransportWarningScene")] public async Task>> GetTransportWarningScene(GetTransportWarningSceneInput input) { var entities = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId); if (entities.Count == 0) return new UnifyResultDto>(true, Empty.List); var areaIds = entities .Select(x => x.AreaId) .Where(x => x != null) .Select(x => x!.Value) .ToHashSet(); // todo:加缓存 Dictionary? areaNameDict = null; if (areaIds.Count > 0) { var areas = await _kpiAreaRepository.GetListAsync(x => areaIds.Contains(x.Id), y => new { y.Id, y.Name }); areaNameDict = areas.ToDictionary(x => x.Id, y => y.Name); } var map = entities.Select(x => new TransportWarningSceneDto { Id = x.Id, WarningId = x.WarningId, Summary = BuildSummary(x), IsTransit = x.IsTransit, CargoType = x.CargoType, LeavePort = x.LeavePort, ProductCode = x.ProductCode, Months = x.Months, AreaId = x.AreaId, AreaName = x.AreaId.HasValue ? areaNameDict?.GetValueOrDefault(x.AreaId.Value) : null }); return new UnifyResultDto>(true, map.ToList()); // 构建场景摘要 string BuildSummary(TransportWarningSceneEntity entity) { const string separator = "/"; // var sb = new StringBuilder(); var list = new List(6); // 货物类型 if (!string.IsNullOrWhiteSpace(entity.CargoType)) list.Add($"{entity.CargoType}"); // 月份 if (!string.IsNullOrWhiteSpace(entity.Months)) list.Add($"{entity.Months.Replace("/", ",")}月份"); // 中转 if (entity.IsTransit != null) list.Add($"中转为{(entity.IsTransit.Value ? "是" : "否")}"); // 起运港口 if (!string.IsNullOrWhiteSpace(entity.LeavePort)) list.Add($"{entity.LeavePort}"); // 产品编码 if (!string.IsNullOrWhiteSpace(entity.ProductCode)) list.Add($"{entity.ProductCode}"); // 区域名称 if (entity.AreaId != null && areaNameDict != null) { var areaName = areaNameDict.GetValueOrDefault(entity.AreaId.Value); if (!string.IsNullOrWhiteSpace(areaName)) list.Add($"{areaName}"); } return string.Join(separator, list); } } /// /// 获取运输时效场景规则项目 /// /// /// [HttpPost("GetTransportWarningSceneRuleItem")] public async Task>> GetTransportWarningSceneRuleItem(GetTransportWarningSceneRuleItemInput input) { var entities = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(input.WarningId, input.SceneId); // todo:缓存优化 var trackStatusIds = entities.SelectMany(x => new List(2) { x.StartTrackStatusId, x.EndTrackStatusId }).ToHashSet(); var trackStatusDict = (await _trackStatusRepository.GetListAsync(x => trackStatusIds.Contains(x.Id), x => new { x.Id, x.Status })).ToDictionary(x => x.Id, y => y.Status); var map = entities.Select(x => new TransportWarningSceneRuleItemDto { Id = x.Id, StartTrackStatusId = x.StartTrackStatusId, StartTrackStatus = trackStatusDict.GetValueOrDefault(x.StartTrackStatusId, GlobalConst.EmptyString), EndTrackStatusId = x.EndTrackStatusId, EndTrackStatus = trackStatusDict.GetValueOrDefault(x.EndTrackStatusId, GlobalConst.EmptyString), TransportTimeSpan = x.TransportTimeSpan, StartTrackWarningTimeSpan = x.StartTrackWarningTimeSpan, NotifyUsers = x.NotifyUsers.ToObject>() }); return new UnifyResultDto>(true, map.ToList()); } /// /// 分页获取运输时效预警明细 /// /// /// [HttpPost("GetPageTransportWarningDetails")] public async Task> GetPageTransportWarningDetails(GetPageTransportWarningDetailsInput input) { var vo = ObjectMapper.Map(input); // 存储只存即将超时,对于已超时的则是查出计算后过滤 if (input.Status == (int) TransportWarningStatus.已超时) vo.Status = (int) TransportWarningStatus.即将超时; var result = await _warningDetailsEsRepository.GetPageTransportWarningDetailsAsync(vo); if (result.List.Count == 0) { return new UnifyPageResultDto(new List(), input.PageIndex, input.PageSize, 0); } // 查最新轨迹 var orderIds = result.List.Select(x => x.OrderId).ToHashSet(); var orderIdTrackDict = await _trackDetailsEsRepository.GetLatestDictAsync(orderIds); // todo:缓存优化 // 订单类型 var orderTypeDict = await _orderRepository.GetTrackOrderTypeDictAsync(); // 客户简称 var customIds = result.List.Select(x => x.CustomId).ToHashSet(); var customDict = (await _vExternalSubjectQueries .GetListAsync(x => customIds.Contains(x.Id), x => new { x.Id, x.Abbreviation })).ToDictionary(x => x.Id, x => x.Abbreviation); // 子系统 var subSystemIds = result.List.Select(x => x.SubSystemId).ToHashSet(); var subSystemDict = (await _vSystemQueries .GetListAsync(x => subSystemIds.Contains(x.Id), x => new { x.Id, x.CnName })).ToDictionary(x => x.Id, x => x.CnName); var map = result.List.Select(x => { var orderIdTrack = orderIdTrackDict.GetValueOrDefault(x.OrderId); return new TransportWarningDetailsDto { EsId = x.Id, SubSystemId = x.SubSystemId, SubSystemName = subSystemDict.GetValueOrDefault(x.SubSystemId, GlobalConst.EmptyString), CustomId = x.CustomId, CustomName = customDict.GetValueOrDefault(x.CustomId, GlobalConst.EmptyString), OrderId = x.OrderId, OrderNo = x.OrderNo, OrderType = x.OrderType, OrderTypeName = orderTypeDict.GetValueOrDefault(x.OrderType.ToString(), GlobalConst.EmptyString), NotifyUserNames = ToUserNames(x.NotifyUsers), // 最新轨迹 (不冗余) LatestTrackContent = orderIdTrack?.TrackContent, LatestTrackTime = orderIdTrack?.TrackTime, LatestTrackTimeZone = DateTimeHelper.ConvertFloatToTimeZone(orderIdTrack?.TrackTimeZone), LatestTrackPlace = orderIdTrack?.TrackPlace, // 起始轨迹 StartTrackStatus = x.StartTrackStatus, StartTrackContent = x.StartTrackContent, StartTrackTime = x.StartTrackTime, StartTrackTimeZone = DateTimeHelper.ConvertFloatToTimeZone(x.StartTrackTimeZone), // 截止轨迹 EndTrackStatus = x.EndTrackStatus, EndTrackContent = x.EndTrackContent, EndTrackTime = x.EndTrackTime, EndTrackTimeZone = DateTimeHelper.ConvertFloatToTimeZone(x.EndTrackTimeZone), WarningName = x.WarningName, WarningRuleDescription = x.WarningRuleDescription, // 累计运输时间 CumulativeTimeSpan = FormatCumulativeTimeSpan(x), // 状态动态计算 Status = x.CalcStatus(), CreateTime = x.CreateTime }; }); // 计算后的状态过滤 var filtered = map.WhereIf(input.Status > 0, x => x.Status == input.Status); // 按状态组+组内时间降序,分割状态组 g1:(即将超时/已超时) g2:(已完成/超时完成/已关闭) 确保g1总是在g2之前 var sorted = filtered .OrderBy(x => x.Status is (int) TransportWarningStatus.即将超时 or (int) TransportWarningStatus.已超时 ? 1 : 2) .ThenByDescending(x => x.CreateTime) //同状态组按时间降序 .ToList(); return new UnifyPageResultDto( sorted, result.Pagination.PageIndex, result.Pagination.PageSize, sorted.Count); // 通知用户提取用户名称 string ToUserNames(string notifyUsers) { if (string.IsNullOrWhiteSpace(notifyUsers)) return string.Empty; return string.Join(',', notifyUsers.ToObject>().Select(x => x.UserName)); } // 格式化累计运输时间为文本形式 string FormatCumulativeTimeSpan(TransportWarningDetailsIndex detail) { // 如果是完成态则直接取缓存的累计运输时间 var timeSpan = detail.CanCompleted() ? TimeSpan.FromMilliseconds(detail.CumulativeTimeSpan) : DateTime.Now.Subtract(detail.StartTrackTime); return $"{timeSpan.Days:00}天{timeSpan.Hours:00}小时{timeSpan.Minutes:00}分钟"; } } /// /// 新增/更新 运输时效场景 /// /// /// [HttpPost("UpsertTransportWarningScene")] public async Task> UpsertTransportWarningScene(UpsertTransportWarningSceneInput input) { var rule = await _warningRuleRepository.GetWarningRuleByIdAsync(input.WarningId, WarningRuleType.运输时效预警); if (rule == null) { throw new BusinessException(message: $"预警规则主体不存在,WarningId=[{input.WarningId}]"); } var postBo = TransportWarningSceneBO.From(input); // 查出旧场景 var oldScene = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId, input.Id); var oldBo = oldScene != null ? TransportWarningSceneBO.From(oldScene) : null; // 如果有月份则排序之后在写入 if (!string.IsNullOrWhiteSpace(postBo.Months)) { var sorted = postBo.Months.Split('/').Select(int.Parse).OrderBy(x => x).ToList(); postBo.Months = string.Join('/', sorted); } // 更新或者新增 var id = await _warningRuleRepository.UpsertTransportSceneReturnIdAsync(postBo.ToEntity()); if (oldBo != null) await _transportWarningHandler.HandleSceneChangedAsync(rule, oldBo, postBo); return new UnifyResultDto(true, new IdBaseDto { Id = id }); } /// /// 删除运输时效规则场景 /// /// /// [HttpPost("DeleteTransportWarningScene")] public async Task DeleteTransportWarningScene(DeleteTransportWarningSceneInput input) { var rule = await _warningRuleRepository.GetWarningRuleByIdAsync(input.WarningId, WarningRuleType.运输时效预警); if (rule == null) { throw new BusinessException(message: $"预警规则主体不存在,WarningId=[{input.WarningId}]"); } var scene = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId, input.Id); if (scene == null) { throw new BusinessException(message: $"预警规则场景不存在,WarningId=[{input.WarningId}]"); } // 执行删除 await _warningRuleRepository.DeleteTransportSceneAsync(input.WarningId, input.Id); // 处理删除后 await _transportWarningHandler.HandleSceneDeletedAsync(rule, TransportWarningSceneBO.From(scene)); return new UnifyResultDto(true); } /// /// 编辑预警规则项目(全量) /// /// /// [HttpPost("EditTransportWarningSceneRuleItem")] public async Task EditTransportWarningSceneRuleItem(EditTransportWarningSceneRuleItemInput input) { // 起始轨迹生成预警时间不能大于时效 if (input.Items.Count > 0 && input.Items.Any(x => x.StartTrackWarningTimeSpan >= x.TransportTimeSpan)) { throw new BusinessException(message: "起始轨迹生成预警时间不能大于等于时效,请检查"); } var rule = await _warningRuleRepository.GetWarningRuleByIdAsync(input.WarningId, WarningRuleType.运输时效预警); if (rule == null) { throw new BusinessException(message: $"预警规则主体不存在,WarningId=[{input.WarningId}]"); } var ruleScene = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId, input.SceneId); if (ruleScene == null) { throw new BusinessException(message: $"运输时效规则场景不存在,WarningIdId=[{input.WarningId}] SceneId=[{input.SceneId}]"); } var postRuleItems = TransportWarningSceneRuleItemBO.From(input); var tuples = postRuleItems.Select(x => (x.StartTrackStatusId, x.EndTrackStatusId)).ToHashSet(); // 规则项目间不允许出现起始轨迹和截止轨迹都重复的项目 if (tuples.Count < postRuleItems.Count) { throw new BusinessException(message: "不允许出现轨迹状态和终止轨迹状态都重复的规则项目"); } // 起始和截止不能相同 if (tuples.Any(x => x.StartTrackStatusId == x.EndTrackStatusId)) { throw new BusinessException(message: "同一规则项目起始轨迹状态和截止轨迹状态不能相同"); } var sceneBo = TransportWarningSceneBO.From(ruleScene); await _transportWarningHandler.HandleEditAsync(rule, sceneBo, postRuleItems); return new UnifyResultDto(true); } }