track/src/TrackCenter.Application/Services/TrackContents/TrackContentService.cs
xiaolipro ef4280ef0f
Some checks failed
CI/CD Pipeline / deploy (push) Has been cancelled
upgrade:框架迁移到ideship
2026-09-05 16:13:08 +08:00

522 lines
20 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 System.Linq.Expressions;
using Fake.Domain.Dtos;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Application.Dtos.TrackContents;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Helpers.OSS;
using TrackCenter.Application.Services.TrackPush;
using TrackCenter.Domain.Aggregates.OrderAggregate;
using TrackCenter.Domain.Aggregates.TrackContentAggregate;
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
using TrackCenter.Domain.Aggregates.TrackFlagAggregate;
using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
using TrackCenter.Domain.Dtos.TrackContents;
namespace TrackCenter.Application.Services.TrackContents;
/// <summary>
/// 轨迹内容服务
/// </summary>
[ApiExplorerSettings(GroupName = "轨迹内容")]
[Route("/Track/Content")]
public class TrackContentService : ApplicationServiceBase
{
private readonly ITrackContentRepository _trackContentRepository;
private readonly IBasicRepository<TrackFlagEntity> _trackFlagBasicQuery;
private readonly IBasicRepository<TrackStatusEntity> _trackStatusBasicQuery;
private readonly IOrderRepository _orderRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
private readonly TrackPushCacheService _trackPushCacheService;
public TrackContentService(
ITrackContentRepository trackContentRepository,
IBasicRepository<TrackStatusEntity> trackStatusBasicQuery,
IBasicRepository<TrackFlagEntity> trackFlagBasicQuery,
IOrderRepository orderRepository,
ITrackDetailsEsRepository trackDetailsEsRepository,
TrackPushCacheService trackPushCacheService)
{
_trackContentRepository = trackContentRepository;
_trackStatusBasicQuery = trackStatusBasicQuery;
_trackFlagBasicQuery = trackFlagBasicQuery;
_orderRepository = orderRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
_trackPushCacheService = trackPushCacheService;
}
/// <summary>
/// 轨迹内容编辑
/// </summary>
/// <returns></returns>
[HttpPost("Edit")]
public async Task EditAsync(EditTrackContentInput input)
{
if (input.TrackStatusId != null)
{
if (!await _trackStatusBasicQuery.IsAnyAsync(x =>
x.AppKey == input.AppKey && x.Id == input.TrackStatusId))
{
throw new BusinessException(message: "轨迹状态不存在");
}
}
if (!input.TrackFlagIds.IsNullOrEmpty())
{
var counter = await _trackFlagBasicQuery.CountAsync(x =>
x.AppKey == input.AppKey && input.TrackFlagIds!.Contains(x.Id));
if (counter != input.TrackFlagIds!.Length)
{
throw new BusinessException(message: "存在非法轨迹标记");
}
}
switch (input.Options)
{
case OptionType.无:
break;
case OptionType.新增:
await AddAsync(input);
break;
case OptionType.删除:
await DeleteAsync(input);
break;
case OptionType.修改:
await UpdateAsync(input);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// 根据内容id获取轨迹内容
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("GetDetail")]
public async Task<GetTrackContentDetailDto?> GetDetailAsync(int id)
{
var detailInfo = await _trackContentRepository.GetDetailAsync(id);
if (detailInfo == null) return null;
return ObjectMapper.Map<GetTrackContentDetailVO, GetTrackContentDetailDto>(detailInfo);
}
/// <summary>
/// 获取分页轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetList")]
public async Task<PagedResult<GetTrackContentListDto>> GetPageTrackContent(GetPageTrackContentInput input)
{
var vo = ObjectMapper.Map<GetPageTrackContentInput, GetPageTrackContentVO>(input);
var resultDto = await _trackContentRepository.GetPageTrackContent(vo);
var pageResultDto =
ObjectMapper.Map<PagedResult<GetTrackContentListVO>, PagedResult<GetTrackContentListDto>>(
resultDto);
return pageResultDto;
}
/// <summary>
/// 轨迹内容映射列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetMapPage")]
public async Task<PagedResult<GetTrackContentMapPageOutputVO>> GetMapPageAsync(
[FromBody] GetTrackContentMapPageInputVO input)
{
return await _trackContentRepository.GetMapPageAsync(input);
}
/// <summary>
/// 轨迹内容映射详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("GetMapDetail")]
public async Task<GetTrackContentMapDetailOutputVO> GetMapDetailAsync(int id)
{
return await _trackContentRepository.GetMapDetailAsync(id);
}
/// <summary>
/// 轨迹内容映射编辑
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("EditMap")]
public async Task<bool> EditMapAsync([FromBody] EditTrackContentMapInput input)
{
if (input.Options != OptionTypeNoNull.删除)
{
if (input.AppKey == input.MapAppKey)
throw new BusinessException("禁止自己映射自己");
Expression<Func<TrackContentMapEntity, bool>> isMapAnyExp = x =>
x.TrackContentId == input.TrackContentId && x.MapTrackContentId == input.MapTrackContentId;
if (input.Options == OptionTypeNoNull.修改) isMapAnyExp.And(x => x.Id != input.Id);
if (await _trackContentRepository.IsMapAnyAsync(isMapAnyExp))
throw new BusinessException("映射已经存在");
}
switch (input.Options)
{
case OptionTypeNoNull.新增:
var trackContentMapEntity = ObjectMapper.Map<EditTrackContentMapInput, TrackContentMapEntity>(input);
await _trackContentRepository.InsertMapAsync(trackContentMapEntity);
break;
case OptionTypeNoNull.修改:
var oldTrackContentMapEntity = await _trackContentRepository.GetMapByIdAsync(input.Id!.Value) ??
throw new BusinessException("找不到数据");
ObjectMapper.Map(input, oldTrackContentMapEntity);
await _trackContentRepository.UpdateMapAsync(oldTrackContentMapEntity);
break;
case OptionTypeNoNull.删除:
await _trackContentRepository.SoftDeleteMapAsync(x => x.Id == input.Id);
break;
default:
throw new ArgumentOutOfRangeException();
}
return true;
}
/// <summary>
/// 根据轨迹内容code获取轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetTrackContentByCode")]
public async Task<GetTrackContentByCodeOutput> GetTrackContentByCodeAsync([FromBody] GetTrackContentByCodeInput input)
{
var content = await _trackContentRepository.GetByCodeAsync(input.AppKey, input.Code);
if (content == null)
throw new BusinessException("在当前应用中未找到指定code的轨迹内容");
return new GetTrackContentByCodeOutput
{
Content = content.Content,
EnContent = content.EnContent,
IsExternal = content.IsExternal
};
}
/// <summary>
/// 根据应用、单号、订单类型、轨迹发生时间、轨迹code获取轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetContentDetail")]
public async Task<GetContentDetailOutput> GetContentDetail([FromBody] GetContentDetailInput input)
{
if (string.IsNullOrEmpty(input.AppKey))
throw new BusinessException("应用必须大于等于0");
if (string.IsNullOrEmpty(input.TrackCode))
throw new BusinessException("轨迹内容code不能为空");
if (string.IsNullOrEmpty(input.OrderNumber))
throw new BusinessException("订单code不能为空");
if (input.OrderType <= 0)
throw new BusinessException("订单类型不能为空");
if (input.TrackTime == default)
throw new BusinessException("轨迹发生时间不能为空");
var content = await _trackDetailsEsRepository.GetDetailAsync(input.OrderNumber, input.TrackCode, input.OrderType, input.AppKey, input.TrackTime);
if (content == default)
throw new BusinessException("在当前应用中未找到指定code的轨迹内容");
var res = ObjectMapper.Map<TrackDetailsIndex, GetContentDetailOutput>(content);
if (res.Files?.Count > 0)
{
var ossKeys = res.Files
.Where(x => !string.IsNullOrWhiteSpace(x.OssKey))
.Select(x => x.OssKey)
.ToArray();
if (ossKeys.Length == 0) return res;
var ossKeyUrlDict = OSSHelper.GetUrlByKeys(ossKeys);
foreach (var item in res.Files)
{
if (string.IsNullOrWhiteSpace(item.OssKey)) continue;
item.Url = ossKeyUrlDict.GetValueOrDefault(item.OssKey)!;
}
}
return res;
}
/// <summary>
/// 创建轨迹时获取可用的轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetAvailableContent")]
public async Task<List<GetAvailableContentOutput>> GetAvailableContent(GetAvailableContentInput input)
{
//获取当前订单下已有的轨迹内容
var order = await _orderRepository.GetFirstAsync(x => x.OrderNo == input.OrderNo && x.AppKey == input.AppKey);
if (order == null)
throw new BusinessException(message: "订单不存在!");
//获取轨迹详情
var trackDetails = await _trackDetailsEsRepository.GetListAsync(order.Id);
// 删除时去除掉外部服务商轨迹
if (input.TrackOptionType == TrackOptionType.删除)
{
trackDetails = trackDetails.Where(m => m.Source != (int) TrackSourceEnum.ExternalServiceProvide).ToList();
}
var res = new List<GetAvailableContentOutput>();
//进行推送时,匹配订单类型一致的轨迹
if (input.TrackOptionType == TrackOptionType.推送)
{
var pushConfigs = await _trackPushCacheService.GetPushContentConfigsAsync(order.AppKey, order.Customer);
//没有推送配置也不会展示任何下拉
if (pushConfigs == null || pushConfigs.Count == 0) return res;
//选订单类型和配置订单类型一致的配置的轨迹代码
var trackCodes = pushConfigs.Where(m => m.OrderType == order.OrderType)
.Select(x => x.TrackCode)
.Distinct()
.ToHashSet();
//根据配置过滤轨迹
trackDetails = trackDetails.Where(m => trackCodes.Contains(m.TrackCode)).ToList();
}
//进行删除和推送行为无任何轨迹内容,直接返回为空即可
if (input.TrackOptionType is TrackOptionType.删除 or TrackOptionType.推送 && trackDetails.Count == 0)
{
return res;
}
//获取当前应用下所有的轨迹内容
var contentList = await _trackContentRepository.GetListByAppKeyAsync(input.AppKey);
if (contentList == null)
throw new BusinessException(message: "当前应用下未找到任何可用的轨迹内容!");
foreach (var item in contentList)
{
//新增时,不允许重复的轨迹,跳过已存在轨迹内容
if (input.TrackOptionType == TrackOptionType.创建 && !item.IsRepeatable && trackDetails?.Any(m => m.TrackCode == item.Code) == true)
continue;
// 修改时跳过不存在轨迹内容
if (input.TrackOptionType == TrackOptionType.修改 && trackDetails != null && trackDetails.All(m => m.TrackCode != item.Code))
continue;
if (!item.IsAllowManuallyCreate && input.TrackOptionType == TrackOptionType.创建) //跳过不允许手动创建的轨迹内容
continue;
if (!item.IsAllowManuallyUpdate && input.TrackOptionType == TrackOptionType.修改) //跳过不允许手动修改的轨迹内容
continue;
res.Add(new GetAvailableContentOutput()
{
IsExternal = item.IsExternal,
EnContent = item.EnContent,
Content = item.Content,
Code = item.Code,
Id = item.Id
});
}
if (input.TrackOptionType == TrackOptionType.创建
|| trackDetails == null
|| trackDetails.Count == 0)
{
return res;
}
//修改/删除/推送 操作,查轨迹内容列表反映明细表数据,重新构造响应数据
var matchContent = new List<GetAvailableContentOutput>();
var contents = res.Select(x => new
{
x.Id,
x.IsExternal,
x.Code
});
foreach (var content in contents)
{
var match = trackDetails
.Where(x => x.TrackCode == content.Code)
.Select(matchItem => new GetAvailableContentOutput
{
IsExternal = content.IsExternal,
Id = content.Id,
EnContent = matchItem.TrackEnContent,
Content = matchItem.TrackContent,
Code = matchItem.TrackCode,
TrackPlace = matchItem.TrackPlace,
TrackTime = matchItem.TrackTime,
TrackTimeZone = matchItem.TrackTimeZone,
Source = matchItem.Source,
DestinationTimeZone = matchItem.DestinationTimeZone,
EstimatedDeliveryDate = matchItem.EstimatedDeliveryDate,
TrackPlacePostcode = matchItem.TrackPlacePostcode,
TrackPlaceNation = matchItem.TrackPlaceNation,
TrackPlaceNationId = matchItem.TrackPlaceNationId,
TrackPlaceProvince = matchItem.TrackPlaceProvince,
TrackPlaceProvinceId = matchItem.TrackPlaceProvinceId,
TrackPlaceCity = matchItem.TrackPlaceCity,
TrackPlaceCityId = matchItem.TrackPlaceCityId,
Remarks = matchItem.Remarks,
TrackTimeZoneValue = matchItem.DestinationTimeZone,
FileItems = matchItem.Files?.Select(x => new FileItemDto
{
FileName = x.FileName,
OssKey = x.OssKey
}).ToList()
});
matchContent.AddRange(match);
}
return res;
}
#region 私有方法
private async Task AddAsync(EditTrackContentInput input)
{
#region 业务逻辑校验
var trackContentEntitys = await _trackContentRepository.GetListAsync(a =>
a.AppKey == input.AppKey && (a.Code == input.Code ||
a.Content == input.Content || a.EnContent == input.EnContent));
if (trackContentEntitys.Any())
{
var codes = trackContentEntitys.FirstOrDefault(a => a.Code == input.Code);
if (codes != null)
{
throw new BusinessException(message: "该应用下轨迹代码已存在");
}
var contents = trackContentEntitys.FirstOrDefault(a => a.Content == input.Content);
if (contents != null)
{
throw new BusinessException(message: "该应用下轨迹内容已存在");
}
var contentEns = trackContentEntitys.FirstOrDefault(a => a.EnContent == input.EnContent);
if (contentEns != null)
{
throw new BusinessException(message: "该应用下轨迹内容英文已存在");
}
}
#endregion 业务逻辑校验
var entity = ObjectMapper.Map<EditTrackContentInput, TrackContentEntity>(input);
entity.TrackFlagId = input.TrackFlagIds?.JoinAsString(",");
await _trackContentRepository.InsertAsync(entity);
}
private async Task UpdateAsync(EditTrackContentInput input)
{
#region 业务逻辑校验
var trackContentEntities = await _trackContentRepository.GetListAsync(
a => a.Id != input.Id && a.AppKey == input.AppKey &&
(a.Code == input.Code || a.Content == input.Content || a.EnContent == input.EnContent));
if (trackContentEntities.Any())
{
var codes = trackContentEntities.FirstOrDefault(a => a.Code == input.Code);
if (codes != null)
{
throw new BusinessException(message: "该应用下轨迹代码已存在");
}
var contents = trackContentEntities.FirstOrDefault(a => a.Content == input.Content);
if (contents != null)
{
throw new BusinessException(message: "该应用下轨迹内容已存在");
}
var contentEns = trackContentEntities.FirstOrDefault(a => a.EnContent == input.EnContent);
if (contentEns != null)
{
throw new BusinessException(message: "该应用下轨迹内容英文已存在");
}
}
#endregion 业务逻辑校验
//更新的时候需要覆盖内容映射轨迹代码
var trackContent = await _trackContentRepository.GetFirstAsync(a => a.Id == input.Id);
if (trackContent == null)
{
throw new BusinessException(message: "轨迹内容不存在");
}
using (var ts = TransacationHelper.GetReadCommitted())
{
// 如果轨迹内容的应用和code发生变化,同步到轨迹映射表
if (trackContent.AppKey != input.AppKey || trackContent.Code != input.Code)
{
await _trackContentRepository.UpdateMapAsync(
x => new TrackContentMapEntity
{
AppKey = input.AppKey,
TrackContentCode = input.Code
},
x => x.TrackContentId == input.Id);
await _trackContentRepository.UpdateMapAsync(
x => new TrackContentMapEntity
{
MapAppKey = input.AppKey,
MapTrackContentCode = input.Code
}, x => x.MapTrackContentId == input.Id);
}
ObjectMapper.Map(input, trackContent);
trackContent.TrackFlagId = input.TrackFlagIds?.JoinAsString(",");
await _trackContentRepository.UpdateAsync(trackContent);
ts.Complete();
}
}
private async Task DeleteAsync(EditTrackContentInput input)
{
#region 业务校验
//var trackTemplateContents = await _trackTemplateContentBasicQuery.GetListAsync(a => a.TrackContentId == input.Id);
//if (trackTemplateContents.Any())
//{
// throw new BusinessException(message: "该轨迹内容关联轨迹模板,禁止删除");
//}
//需要加个判断,存在内容映射,则出提示
//var trackContentMapEntities = await _trackContentMapBasicQuery.GetListAsync(a => a.TrackContentId == input.Id || a.MapTrackContentId == input.Id);
//if (trackContentMapEntities.Any())
//{
// throw new BusinessException(message: "无法删除该内容,该轨迹内容已在轨迹内容映射中使用了,请检查");
//}
#endregion 业务校验
var isTrue = await _trackContentRepository.SoftDeleteAsync(x => x.Id == input.Id);
}
#endregion 私有方法
}