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.AreaId != null && areaNameDict != null) { var areaName = areaNameDict.GetValueOrDefault(entity.AreaId.Value); if (!string.IsNullOrWhiteSpace(areaName)) list.Add($"{areaName}"); } // 中转 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}"); 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(), result.Pagination.PageIndex, result.Pagination.PageSize, result.Pagination.Total); } // 查最新轨迹(创建时间) var orderIds = result.List.Select(x => x.OrderId).ToHashSet(); var orderIdTrackDict = await _trackDetailsEsRepository.GetLatestDictAsync(orderIds); // 查轨迹状态最早轨迹 var orderIdTrackStatusEarliestTrackDict = await _trackDetailsEsRepository.GetTrackStatusEarliestDictAsync(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); var orderIdTrackStatusEarliestTracks = orderIdTrackStatusEarliestTrackDict.GetValueOrDefault(x.OrderId); var startStatusTrack = orderIdTrackStatusEarliestTracks?.GetValueOrDefault(x.StartTrackStatusId); var endStatusTrack = orderIdTrackStatusEarliestTracks?.GetValueOrDefault(x.EndTrackStatusId); 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 = startStatusTrack?.TrackContent ?? x.StartTrackContent, StartTrackTime = startStatusTrack?.TrackTime ?? x.StartTrackTime, StartTrackTimeZone = DateTimeHelper.ConvertFloatToTimeZone(startStatusTrack?.TrackTimeZone ?? x.StartTrackTimeZone), // 截止轨迹(最早发生的截止轨迹状态轨迹) EndTrackStatus = x.EndTrackStatus, EndTrackContent = endStatusTrack?.TrackContent ?? x.EndTrackContent, EndTrackTime = endStatusTrack?.TrackTime ?? x.EndTrackTime, EndTrackTimeZone = DateTimeHelper.ConvertFloatToTimeZone(endStatusTrack?.TrackTimeZone ?? x.EndTrackTimeZone), WarningName = x.WarningName, WarningRuleDescription = x.WarningRuleDescription, // 累计运输时间 CumulativeTimeSpan = FormatCumulativeTimeSpan(x, startStatusTrack, endStatusTrack), // 状态动态计算 Status = x.CalcStatus(startStatusTrack, endStatusTrack), 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.已超时 ? x.Status : 3) .ThenByDescending(x => x.CreateTime) //同状态组按时间降序 .ToList(); return new UnifyPageResultDto( sorted, result.Pagination.PageIndex, result.Pagination.PageSize, result.Pagination.Total); // 通知用户提取用户名称 string ToUserNames(string notifyUsers) { if (string.IsNullOrWhiteSpace(notifyUsers)) return string.Empty; return string.Join(',', notifyUsers.ToObject>().Select(x => x.UserName)); } // 格式化累计运输时间为文本形式 string FormatCumulativeTimeSpan(TransportWarningDetailsIndex detail, TrackDetailsIndex? startStatusTrack, TrackDetailsIndex? endStatusTrack) { // 计算累计运输时间 var timeSpan = detail.CalcCumulativeTimeSpan(startStatusTrack, endStatusTrack); 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 oldScenes = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId); var oldBos = oldScenes.Count > 0 ? TransportWarningSceneBO.From(oldScenes) : Empty.List; // 验证规则场景是否重复 if (oldBos.Any(old => old.Equals(postBo) && old.Id != postBo.Id)) { throw new BusinessException(message: "同一规则主体下,不允许出现因素完全相同的场景!"); } // 更新或者新增 var id = await _warningRuleRepository.UpsertTransportSceneReturnIdAsync(postBo.ToEntity()); var oldBo = oldBos.FirstOrDefault(x => x.Id == postBo.Id); 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); } }