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);
}
}