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; /// /// 轨迹内容服务 /// [ApiExplorerSettings(GroupName = "轨迹内容")] [Route("/Track/Content")] public class TrackContentService : ApplicationServiceBase { private readonly ITrackContentRepository _trackContentRepository; private readonly IBasicRepository _trackFlagBasicQuery; private readonly IBasicRepository _trackStatusBasicQuery; private readonly IOrderRepository _orderRepository; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; private readonly TrackPushCacheService _trackPushCacheService; public TrackContentService( ITrackContentRepository trackContentRepository, IBasicRepository trackStatusBasicQuery, IBasicRepository trackFlagBasicQuery, IOrderRepository orderRepository, ITrackDetailsEsRepository trackDetailsEsRepository, TrackPushCacheService trackPushCacheService) { _trackContentRepository = trackContentRepository; _trackStatusBasicQuery = trackStatusBasicQuery; _trackFlagBasicQuery = trackFlagBasicQuery; _orderRepository = orderRepository; _trackDetailsEsRepository = trackDetailsEsRepository; _trackPushCacheService = trackPushCacheService; } /// /// 轨迹内容编辑 /// /// [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(); } } /// /// 根据内容id获取轨迹内容 /// /// /// [HttpGet("GetDetail")] public async Task GetDetailAsync(int id) { var detailInfo = await _trackContentRepository.GetDetailAsync(id); if (detailInfo == null) return null; return ObjectMapper.Map(detailInfo); } /// /// 获取分页轨迹内容 /// /// /// [HttpPost("GetList")] public async Task> GetPageTrackContent(GetPageTrackContentInput input) { var vo = ObjectMapper.Map(input); var resultDto = await _trackContentRepository.GetPageTrackContent(vo); var pageResultDto = ObjectMapper.Map, PagedResult>( resultDto); return pageResultDto; } /// /// 轨迹内容映射列表 /// /// /// [HttpPost("GetMapPage")] public async Task> GetMapPageAsync( [FromBody] GetTrackContentMapPageInputVO input) { return await _trackContentRepository.GetMapPageAsync(input); } /// /// 轨迹内容映射详情 /// /// /// [HttpGet("GetMapDetail")] public async Task GetMapDetailAsync(int id) { return await _trackContentRepository.GetMapDetailAsync(id); } /// /// 轨迹内容映射编辑 /// /// /// [HttpPost("EditMap")] public async Task EditMapAsync([FromBody] EditTrackContentMapInput input) { if (input.Options != OptionTypeNoNull.删除) { if (input.AppKey == input.MapAppKey) throw new BusinessException("禁止自己映射自己"); Expression> 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(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; } /// /// 根据轨迹内容code获取轨迹内容 /// /// /// [HttpPost("GetTrackContentByCode")] public async Task 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 }; } /// /// 根据应用、单号、订单类型、轨迹发生时间、轨迹code获取轨迹内容 /// /// /// [HttpPost("GetContentDetail")] public async Task 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(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; } /// /// 创建轨迹时获取可用的轨迹内容 /// /// /// [HttpPost("GetAvailableContent")] public async Task> 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(); //进行推送时,匹配订单类型一致的轨迹 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(); 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(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 私有方法 }