using System.Linq.Expressions; using Fake.Application.Dtos; using InterfaceForward.Domain.Shared.Helpers; using TrackCenter.Application.Dtos.TrackPush; using TrackCenter.Application.Helpers; using TrackCenter.Application.Services.Warning.Handlers; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.TrackPushAggregate; using TrackCenter.Domain.Dtos.TrackPush; namespace TrackCenter.Application.Services.TrackPush; /// /// 轨迹推送 /// [ApiExplorerSettings(GroupName = "轨迹推送")] [Route("/Track/PushConfig")] public class TrackPushService( ITrackPushConfigRepository trackPushConfigRepository, TrackPushCacheService trackPushCacheService, IServiceProvider serviceProvider) : ApplicationServiceBase, ISingletonDependency { /// /// 轨迹推送配置列表 /// /// /// [HttpPost("GetPage")] public async Task> GetPageAsync([FromBody] KeywordsInputVO input) { return await trackPushConfigRepository.GetPageAsync(input); } /// /// 轨迹推送配置内容列表 /// /// /// [HttpPost("GetContentPage")] public async Task> GetContentPageAsync( [FromBody] GetTrackPushContentPageInputVO input) { return await trackPushConfigRepository.GetContentPageAsync(input); } /// /// 轨迹推送配置详情 /// /// /// [HttpGet("GetDetail")] public async Task GetDetailAsync(int id) { return await trackPushConfigRepository.GetDetailAsync(id); } /// /// 轨迹推送配置内容详情 /// /// /// [HttpGet("GetContentDetail")] public async Task GetContentDetailAsync(int id) { return await trackPushConfigRepository.GetContentDetailAsync(id); } /// /// 轨迹推送配置编辑 /// /// /// [HttpPost("Edit")] public async Task EditAsync([FromBody] EditTrackPushConfigInput input) { if (input.Options != OptionTypeNoNull.删除) { Expression> isAnyExp = x => x.Customer == input.Customer; if (input.Options == OptionTypeNoNull.修改) isAnyExp = isAnyExp.And(x => x.Id != input.Id); if (await trackPushConfigRepository.IsAnyAsync(isAnyExp)) throw new BusinessException(message: "配置已存在"); } var msg = string.Empty; if (input.Options is OptionTypeNoNull.新增 or OptionTypeNoNull.修改) { var (success, message) = CheckDateTimeFormat(input.DateTimeFormat, input.DateTimeFormatType, input.TimezoneFormat); if (!success) throw new BusinessException(message: message); msg = message; } using var ts = TransacationHelper.GetReadCommitted(); switch (input.Options) { case OptionTypeNoNull.新增: var trackPushEntity = ObjectMapper.Map(input); await trackPushConfigRepository.InsertReturnIdentityAsync(trackPushEntity); await trackPushCacheService.SetPushContentConfigsAsync(trackPushEntity.AppKey, trackPushEntity.Customer); break; case OptionTypeNoNull.修改: var oldTrackPushEntity = await trackPushConfigRepository.GetFirstAsync(x => x.Id == input.Id) ?? throw new BusinessException(message: "找不到数据"); ObjectMapper.Map(input, oldTrackPushEntity); await trackPushConfigRepository.UpdateAsync(oldTrackPushEntity); await trackPushCacheService.SetPushContentConfigsAsync(oldTrackPushEntity.AppKey, oldTrackPushEntity.Customer); break; case OptionTypeNoNull.删除: var entity = await trackPushConfigRepository.GetFirstAsync(x => x.Id == input.Id) ?? throw new BusinessException(message: "找不到数据"); await trackPushConfigRepository.SoftDeleteAsync(x => x.Id == input.Id); await trackPushConfigRepository.SoftDeleteContentAsync(x => x.TrackPushConfigId == input.Id); await trackPushCacheService.SetPushContentConfigsAsync(entity.AppKey, entity.Customer); break; default: throw new ArgumentOutOfRangeException(); } ts.Complete(); return msg; } /// /// 轨迹推送配置内容编辑 /// /// /// [HttpPost("EditContent")] public async Task EditContentAsync([FromBody] EditTrackPushContentInput input) { if (input.Options != OptionTypeNoNull.删除) { Expression> isContentAnyExp = x => x.TrackPushConfigId == input.TrackPushConfigId && x.OrderNoType == input.OrderNoType; if (input.Options == OptionTypeNoNull.修改) isContentAnyExp = isContentAnyExp.And(x => x.Id != input.Id); if (await trackPushConfigRepository.IsContentAnyAsync(isContentAnyExp.And(x => x.TrackContentId == input.TrackContentId))) throw new BusinessException(message: "TrackContentId配置已存在"); if (!input.TrackContentCodeValue.IsNullOrWhiteSpace() && await trackPushConfigRepository.IsContentAnyAsync(isContentAnyExp.And(x => x.TrackContentCodeValue == input.TrackContentCodeValue))) throw new BusinessException(message: "TrackContentCodeValue配置已存在"); if (!input.TrackStatusValue.IsNullOrWhiteSpace() && await trackPushConfigRepository.IsContentAnyAsync(isContentAnyExp.And(x => x.TrackStatusValue == input.TrackStatusValue))) throw new BusinessException(message: "TrackStatusValue配置已存在"); if (!input.TrackContentValue.IsNullOrWhiteSpace() && await trackPushConfigRepository.IsContentAnyAsync(isContentAnyExp.And(x => x.TrackContentValue == input.TrackContentValue))) throw new BusinessException(message: "TrackContentValue配置已存在"); if (!input.TrackEnContentValue.IsNullOrWhiteSpace() && await trackPushConfigRepository.IsContentAnyAsync(isContentAnyExp.And(x => x.TrackEnContentValue == input.TrackEnContentValue))) throw new BusinessException(message: "TrackEnContentValue配置已存在"); } var trackPushConfigId = input.TrackPushConfigId; using var ts = TransacationHelper.GetReadCommitted(); TrackPushContentConfigEntity? oldContentConfig = null; TrackPushContentConfigEntity? postContentConfig = null; switch (input.Options) { case OptionTypeNoNull.新增: { var maxSort = await trackPushConfigRepository.ContentMaxAsync(x => x.Sort, x => x.OrderNoType == input.OrderNoType && x.TrackPushConfigId == input.TrackPushConfigId ); var entity = ObjectMapper.Map(input); entity.Sort = maxSort + 1; await trackPushConfigRepository.InsertContentAsync(entity); break; } case OptionTypeNoNull.修改: { var entity = await trackPushConfigRepository.GetContentByIdAsync(input.Id!.Value) ?? throw new BusinessException(message: "找不到数据"); oldContentConfig = entity.DeepCopy(); ObjectMapper.Map(input, entity); await trackPushConfigRepository.UpdateContentAsync(entity); postContentConfig = entity; break; } case OptionTypeNoNull.删除: { var entity = await trackPushConfigRepository.GetContentByIdAsync(input.Id!.Value) ?? throw new BusinessException(message: "找不到数据"); oldContentConfig = entity; trackPushConfigId = entity.TrackPushConfigId; await trackPushConfigRepository.SoftDeleteContentAsync(x => x.Id == input.Id); await trackPushConfigRepository.UpdateContentAsync(x => x.Sort == x.Sort - 1, x => x.OrderNoType == entity.OrderNoType && x.TrackPushConfigId == entity.TrackPushConfigId && x.Sort > entity.Sort ); break; } default: throw new ArgumentOutOfRangeException(); } var pushConfig = await trackPushConfigRepository.GetFirstAsync(x => x.Id == trackPushConfigId); if (pushConfig != null) { await trackPushCacheService.SetPushContentConfigsAsync(pushConfig.AppKey, pushConfig.Customer); } ts.Complete(); // 引发预警计算 WarningCalc_PushContentConfigChanged(pushConfig, oldContentConfig, postContentConfig); return true; } /// /// 轨迹推送配置内容排序 /// /// /// [HttpPost("SortContent")] public async Task SortContentAsync([FromBody] SortTrackPushContentInput input) { var ids = input.SortInfo.Select(x => int.Parse(x.Key)).ToList(); var trackPushContentEntities = await trackPushConfigRepository.GetContentListAsync(x => ids.Contains(x.Id)); trackPushContentEntities.ForEach(item => { item.Sort = input.SortInfo.Single(x => x.Key == item.Id.ToString()).Value; }); if (trackPushContentEntities.Count > 0) { using var ts = TransacationHelper.GetReadCommitted(); await trackPushConfigRepository.UpdateContentRangeAsync(trackPushContentEntities); var pushConfig = await trackPushConfigRepository.GetFirstAsync( x => x.Id == trackPushContentEntities[0].TrackPushConfigId, y => new { y.AppKey, y.Customer }); if (pushConfig != null) { await trackPushCacheService.SetPushContentConfigsAsync(pushConfig.AppKey, pushConfig.Customer); } ts.Complete(); } return true; } private void WarningCalc_PushContentConfigChanged(TrackPushConfigEntity? pushConfig, TrackPushContentConfigEntity? oldContentConfig, TrackPushContentConfigEntity? postContentConfig) { // 推送配置主体不存在? if (pushConfig == null) return; // 说明是新增 if (oldContentConfig == null) return; // 删除 postContentConfig == null 否则为更新(tips:判断更新差异化比较?) var contentConfigEntity = postContentConfig ?? oldContentConfig; _ = Task.Run(async () => { var scope = serviceProvider.CreateScope(); try { var handler = scope.ServiceProvider.GetRequiredService(); await handler.PushConfigChangedHandleAsync(pushConfig, contentConfigEntity); } catch (Exception ex) { Log.Error(ex.ToString()); } finally { scope.Dispose(); } }); } private static (bool Success, string Message) CheckDateTimeFormat(string? dateTimeFormat, int? dateTimeFormatType, string? timezoneFormat) { const int defaultTimeZone = 8; var dateTime = DateTime.Now; string dateStr; try { if (dateTimeFormatType == (int) DateTimeFormatEnum.Timestamp) { dateStr = DateTimeHelper.ConvertToUnixTimestamp(dateTime).ToString(); return (true, $"格式化样例:{dateStr}"); } if (dateTimeFormatType == (int) DateTimeFormatEnum.DateTimeAndTimezone) { // 没时间有时区,出错 if (string.IsNullOrWhiteSpace(dateTimeFormat) && !string.IsNullOrWhiteSpace(timezoneFormat)) return (false, "如果填写了时区格式,那么时间格式是必须的"); // 只有时间 if (!string.IsNullOrWhiteSpace(dateTimeFormat) && string.IsNullOrWhiteSpace(timezoneFormat)) { var datetimeStr = DateTimeHelper.FormatDateTime(dateTime, dateTimeFormat); return (true, $"格式化样例:{datetimeStr}"); } // 时间和时区同时存在 if (!string.IsNullOrWhiteSpace(dateTimeFormat) && !string.IsNullOrWhiteSpace(timezoneFormat)) { var zoneStr = DateTimeHelper.TimezoneFormat(defaultTimeZone, timezoneFormat); var datetimeStr = DateTimeHelper.FormatDateTime(dateTime, dateTimeFormat); dateStr = $"{datetimeStr} {zoneStr}"; return (true, $"格式化样例:{dateStr}"); } } } catch (Exception e) { return (false, $"时间格式错误请检查:{e.Message}"); } // 默认情况 dateTimeFormat ??= "yyyy-MM-ddTHH:mm:ss.fffK"; dateStr = dateTime.ToString(dateTimeFormat); return (true, $"格式化样例:{dateStr}"); } }