522 lines
20 KiB
C#
522 lines
20 KiB
C#
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 私有方法
|
||
}
|