track/src/SJZY.Track.Application/Services/Warning/TransportWarningService.cs
2025-03-31 16:03:33 +08:00

411 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// 运输时效预警服务
/// </summary>
[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;
}
/// <summary>
/// 获取预警规则场景
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetTransportWarningScene")]
public async Task<UnifyResultDto<List<TransportWarningSceneDto>>> GetTransportWarningScene(GetTransportWarningSceneInput input)
{
var entities = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId);
if (entities.Count == 0) return new UnifyResultDto<List<TransportWarningSceneDto>>(true, Empty<TransportWarningSceneDto>.List);
var areaIds = entities
.Select(x => x.AreaId)
.Where(x => x != null)
.Select(x => x!.Value)
.ToHashSet();
// todo:缓存优化
Dictionary<int, string>? 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<List<TransportWarningSceneDto>>(true, map.ToList());
// 构建场景摘要
string BuildSummary(TransportWarningSceneEntity entity)
{
const string separator = "/";
// var sb = new StringBuilder();
var list = new List<string>(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);
}
}
/// <summary>
/// 获取运输时效场景规则项目
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetTransportWarningSceneRuleItem")]
public async Task<UnifyResultDto<List<TransportWarningSceneRuleItemDto>>> GetTransportWarningSceneRuleItem(GetTransportWarningSceneRuleItemInput input)
{
var entities = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(input.WarningId, input.SceneId);
// todo:缓存优化
var trackStatusIds = entities.SelectMany(x => new List<int>(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<List<NotifyUserDto>>()
});
return new UnifyResultDto<List<TransportWarningSceneRuleItemDto>>(true, map.ToList());
}
/// <summary>
/// 分页获取运输时效预警明细
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetPageTransportWarningDetails")]
public async Task<UnifyPageResultDto<TransportWarningDetailsDto>> GetPageTransportWarningDetails(GetPageTransportWarningDetailsInput input)
{
var vo = ObjectMapper.Map<GetPageTransportWarningDetailsInput, GetPageTransportWarningDetailsInputVO>(input);
// 存储只存即将超时,对于已超时的则是查出计算后过滤
if (input.Status == (int) TransportWarningStatus.已超时)
vo.Status = (int) TransportWarningStatus.即将超时;
var result = await _warningDetailsEsRepository.GetPageTransportWarningDetailsAsync(vo);
if (result.List.Count == 0)
{
return new UnifyPageResultDto<TransportWarningDetailsDto>(new List<TransportWarningDetailsDto>(),
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<TransportWarningDetailsDto>(
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<List<NotifyUserDto>>().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}分钟";
}
}
/// <summary>
/// 新增/更新 运输时效场景
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("UpsertTransportWarningScene")]
public async Task<UnifyResultDto<IdBaseDto>> 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<TransportWarningSceneBO>.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<IdBaseDto>(true, new IdBaseDto
{
Id = id
});
}
/// <summary>
/// 删除运输时效规则场景
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("DeleteTransportWarningScene")]
public async Task<UnifyResultDto> 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);
}
/// <summary>
/// 编辑预警规则项目(全量)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("EditTransportWarningSceneRuleItem")]
public async Task<UnifyResultDto> 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);
}
}