diff --git a/scripts/sql/20260801_transport_warning_scene_drop_legacy_cols.sql b/scripts/sql/20260801_transport_warning_scene_drop_legacy_cols.sql new file mode 100644 index 00000000..7636d957 --- /dev/null +++ b/scripts/sql/20260801_transport_warning_scene_drop_legacy_cols.sql @@ -0,0 +1,211 @@ +-- 运输时效场景表瘦身:删除历史匹配字段,并同步现行字段注释 +-- 执行前请备份 t_warning_transport_scene +-- 依赖:已执行 20260801_transport_warning_simplify.sql(CountryCode / AssessmentTotalTimeSpan 已存在) +-- 注释更新通过 information_schema 保留原列类型,避免误改长度/默认值 + +-- 1) 删除历史字段(列不存在则跳过,避免 1091) +SET @schema = DATABASE(); + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning_transport_scene` DROP COLUMN `IsTransit`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning_transport_scene' AND COLUMN_NAME = 'IsTransit' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning_transport_scene` DROP COLUMN `CargoType`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning_transport_scene' AND COLUMN_NAME = 'CargoType' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning_transport_scene` DROP COLUMN `LeavePort`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning_transport_scene' AND COLUMN_NAME = 'LeavePort' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning_transport_scene` DROP COLUMN `Months`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning_transport_scene' AND COLUMN_NAME = 'Months' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning_transport_scene` DROP COLUMN `AreaId`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning_transport_scene' AND COLUMN_NAME = 'AreaId' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 2) 表注释 +ALTER TABLE t_warning_transport_scene + COMMENT = '运输时效场景(产品+国家+考核总时效)'; + +-- 3) 现行字段注释(保留原 COLUMN_TYPE / NULLABLE) + +-- WarningId +SELECT CONCAT( + 'ALTER TABLE `t_warning_transport_scene` MODIFY COLUMN `', COLUMN_NAME, '` ', COLUMN_TYPE, + IF(IS_NULLABLE = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ''预警规则主体Id(t_warning.Id,运输时效为 AppKey 级方案)''' + ) +INTO @sql +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = @schema + AND TABLE_NAME = 't_warning_transport_scene' + AND COLUMN_NAME = 'WarningId'; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- ProductCode +SELECT CONCAT( + 'ALTER TABLE `t_warning_transport_scene` MODIFY COLUMN `', COLUMN_NAME, '` ', COLUMN_TYPE, + IF(IS_NULLABLE = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ''销售产品编码(单一,命中订单产品)''' + ) +INTO @sql +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = @schema + AND TABLE_NAME = 't_warning_transport_scene' + AND COLUMN_NAME = 'ProductCode'; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- CountryCode +SELECT CONCAT( + 'ALTER TABLE `t_warning_transport_scene` MODIFY COLUMN `', COLUMN_NAME, '` ', COLUMN_TYPE, + IF(IS_NULLABLE = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ''到达国家二字码(单一,命中订单到达国家)''' + ) +INTO @sql +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = @schema + AND TABLE_NAME = 't_warning_transport_scene' + AND COLUMN_NAME = 'CountryCode'; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- AssessmentTotalTimeSpan +SELECT CONCAT( + 'ALTER TABLE `t_warning_transport_scene` MODIFY COLUMN `', COLUMN_NAME, '` ', COLUMN_TYPE, + IF(IS_NULLABLE = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ''考核总时效(小时;前端按天录入后×24入库)''' + ) +INTO @sql +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = @schema + AND TABLE_NAME = 't_warning_transport_scene' + AND COLUMN_NAME = 'AssessmentTotalTimeSpan'; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +ALTER TABLE t_warning_transport_scene_rule_item + COMMENT = '运输时效场景分段规则项'; + +-- WarningId / TransportWarningSceneId 注释 +SELECT CONCAT( + 'ALTER TABLE `t_warning_transport_scene_rule_item` MODIFY COLUMN `', COLUMN_NAME, '` ', COLUMN_TYPE, + IF(IS_NULLABLE = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ''预警规则主体Id(t_warning.Id)''' + ) +INTO @sql +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = @schema + AND TABLE_NAME = 't_warning_transport_scene_rule_item' + AND COLUMN_NAME = 'WarningId'; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SELECT CONCAT( + 'ALTER TABLE `t_warning_transport_scene_rule_item` MODIFY COLUMN `', COLUMN_NAME, '` ', COLUMN_TYPE, + IF(IS_NULLABLE = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ''运输时效场景Id(t_warning_transport_scene.Id)''' + ) +INTO @sql +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = @schema + AND TABLE_NAME = 't_warning_transport_scene_rule_item' + AND COLUMN_NAME = 'TransportWarningSceneId'; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 4) t_warning.Customer:运输时效 AppKey 级方案(固定空串);回传/完整预警仍按客户匹配 +SELECT CONCAT( + 'ALTER TABLE `t_warning` MODIFY COLUMN `', COLUMN_NAME, '` ', COLUMN_TYPE, + IF(IS_NULLABLE = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ''客户(运输时效固定空串;回传/完整预警按客户匹配)''' + ) +INTO @sql +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = @schema + AND TABLE_NAME = 't_warning' + AND COLUMN_NAME = 'Customer'; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 5) t_warning 上若仍残留旧匹配列 / CustomName(实体已不映射),按列存在则删 +-- drop helper: 仅当列存在时生成 DROP,否则 SELECT 1 +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning` DROP COLUMN `IsTransit`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning' AND COLUMN_NAME = 'IsTransit' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning` DROP COLUMN `CargoType`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning' AND COLUMN_NAME = 'CargoType' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning` DROP COLUMN `LeavePort`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning' AND COLUMN_NAME = 'LeavePort' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning` DROP COLUMN `Months`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning' AND COLUMN_NAME = 'Months' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning` DROP COLUMN `AreaId`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning' AND COLUMN_NAME = 'AreaId' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE `t_warning` DROP COLUMN `CustomName`', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = 't_warning' AND COLUMN_NAME = 'CustomName' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 6) 废表清理(代码已无映射;未上线可直接删) +-- 现行预警相关表(勿删): +-- t_warning +-- t_warning_transport_scene +-- t_warning_transport_scene_rule_item +-- t_warning_transport_generate_task +-- t_warning_notify_task +-- t_warning_return_rule_item +-- t_warning_return_retry_task +-- t_warning_complete_rule_item +-- +-- 执行前可先核对: +-- SHOW TABLES LIKE 't_warning%'; +-- SHOW TABLES LIKE 't_area%'; +-- SHOW TABLES LIKE 't_rule_area%'; + +-- 6.1 WarningRuleEntity 映射为 t_warning,无 t_warning_rule +DROP TABLE IF EXISTS t_warning_rule; + +-- 6.2 预警分区已下线(原 KPIAreaAggregate:RuleAreaEntity / RuleAreaInfoEntity) +DROP TABLE IF EXISTS t_area_details; +DROP TABLE IF EXISTS t_area; +-- 注释里曾写 t_rule_area,若库中存在且无代码引用也可删 +DROP TABLE IF EXISTS t_rule_area; +DROP TABLE IF EXISTS t_rule_area_details; diff --git a/scripts/sql/20260801_transport_warning_simplify.sql b/scripts/sql/20260801_transport_warning_simplify.sql new file mode 100644 index 00000000..a68121ed --- /dev/null +++ b/scripts/sql/20260801_transport_warning_simplify.sql @@ -0,0 +1,31 @@ +-- 运输时效:一产品一国国家 + 考核总时效;规则主体按 AppKey 方案级 +-- 执行前请备份 t_warning / t_warning_transport_scene + +-- 1) 国家列(若已存在可跳过) +ALTER TABLE t_warning_transport_scene + ADD COLUMN CountryCode VARCHAR(16) NULL COMMENT '到达国家二字码(单一,命中订单到达国家)' AFTER ProductCode; + +-- 2) 考核总时效(小时) +ALTER TABLE t_warning_transport_scene + ADD COLUMN AssessmentTotalTimeSpan FLOAT NULL COMMENT '考核总时效(小时;前端按天录入后×24入库)' AFTER CountryCode; + +-- 3) 运输时效规则主体改为方案级:客户清空 +UPDATE t_warning +SET Customer = '' +WHERE WarningType = 1 + AND (Customer IS NULL OR Customer <> ''); + +-- 4) 同一 AppKey 下多个已启用运输时效方案,仅保留 Id 最小的一条 +UPDATE t_warning w + INNER JOIN ( + SELECT AppKey, MIN(Id) AS KeepId + FROM t_warning + WHERE WarningType = 1 + AND IsEnabled = 1 + GROUP BY AppKey + HAVING COUNT(1) > 1 + ) d ON w.AppKey = d.AppKey +SET w.IsEnabled = 0 +WHERE w.WarningType = 1 + AND w.IsEnabled = 1 + AND w.Id <> d.KeepId; diff --git a/src/TrackCenter.Application/AutoMapper/TrackContentAutoMapperProfile.cs b/src/TrackCenter.Application/AutoMapper/TrackContentAutoMapperProfile.cs index 6013d563..86705d98 100644 --- a/src/TrackCenter.Application/AutoMapper/TrackContentAutoMapperProfile.cs +++ b/src/TrackCenter.Application/AutoMapper/TrackContentAutoMapperProfile.cs @@ -18,7 +18,6 @@ public class TrackContentAutoMapperProfile : Profile CreateMap, PagedResult>( MemberList.None); CreateMap(MemberList.None); - CreateMap(MemberList.None); CreateMap(MemberList.None).ReverseMap(); CreateMap(MemberList.None).ReverseMap(); diff --git a/src/TrackCenter.Application/AutoMapper/WarningMapperProfile.cs b/src/TrackCenter.Application/AutoMapper/WarningMapperProfile.cs index 43f9043c..8026706d 100644 --- a/src/TrackCenter.Application/AutoMapper/WarningMapperProfile.cs +++ b/src/TrackCenter.Application/AutoMapper/WarningMapperProfile.cs @@ -2,8 +2,6 @@ using AutoMapper; using TrackCenter.Application.Dtos.Warning.Complete; using TrackCenter.Application.Dtos.Warning.Return; using TrackCenter.Application.Dtos.Warning.Transport; -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; -using TrackCenter.Domain.Dtos.Forewarning; using TrackCenter.Domain.Dtos.Warning; namespace TrackCenter.Application.AutoMapper; @@ -15,27 +13,5 @@ public class WarningMapperProfile : Profile CreateMap(MemberList.None); CreateMap(MemberList.None); CreateMap(MemberList.None); - // kpi区域 - CreateMap(MemberList.None); - CreateMap(MemberList.None) - .ForMember(dest => dest.CountryCode, opt => opt.MapFrom(src => src.CountryCode)) - .ForMember(dest => dest.CountryName, opt => opt.MapFrom(src => src.CountryName)) - .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province)) - .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City)) - .ForMember(dest => dest.LeftPostcode, opt => opt.MapFrom(src => src.LeftPostCode)) - .ForMember(dest => dest.RightPostcode, opt => opt.MapFrom(src => src.RightPostCode)) - .ForMember(dest => dest.AreaId, opt => opt.MapFrom(src => src.AreaId)); - - CreateMap(MemberList.None) - .ForMember(dest => dest.AppKey, opt => opt.MapFrom(src => src.AppKey)) - .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)); - - CreateMap(MemberList.None) - .ForMember(dest => dest.CountryCode, opt => opt.MapFrom(src => src.CountryCode)) - .ForMember(dest => dest.CountryName, opt => opt.MapFrom(src => src.CountryName)) - .ForMember(dest => dest.Province, opt => opt.MapFrom(src => src.Province)) - .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City)) - .ForMember(dest => dest.LeftPostcode, opt => opt.MapFrom(src => src.LeftPostCode)) - .ForMember(dest => dest.RightPostcode, opt => opt.MapFrom(src => src.RightPostCode)); } } diff --git a/src/TrackCenter.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs b/src/TrackCenter.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs index 2244453f..d491de7a 100644 --- a/src/TrackCenter.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs +++ b/src/TrackCenter.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs @@ -56,22 +56,22 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe await HandleTrackAsync(@event, canNextOrderIds); if (canNextOrderIds.Count == 0) return; - // 根据订单关联关系异步向下刷(异步) + // ????????????????? await DownByOrderLink(@event, canNextOrderIds); } private async Task DownByOrderLink(TrackUpsertEvent @event, List canNextOrderIds) { - // 根据订单关联关系异步向下刷(异步) + // ????????????????? var linkedOrders = await _orderRepository.GetLinkedOrdersAsync(canNextOrderIds.ToArray()); foreach (var order in @event.OrderIdentifiers) { - // 理论上小包在这不会分组 + // ??????????? foreach (var group in linkedOrders.Where(x => x.OwnerOrderId == order.OrderId) .GroupBy(x => new { x.AppKey, - x.Customer // 这里按客户分组是为了后续的推送方便 + x.Customer // ????????????????? })) { var nextEvent = @event.DeepCopy(); @@ -85,7 +85,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe OriginTrackDataHash = @event.TrackRefreshData.OriginTrackDataHash, }; - // 不同应用需要做内容映射 + // ??????????? if (@event.AppKey != nextEvent.AppKey) { var mapCode = (await _provideQueries.MapTrackCodesAsync(@event.AppKey, nextEvent.AppKey, @event.TrackCode)) @@ -93,7 +93,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe if (mapCode == null) { - // 跨应用没做映射的轨迹不处理 + // ????????????? continue; } @@ -119,10 +119,10 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe private static bool CanDelete(TrackUpsertEvent @event) { - // 系统创建的轨迹不允许修改 + // ???????????? if (@event.Visited.IsNullOrEmpty() && @event is { OperationMan: GlobalConst.SystemUser, IsAdd: false }) { - Log.Warning($"系统创建的轨迹不允许被修改:{@event}", true); + Log.Warning($"??????????????{@event}", true); return false; } @@ -131,7 +131,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe var dup = @event.Visited.Intersect(orderIds).ToArray(); if (dup.Any()) { - Log.Warning($"{(@event.IsAdd ? "添加" : "修改")}轨迹时,发现循环依赖:{dup.JoinAsString(",")}", true); + Log.Warning($"{(@event.IsAdd ? "??" : "??")}???????????{dup.JoinAsString(",")}", true); return false; } @@ -140,29 +140,29 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe private async Task HandleTrackAsync(TrackUpsertEvent @event, List canNextOrderIds) { - //#7001:未来潜在需求,当轨迹内容匹配预设,记录一个标记,保证下刷过程都是使用预设内容 + //#7001:?????????????????????????????????????? var trackContent = await _provideQueries.GetTrackContentAsync(@event.AppKey, @event.TrackCode); if (trackContent == null) { - // 找不到轨迹内容,直接结束 - Log.Warning($"找不到轨迹内容:{@event}", true); + // ???????????? + Log.Warning($"????????{@event}", true); return; } - // 根据轨迹内容标识,确定操作权限 + // ??????????????? var manualOperation = @event.Source == (int) TrackSourceEnum.Artificial; - // 不允许手动创建 + // ??????? if (manualOperation && @event.IsAdd && trackContent.IsAllowManuallyCreate == false) { - PublicTopicAllFailed(@event, trackContent, "轨迹内容标识不允许手动创建"); + PublicTopicAllFailed(@event, trackContent, "?????????????"); return; } - // 不允许手动更新 + // ??????? if (manualOperation && !@event.IsAdd && trackContent.IsAllowManuallyUpdate == false) { - PublicTopicAllFailed(@event, trackContent, "轨迹标识内容不允许手动更新"); + PublicTopicAllFailed(@event, trackContent, "?????????????"); return; } @@ -181,27 +181,27 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe if (insertTracks.Any()) { - // 批量add to es tips:注意副作用填充版本号 + // ??add to es tips:?????????? await _trackDetailsEsRepository.AddManyAsync(insertTracks); - // es新增后填充版本号字典 + // es?????????? insertTracks.ForEach(t => orderIdTrackMetadataDict.Add(t.OrderId, TrackMetadata.Build(t.Id, t.Version, t.CreateTime))); - //批量写入订单操作日志 + //?????????? _ = _orderOperationRecordEsRepository.AddManyAsync(insertTracks.Select(t => BuildOrderOperationRecord(@event, t, new OrderIdentifier { OrderNo = t.OrderNo, OrderId = t.OrderId })).ToList()); - // 添加轨迹触发订单拉取状态更新 + // ?????????????? await _orderRepository.UpdatePullStatusToSilenceAsync(insertTracks.Select(x => x.OrderId).ToList(), @event.TrackCode); - // 发布预警信号,稍后将进行预警计算 + // ???????????????? _warningSignalPublisher.PublishSignal(WarningSignalType.轨迹新增, insertTracks); - // 如果存在轨迹标记,则触发订单pod拉取 + // ??????????????pod?? if (!string.IsNullOrWhiteSpace(trackContent.TrackFlagId)) { await _capPublisher.PublishAsync(GlobalConst.OrderPodPullSubscribe, new OrderPodPullEvent @@ -213,10 +213,10 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } } - // 触发轨迹变更广播 + // ???????? await TrackChangePublicTopicAsync(trackContent, trackChangeEtos, true); - // 触发订单主表更新事件(异步) + // ?????????????? await _capPublisher.PublishAsync(GlobalConst.TrackChangeAfterUpdateOrderSubscribe, new TrackChangeAfterUpdateOrderEvent { @@ -226,14 +226,12 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe TrackContent = trackContent.TrackContent, TrackEnContent = trackContent.TrackEnContent, TrackPlace = @event.TrackPlace, - TrackTime = @event.NewTrackTime ?? @event.TrackTime, + TrackTime = @event.TrackTime, TrackTimeZone = @event.TrackTimeZone, - DestinationTimeZone = @event.DestinationTimeZone, - EstimatedDeliveryDate = @event.EstimatedDeliveryDate }); // 触发轨迹推送事件(异步) - // tips:同一个轨迹内容,多个订单多条轨迹 + // tips:???????????????? var trackPushEvent = new TrackPushEvent { OptionType = @event.IsAdd ? OptionType.新增 : OptionType.修改, @@ -248,10 +246,9 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe TrackContentId = trackContent.TrackContentId, TrackContent = trackContent.TrackContent, TrackPlace = @event.TrackPlace, - TrackTime = @event.NewTrackTime ?? @event.TrackTime, + TrackTime = @event.TrackTime, TrackTimeZone = @event.TrackTimeZone, - CompareFields = @event.CompareFields, - Extra = @event.NewExtra ?? @event.Extra + Extra = @event.Extra }, ForcedPush = @event.ForcedPush, IsToMq = (trackContent.IsToMq || @event.IsToMq) @@ -293,7 +290,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } else { - var message = $"没有通过传入的EsId:[{@event.EsId}]查询到轨迹信息,更新失败"; + var message = $"???????EsId:[{@event.EsId}]????????????"; AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message); } @@ -302,31 +299,21 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe var existTracks = tracks.Where(x => x.OrderId == order.OrderId).ToList(); - // tips:如果没传时区则使用+8兜底进行过滤 + // tips:??????????8?????? var timezone = Math.Abs(@event.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 0.01f ? 8.0f : @event.TrackTimeZone; - // 根据时间过滤 - existTracks = @event.IsAdd - ? existTracks.Where(x => x.GetUtc0TrackTime() == (@event.NewTrackTime ?? @event.TrackTime).Subtract(TimeSpan.FromHours(timezone))).ToList() - : existTracks.Where(x => - x.GetUtc0TrackTime() == @event.TrackTime.Subtract(TimeSpan.FromHours(timezone)) - // 修改了时间,要校验修改后的时间是否存在 - || (@event.NewTrackTime != null && x.GetUtc0TrackTime() == @event.NewTrackTime.Value.Subtract(TimeSpan.FromHours(timezone))) - ).ToList(); + // ?????? + existTracks = existTracks.Where(x => + x.GetUtc0TrackTime() == @event.TrackTime.Subtract(TimeSpan.FromHours(timezone))).ToList(); - // 根据扩展信息过滤 - existTracks = @event.IsAdd - ? existTracks.Where(x => x.Extra == (@event.NewExtra ?? @event.Extra)).ToList() - : existTracks.Where(x => - x.Extra == @event.Extra - // 修改了extra,要校验修改后的extra是否存在 - || (!string.IsNullOrWhiteSpace(@event.NewExtra) && x.Extra == @event.NewExtra)).ToList(); + // ???????? + existTracks = existTracks.Where(x => x.Extra == @event.Extra).ToList(); if (@event.IsAdd) { if (existTracks.Count > 0) { - const string message = "订单已经存在不允许重复的轨迹,不允许重复添加"; + const string message = "??????????????????????"; traceLog.Add($"OrderNo-{order.OrderNo}", message); AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message); continue; @@ -338,10 +325,10 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } else { - // 不存在不让改 + // ?????? if (existTracks.Count == 0) { - const string message = "订单不存在轨迹,不允许修改"; + const string message = "?????????????"; traceLog.Add($"OrderNo-{order.OrderNo}", message); AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message); continue; @@ -349,7 +336,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe if (existTracks.Count > 1) { - const string message = "订单存在多条轨迹不允许修改"; + const string message = "?????????????"; traceLog.Add($"OrderNo-{order.OrderNo}", message); AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message); continue; @@ -369,13 +356,12 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe if (updateTracks.Any()) { - // 批量差异化更新轨迹 + // ????????? await _trackDetailsEsRepository.BulkUpdateAsync(updateTracks); - // 填充轨迹元数据 - updateTracks.ForEach(x => orderIdTrackMetadataDict.Add(x.OrderId, TrackMetadata.Build(x.Id, x.Version, x.CreateTime))); - // 批量写入操作日志 + // ???????? updateTracks.ForEach(x => orderIdTrackMetadataDict.Add(x.OrderId, TrackMetadata.Build(x.Id, x.Version, x.CreateTime))); + // ???????? _ = _orderOperationRecordEsRepository.AddManyAsync(operationRecords); - // 发布预警信号稍后将进行预警计算 + // ??????????????? _warningSignalPublisher.PublishSignal(WarningSignalType.轨迹更新, tracks); } @@ -385,41 +371,41 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe private static TrackDetailsIndex BuiltUpdateTrack(TrackUpsertEvent @event, TrackDetailsIndex track, OrderIdentifier order, TrackContentVO trackContent) { - // 非外部轨迹才允许修改 【发生地】【时间】【时区】 + // ?????????? ????????????? if (track.Source != (int) TrackSourceEnum.ExternalServiceProvide) { track.TrackPlace = @event.TrackPlace; - track.TrackTime = @event.NewTrackTime ?? @event.TrackTime; + track.TrackTime = @event.TrackTime; track.TrackTimeZone = @event.TrackTimeZone; } - //允许二次修改轨迹内容 + //?????????? track.TrackContent = string.IsNullOrWhiteSpace(@event.Content) ? trackContent.TrackContent : @event.Content; track.TrackEnContent = string.IsNullOrWhiteSpace(@event.EnStatus) ? trackContent.TrackEnContent : @event.EnStatus; - // 轨迹发生地的更多信息 - track.TrackPlaceCity = @event.TrackPlaceCity ?? GlobalConst.EmptyString; - track.TrackPlaceCityId = @event.TrackPlaceCityId ?? GlobalConst.NotExistId; - track.TrackPlaceProvince = @event.TrackPlaceProvince ?? GlobalConst.EmptyString; - track.TrackPlaceProvinceId = @event.TrackPlaceProvinceId ?? GlobalConst.NotExistId; - track.TrackPlaceNation = @event.TrackPlaceNation ?? GlobalConst.EmptyString; - track.TrackPlaceNationId = @event.TrackPlaceNationId ?? GlobalConst.NotExistId; - track.TrackPlacePostcode = @event.TrackPlacePostcode ?? GlobalConst.EmptyString; + // ?????????? + track.TrackPlaceCity = GlobalConst.EmptyString; + track.TrackPlaceCityId = GlobalConst.NotExistId; + track.TrackPlaceProvince = GlobalConst.EmptyString; + track.TrackPlaceProvinceId = GlobalConst.NotExistId; + track.TrackPlaceNation = GlobalConst.EmptyString; + track.TrackPlaceNationId = GlobalConst.NotExistId; + track.TrackPlacePostcode = GlobalConst.EmptyString; - // 签收地时区,签收地时间 - track.DestinationTimeZone = @event.DestinationTimeZone; - track.EstimatedDeliveryDate = @event.EstimatedDeliveryDate; + // ?????????????????????? + track.DestinationTimeZone = null; + track.EstimatedDeliveryDate = null; - // 转换为utc0的时间 + // ???utc0??? track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1 ? track.TrackTime : track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone)); - // 轨迹标记ids,是否对外,备注,扩展信息,附件 + // ????ids???????????????? track.TrackFlagIds = trackContent.TrackFlagId; track.IsExternal = @event.IsExternal ?? trackContent.IsExternal; track.Remarks = @event.Remarks ?? GlobalConst.EmptyString; - track.Extra = @event.NewExtra ?? @event.Extra; + track.Extra = @event.Extra; track.Files = @event.Files?.Select(x => new FileItem { OssKey = x.OssKey, @@ -427,7 +413,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe }).ToList(); - // 审计属性 + // ???? track.UpdateUser = @event.OperationMan ?? GlobalConst.SystemUser; track.UpdateTime = DateTime.Now; @@ -438,13 +424,13 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe { var track = new TrackDetailsIndex { - // 订单信息 + // ???? AppKey = @event.AppKey, OrderId = order.OrderId, OrderNo = order.OrderNo, OrderType = order.OrderType, - // 轨迹信息 + // ???? TrackCode = @event.TrackCode, TrackContentId = trackContent.TrackContentId, TrackContent = string.IsNullOrWhiteSpace(@event.Content) ? trackContent.TrackContent : @event.Content, @@ -452,44 +438,41 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe TrackStatusId = trackContent.TrackStatusId, TrackStatus = trackContent.TrackStatus, TrackStatusCode = trackContent.TrackStatusCode, - TrackTime = @event.NewTrackTime ?? @event.TrackTime, + TrackTime = @event.TrackTime, TrackTimeZone = @event.TrackTimeZone, TrackPlace = @event.TrackPlace, - // 服务商轨迹信息 + // ??????? ServiceProviderCode = @event.TrackRefreshData.ServiceProviderCode ?? GlobalConst.EmptyString, OriginTrackContent = @event.TrackRefreshData.OriginTrackContent ?? GlobalConst.EmptyString, OriginTrackDataHash = @event.TrackRefreshData.OriginTrackDataHash ?? GlobalConst.EmptyString, - // 轨迹发生地的更多描述 - TrackPlaceCity = @event.TrackPlaceCity ?? GlobalConst.EmptyString, - TrackPlaceCityId = @event.TrackPlaceCityId ?? GlobalConst.NotExistId, - TrackPlaceProvince = @event.TrackPlaceProvince ?? GlobalConst.EmptyString, - TrackPlaceProvinceId = @event.TrackPlaceProvinceId ?? GlobalConst.NotExistId, - TrackPlaceNation = @event.TrackPlaceNation ?? GlobalConst.EmptyString, - TrackPlaceNationId = @event.TrackPlaceNationId ?? GlobalConst.NotExistId, - TrackPlacePostcode = @event.TrackPlacePostcode ?? GlobalConst.EmptyString, - DestinationTimeZone = @event.DestinationTimeZone, - EstimatedDeliveryDate = @event.EstimatedDeliveryDate, - - // 轨迹标记ids,轨迹来源,是否对外,备注,扩展信息,附件 + // ?????????? + TrackPlaceCity = GlobalConst.EmptyString, + TrackPlaceCityId = GlobalConst.NotExistId, + TrackPlaceProvince = GlobalConst.EmptyString, + TrackPlaceProvinceId = GlobalConst.NotExistId, + TrackPlaceNation = GlobalConst.EmptyString, + TrackPlaceNationId = GlobalConst.NotExistId, + TrackPlacePostcode = GlobalConst.EmptyString, + // ????ids????????????????????? TrackFlagIds = trackContent.TrackFlagId, Source = @event.Source, IsExternal = @event.IsExternal ?? trackContent.IsExternal, Remarks = @event.Remarks ?? GlobalConst.EmptyString, - Extra = @event.NewExtra ?? @event.Extra, + Extra = @event.Extra, Files = @event.Files?.Select(x => new FileItem { OssKey = x.OssKey, FileName = x.FileName }).ToList(), - // 审计属性 + // ???? CreateUser = @event.OperationMan ?? GlobalConst.SystemUser, CreateTime = DateTime.Now }; - // 转换为utc0的时间 + // ???utc0??? track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1 ? track.TrackTime : track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone)); @@ -514,12 +497,10 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe private async Task TrackChangePublicTopicAsync(TrackContentVO trackContent, List list, bool isDelay = false) { if (list.Count == 0) return; - //理想情况在写入之后1s会刷盘 https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-refresh.html - //增加一个版本号来确定是否真的变更了 - //没有刷盘成功事件回调 - //方案1:!!【轮询等待静默刷盘】查出版本号确实发生了变更在发送广播 - //方案2:考虑强制刷盘(对es压力大)后发送 - //方案3:采用一个较大延时,不查询直接发 + //??????????s????https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-refresh.html + //????????????????? + //?????????? + //??1??!???????????????????????????? //??2?????????es???????? //??3???????????????? if (isDelay) await Task.Delay(1000); @@ -551,21 +532,19 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe Option = @event.IsAdd ? 1 : 3, IsSuccess = string.IsNullOrWhiteSpace(failedReason), FailedReason = failedReason, - EstimatedDeliveryDate = @event.EstimatedDeliveryDate, - DestinationTimeZone = @event.DestinationTimeZone, - TrackPlacePostcode = @event.TrackPlacePostcode, - TrackPlaceNation = @event.TrackPlaceNation, - TrackPlaceNationId = @event.TrackPlaceNationId, - TrackPlaceProvince = @event.TrackPlaceProvince, - TrackPlaceProvinceId = @event.TrackPlaceProvinceId, - TrackPlaceCity = @event.TrackPlaceCity, - TrackPlaceCityId = @event.TrackPlaceCityId, + TrackPlacePostcode = null, + TrackPlaceNation = null, + TrackPlaceNationId = null, + TrackPlaceProvince = null, + TrackPlaceProvinceId = null, + TrackPlaceCity = null, + TrackPlaceCityId = null, Files = @event.Files, Remarks = @event.Remarks, Source = @event.Source, IsExternal = @event.IsExternal ?? trackContent.IsExternal, TrackTimeZone = @event.TrackTimeZone, - TrackTime = @event.NewTrackTime ?? @event.TrackTime, + TrackTime = @event.TrackTime, TrackPlace = @event.TrackPlace, TrackStatusCode = trackContent.TrackStatusCode, OrderNo = orderNo, @@ -576,7 +555,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe OrderIdentifiers = @event.OrderIdentifiers, Customer = @event.Customer, AppKey = @event.AppKey, - Extra = @event.NewExtra ?? @event.Extra + Extra = @event.Extra }; } } diff --git a/src/TrackCenter.Application/Dtos/Forewarning/AreaCacheEntry.cs b/src/TrackCenter.Application/Dtos/Forewarning/AreaCacheEntry.cs deleted file mode 100644 index 10c2582e..00000000 --- a/src/TrackCenter.Application/Dtos/Forewarning/AreaCacheEntry.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace TrackCenter.Application.Dtos.Forewarning; - -public class AreaCacheEntry -{ - /// - /// 国家或地区 - /// - public string CountryCode { get; set; } = null!; - - /// - /// 省份 - /// - public string? Province { get; set; } - - /// - /// 城市 - /// - public string? City { get; set; } - - /// - /// 开始邮编 - /// - public string? LeftPostcode { get; set; } - - /// - /// 截止邮编 - /// - public string? RightPostcode { get; set; } -} diff --git a/src/TrackCenter.Application/Dtos/Forewarning/RuleAreaBaseInfo.cs b/src/TrackCenter.Application/Dtos/Forewarning/RuleAreaBaseInfo.cs deleted file mode 100644 index a11fa3f2..00000000 --- a/src/TrackCenter.Application/Dtos/Forewarning/RuleAreaBaseInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace TrackCenter.Application.Dtos.Forewarning; - -public class RuleAreaBaseInfo -{ - public RuleAreaBaseInfo() - { - } - - public RuleAreaBaseInfo(string appKey, string customer) - { - AppKey = appKey; - Customer = customer; - } - - public string AppKey { get; set; } - public string Customer { get; set; } -} diff --git a/src/TrackCenter.Application/Dtos/ProvideServices/BatchDeleteTrackByEsIdInput.cs b/src/TrackCenter.Application/Dtos/ProvideServices/BatchDeleteTrackByEsIdInput.cs deleted file mode 100644 index d721168d..00000000 --- a/src/TrackCenter.Application/Dtos/ProvideServices/BatchDeleteTrackByEsIdInput.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace TrackCenter.Application.Dtos.ProvideServices; - -public class BatchDeleteTrackByEsIdInput -{ - - /// - /// 轨迹内容es id,必填 - /// - [MaxLength(500)] - public List TrackContentEsIds { get; set; } = new(); - - /// - /// 删除轨迹来源 1.外部服务商 2.内部系统 3.人工 - /// - public int? Source { get; set; } = 2; -} diff --git a/src/TrackCenter.Application/Dtos/ProvideServices/BatchUpsertTrackInput.cs b/src/TrackCenter.Application/Dtos/ProvideServices/BatchUpsertTrackInput.cs index 90725a84..0a0957fa 100644 --- a/src/TrackCenter.Application/Dtos/ProvideServices/BatchUpsertTrackInput.cs +++ b/src/TrackCenter.Application/Dtos/ProvideServices/BatchUpsertTrackInput.cs @@ -29,26 +29,11 @@ public class BatchUpsertTrackInput : OrderNosInfo [Required] public DateTime TrackTime { get; set; } - /// - /// 新的轨迹发生时间 - /// 如果是新增,那么优先级高于TrackTime - /// 如果要修改TrackTime,请同时传递TrackTime(旧的用于匹配),和NewTrackTime - /// - public DateTime? NewTrackTime { get; set; } - /// /// 轨迹发生时区 /// - // [Required] - // [Range(-12, 12)] public float? TrackTimeZone { get; set; } - /// - /// 客户是否可见 - /// - [Obsolete("废弃的,请使用IsExternal")] - public bool IsVisibility { get; set; } - /// /// 是否对外(可选的) /// 如果未填则按照【轨迹内容】中的【是否可见】做为默认值 @@ -57,7 +42,6 @@ public class BatchUpsertTrackInput : OrderNosInfo /// /// 数据来源:1外部服务商创建,2内部系统创建,3人工创建 - /// 批量新增时使用指定轨迹来源,更新时无需指定 /// [Required] public int Source { get; set; } @@ -83,68 +67,10 @@ public class BatchUpsertTrackInput : OrderNosInfo public string? EnStatus { get; set; } /// - /// 轨迹发生地(城市) - /// - public string? TrackPlaceCity { get; set; } - - /// - /// 轨迹发生地(城市)Id - /// - public int? TrackPlaceCityId { get; set; } - - /// - /// 轨迹发生地(省/州) - /// - public string? TrackPlaceProvince { get; set; } - - /// - ///轨迹发生地(省/州)Id - /// - public int? TrackPlaceProvinceId { get; set; } - - /// - /// 轨迹发生地(国家/地区) - /// - public string? TrackPlaceNation { get; set; } - - /// - /// 轨迹发生地(国家/地区)Id - /// - public int? TrackPlaceNationId { get; set; } - - /// - /// 轨迹发生地邮编 - /// - public string? TrackPlacePostcode { get; set; } - - /// - /// 预计到达时间 - /// - public DateTime? EstimatedDeliveryDate { get; set; } - - /// - /// 目的地时区 - /// - [Range(-12, 12)] - public float? DestinationTimeZone { get; set; } - - /// - /// 重复的情况下,用什么比较 - /// - public string? CompareFields { get; set; } - - /// - /// 扩展信息字段,用与轨迹推送的附加字段 + /// 扩展信息字段,用于轨迹推送的附加字段 /// public string? Extra { get; set; } - /// - /// 新的扩展信息, - /// 如果是新增,那么优先级高于Extra - /// 如果要修改Extra,请同时传递Extra(用于匹配旧的)和NewExtra - /// - public string? NewExtra { get; set; } - /// /// 是否强制推送 /// diff --git a/src/TrackCenter.Application/Dtos/ProvideServices/ProviderServiceActionResult.cs b/src/TrackCenter.Application/Dtos/ProvideServices/ProviderServiceActionResult.cs new file mode 100644 index 00000000..22422528 --- /dev/null +++ b/src/TrackCenter.Application/Dtos/ProvideServices/ProviderServiceActionResult.cs @@ -0,0 +1,16 @@ +namespace TrackCenter.Application.Dtos.ProvideServices; + +/// +/// ProviderService 提示文案助手(不改变接口返回结构,仅用于异常 message:结果;原因:...) +/// +public static class ProviderServiceActionResult +{ + /// + /// 抛出结构化业务异常(message = 结果;原因:...) + /// + public static BusinessException Exception(string result, string reason) => + new(message: Format(result, reason)); + + public static string Format(string result, string reason) => + string.IsNullOrWhiteSpace(reason) ? result : $"{result};原因:{reason}"; +} diff --git a/src/TrackCenter.Application/Dtos/ProvideServices/UpsertTrackInput.cs b/src/TrackCenter.Application/Dtos/ProvideServices/UpsertTrackInput.cs index 2e999605..b31e0471 100644 --- a/src/TrackCenter.Application/Dtos/ProvideServices/UpsertTrackInput.cs +++ b/src/TrackCenter.Application/Dtos/ProvideServices/UpsertTrackInput.cs @@ -10,7 +10,7 @@ public class UpsertTrackInput : OrderNoInfo public bool IsAdd { get; set; } /// - /// 轨迹唯一标识(可选) + /// 轨迹唯一标识(可选,更新时优先按 EsId 匹配) /// public string? EsId { get; set; } @@ -34,26 +34,11 @@ public class UpsertTrackInput : OrderNoInfo [Required] public DateTime TrackTime { get; set; } - /// - /// 新的轨迹发生时间 - /// 如果是新增,那么优先级高于TrackTime - /// 如果要修改TrackTime,请同时传递TrackTime(旧的用于匹配),和NewTrackTime - /// - public DateTime? NewTrackTime { get; set; } - /// /// 轨迹发生时区 /// - // [Required] - // [Range(-12, 12)] public float? TrackTimeZone { get; set; } - /// - /// 客户是否可见 - /// - [Obsolete("废弃的,请使用IsExternal")] - public bool IsVisibility { get; set; } - /// /// 是否对外(可选的) /// 如果未填则按照【轨迹内容】中的【是否可见】做为默认值 @@ -87,68 +72,10 @@ public class UpsertTrackInput : OrderNoInfo public string? EnStatus { get; set; } /// - /// 轨迹发生地(城市) - /// - public string? TrackPlaceCity { get; set; } - - /// - /// 轨迹发生地(城市)Id - /// - public int? TrackPlaceCityId { get; set; } - - /// - /// 轨迹发生地(省/州) - /// - public string? TrackPlaceProvince { get; set; } - - /// - /// 轨迹发生地(省/州)Id - /// - public int? TrackPlaceProvinceId { get; set; } - - /// - /// 轨迹发生地(国家/地区) - /// - public string? TrackPlaceNation { get; set; } - - /// - /// 轨迹发生地(国家/地区)Id - /// - public int? TrackPlaceNationId { get; set; } - - /// - /// 轨迹发生地邮编 - /// - public string? TrackPlacePostcode { get; set; } - - /// - /// 预计到达时间 - /// - public DateTime? EstimatedDeliveryDate { get; set; } - - /// - /// 目的地时区 - /// - [Range(-12, 12)] - public float? DestinationTimeZone { get; set; } - - /// - /// 重复的情况下,用什么比较 - /// - public string? CompareFields { get; set; } - - /// - /// 扩展信息字段,用与轨迹推送的附加字段 + /// 扩展信息字段,用于轨迹推送的附加字段 /// public string? Extra { get; set; } - /// - /// 新的扩展信息, - /// 如果是新增,那么优先级高于Extra - /// 如果要修改Extra,请同时传递Extra(用于匹配旧的)和NewExtra - /// - public string? NewExtra { get; set; } - /// /// 是否强制推送 /// diff --git a/src/TrackCenter.Application/Dtos/TrackContents/TrackContentOutput.cs b/src/TrackCenter.Application/Dtos/TrackContents/TrackContentOutput.cs deleted file mode 100644 index c170cbea..00000000 --- a/src/TrackCenter.Application/Dtos/TrackContents/TrackContentOutput.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace TrackCenter.Application.Dtos.TrackContents; - -public sealed class TrackContentOutput -{ - /// - /// 应用 - /// - public string AppKey { get; set; } - - /// - /// 应用代码 - /// - public string AppCode { get; set; } - - /// - /// 轨迹状态Id - /// - public int TrackStatusId { get; set; } - - /// - /// 轨迹状态 - /// - public string TrackStatus { get; set; } = null!; - - /// - /// 轨迹内容Id - /// - public int TrackContentId { get; set; } - - /// - /// 轨迹内容 - /// - public string TrackContent { get; set; } = null!; - - /// - /// 轨迹内容英文 - /// - public string TrackEnContent { get; set; } = null!; - - /// - /// 是否完成态 - /// - public bool IsCompleted { get; set; } - - /// - /// 轨迹状态代码 - /// - public string TrackStatusCode { get; set; } - - /// - /// 是否可重复 - /// - public bool IsRepeatable { get; set; } - - /// - /// 是否允许手动修改 - /// - public bool IsAllowManuallyUpdate { get; set; } - - /// - /// 是否允许手动创建 - /// - public bool IsAllowManuallyCreate { get; set; } - - /// - /// 是否广播 - /// - public bool IsToMq { get; set; } - - /// - /// 是否对外 - /// - public bool IsExternal { get; set; } - - /// - /// 轨迹内容code - /// - public string TrackContentCode { get; set; } -} diff --git a/src/TrackCenter.Application/Dtos/Warning/Transport/TransportWarningSceneDto.cs b/src/TrackCenter.Application/Dtos/Warning/Transport/TransportWarningSceneDto.cs index dcaffc58..64b4ef28 100644 --- a/src/TrackCenter.Application/Dtos/Warning/Transport/TransportWarningSceneDto.cs +++ b/src/TrackCenter.Application/Dtos/Warning/Transport/TransportWarningSceneDto.cs @@ -1,7 +1,7 @@ namespace TrackCenter.Application.Dtos.Warning.Transport; /// -/// 预警规则场景 +/// 运输时效场景(一产品 + 一国家 + 考核总时效) /// public class TransportWarningSceneDto { @@ -11,47 +11,27 @@ public class TransportWarningSceneDto public int Id { get; set; } /// - /// 预警规则主体id + /// 预警规则主体id(AppKey 级方案) /// public int WarningId { get; set; } /// - /// 场景摘要(用于展示场景tabs) + /// 场景摘要(产品 / 国家 / 考核总时效) /// public string Summary { get; set; } /// - /// 是否中转(单一) - /// - public bool? IsTransit { get; set; } - - /// - /// 货物类型(多个,分割) - /// - public string? CargoType { get; set; } - - /// - /// 起运港口(多个,分割) - /// - public string? LeavePort { get; set; } - - /// - /// 产品编码(多个,分割) + /// 销售产品编码(单一) /// public string? ProductCode { get; set; } /// - /// 月份(多个/分割) + /// 到达国家二字码(单一) /// - public string? Months { get; set; } + public string? CountryCode { get; set; } /// - /// 区域Id(单一) + /// 考核总时效(小时;前端按天展示) /// - public int? AreaId { get; set; } - - /// - /// 区域id对应的名称 - /// - public string? AreaName { get; set; } + public float? AssessmentTotalTimeSpan { get; set; } } diff --git a/src/TrackCenter.Application/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs b/src/TrackCenter.Application/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs index 78426db4..0c6f818b 100644 --- a/src/TrackCenter.Application/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs +++ b/src/TrackCenter.Application/Dtos/Warning/Transport/UpsertTransportWarningSceneInput.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using TrackCenter.Application.Dtos.Warning.DataAnnotations; namespace TrackCenter.Application.Dtos.Warning.Transport; @@ -10,38 +11,21 @@ public sealed class UpsertTransportWarningSceneInput public int Id { get; set; } /// - /// 预警规则主体id + /// 预警规则主体id(AppKey 级方案) /// [IdRequired] public int WarningId { get; set; } - /// - /// 是否中转(单一) - /// - public bool? IsTransit { get; set; } - - /// - /// 货物类型(多个,分割) - /// - public string? CargoType { get; set; } - - /// - /// 起运港口(多个,分割) - /// - public string? LeavePort { get; set; } - - /// - /// 产品编码(多个,分割) - /// + /// 销售产品编码(单一) + [Required(ErrorMessage = "请配置销售产品")] public string? ProductCode { get; set; } - /// - /// 月份(多个/分割) - /// - public string? Months { get; set; } + /// 到达国家二字码(单一) + [Required(ErrorMessage = "请配置国家")] + public string? CountryCode { get; set; } - /// - /// 区域Id(单一) - /// - public int? AreaId { get; set; } + /// 考核总时效(小时;前端按天录入后×24) + [Required(ErrorMessage = "请配置考核总时效")] + [Range(0.1, 99999, ErrorMessage = "考核总时效须大于0")] + public float? AssessmentTotalTimeSpan { get; set; } } diff --git a/src/TrackCenter.Application/Dtos/Warning/UpsertWarningRuleInput.cs b/src/TrackCenter.Application/Dtos/Warning/UpsertWarningRuleInput.cs index 4d705397..68cf5c97 100644 --- a/src/TrackCenter.Application/Dtos/Warning/UpsertWarningRuleInput.cs +++ b/src/TrackCenter.Application/Dtos/Warning/UpsertWarningRuleInput.cs @@ -19,9 +19,9 @@ public class UpsertWarningRuleInput public string AppKey { get; set; } //应用 /// - /// 客户 + /// 客户(运输时效可空,后端会置空;回传/完整预警必填) /// - public string Customer { get; set; } //客户 + public string? Customer { get; set; } /// /// 预警规则名称 diff --git a/src/TrackCenter.Application/Events/ClearRulCacheEvent.cs b/src/TrackCenter.Application/Events/ClearRulCacheEvent.cs index 54cb8ac9..d5043eeb 100644 --- a/src/TrackCenter.Application/Events/ClearRulCacheEvent.cs +++ b/src/TrackCenter.Application/Events/ClearRulCacheEvent.cs @@ -1,8 +1,9 @@ -using TrackCenter.Application.Dtos.Forewarning; - namespace TrackCenter.Application.Events; +/// +/// 历史区域缓存清理事件(预警分区已下线,保留空壳兼容 Redis 订阅反序列化) +/// public class ClearRulCacheEvent { - public List Items { get; set; } + public List AreaIds { get; set; } = new(); } diff --git a/src/TrackCenter.Application/Events/TrackUpsertEvent.cs b/src/TrackCenter.Application/Events/TrackUpsertEvent.cs index 2a6278b7..503161c6 100644 --- a/src/TrackCenter.Application/Events/TrackUpsertEvent.cs +++ b/src/TrackCenter.Application/Events/TrackUpsertEvent.cs @@ -54,13 +54,6 @@ public class TrackUpsertEvent /// public DateTime TrackTime { get; set; } - /// - /// 新的轨迹发生时间 - /// 如果是新增,那么优先级高于TrackTime - /// 如果要修改TrackTime,请同时传递TrackTime(旧的用于匹配),和NewTrackTime - /// - public DateTime? NewTrackTime { get; set; } - /// /// 轨迹发生时区 /// @@ -104,67 +97,10 @@ public class TrackUpsertEvent public string? EnStatus { get; set; } /// - /// 轨迹发生地(城市) - /// - public string? TrackPlaceCity { get; set; } - - /// - /// 轨迹发生地(城市)Id - /// - public int? TrackPlaceCityId { get; set; } - - /// - /// 轨迹发生地(省/州) - /// - public string? TrackPlaceProvince { get; set; } - - /// - ///轨迹发生地(省/州)Id - /// - public int? TrackPlaceProvinceId { get; set; } - - /// - /// 轨迹发生地(国家/地区) - /// - public string? TrackPlaceNation { get; set; } - - /// - /// 轨迹发生地(国家/地区)Id - /// - public int? TrackPlaceNationId { get; set; } - - /// - /// 轨迹发生地邮编 - /// - public string? TrackPlacePostcode { get; set; } - - /// - /// 预计到达时间 - /// - public DateTime? EstimatedDeliveryDate { get; set; } - - /// - /// 目的地时区 - /// - public float? DestinationTimeZone { get; set; } - - /// - /// 重复的情况下,用什么比较 - /// - public string? CompareFields { get; set; } - - /// - /// 扩展信息字段,用与轨迹推送的附加字段 + /// 扩展信息字段,用于轨迹推送的附加字段 /// public string? Extra { get; set; } - /// - /// 新的扩展信息, - /// 如果是新增,那么优先级高于Extra - /// 如果要修改Extra,请同时传递Extra(用于匹配旧的)和NewExtra - /// - public string? NewExtra { get; set; } - /// /// 是否强制推送 /// diff --git a/src/TrackCenter.Application/Hangfire/WarningNotifyAggregator.cs b/src/TrackCenter.Application/Hangfire/WarningNotifyAggregator.cs index 55afb6d8..28097c09 100644 --- a/src/TrackCenter.Application/Hangfire/WarningNotifyAggregator.cs +++ b/src/TrackCenter.Application/Hangfire/WarningNotifyAggregator.cs @@ -2,11 +2,9 @@ using Microsoft.Extensions.Options; using TrackCenter.Application.Dtos.Warning; using TrackCenter.Application.Options; using TrackCenter.Application.Services.Warning.Model; -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.TrackDetailsAggregate; using TrackCenter.Domain.Aggregates.WarningAggregate; -using TrackCenter.Domain.Dtos.Forewarning; using TrackCenter.Domain.Dtos.Warning; namespace TrackCenter.Application.Hangfire; @@ -17,21 +15,25 @@ namespace TrackCenter.Application.Hangfire; public sealed class WarningNotifyAggregator : ITransientDependency { private readonly IOrderRepository _orderRepository; - private readonly IKpiAreaRepository _kpiAreaRepository; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; private readonly IWarningTaskRepository _warningTaskRepository; private readonly IWarningRuleRepository _warningRuleRepository; private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; private readonly IOptionsMonitor _optionsMonitor; - public WarningNotifyAggregator(IWarningTaskRepository warningTaskRepository, IWarningRuleRepository warningRuleRepository, IOrderRepository orderRepository, ITrackDetailsEsRepository trackDetailsEsRepository, IWarningDetailsEsRepository warningDetailsEsRepository, IKpiAreaRepository kpiAreaRepository, IOptionsMonitor optionsMonitor) + public WarningNotifyAggregator( + IWarningTaskRepository warningTaskRepository, + IWarningRuleRepository warningRuleRepository, + IOrderRepository orderRepository, + ITrackDetailsEsRepository trackDetailsEsRepository, + IWarningDetailsEsRepository warningDetailsEsRepository, + IOptionsMonitor optionsMonitor) { _warningTaskRepository = warningTaskRepository; _warningRuleRepository = warningRuleRepository; _orderRepository = orderRepository; _trackDetailsEsRepository = trackDetailsEsRepository; _warningDetailsEsRepository = warningDetailsEsRepository; - _kpiAreaRepository = kpiAreaRepository; _optionsMonitor = optionsMonitor; } @@ -140,19 +142,13 @@ public sealed class WarningNotifyAggregator : ITransientDependency // 2.场景验证 var sceneDict = scenes.ToDictionary(x => x.Id.ToString(), TransportWarningSceneBO.From); - // 2.1查找场景关联的区域信息 - var areaIds = scenes.Select(x => x.AreaId ?? 0).Where(id => id != 0).ToHashSet(); - var ruleAreaInfosDict = areaIds.Count > 0 - ? await _kpiAreaRepository.GetAreaInfosDictByAreaId(areaIds) - : new Dictionary>(0); - - // 2.2场景验证 + // 2.1场景验证(产品+国家) var validTasks = new List(filtered.Count); foreach (var task in filtered) { if (sceneDict.TryGetValue(task.AuxiliaryId!, out var scene) && orderDict.TryGetValue(task.OrderId, out var order) - && scene.CanHitScene(order, ruleAreaInfosDict.GetValueOrDefault(scene.AreaId ?? 0))) + && scene.CanHitScene(order)) { validTasks.Add(task); } diff --git a/src/TrackCenter.Application/Hangfire/WarningTaskScanner.cs b/src/TrackCenter.Application/Hangfire/WarningTaskScanner.cs index a94616e0..ed322023 100644 --- a/src/TrackCenter.Application/Hangfire/WarningTaskScanner.cs +++ b/src/TrackCenter.Application/Hangfire/WarningTaskScanner.cs @@ -1,7 +1,4 @@ using Hangfire; -using Microsoft.Extensions.Options; -using TrackCenter.Application.Options; -using TrackCenter.Application.Services.Warning.Handlers; using TrackCenter.Domain.Aggregates.WarningAggregate; namespace TrackCenter.Application.Hangfire; @@ -11,8 +8,6 @@ public interface IWarningTaskScanner Task ScanNotifyTaskAndSchedulingAsync(); Task ScanReturnRetryPushTaskAndSchedulingAsync(); - - Task ScanTransportDetailsGenerateTaskAndSchedulingAsync(); } /// @@ -21,23 +16,17 @@ public interface IWarningTaskScanner /// public class WarningTaskScanner : IWarningTaskScanner, ITransientDependency { - // private readonly IWarningTaskExecuteWrapper _taskExecuteWrapper; - // private readonly IWarningTaskStatusCacheService _warningTaskStatusCacheService; private readonly IWarningTaskRepository _warningTaskRepository; - private readonly ITransportWarningHandler _transportWarningHandler; private readonly WarningNotifyAggregator _warningNotifyAggregator; private readonly IWarningTaskService _warningTaskService; - private readonly IOptionsMonitor _optionsMonitor; public WarningTaskScanner(IWarningTaskRepository warningTaskRepository, - ITransportWarningHandler transportWarningHandler, WarningNotifyAggregator warningNotifyAggregator, - IWarningTaskService warningTaskService, IOptionsMonitor optionsMonitor) + WarningNotifyAggregator warningNotifyAggregator, + IWarningTaskService warningTaskService) { _warningTaskRepository = warningTaskRepository; - _transportWarningHandler = transportWarningHandler; _warningNotifyAggregator = warningNotifyAggregator; _warningTaskService = warningTaskService; - _optionsMonitor = optionsMonitor; } // 入队等待调度 @@ -73,20 +62,4 @@ public class WarningTaskScanner : IWarningTaskScanner, ITransientDependency !x.IsClosed && taskIds.Contains(x.Id)); } } - - // 批量执行 - public async Task ScanTransportDetailsGenerateTaskAndSchedulingAsync() - { - // 1.查出所有可以执行预警生成任务(未关闭&当前时间大于等于计划执行时间) - var tasks = await _warningTaskRepository.ScanTransportWarningDetailsGenerateTaskAsync(); - if (tasks.Count == 0) return; - - // 2.执行预警生成任务(批量) - await _transportWarningHandler.HandleWarningDetailsGenerateTask(tasks); - - // 3.关闭预警生成任务 - var taskIds = tasks.Select(x => x.Id).ToList(); - await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x => - !x.IsClosed && taskIds.Contains(x.Id)); - } } diff --git a/src/TrackCenter.Application/HostServices/WarningTaskComponentHostedService.cs b/src/TrackCenter.Application/HostServices/WarningTaskComponentHostedService.cs index 6c49d638..fbb90437 100644 --- a/src/TrackCenter.Application/HostServices/WarningTaskComponentHostedService.cs +++ b/src/TrackCenter.Application/HostServices/WarningTaskComponentHostedService.cs @@ -47,10 +47,9 @@ public class WarningTaskComponentHostedService : IHostedService private void ReloadConfig(IOptionsMonitor optionsMonitor) { var newVal = optionsMonitor.CurrentValue; - _logger.LogInformation("扫描任务配置,预警通知扫描周期{a}h,回传重试推送任务扫描周期{b}min,运输预警明细扫描周期{c}min", + _logger.LogInformation("扫描任务配置,预警通知扫描周期{a}h,回传重试推送任务扫描周期{b}min", newVal.ScanWarningNotifyTaskHourInterval, - newVal.ScanReturnWarningRetryPushTaskMinuteInterval, - newVal.ScanTransportWarningDetailsGenerateTaskMinuteInterval); + newVal.ScanReturnWarningRetryPushTaskMinuteInterval); if ((_oldOptions?.ScanWarningNotifyTaskHourInterval ?? 0) != newVal.ScanWarningNotifyTaskHourInterval) { var q = GlobalConst.TaskQueueLevelDict[_optionsMonitor.CurrentValue.ScanWarningNotifyTaskLevel]; @@ -71,15 +70,8 @@ public class WarningTaskComponentHostedService : IHostedService $"*/{newVal.ScanReturnWarningRetryPushTaskMinuteInterval} * * * *"); } - if ((_oldOptions?.ScanTransportWarningDetailsGenerateTaskMinuteInterval ?? 0) != newVal.ScanTransportWarningDetailsGenerateTaskMinuteInterval) - { - var q = GlobalConst.TaskQueueLevelDict[_optionsMonitor.CurrentValue.ScanTransportWarningDetailsGenerateTaskLevel]; - // 运输预警明细生成任务扫描 - _recurringJobManager.AddOrUpdate("Scan_TransportWarningDetailsGenerateTask", - q, - x => x.ScanTransportDetailsGenerateTaskAndSchedulingAsync(), - $"*/{newVal.ScanTransportWarningDetailsGenerateTaskMinuteInterval} * * * *"); - } + // 移除历史运输预警明细生成任务扫描(若仍挂在 Hangfire 上) + _recurringJobManager.RemoveIfExists("Scan_TransportWarningDetailsGenerateTask"); _oldOptions = newVal; } diff --git a/src/TrackCenter.Application/Options/TrackWarningTaskOptions.cs b/src/TrackCenter.Application/Options/TrackWarningTaskOptions.cs index 6878d555..32a01390 100644 --- a/src/TrackCenter.Application/Options/TrackWarningTaskOptions.cs +++ b/src/TrackCenter.Application/Options/TrackWarningTaskOptions.cs @@ -28,10 +28,4 @@ public sealed class TrackWarningTaskOptions /// 回传重试推送任务扫描间隔(分钟) public int ScanReturnWarningRetryPushTaskMinuteInterval { get; set; } - - /// 运输预警明细生成任务扫描优先级 - public int ScanTransportWarningDetailsGenerateTaskLevel { get; set; } - - /// 预警明细生成任务扫描间隔(分钟) - public int ScanTransportWarningDetailsGenerateTaskMinuteInterval { get; set; } } diff --git a/src/TrackCenter.Application/Queries/ProvideQueries.cs b/src/TrackCenter.Application/Queries/ProvideQueries.cs index df3fa14a..3a101391 100644 --- a/src/TrackCenter.Application/Queries/ProvideQueries.cs +++ b/src/TrackCenter.Application/Queries/ProvideQueries.cs @@ -70,6 +70,84 @@ public class ProvideQueries : SqlSugarRootlessRepository, .ToListAsync(); } + /// + /// 手工刷新诊断:区分可刷新订单与不可刷新原因 + /// + public async Task<(List CanRefresh, List SkipReasons)> + DiagnoseManualRefreshAsync(string appKey, List orderNos) + { + var now = DateTime.Now; + var canRefresh = await FindOrderWithServiceProviderAsync(appKey, orderNos); + + var eligibleOrderNos = await Context.Queryable() + .InnerJoin((a, b) => a.Id == b.OrderId) + .WhereIF(!appKey.IsNullOrWhiteSpace(), a => a.AppKey == appKey) + .Where((a, b) => !a.IsCompleted && orderNos.Contains(a.OrderNo) + && b.StartTime <= now && b.EndTime >= now) + .Select((a, b) => a.OrderNo) + .Distinct() + .ToListAsync(); + var eligibleSet = eligibleOrderNos.ToHashSet(StringComparer.OrdinalIgnoreCase); + + var orderStates = await Context.Queryable() + .LeftJoin((a, b) => a.Id == b.OrderId) + .WhereIF(!appKey.IsNullOrWhiteSpace(), a => a.AppKey == appKey) + .Where(a => orderNos.Contains(a.OrderNo)) + .Select((a, b) => new + { + a.OrderNo, + a.IsCompleted, + PullStatusId = SqlFunc.IsNull(b.Id, 0), + b.StartTime, + b.EndTime + }) + .ToListAsync(); + + var skipReasons = new List(); + var foundSet = orderStates.Select(x => x.OrderNo).ToHashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var orderNo in orderNos) + { + if (eligibleSet.Contains(orderNo)) + { + continue; + } + + if (!foundSet.Contains(orderNo)) + { + skipReasons.Add($"{orderNo}:订单不存在或不属于当前应用"); + continue; + } + + var rows = orderStates.Where(x => string.Equals(x.OrderNo, orderNo, StringComparison.OrdinalIgnoreCase)) + .ToList(); + if (rows.Any(x => x.IsCompleted)) + { + skipReasons.Add($"{orderNo}:订单已完成,不再拉取轨迹"); + continue; + } + + var pullRows = rows.Where(x => x.PullStatusId > 0).ToList(); + if (pullRows.Count == 0) + { + skipReasons.Add($"{orderNo}:无拉取状态(未配置拉取或尚未生成拉取任务)"); + continue; + } + + if (!pullRows.Any(x => x.StartTime <= now && x.EndTime >= now)) + { + var latest = pullRows.OrderByDescending(x => x.EndTime).First(); + skipReasons.Add( + $"{orderNo}:不在拉取时间窗口内({latest.StartTime:yyyy-MM-dd HH:mm} ~ {latest.EndTime:yyyy-MM-dd HH:mm})"); + continue; + } + + skipReasons.Add($"{orderNo}:暂不可刷新,请检查拉取配置/服务商映射"); + } + + return (canRefresh, skipReasons); + } + public async Task> FindOrderWithServiceProviderAsync( TrackPullConfigVo config, int pageIndex, int pageSize = 1000) diff --git a/src/TrackCenter.Application/Services/DebugApis/NormalService.cs b/src/TrackCenter.Application/Services/DebugApis/NormalService.cs deleted file mode 100644 index 815a348e..00000000 --- a/src/TrackCenter.Application/Services/DebugApis/NormalService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using TrackCenter.Domain.Aggregates.OrderAggregate; - -namespace TrackCenter.Application.Services.DebugApis; - -[AllowAnonymous] -[ApiExplorerSettings(GroupName = "调试专用")] -public class NormalService : ApplicationServiceBase -{ - private readonly IOrderRepository _orderRepository; - - public NormalService(IOrderRepository orderRepository) - { - _orderRepository = orderRepository; - } - - public async Task Hello(string name) - { - var order = await _orderRepository.GetFirstAsync(x => true); - if (order == null) - { - throw new BusinessException(message: "订单不存在"); - } - - return $"hello {name}"; - } -} diff --git a/src/TrackCenter.Application/Services/DebugApis/OTelService.cs b/src/TrackCenter.Application/Services/DebugApis/OTelService.cs deleted file mode 100644 index 1e0bf633..00000000 --- a/src/TrackCenter.Application/Services/DebugApis/OTelService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using TrackCenter.Domain.Aggregates.OrderAggregate; - -namespace TrackCenter.Application.Services.DebugApis; - -[AllowAnonymous] -[ApiExplorerSettings(GroupName = "调试专用")] -public class OTelService : ApplicationServiceBase -{ - private readonly IBasicRepository _orderRepository; - - public OTelService(IBasicRepository orderRepository) - { - _orderRepository = orderRepository; - } - - // [OTel] - public async Task Hello(string name) - { - var order = await _orderRepository.GetFirstAsync(x => true); - if (order == null) - { - throw new BusinessException(message: "订单不存在"); - } - - await DoSomethingAsync(order); - - return $"hello {name}"; - } - - // [OTel] - private async Task DoSomethingAsync(OrderEntity order) - { - var order2 = await _orderRepository.GetFirstAsync(x => true); - - if (order.Id == order2?.Id) - { - await Task.Delay(3000); - } - - - await DoWorkAsync(); - } - - // [OTel] - private async Task DoWorkAsync() - { - await Task.Delay(1000); - } -} diff --git a/src/TrackCenter.Application/Services/DebugApis/TransportDetailsGenerate/_TransportDetailsGenerateService.cs b/src/TrackCenter.Application/Services/DebugApis/TransportDetailsGenerate/_TransportDetailsGenerateService.cs deleted file mode 100644 index dd834e61..00000000 --- a/src/TrackCenter.Application/Services/DebugApis/TransportDetailsGenerate/_TransportDetailsGenerateService.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using TrackCenter.Application.Services.Warning.Handlers; -using TrackCenter.Domain.Aggregates.WarningAggregate; - -namespace TrackCenter.Application.Services.DebugApis.TransportDetailsGenerate; - -/// -/// 运输预警明细生成任务 -/// -[AllowAnonymous] -[ApiExplorerSettings(GroupName = "调试专用")] -[Route("/S07")] -public class _TransportDetailsGenerateService : ApplicationServiceBase -{ - private readonly IWarningTaskRepository _warningTaskRepository; - private readonly ITransportWarningHandler _transportWarningHandler; - - public _TransportDetailsGenerateService(IWarningTaskRepository warningTaskRepository, ITransportWarningHandler transportWarningHandler) - { - _warningTaskRepository = warningTaskRepository; - _transportWarningHandler = transportWarningHandler; - } - - /// - /// 运输预警明细生成任务立即执行,不考虑计划执行时间,执行完成任务会关闭 - /// - /// - [HttpGet("TransportWarningDetailsGenerate_ScanAndExec")] - public async Task TransportWarningDetailsGenerate_ScanAndExec() - { - var tasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x => !x.IsClosed); - var taskIds = tasks.Select(x => x.Id).ToHashSet(); - await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x => !x.IsClosed && taskIds.Contains(x.Id)); - await _transportWarningHandler.HandleWarningDetailsGenerateTask(tasks); - } - -} diff --git a/src/TrackCenter.Application/Services/DebugApis/_EsInitService.cs b/src/TrackCenter.Application/Services/DebugApis/_EsInitService.cs deleted file mode 100644 index 8291caa2..00000000 --- a/src/TrackCenter.Application/Services/DebugApis/_EsInitService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using TrackCenter.Domain.Aggregates.TrackDetailsAggregate; -using TrackCenter.Domain.Aggregates.WarningAggregate; - -namespace TrackCenter.Application.Services.DebugApis; - -[ApiExplorerSettings(GroupName = "调试专用")] -public class _EsInitService( - ITrackDetailsEsRepository trackDetailsEsRepository, - IWarningDetailsEsRepository warningDetailsEsRepository) - : ApplicationService -{ - [HttpGet("EsInit")] - public async Task EsInit() - { - Log.Warning($"ES索引初始化开始 OperationUserId:[{CurrentUser.Id}]", true); - await trackDetailsEsRepository.MigrateAsync(); - await warningDetailsEsRepository.CreateAliasAsync(); - Log.Warning($"ES索引初始化完成 OperationUserId:[{CurrentUser.Id}]", true); - } -} diff --git a/src/TrackCenter.Application/Services/DebugApis/_MockReturnWarningService.cs b/src/TrackCenter.Application/Services/DebugApis/_MockReturnWarningService.cs deleted file mode 100644 index c2b71bff..00000000 --- a/src/TrackCenter.Application/Services/DebugApis/_MockReturnWarningService.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using TrackCenter.Application.Dtos.TrackPush; -using TrackCenter.Application.Services.Warning.Handlers; -using TrackCenter.Domain.Aggregates.TrackPushAggregate; - -namespace TrackCenter.Application.Services.DebugApis; - -[AllowAnonymous] -[ApiExplorerSettings(GroupName = "调试专用")] -[Route("/S07")] -public class _MockReturnWarningService : ApplicationServiceBase -{ - private readonly IReturnWarningHandler _returnWarningHandler; - - public _MockReturnWarningService(IReturnWarningHandler returnWarningHandler) - { - _returnWarningHandler = returnWarningHandler; - } - - // /// - // /// 模拟接口通回调 - // /// - // [HttpPost("MockReturn")] - // public async Task MockReturn(TrackPushRecordIndex record) - // { - // await _returnWarningHandler.ReceiveTrackPushRecordHandleAsync(record); - // return "Ok"; - // } - - - - /// - /// 模拟接口通回调 - /// - /// - /// - [HttpPost("MockReturn")] - public async Task MockReturn(MockReturnInput input) - { - var requestPrams = TextJsonHelper.Serialize(input.PushInputs)!; - input.Records.ForEach(x => - { - x.RequestId = input.RequestId; - x.IsSuccess = input.IsSuccess; - x.Message = input.ReturnMessage; - }); - await _returnWarningHandler.ReceiveTrackPushRecordHandleAsync(input.IsSuccess, requestPrams, input.Records, input.PushInputs, new List(0)); - return "Ok"; - } - - -} - -public record MockReturnInput(bool IsSuccess, string ReturnMessage, Guid RequestId, List PushInputs,List Records); diff --git a/src/TrackCenter.Application/Services/ForewarningServices/KpiAreaCacheService.cs b/src/TrackCenter.Application/Services/ForewarningServices/KpiAreaCacheService.cs deleted file mode 100644 index 04290200..00000000 --- a/src/TrackCenter.Application/Services/ForewarningServices/KpiAreaCacheService.cs +++ /dev/null @@ -1,24 +0,0 @@ -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; - -namespace TrackCenter.Application.Services.ForewarningServices; - -public class KpiAreaCacheService : ITransientDependency -{ - const string GlobalKpiAreaLockKey = "KpiAreaCacheService_GlobalLock"; - IKpiAreaRepository _kpiAreaRepository; - - public KpiAreaCacheService(IKpiAreaRepository kpiAreaRepository) - { - _kpiAreaRepository = kpiAreaRepository; - } - - private string GetKey(string appKey, string country) - { - return $"{GlobalConst.KpiAreaKeyPrefix}:{appKey}:{country}"; - } - - public Task GetKpiArea(string appKey, string country) - { - throw new NotImplementedException(); - } -} diff --git a/src/TrackCenter.Application/Services/ForewarningServices/KpiAreaService.cs b/src/TrackCenter.Application/Services/ForewarningServices/KpiAreaService.cs deleted file mode 100644 index 6b5dee6a..00000000 --- a/src/TrackCenter.Application/Services/ForewarningServices/KpiAreaService.cs +++ /dev/null @@ -1,147 +0,0 @@ -using Fake.Application.Dtos; -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; -using TrackCenter.Domain.Dtos.Forewarning; - -namespace TrackCenter.Application.Services.ForewarningServices; - -[ApiExplorerSettings(GroupName = "预警服务")] -[Route("/ForewarningService")] -public class KpiAreaService : ApplicationServiceBase -{ - private readonly IKpiAreaRepository _ruleAreaRepository; - private readonly KpiAreaCacheService _kpiAreaCacheService; - - public KpiAreaService(IKpiAreaRepository ruleAreaRepository, KpiAreaCacheService kpiAreaCacheService) - { - _ruleAreaRepository = ruleAreaRepository; - _kpiAreaCacheService = kpiAreaCacheService; - } - - /// - /// 创建分区 - /// - /// - /// 分区id - [HttpPost("KpiArea/CreateKpiArea")] - public async ValueTask CreateKpiArea(CreateRuleAreaRequest input) - { - var ruleAreaEntity = ObjectMapper.Map(input); - var hasSameName = - await _ruleAreaRepository.IsAnyAsync(val => val.AppKey == input.AppKey && val.Name == input.Name); - if (hasSameName) - { - throw new BusinessException("分区名称重复,请重新修改提交"); - } - - var areaId = await _ruleAreaRepository.CreateAreaAsync(ruleAreaEntity); - return new IdBaseDto - { - Id = areaId, - }; - } - - /// - /// 添加区域信息 - /// - /// - /// 区域id - [HttpPost("KpiArea/AddAreaInfo")] - public async ValueTask AddAreaInfoAsync(AddRuleAreaInfoRequest input) - { - var ruleAreaEntity = ObjectMapper.Map(input); - var ruleInfoId = await _ruleAreaRepository.AddAreaInfoAsync(ruleAreaEntity); - // todo: 删除 kpi区域缓存 - return new IdBaseDto - { - Id = ruleInfoId, - }; - } - - /// - /// 删除分区 - /// - /// 分区id - /// - [HttpDelete("KpiArea/DeleteKpiArea")] - public async ValueTask DeleteKpiArea(int id) - { - await _ruleAreaRepository.DeleteArea(id); - // todo: 删除 kpi区域缓存 - } - - /// - /// 删除分区信息 - /// - /// 分区信息id - /// - [HttpDelete("KpiArea/DeleteKpiAreaInfo")] - public async ValueTask DeleteKpiAreaInfo(int id) - { - var info = await _ruleAreaRepository.GetAreaInfoById(id); - if (info == null) throw new BusinessException("未找到该区域信息"); - await _ruleAreaRepository.DeleteAreaInfo(id); - // todo: 删除 kpi区域缓存 - } - - /// - /// 更新区域信息 - /// - /// - /// - [HttpPost("KpiArea/ModifyKpiArea")] - public async Task ModifyKpiArea(UpdateRuleAreaRequest input) - { - if (input.Id <= 0) - throw new BusinessException("id不能为空"); - - var ruleArea = await _ruleAreaRepository.GetFirstAsync(val => val.Id == input.Id); - if (ruleArea == null) - throw new BusinessException("未找到该区域"); - - ruleArea.Name = input.Name; - await _ruleAreaRepository.UpdateAsync(ruleArea); - } - - /// - /// 更新区域信息 - /// - /// - /// - [HttpPost("KpiArea/ModifyKpiAreaInfo")] - public async Task ModifyKpiAreaInfo(UpdateRuleAreaInfoRequest input) - { - if (input.Id <= 0) - throw new BusinessException("id不能为空"); - - var areaInfo = await _ruleAreaRepository.GetAreaInfoById(input.Id); - if (areaInfo == null) - throw new BusinessException("未找到该区域信息"); - - var ruleAreaEntity = ObjectMapper.Map(input, areaInfo); - await _ruleAreaRepository.UpdateAreaInfoAsync(ruleAreaEntity); - - // todo: 删除 kpi区域缓存 - } - - /// - /// 查询分区列表 - /// - /// - /// - [HttpPost("KpiArea/GetKpiAreas")] - public async Task> GetKpiAreas(QueryAreaRequest input) - { - return await _ruleAreaRepository.GetAreas(input); - } - - /// - /// 查询分区列表 - /// - /// - /// - [HttpPost("KpiArea/GetKpiAreaInfos")] - public async Task> GetKpiAreaInfos(QueryAreaInfoRequest query) - { - return await _ruleAreaRepository.GetAreaInfos(query); - } -} diff --git a/src/TrackCenter.Application/Services/ProvideServices/OrderService.Link.cs b/src/TrackCenter.Application/Services/ProvideServices/OrderService.Link.cs index 938bc44e..255dbbfb 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/OrderService.Link.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/OrderService.Link.cs @@ -1,4 +1,4 @@ -using DotNetCore.CAP; +using DotNetCore.CAP; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Events; using TrackCenter.Domain.Aggregates.OrderAggregate; @@ -28,16 +28,19 @@ public partial class OrderService x => x.Id); if (orderId == default) { - throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); + throw ProviderServiceActionResult.Exception("关联失败", $"订单:{input.OrderNo}不存在"); } // tips:2025-11-29 zzh 订单类型(1是小包,2是大包,4是主单),必填,同时orderType小于linkOrderType if (input.OrderType < input.LinkOrderType) { - throw new BusinessException(message: "orderType小于linkOrderType"); + throw ProviderServiceActionResult.Exception("关联失败", "orderType小于linkOrderType"); } - if (input.LinkOrderNos.Count < 1 || input.LinkOrderType == 0) return; + if (input.LinkOrderNos.Count < 1 || input.LinkOrderType == 0) + { + return; + } // 单号去重,去空,去自己 input.LinkOrderNos = input.LinkOrderNos @@ -46,17 +49,14 @@ public partial class OrderService if (!input.IsLink) { - // 解绑 await UnLinkOrderAsync(input, orderId); + return; } - else - { - // 关联订单 - await DoLinkOrderAsync(input, orderId); - } + + await DoLinkOrderAsync(input, orderId); } - private async Task DoLinkOrderAsync(LinkOrderInput input, int orderId) + private async Task DoLinkOrderAsync(LinkOrderInput input, int orderId) { // 获取已关联的订单 var linkOrderNos = @@ -81,8 +81,13 @@ public partial class OrderService // 如果有不存在的单,需要抛异常 if (existedOrders.Count != noLinkOrderNos.Count) { - throw new BusinessException( - message: $"关联单:{noLinkOrderNos.Except(existedOrders.Select(x => x.OrderNo)).JoinAsString(", ")}不存在"); + throw ProviderServiceActionResult.Exception("关联失败", + $"关联单:{noLinkOrderNos.Except(existedOrders.Select(x => x.OrderNo)).JoinAsString(", ")}不存在"); + } + + if (existedOrders.Count == 0) + { + return 0; } await _orderRepository.LinkOrderAsync(existedOrders.Select(x => new OrderLinkEntity @@ -125,14 +130,17 @@ public partial class OrderService await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, fillTrackEvent); } } + + return existedOrders.Count; } - private async Task UnLinkOrderAsync(LinkOrderInput input, int orderId) + private async Task UnLinkOrderAsync(LinkOrderInput input, int orderId) { var unLinkIds = await _orderRepository.GetLinkedOrdersAsync(orderId, input.LinkAppKey, input.LinkOrderType, input.LinkOrderNos); // real del await _orderRepository.UnLinkByIdsAsync(unLinkIds); + return unLinkIds.Count; } } diff --git a/src/TrackCenter.Application/Services/ProvideServices/OrderService.PullConfigs.cs b/src/TrackCenter.Application/Services/ProvideServices/OrderService.PullConfigs.cs index 2edea9eb..85bc64a0 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/OrderService.PullConfigs.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/OrderService.PullConfigs.cs @@ -1,4 +1,4 @@ -using InterfaceForward.Domain.Shared.Helpers; +using InterfaceForward.Domain.Shared.Helpers; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Domain.Dtos.Order; @@ -61,6 +61,11 @@ public partial class OrderService x => x.AppKey == input.AppKey && x.OrderType == input.OrderType && input.OrderNos.Contains(x.OrderNo), x => x.Id); + if (orderIds.Count == 0) + { + throw ProviderServiceActionResult.Exception("修改拉取配置失败", "未找到匹配订单"); + } + var inputVo = ObjectMapper .Map(input.Config); inputVo.OrderIds = orderIds.ToArray(); @@ -69,5 +74,6 @@ public partial class OrderService Log.Warning( $"{CurrentUser.UserName}正在修改服务商拉取信息,本次共计影响了{orderIds.Count}个订单、{cnt}段拉取,请求参数是:{input.ToJson()}", true); + } } diff --git a/src/TrackCenter.Application/Services/ProvideServices/OrderServiceV2.cs b/src/TrackCenter.Application/Services/ProvideServices/OrderService.V2.cs similarity index 66% rename from src/TrackCenter.Application/Services/ProvideServices/OrderServiceV2.cs rename to src/TrackCenter.Application/Services/ProvideServices/OrderService.V2.cs index a0ba0365..f8949f36 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/OrderServiceV2.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/OrderService.V2.cs @@ -1,36 +1,31 @@ -using InterfaceForward.Domain.Shared.Helpers; +using InterfaceForward.Domain.Shared.Helpers; +using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Dtos.ProvideServices.V2; using TrackCenter.Application.Filters; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.TrackPullAggregate; -using TrackCenter.Repository.Aggregates.OrderAggregate; namespace TrackCenter.Application.Services.ProvideServices; -[VerifySignature] -public class OrderServiceV2( - IOrderRepository orderRepository, - IOrderOperationRecordEsRepository orderOperationRecordEsRepository, - ITrackPullConfigRepository trackPullConfigRepository) - : ApplicationServiceBase +/// +/// 订单服务 - 小包/大包/主单 Upsert(原 OrderServiceV2) +/// +public partial class OrderService { /// /// 新增、更新小包(存在就更新) /// - /// - /// - [HttpPost("OrderV2/UpsertSmallBag")] - public async Task UpsertAsync(UpsertSmallBagInput input) + [HttpPost("Order/UpsertSmallBag")] + [VerifySignature] + public async Task UpsertSmallBagAsync(UpsertSmallBagInput input) { - // 校验订单是否已存在,存在则更新 - var order = await orderRepository.GetFirstAsync(x => + var order = await _orderRepository.GetFirstAsync(x => x.AppKey == AppKey && x.OrderType == TrackOrderType.小包 && x.OrderNo == input.OrderNo); - // 订单已存在则视为更新 if (order != null) { order = await BuiltOrder(input); - await orderRepository.UpdateOrderAsync(order); + await _orderRepository.UpdateOrderAsync(order); var index = new OrderOperationRecordIndex { @@ -42,18 +37,14 @@ public class OrderServiceV2( CreateUserName = CurrentUser.UserName ?? string.Empty, }; - _ = orderOperationRecordEsRepository.AddAsync(index); + _ = _orderOperationRecordEsRepository.AddAsync(index); - return new IdBaseDto - { - Id = order.Id - }; + return new IdBaseDto { Id = order.Id }; } order = await BuiltOrder(input); - var orderId = await orderRepository.CreateOrderAsync(order); + var orderId = await _orderRepository.CreateOrderAsync(order); - // 写入操作日志 var indexForAdd = new OrderOperationRecordIndex { OrderId = orderId, @@ -64,32 +55,25 @@ public class OrderServiceV2( CreateUserName = CurrentUser.UserName ?? string.Empty, CreateTime = DateTime.Now }; - _ = orderOperationRecordEsRepository.AddAsync(indexForAdd); + _ = _orderOperationRecordEsRepository.AddAsync(indexForAdd); - - return new IdBaseDto - { - Id = orderId - }; + return new IdBaseDto { Id = orderId }; } /// /// 新增、更新大包(存在就更新) /// - /// - /// - [HttpPost("OrderV2/UpsertBigBag")] - public async Task UpsertAsync(UpsertBigBagInput input) + [HttpPost("Order/UpsertBigBag")] + [VerifySignature] + public async Task UpsertBigBagAsync(UpsertBigBagInput input) { - // 校验订单是否已存在,存在则更新 - var order = await orderRepository.GetFirstAsync(x => + var order = await _orderRepository.GetFirstAsync(x => x.AppKey == AppKey && x.OrderType == TrackOrderType.大包 && x.OrderNo == input.BagNo); - // 订单已存在则视为更新 if (order != null) { order = await BuiltOrder(input); - await orderRepository.UpdateOrderAsync(order); + await _orderRepository.UpdateOrderAsync(order); var index = new OrderOperationRecordIndex { @@ -101,18 +85,14 @@ public class OrderServiceV2( CreateUserName = CurrentUser.UserName ?? string.Empty, }; - _ = orderOperationRecordEsRepository.AddAsync(index); + _ = _orderOperationRecordEsRepository.AddAsync(index); - return new IdBaseDto - { - Id = order.Id - }; + return new IdBaseDto { Id = order.Id }; } order = await BuiltOrder(input); - var orderId = await orderRepository.CreateOrderAsync(order); + var orderId = await _orderRepository.CreateOrderAsync(order); - // 写入操作日志 var indexForAdd = new OrderOperationRecordIndex { OrderId = orderId, @@ -123,32 +103,25 @@ public class OrderServiceV2( CreateUserName = CurrentUser.UserName ?? string.Empty, CreateTime = DateTime.Now }; - _ = orderOperationRecordEsRepository.AddAsync(indexForAdd); + _ = _orderOperationRecordEsRepository.AddAsync(indexForAdd); - - return new IdBaseDto - { - Id = orderId - }; + return new IdBaseDto { Id = orderId }; } /// /// 新增、更新主单(存在就更新) /// - /// - /// - [HttpPost("OrderV2/UpsertMasterOrder")] - public async Task UpsertAsync(UpsertMasterOrderInput input) + [HttpPost("Order/UpsertMasterOrder")] + [VerifySignature] + public async Task UpsertMasterOrderAsync(UpsertMasterOrderInput input) { - // 校验订单是否已存在,存在则更新 - var order = await orderRepository.GetFirstAsync(x => + var order = await _orderRepository.GetFirstAsync(x => x.AppKey == AppKey && x.OrderType == TrackOrderType.主单 && x.OrderNo == input.MasterNo); - // 订单已存在则视为更新 if (order != null) { order = await BuiltOrder(input); - await orderRepository.UpdateOrderAsync(order); + await _orderRepository.UpdateOrderAsync(order); var index = new OrderOperationRecordIndex { @@ -160,18 +133,14 @@ public class OrderServiceV2( CreateUserName = CurrentUser.UserName ?? string.Empty, }; - _ = orderOperationRecordEsRepository.AddAsync(index); + _ = _orderOperationRecordEsRepository.AddAsync(index); - return new IdBaseDto - { - Id = order.Id - }; + return new IdBaseDto { Id = order.Id }; } order = await BuiltOrder(input); - var orderId = await orderRepository.CreateOrderAsync(order); + var orderId = await _orderRepository.CreateOrderAsync(order); - // 写入操作日志 var indexForAdd = new OrderOperationRecordIndex { OrderId = orderId, @@ -182,13 +151,9 @@ public class OrderServiceV2( CreateUserName = CurrentUser.UserName ?? string.Empty, CreateTime = DateTime.Now }; - _ = orderOperationRecordEsRepository.AddAsync(indexForAdd); + _ = _orderOperationRecordEsRepository.AddAsync(indexForAdd); - - return new IdBaseDto - { - Id = orderId - }; + return new IdBaseDto { Id = orderId }; } private async Task BuiltOrder(UpsertSmallBagInput input) @@ -212,11 +177,15 @@ public class OrderServiceV2( ArrivalEmail = input.Consignee.Email, }; - var config = await trackPullConfigRepository + var config = await _trackPullConfigRepository .FirstOrDefaultAsync(x => x.AppKey == AppKey && x.ServiceProviderCode == input.ApiCode); - if (config == null) throw new BusinessException(message: "没有一个拉取配置可以适用,请配置,否则无法拉取轨迹"); + if (config == null) + { + throw ProviderServiceActionResult.Exception("创建/更新失败", + "没有适用的拉取配置,请先配置轨迹拉取,否则无法拉取轨迹"); + } order.OrderPullStatus = [CreateOrderPullStatus(config, order)]; @@ -228,7 +197,6 @@ public class OrderServiceV2( var order = new OrderEntity { AppKey = AppKey!, - //ServiceProviderCode = input.ApiCode, OrderType = TrackOrderType.大包, OrderNo = input.BagNo, ThirdPartyOrderNumber = input.OtherNo, @@ -239,15 +207,6 @@ public class OrderServiceV2( }; // tips: 2025-11-29 zzh 大包没有轨迹拉取能力 - - // var config = await trackPullConfigRepository - // .FirstOrDefaultAsync(x => x.AppKey == AppKey - // && x.ServiceProviderCode == input.ApiCode); - // - // if (config == null) throw new BusinessException(message: "没有一个拉取配置可以适用,请配置,否则无法拉取轨迹"); - // - // order.OrderPullStatus = [CreateOrderPullStatus(config, order)]; - return Task.FromResult(order); } @@ -266,11 +225,15 @@ public class OrderServiceV2( order.OrderFlights = new List(); - var config = await trackPullConfigRepository + var config = await _trackPullConfigRepository .FirstOrDefaultAsync(x => x.AppKey == AppKey && x.ServiceProviderCode == input.ApiCode); - if (config == null) throw new BusinessException(message: "没有一个拉取配置可以适用,请配置,否则无法拉取轨迹"); + if (config == null) + { + throw ProviderServiceActionResult.Exception("创建/更新失败", + "没有适用的拉取配置,请先配置轨迹拉取,否则无法拉取轨迹"); + } order.OrderPullStatus = [CreateOrderPullStatus(config, order)]; diff --git a/src/TrackCenter.Application/Services/ProvideServices/OrderService.cs b/src/TrackCenter.Application/Services/ProvideServices/OrderService.cs index 870f6999..4c733266 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/OrderService.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/OrderService.cs @@ -1,9 +1,8 @@ using DotNetCore.CAP; -using InterfaceForward.Domain.Shared.Helpers; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.TrackDetailsAggregate; -using SqlSugar; +using TrackCenter.Domain.Aggregates.TrackPullAggregate; using TrackCenter.Application.Services.Warning.Handlers; using TrackCenter.Repository.Aggregates.OrderAggregate; @@ -12,187 +11,32 @@ namespace TrackCenter.Application.Services.ProvideServices; /// /// 订单服务 /// -[ApiExplorerSettings(GroupName = "提供接口")] +[ApiExplorerSettings(GroupName = "OpenApi")] [Route("/ProviderService")] public partial class OrderService : ApplicationServiceBase { - private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository; private readonly IOrderRepository _orderRepository; + private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository; + private readonly ITrackPullConfigRepository _trackPullConfigRepository; private readonly IServiceProvider _serviceProvider; public OrderService( IOrderRepository orderRepository, + IOrderOperationRecordEsRepository orderOperationRecordEsRepository, + ITrackPullConfigRepository trackPullConfigRepository, ICapPublisher capPublisher, ITrackDetailsEsRepository trackDetailsEsRepository, - IOrderOperationRecordEsRepository orderOperationRecordEsRepository, IServiceProvider serviceProvider ) { _orderRepository = orderRepository; _orderOperationRecordEsRepository = orderOperationRecordEsRepository; + _trackPullConfigRepository = trackPullConfigRepository; _serviceProvider = serviceProvider; _trackDetailsEsRepository = trackDetailsEsRepository; _capPublisher = capPublisher; } - /// - /// 新增、更新小包(存在就更新) - /// - /// - /// - [HttpPost("Order/Upsert")] - public async Task UpsertAsync(UpsertOrderInput input) - { - // 校验预计到达时间是否不是同时存在或同时不存在 - if ((input.EstimatedDeliveryDate.HasValue && input.DestinationTimeZone.HasValue == false) || - (input.EstimatedDeliveryDate.HasValue == false && input.DestinationTimeZone.HasValue)) - { - throw new BusinessException(message: "【预计签收时间】和【签收地时区】必须同时输入"); - } - - // 校验订单是否已存在,存在则更新 - var order = await _orderRepository.GetFirstAsync(x => - x.AppKey == input.AppKey && x.OrderType == input.OrderType && x.OrderNo == input.OrderNo); - if (order != null) - { - // 订单已存在则视为更新 - await UpdateOrderAsync(input, order); - - var index = new OrderOperationRecordIndex - { - OrderId = order.Id, - OrderNo = input.OrderNo, - OptionType = OrderOperationType.修改订单.ToString(), - Content = input.ToJson(), - CreateUserId = CurrentUserId, - CreateUserName = CurrentUser.UserName ?? string.Empty, - }; - - _ = _orderOperationRecordEsRepository.AddAsync(index); - - return new IdBaseDto - { - Id = order.Id - }; - } - - order = ObjectMapper.Map(input); - order.OrderFlights = ObjectMapper.Map, List>(input.FlightInfos); - order.OrderPullStatus = BuiltOrderPullStatus(input.PullConfigInfos); - order.SetThirdPartyOrderNumber(order.OrderPullStatus); - - var orderId = await _orderRepository.CreateOrderAsync(order); - - // 写入操作日志 - var indexForAdd = new OrderOperationRecordIndex - { - OrderId = orderId, - OrderNo = input.OrderNo, - OptionType = nameof(OrderOperationType.创建订单), - Content = input.ToJson(), - CreateUserId = CurrentUserId, - CreateUserName = CurrentUser.UserName ?? string.Empty, - CreateTime = DateTime.Now - }; - _ = _orderOperationRecordEsRepository.AddAsync(indexForAdd); - - - return new IdBaseDto - { - Id = orderId - }; - } - - /// - /// 批量创建/新增 轨迹订单 - /// - /// - /// - /// - [HttpPost("Order/BatchUpsert")] - public async Task BatchUpsertAsync(BatchUpsertOrderInput input) - { - if (input.OrderInputs.Any(x => (x.EstimatedDeliveryDate.HasValue && x.DestinationTimeZone.HasValue == false) || - (x.EstimatedDeliveryDate.HasValue == false && x.DestinationTimeZone.HasValue))) - { - throw new BusinessException(message: "【预计签收时间】和【签收地时区】必须同时输入"); - } - - var exp = Expressionable.Create(); - foreach (var order in input.OrderInputs) - exp.Or(x => x.OrderType == order.OrderType && x.AppKey == order.AppKey && x.OrderNo == order.OrderNo); - - // 获取已存在的订单 - var existsOrders = await _orderRepository.GetListAsync(exp.ToExpression()); - var updateOrders = existsOrders.Count > 0 ? new List(existsOrders.Count) : Empty.List; - var updateRecords = existsOrders.Count > 0 ? new List(existsOrders.Count) : Empty.List; - if (existsOrders.Count > 0) - { - foreach (var order in existsOrders) - { - var input2 = input.OrderInputs.First(x => x.OrderType == order.OrderType && x.AppKey == order.AppKey && x.OrderNo == order.OrderNo); - var order2 = ObjectMapper.Map(input2, order); - order2.OrderFlights = ObjectMapper.Map, List>(input2.FlightInfos); - order2.OrderPullStatus = BuiltOrderPullStatus(input2.PullConfigInfos); - order2.SetThirdPartyOrderNumber(order.OrderPullStatus); - updateOrders.Add(order2); - updateRecords.Add(new OrderOperationRecordIndex - { - OrderId = order.Id, - OrderNo = order.OrderNo, - OptionType = nameof(OrderOperationType.修改订单), - Content = input2.ToJson(), - CreateUserId = CurrentUserId, - CreateUserName = CurrentUser.UserName ?? string.Empty, - CreateTime = DateTime.Now - }); - } - - await _orderRepository.UpdateBatchOrderAsync(updateOrders); - _ = _orderOperationRecordEsRepository.AddManyAsync(updateRecords); - } - - // 如果全部操作为修改 - if (existsOrders.Count == input.OrderInputs.Count) return; - - // 新增订单 - var existsOrderNos = existsOrders.Select(x => x.OrderNo).ToHashSet(); - var insertInputs = input.OrderInputs - .WhereIf(existsOrderNos.Count > 0, x => !existsOrderNos.Contains(x.OrderNo)) - .ToList(); - - var insertOrders = new List(insertInputs.Count); - var createRecords = new List(insertInputs.Count); - foreach (var insertInput in insertInputs) - { - var order = ObjectMapper.Map(insertInput); - order.OrderFlights = ObjectMapper.Map, List>(insertInput.FlightInfos); - order.OrderPullStatus = BuiltOrderPullStatus(insertInput.PullConfigInfos); - order.SetThirdPartyOrderNumber(order.OrderPullStatus); - insertOrders.Add(order); - createRecords.Add(new OrderOperationRecordIndex - { - //OrderId = default, //稍后补充 - OrderNo = order.OrderNo, - OptionType = OrderOperationType.创建订单.ToString(), - Content = insertInput.ToJson(), - CreateUserId = CurrentUserId, - CreateUserName = CurrentUser.UserName ?? string.Empty, - CreateTime = DateTime.Now - }); - } - - // 创建订单,创建操作记录 - var orderNoIdDict = await _orderRepository.CreateBatchOrderAsync(insertOrders); - foreach (var record in createRecords) - { - var orderId = orderNoIdDict[record.OrderNo]; - record.OrderId = orderId; - } - - _ = _orderOperationRecordEsRepository.AddManyAsync(createRecords); - } - /// /// 删除订单 /// @@ -206,7 +50,7 @@ public partial class OrderService : ApplicationServiceBase x.AppKey == input.AppKey && x.OrderType == input.OrderType && x.OrderNo == input.OrderNo); if (order == null) { - throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); + throw ProviderServiceActionResult.Exception("删除订单失败", $"订单:{input.OrderNo}不存在"); } await _orderRepository.DeleteOrderAsync(order.Id); @@ -214,50 +58,7 @@ public partial class OrderService : ApplicationServiceBase // 预警计算 WarningCalc_OrderDeletedOrCompleted(order); - return new IdBaseDto - { - Id = order.Id - }; - } - - private async Task UpdateOrderAsync(UpdateOrderInput input, OrderEntity order) - { - order = ObjectMapper.Map(input, order); - order.OrderFlights = ObjectMapper.Map, List>(input.FlightInfos); - order.OrderPullStatus = BuiltOrderPullStatus(input.PullConfigInfos); - order.SetThirdPartyOrderNumber(order.OrderPullStatus); - await _orderRepository.UpdateOrderAsync(order); - } - - - private static List BuiltOrderPullStatus(List? pullConfigInfos) - { - if (pullConfigInfos == null) return new List(); - - return pullConfigInfos.Select(x => - { - var status = new OrderPullStatusEntity - { - ServiceProviderCode = x.ServiceCode, - OrderNo = x.OrderNo, - PullStatus = TrackPullStatus.Init, - StartTrackCode = x.StartCode ?? GlobalConst.EmptyString, - EndTrackCode = x.EndCode ?? GlobalConst.EmptyString, - SilenceBeforeStart = x.SilenceTime, - SilenceAfterEnd = x.StopTime, - StartTime = GlobalConst.MaxDateTime, - EndTime = GlobalConst.MaxDateTime, - }; - - // 没有开始轨迹则立即进入拉取前静默期 - if (x.StartCode == null) - { - status.PullStatus = TrackPullStatus.SilenceBeforeStart; // 会被定时任务扫描到并启动 - status.StartTime = DateTime.Now.AddHours(x.SilenceTime); - } - - return status; - }).ToList(); + return new IdBaseDto { Id = order.Id }; } private void WarningCalc_OrderDeletedOrCompleted(OrderEntity order) diff --git a/src/TrackCenter.Application/Services/ProvideServices/TrackContentService.cs b/src/TrackCenter.Application/Services/ProvideServices/TrackContentService.cs deleted file mode 100644 index e6d72588..00000000 --- a/src/TrackCenter.Application/Services/ProvideServices/TrackContentService.cs +++ /dev/null @@ -1,34 +0,0 @@ -using TrackCenter.Application.Dtos.TrackContents; -using TrackCenter.Application.Services.ProvideServices; -using TrackCenter.Domain.Aggregates.TrackContentAggregate; -using TrackCenter.Domain.Dtos.TrackContents; - -namespace TrackCenter.Application.Services.ProvideServices; - -/// -/// 轨迹内容服务 -/// -[ApiExplorerSettings(GroupName = "提供接口")] -[Route("/ProviderService")] -public class TrackContentService : ApplicationServiceBase -{ - private readonly ITrackContentRepository _trackContentRepository; - - public TrackContentService(ITrackContentRepository trackContentRepository) - { - _trackContentRepository = trackContentRepository; - } - - /// - /// 根据应用获取轨迹内容列表 - /// - /// - /// - [HttpPost("Track/GetTrackContents")] - public async Task> GetTrackContentsAsync(string AppKey) - { - var res = await _trackContentRepository.GetTrackContentByAppKeyAsync(AppKey); - var result = ObjectMapper.Map, List>(res); - return result; - } -} diff --git a/src/TrackCenter.Application/Services/ProvideServices/TrackPullService.cs b/src/TrackCenter.Application/Services/ProvideServices/TrackPullService.cs index 87e185e4..22fd0039 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/TrackPullService.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/TrackPullService.cs @@ -1,20 +1,19 @@ -using InterfaceForward.Domain.Shared.Helpers; -using Microsoft.AspNetCore.Authorization; +using InterfaceForward.Domain.Shared.Helpers; using Microsoft.Extensions.Options; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Dtos.TrackPull; using TrackCenter.Application.Helpers; using TrackCenter.Application.Options; using TrackCenter.Application.Queries; -using TrackCenter.Application.Rabbit; using TrackCenter.Application.Track2InterfaceRelay; +using TrackCenter.Domain.Consts; namespace TrackCenter.Application.Services.ProvideServices; /// /// 轨迹刷新服务 /// -[ApiExplorerSettings(GroupName = "提供接口")] +[ApiExplorerSettings(GroupName = "OpenApi")] [Route("/ProviderService")] public class TrackPullService : ApplicationServiceBase { @@ -39,39 +38,45 @@ public class TrackPullService : ApplicationServiceBase [HttpPost("Track/Refresh")] public async Task RefreshTrackAsync(RefreshTrackInput input) { - // todo:罚时? - input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空 + input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); + if (input.OrderNos.Count == 0) + { + throw ProviderServiceActionResult.Exception("刷新失败", "未传入有效订单号"); + } // tips:这里没有区分订单类型,就算的不同类型的单一起刷新了也无所谓 - var orders = await _provideQueries.FindOrderWithServiceProviderAsync(input.AppKey, input.OrderNos); + var (orders, skipReasons) = + await _provideQueries.DiagnoseManualRefreshAsync(input.AppKey, input.OrderNos); if (orders.Count == 0) { - // tips:可能是没有配置、或者所有单都刷完了、或者单不存在 - throw new BusinessException(message: "一个能更新的都没有"); + var reason = skipReasons.Count > 0 + ? string.Join(";", skipReasons) + : "可能原因:订单不存在、已完成、无拉取状态或不在拉取时间窗口内"; + throw ProviderServiceActionResult.Exception("刷新失败:没有可刷新的订单", reason); } - foreach (var group in orders.GroupBy(x => - new - { - x.ServiceProviderCode, - x.AppKey, - x.OrderType - })) + var forwardErrors = new List(); + var submittedOrderCount = 0; + + foreach (var group in orders.GroupBy(x => new + { + x.ServiceProviderCode, + x.AppKey, + x.OrderType + })) { var data = new List(group.Select(x => new TrackPullPayload { OrderNo = x.OrderNo })); - var serviceProviderCode = group.FirstOrDefault()!.ServiceProviderCode; - + var serviceProviderCode = group.Key.ServiceProviderCode; var attach = Base64Helper.SerializeObject(new TrackRefreshAttach(group.Key.ServiceProviderCode, group.Key.AppKey, group.Key.OrderType)); using (Track2InterfaceRelayAppContext.Use( new Track2InterfaceRelayAppContext(group.Key.AppKey))) { - // 批量获取轨迹 var res = await _interfaceForwardService.BatchForwardThenPushToQueueAsync( _options.CurrentValue.GetTrackInterfaceCode, serviceProviderCode, data, GlobalConst.TrackRefreshSubscribe, @@ -79,30 +84,22 @@ public class TrackPullService : ApplicationServiceBase if (res.Code != GlobalConst.SuccessCode) { + var err = + $"服务商[{serviceProviderCode}]下{group.Count()}单转发失败:{(string.IsNullOrWhiteSpace(res.Msg) ? res.ToJson() : res.Msg)}"; Log.Error($"刷新轨迹失败:{res.ToJson()}"); + forwardErrors.Add(err); + continue; } + + submittedOrderCount += group.Count(); } } + if (submittedOrderCount == 0) + { + throw ProviderServiceActionResult.Exception("刷新失败:下游转发均未成功", string.Join(";", forwardErrors)); + } + return true; } - - - /// - /// 轨迹刷新测试 - /// - /// - /// - /// - [HttpPost("Track/TrackRefreshEventTest")] -#if DEBUG - [AllowAnonymous] -#endif - public async Task TrackRefreshEventTest([FromServices] TrackRefreshEventHandler trackRefreshEventHandler, - InterfaceRelayForwardReplayEvent input) - { - await trackRefreshEventHandler.ConsumerAsync(input); - - return true; - } } diff --git a/src/TrackCenter.Application/Services/ProvideServices/TrackPushConfigService.cs b/src/TrackCenter.Application/Services/ProvideServices/TrackPushConfigService.cs index 6ee2295b..25fa71dd 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/TrackPushConfigService.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/TrackPushConfigService.cs @@ -1,4 +1,4 @@ -using TrackCenter.Application.Dtos.ProvideServices; +using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Services; using TrackCenter.Domain.Aggregates.TrackPushAggregate; using TrackCenter.Domain.Dtos.TrackPush; @@ -8,7 +8,7 @@ namespace TrackCenter.Application.Services.ProvideServices; /// /// 轨迹推送配置服务 /// -[ApiExplorerSettings(GroupName = "提供接口")] +[ApiExplorerSettings(GroupName = "OpenApi")] [Route("/ProviderService")] public class TrackPushConfigService : ApplicationServiceBase { diff --git a/src/TrackCenter.Application/Services/ProvideServices/TrackPushService.cs b/src/TrackCenter.Application/Services/ProvideServices/TrackPushService.cs index 7d24e23e..4afc5335 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/TrackPushService.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/TrackPushService.cs @@ -1,4 +1,4 @@ -using DotNetCore.CAP; +using DotNetCore.CAP; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Dtos.TrackOrder; using TrackCenter.Application.Dtos.TrackPush; @@ -14,7 +14,7 @@ namespace TrackCenter.Application.Services.ProvideServices; /// /// 轨迹推送服务 /// -[ApiExplorerSettings(GroupName = "提供接口")] +[ApiExplorerSettings(GroupName = "OpenApi")] [Route("/ProviderService")] public class TrackPushService : ApplicationServiceBase { @@ -45,7 +45,16 @@ public class TrackPushService : ApplicationServiceBase [HttpPost("Track/Push")] public async Task PushTrackAsync(PushTrackInput input) { - input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空 + input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); + if (input.OrderNos.Count == 0) + { + throw ProviderServiceActionResult.Exception("推送失败", "未传入有效订单号"); + } + + if (string.IsNullOrWhiteSpace(input.TrackCode)) + { + throw ProviderServiceActionResult.Exception("推送失败", "未指定轨迹代码 TrackCode"); + } var orders = await _orderRepository.GetListAsync(x => x.AppKey == input.AppKey @@ -53,62 +62,70 @@ public class TrackPushService : ApplicationServiceBase && input.OrderNos.Contains(x.OrderNo), x => new { x.Id, + x.OrderNo, x.Customer }); - if (!orders.Any()) + var foundOrderNos = orders.Select(x => x.OrderNo).ToHashSet(StringComparer.OrdinalIgnoreCase); + var missingOrderNos = input.OrderNos + .Where(x => !foundOrderNos.Contains(x)) + .ToList(); + + if (orders.Count == 0) { - throw new BusinessException(message: "一个订单都不存在"); + throw ProviderServiceActionResult.Exception("推送失败:没有匹配的订单", $"订单不存在或不匹配 AppKey/OrderType:{string.Join("、", input.OrderNos)}"); } + var name = CurrentUser.UserName; - //查出可能需要推送的轨迹 - var tracks = await _trackDetailsEsRepository.GetListAsync(input.AppKey, input.OrderType, input.OrderNos, new List - { - input.TrackCode - }); + var tracks = await _trackDetailsEsRepository.GetListAsync(input.AppKey, input.OrderType, input.OrderNos, + new List + { + input.TrackCode + }); if (tracks.Count == 0) { - throw new BusinessException(message: "没有需要推送的轨迹"); + throw ProviderServiceActionResult.Exception("推送失败:没有需要推送的轨迹", $"订单存在,但未找到轨迹代码[{input.TrackCode}]对应的轨迹明细"); } - //存在比较字段,需要做过滤处理 if (!string.IsNullOrWhiteSpace(input.CompareFields)) { - //根据输入值来进行过滤,比如选取code1,批次1的轨迹,可能会有多条,下面使用创建时间最新的一条 tracks = tracks.Where(x => { if (string.IsNullOrWhiteSpace(x.Extra)) { - throw new BusinessException(message: $"当前轨迹不存在扩展信息,无法利用扩展字段:{input.CompareFields}"); + throw ProviderServiceActionResult.Exception("推送失败:扩展字段比较失败", $"轨迹[{x.OrderNo}/{input.TrackCode}]不存在扩展信息 Extra,无法用比较字段[{input.CompareFields}]过滤"); } var extraDict = TextJsonHelper.DeserializeToDictionary(x.Extra); if (extraDict == null || !extraDict.TryGetValue(input.CompareFields, out var compareValue)) { - throw new BusinessException(message: $"请检查拓展字段,为什么找不到比较字段:{input.CompareFields}"); + throw ProviderServiceActionResult.Exception("推送失败:扩展字段比较失败", $"轨迹[{x.OrderNo}/{input.TrackCode}]的 Extra 中找不到比较字段[{input.CompareFields}]"); } - //补充轨迹内容 return TextJsonHelper.EqJsonElementAndObject(compareValue, input.CompareValue); }).ToList(); } - // 经过过滤后不存在需要推送的轨迹 if (tracks.Count == 0) { - throw new BusinessException(message: "没有需要推送的轨迹"); + throw ProviderServiceActionResult.Exception("推送失败:没有需要推送的轨迹", $"按比较字段[{input.CompareFields}]过滤后无匹配轨迹"); } - // 构建每个单最早的轨迹(已经指定了code) var orderIdTrackDict = tracks.GroupBy(x => x.OrderId) .ToDictionary(x => x.Key, y => y.OrderByDescending(a => a.CreateTime).First()); - //不同单可能会有不同的轨迹内容,手动推送只推送最新的一条 + var pushedOrderNos = new List(); + var skippedNoTrack = new List(); + foreach (var order in orders) { var track = orderIdTrackDict.GetValueOrDefault(order.Id); - if (track == null) continue; + if (track == null) + { + skippedNoTrack.Add(order.OrderNo); + continue; + } var trackPushEvent = new TrackPushEvent { @@ -119,7 +136,6 @@ public class TrackPushService : ApplicationServiceBase { [order.Id] = TrackMetadata.Build(track.Id, track.Version, track.CreateTime) }, - // OrderIds = new[] { order.Id }, TrackCode = input.TrackCode, OperationMan = name, SystemPushInfo = new SystemPushStruct @@ -137,9 +153,15 @@ public class TrackPushService : ApplicationServiceBase }; await _capPublisher.PublishAsync(GlobalConst.TrackManualPushSubscribe, trackPushEvent); + pushedOrderNos.Add(order.OrderNo); } - Log.Information($"即将推送轨迹代码{input.TrackCode}给{orderIdTrackDict.Count}个订单"); + if (pushedOrderNos.Count == 0) + { + throw ProviderServiceActionResult.Exception("推送失败:没有订单成功提交推送", $"请求 {input.OrderNos.Count} 单均未匹配到可推送轨迹[{input.TrackCode}]"); + } + + Log.Information($"即将推送轨迹代码{input.TrackCode}给{pushedOrderNos.Count}个订单"); } @@ -175,7 +197,8 @@ public class TrackPushService : ApplicationServiceBase ); if (orderId == default) { - throw new BusinessException(message: "订单不存在"); + throw ProviderServiceActionResult.Exception("查询推送记录失败", + $"订单不存在:{input.OrderNo}(AppKey/OrderType 不匹配)"); } var records = await _trackPushRecordEsRepository.GetTrackPushRecordsAsync(orderId); @@ -200,7 +223,11 @@ public class TrackPushService : ApplicationServiceBase y => y.Id ); - if (orderIds.Count == 0) throw new BusinessException(message: "订单一个都不存在"); + if (orderIds.Count == 0) + { + throw ProviderServiceActionResult.Exception("查询推送记录失败", + $"订单一个都不存在:{string.Join("、", input.OrderNos)}"); + } var isSuccess = input.ResultType switch { @@ -217,7 +244,7 @@ public class TrackPushService : ApplicationServiceBase { false }, - _ => throw new BusinessException(message: "结果类型值只能取0,1,2") + _ => throw ProviderServiceActionResult.Exception("查询推送记录失败", "结果类型 ResultType 只能取 0/1/2") }; var records = await _trackPushRecordEsRepository.GetTrackPushRecordsAsync(new GetTrackPushRecordsVO diff --git a/src/TrackCenter.Application/Services/ProvideServices/TrackService.Query.cs b/src/TrackCenter.Application/Services/ProvideServices/TrackService.Query.cs index 0e56cbe0..9120a8e6 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/TrackService.Query.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/TrackService.Query.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Helpers; using TrackCenter.Application.Helpers.OSS; diff --git a/src/TrackCenter.Application/Services/ProvideServices/TrackService.cs b/src/TrackCenter.Application/Services/ProvideServices/TrackService.cs index 992073ed..70189343 100644 --- a/src/TrackCenter.Application/Services/ProvideServices/TrackService.cs +++ b/src/TrackCenter.Application/Services/ProvideServices/TrackService.cs @@ -1,4 +1,4 @@ -using DotNetCore.CAP; +using DotNetCore.CAP; using InterfaceForward.Domain.Shared.Helpers; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Events; @@ -13,7 +13,7 @@ namespace TrackCenter.Application.Services.ProvideServices; /// /// 轨迹服务 /// -[ApiExplorerSettings(GroupName = "提供接口")] +[ApiExplorerSettings(GroupName = "OpenApi")] [Route("/ProviderService")] public partial class TrackService : ApplicationServiceBase { @@ -38,7 +38,6 @@ public partial class TrackService : ApplicationServiceBase /// 删除轨迹 /// /// - /// /// [HttpPost("Track/Delete")] public async Task DeleteTrackAsync(DeleteTrackInput input) @@ -51,7 +50,7 @@ public partial class TrackService : ApplicationServiceBase if (orderId == 0) { - throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); + throw ProviderServiceActionResult.Exception("删除失败", $"订单:{input.OrderNo}不存在"); } // 查询轨迹优先使用esId,tips:考虑删除事件处理直接通过EsId而不是再查询一遍? @@ -76,7 +75,8 @@ public partial class TrackService : ApplicationServiceBase .WhereIf(!input.CompareFields.IsNullOrWhiteSpace(), y => { if (string.IsNullOrWhiteSpace(y.Extra)) - throw new BusinessException(message: "轨迹extra为空无法使用比较字段比较,请检查轨迹"); + throw ProviderServiceActionResult.Exception("删除失败", + "轨迹extra为空无法使用比较字段比较,请检查轨迹"); var extraDict = System.Text.Json.JsonSerializer.Deserialize>(y.Extra); if (extraDict!.TryGetValue(input.CompareFields!, out var compareValueJsonElement)) @@ -89,13 +89,13 @@ public partial class TrackService : ApplicationServiceBase if (tracks == null || tracks.Count == 0) { - throw new BusinessException(message: "没有任何轨迹要删除,请检查输入参数"); + throw ProviderServiceActionResult.Exception("删除失败", "没有任何轨迹要删除,请检查输入参数"); } //删除轨迹约束,只要其中有一条来源是外部的就立即返回错误 if (tracks.Any(x => x.Source == (int) TrackSourceEnum.ExternalServiceProvide)) { - throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除"); + throw ProviderServiceActionResult.Exception("删除失败", "存在来源于外部服务商的轨迹,不允许删除"); } // 删除轨迹(异步) @@ -112,57 +112,26 @@ public partial class TrackService : ApplicationServiceBase }); } - /// - /// 根据esid删除轨迹内容(FBA专用) - /// - /// - /// - [HttpPost("Track/BatchDeleteByEsId")] - public async Task BatchDeleteTrackByEsIdAsync(BatchDeleteTrackByEsIdInput input) - { - var esIds = input.TrackContentEsIds.ToArray(); - var tracks = await _trackDetailsEsRepository.GetDataByIds(esIds); - if (tracks.Count == 0) - throw new BusinessException("未找到指定id下的轨迹内容"); - - if (tracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode())) - throw new BusinessException("存在来源于外部服务商的轨迹,不允许删除"); - - if (tracks.GroupBy(m => m.AppKey).Count() > 1) - throw new BusinessException("暂不支持跨应用批量删除"); - - - // 删除轨迹 - var @event = new TrackDeleteEvent - { - EsIds = esIds, - AppKey = tracks.First().AppKey, - Source = input.Source, - OperationMan = CurrentUser.UserName, - OperationManId = CurrentUserId, - }; - await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event); - } - /// /// 批量删除轨迹 /// /// - /// /// [HttpPost("Track/BatchDelete")] public async Task BatchDeleteTrackAsync(BatchDeleteTrackInput input) { input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空 - var orderIds = await _orderRepository.GetListAsync(x => + var orders = await _orderRepository.GetListAsync(x => x.AppKey == input.AppKey && x.OrderType == input.OrderType && input.OrderNos.Contains(x.OrderNo), - x => x.Id); + x => new { x.Id, x.OrderNo }); + + var orderIds = orders.Select(x => x.Id).ToList(); if (orderIds.Count == 0) { - throw new BusinessException(message: "订单一个都不存在"); + throw ProviderServiceActionResult.Exception("删除失败", "订单一个都不存在"); } //删除轨迹约束,只要其中有一条来源是外部的就立即返回错误 @@ -176,7 +145,8 @@ public partial class TrackService : ApplicationServiceBase .WhereIf(!input.CompareFields.IsNullOrWhiteSpace(), y => { if (string.IsNullOrWhiteSpace(y.Extra)) - throw new BusinessException(message: "轨迹extra为空无法使用比较字段比较,请检查轨迹"); + throw ProviderServiceActionResult.Exception("删除失败", + "轨迹extra为空无法使用比较字段比较,请检查轨迹"); var extraDict = TextJsonHelper.DeserializeToDictionary(y.Extra); if (extraDict!.TryGetValue(input.CompareFields!, out var compareValueJsonElement)) @@ -189,7 +159,7 @@ public partial class TrackService : ApplicationServiceBase .ToList(); if (intersect.Count > 0) { - throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除"); + throw ProviderServiceActionResult.Exception("删除失败", "存在来源于外部服务商的轨迹,不允许删除"); } // 删除轨迹 @@ -210,7 +180,6 @@ public partial class TrackService : ApplicationServiceBase /// 新增、更新轨迹(存在就更新) /// /// - /// /// [HttpPost("Track/Upsert")] public async Task UpsertTrackAsync(UpsertTrackInput input) @@ -229,12 +198,9 @@ public partial class TrackService : ApplicationServiceBase }); if (order == null) { - throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); + throw ProviderServiceActionResult.Exception("提交失败", $"订单:{input.OrderNo}不存在"); } - // 输入参数的静态规则检查 - // 轨迹内容上的约束检查 - // 更新轨迹时是否可以匹配到轨迹的检查 await TrackUpsertValidation(input.IsAdd, input.EsId, input.AppKey, @@ -243,16 +209,10 @@ public partial class TrackService : ApplicationServiceBase input.TrackCode, input.Source, input.TrackTime, - input.TrackTimeZone ?? 8.0f, //没传时区默认使用+8参与过滤 - input.NewTrackTime, - input.EstimatedDeliveryDate, - input.DestinationTimeZone, - input.Extra, - input.NewExtra, - input.CompareFields); + input.TrackTimeZone ?? 8.0f, + input.Extra); - // 新增/更新轨迹 - input.TrackTimeZone ??= GlobalConst.NoneTrackTimeZone; //时区为空使用一个特殊值填充 + input.TrackTimeZone ??= GlobalConst.NoneTrackTimeZone; var @event = ObjectMapper.Map(input); @event.AppKey = input.AppKey; @event.Customer = order.Customer; @@ -273,14 +233,13 @@ public partial class TrackService : ApplicationServiceBase /// 批量新增、更新轨迹 /// /// - /// /// [HttpPost("Track/BatchUpsert")] public async Task BatchUpsertTrackAsync(BatchUpsertTrackInput input) { using var activity = ActivityHelper.CreateSpanActivity("BatchUpsert"); activity.SetTag("BatchUpsertTrackInput", input.ToJson()); - input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空 + input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); var orders = await _orderRepository.GetListAsync(x => x.AppKey == input.AppKey && x.OrderType == input.OrderType @@ -294,17 +253,11 @@ public partial class TrackService : ApplicationServiceBase if (orders.Count == 0) { - throw new BusinessException(message: "订单一个都不存在"); + throw ProviderServiceActionResult.Exception("提交失败", "订单一个都不存在"); } - // 校验主体:批量新增只选取第一条订单进行校验 - //【新增】存在指定轨迹的内容,进行更新覆盖(只处理一条的情况,并且决定权交给用户),不存在直接插入即可 - //【更新】存在指定轨迹的内容(匹配到多条则不处理) var order = orders.First(); - // 输入参数的静态规则检查 - // 轨迹内容上的约束检查 - // 更新轨迹时是否可以匹配到轨迹的检查 await TrackUpsertValidation(input.IsAdd, null, input.AppKey, @@ -313,15 +266,9 @@ public partial class TrackService : ApplicationServiceBase input.TrackCode, input.Source, input.TrackTime, - input.TrackTimeZone ?? 8.0f, //没传时区默认使用+8参与过滤 - input.NewTrackTime, - input.EstimatedDeliveryDate, - input.DestinationTimeZone, - input.Extra, - input.NewExtra, - input.CompareFields); + input.TrackTimeZone ?? 8.0f, + input.Extra); - // 按客户新增/更新轨迹(为了推送轨迹时方便) foreach (var group in orders.GroupBy(x => x.Customer)) { var @event = new TrackUpsertEvent @@ -338,8 +285,7 @@ public partial class TrackService : ApplicationServiceBase TrackCode = input.TrackCode, TrackPlace = input.TrackPlace, TrackTime = input.TrackTime, - NewTrackTime = input.NewTrackTime, - TrackTimeZone = input.TrackTimeZone ?? GlobalConst.NoneTrackTimeZone, //时区为空使用一个特殊值填充 + TrackTimeZone = input.TrackTimeZone ?? GlobalConst.NoneTrackTimeZone, OperationMan = CurrentUser.UserName, OperationManId = CurrentUserId, IsExternal = input.IsExternal, @@ -348,18 +294,7 @@ public partial class TrackService : ApplicationServiceBase Files = input.Files, Content = input.Content, EnStatus = input.EnStatus, - TrackPlaceCity = input.TrackPlaceCity, - TrackPlaceCityId = input.TrackPlaceCityId, - TrackPlaceProvince = input.TrackPlaceProvince, - TrackPlaceProvinceId = input.TrackPlaceProvinceId, - TrackPlaceNation = input.TrackPlaceNation, - TrackPlaceNationId = input.TrackPlaceNationId, - TrackPlacePostcode = input.TrackPlacePostcode, - EstimatedDeliveryDate = input.EstimatedDeliveryDate, - DestinationTimeZone = input.DestinationTimeZone, - CompareFields = input.CompareFields, Extra = input.Extra, - NewExtra = input.NewExtra, ForcedPush = input.ForcedPush, IsToMq = input.IsToMq, }; @@ -367,7 +302,6 @@ public partial class TrackService : ApplicationServiceBase } } - //在轨迹更新主流程之前的前置检查 private async Task TrackUpsertValidation( bool isAdd, string? esId, @@ -378,46 +312,27 @@ public partial class TrackService : ApplicationServiceBase int source, DateTime trackTime, float trackTimezone, - DateTime? newTrackTime, - DateTime? estimatedDeliveryDate, - float? destinationTimeZone, - string? extra, - string? newExtra, - string? compareFields + string? extra ) { #region 静态规则检查 - // 约束轨迹来源只能选取 1-外部 2-内部 3-手动 if (!GlobalConst.SupportTrackSources.Contains(source)) { - throw new BusinessException(message: "轨迹来源是必须的,1:外部服务商 2:内部系统 3:人工操作"); + throw ProviderServiceActionResult.Exception("校验失败", "轨迹来源是必须的,1:外部服务商 2:内部系统 3:人工操作"); } - //当修改时,如果为来源为外部提供商则不允许修改 if (!isAdd && source == (int) TrackSourceEnum.ExternalServiceProvide) { - throw new BusinessException(message: "来源为外部服务商轨迹不允许修改"); + throw ProviderServiceActionResult.Exception("校验失败", "来源为外部服务商轨迹不允许修改"); } - //要求时间在当前时间的前后100年之间内 bool ValidateTrackTime(DateTime? d) => - (d != null && d < DateTime.Now.AddYears(-100) || d > DateTime.Now.AddYears(100)); + d != null && (d < DateTime.Now.AddYears(-100) || d > DateTime.Now.AddYears(100)); - if (ValidateTrackTime(trackTime) || ValidateTrackTime(newTrackTime) || ValidateTrackTime(estimatedDeliveryDate)) + if (ValidateTrackTime(trackTime)) { - throw new BusinessException(message: "轨迹时间不合法"); - } - - if ((estimatedDeliveryDate.HasValue == false && destinationTimeZone.HasValue) || - (estimatedDeliveryDate.HasValue && destinationTimeZone.HasValue == false)) - { - throw new BusinessException(message: "【预计签收时间】和【签收地时区】必须同时输入"); - } - - if (!string.IsNullOrWhiteSpace(compareFields) && string.IsNullOrWhiteSpace(newExtra ?? extra)) - { - throw new BusinessException(message: "当输入了【轨迹对比字段】则轨迹扩展是必须的的"); + throw ProviderServiceActionResult.Exception("校验失败", "轨迹时间不合法"); } #endregion @@ -427,22 +342,19 @@ public partial class TrackService : ApplicationServiceBase var trackContent = await _trackContentRepository.GetByCodeAsync(appKey, trackCode); if (trackContent == null) { - throw new BusinessException(message: $"轨迹代码:{trackCode}轨迹内容不存在"); + throw ProviderServiceActionResult.Exception("校验失败", $"轨迹代码:{trackCode}轨迹内容不存在"); } - //指示是否为手动操作 var manualOperation = source == (int) TrackSourceEnum.Artificial; - //不允许手动创建 if (manualOperation && isAdd && !trackContent.IsAllowManuallyCreate) { - throw new BusinessException(message: $"轨迹代码:{trackCode}不允许手动创建"); + throw ProviderServiceActionResult.Exception("校验失败", $"轨迹代码:{trackCode}不允许手动创建"); } - //不允许手动更新 if (manualOperation && !isAdd && !trackContent.IsAllowManuallyUpdate) { - throw new BusinessException(message: $"轨迹代码:{trackCode}不允许手动更新"); + throw ProviderServiceActionResult.Exception("校验失败", $"轨迹代码:{trackCode}不允许手动更新"); } #endregion @@ -454,7 +366,7 @@ public partial class TrackService : ApplicationServiceBase var tracks = await _trackDetailsEsRepository.GetDataByIds(new[] { esId }); if (tracks.Count == 0) { - throw new BusinessException(message: $"轨迹esId:[{esId}]不存在,无法修改轨迹"); + throw ProviderServiceActionResult.Exception("校验失败", $"轨迹esId:[{esId}]不存在,无法修改轨迹"); } return; @@ -462,83 +374,61 @@ public partial class TrackService : ApplicationServiceBase #endregion - //轨迹新增,不允许重复的轨迹提前拦截 if (isAdd) { - //找到这个订单的这个轨迹code的所有轨迹 - var tracks = await RetryQueryTrackAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, newTrackTime, - extra, newExtra, compareFields, trackContent.IsRepeatable); + var tracks = await RetryQueryTrackAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, extra, + trackContent.IsRepeatable); if (tracks.Count > 0) - throw new BusinessException(message: $"订单:[{orderNo}],轨迹代码:{trackCode}不允许重复创建轨迹"); + throw ProviderServiceActionResult.Exception("校验失败", + $"订单:[{orderNo}],轨迹代码:{trackCode}不允许重复创建轨迹"); } - //针对于轨迹更新检查 else { - //找到这个订单的这个轨迹code的所有轨迹, - var tracks = await RetryQueryTrackAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, newTrackTime, - extra, newExtra, compareFields, trackContent.IsRepeatable); + var tracks = await RetryQueryTrackAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, extra, + trackContent.IsRepeatable); - // 不存在需要更新的轨迹 if (tracks.Count == 0) - throw new BusinessException(message: $"订单:[{orderNo}],轨迹代码:[{trackCode}]没有要更新的记录"); + throw ProviderServiceActionResult.Exception("校验失败", + $"订单:[{orderNo}],轨迹代码:[{trackCode}]没有要更新的记录"); - // 更新轨迹,同一时间存在多条轨迹记录,理由:不清晰到底要修改哪一条或是全部修改 if (tracks.Count > 1) { - if (newTrackTime.HasValue && tracks.Any(track => - DateTimeHelper.ConvertUtc0Compare(track.TrackTime, track.TrackTimeZone, newTrackTime.Value, - trackTimezone) == 0)) - { - throw new BusinessException( - message: $"订单:[{orderNo}],轨迹代码:[{trackCode}]和现存轨迹时间重复,不允许修改,请检查轨迹NewTrackTime"); - } - - if (!string.IsNullOrWhiteSpace(newExtra) && tracks.Any(track => string.Equals(track.Extra, newExtra))) - { - throw new BusinessException( - message: $"订单:[{orderNo}],轨迹代码:[{trackCode}]和现存轨迹Extra重复,不允许修改,请检查轨迹NewExtra"); - } - - throw new BusinessException(message: $"订单:{orderNo},轨迹代码:[{trackCode}]存在多条记录,不允许修改"); + throw ProviderServiceActionResult.Exception("校验失败", + $"订单:{orderNo},轨迹代码:[{trackCode}]存在多条记录,不允许修改"); } var track = tracks.First(); if (DateTimeHelper.ConvertUtc0Compare(track.TrackTime, track.TrackTimeZone, trackTime, trackTimezone) != 0) { - throw new BusinessException( - message: $"订单:[{orderNo}],轨迹代码:[{trackCode}]找不到要更新的记录,不允许修改,请检查轨迹TrackTime"); + throw ProviderServiceActionResult.Exception("校验失败", + $"订单:[{orderNo}],轨迹代码:[{trackCode}]找不到要更新的记录,请检查轨迹TrackTime"); } if (!string.Equals(track.Extra, extra)) { - throw new BusinessException(message: $"订单:[{orderNo}],轨迹代码:[{trackCode}]找不到要更新的记录,不允许修改,请检查轨迹Extra"); + throw ProviderServiceActionResult.Exception("校验失败", + $"订单:[{orderNo}],轨迹代码:[{trackCode}]找不到要更新的记录,请检查轨迹Extra"); } } } - // 重试查询轨迹,直到存在符合要求的轨迹,或者超过最大重试次数 private async Task> RetryQueryTrackAsync(bool isAdd, int orderId, string trackCode, - DateTime trackTime, float trackTimezone, DateTime? newTrackTime, string? extra, string? newExtra, - string? compareFields, bool isRepeatable) + DateTime trackTime, float trackTimezone, string? extra, bool isRepeatable) { - var tracks = await QueryAndFilterAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, newTrackTime, extra, - newExtra, compareFields, isRepeatable); + var tracks = await QueryAndFilterAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, extra, isRepeatable); if (tracks.Count > 0) return tracks; - //重试阶段 var curRetryCount = 0; - while (curRetryCount < GlobalConst.MaxUpdateTrackRetryCount) //tips:后续将最大重试次数提出到配置中 + while (curRetryCount < GlobalConst.MaxUpdateTrackRetryCount) { try { - tracks = await QueryAndFilterAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, newTrackTime, - extra, newExtra, compareFields, isRepeatable); + tracks = await QueryAndFilterAsync(isAdd, orderId, trackCode, trackTime, trackTimezone, extra, + isRepeatable); if (tracks.Count > 0) return tracks; - //指数退避等待 - var delay = TimeSpan.FromMilliseconds(Math.Pow(2, curRetryCount) * 150); // 150ms, 300ms, 600ms, ... - // 可以执行下一次才延时,否则延时没意义 + var delay = TimeSpan.FromMilliseconds(Math.Pow(2, curRetryCount) * 150); if (curRetryCount + 1 < GlobalConst.MaxUpdateTrackRetryCount) await Task.Delay(delay); } @@ -548,42 +438,19 @@ public partial class TrackService : ApplicationServiceBase } } - //超过最大重试次数还没找到 return tracks; } - //查询并按比较字段过滤轨迹,当比较字段为空时则使用轨迹发生时间过滤 - //tips:后续进行投影缩参优化 private async Task> QueryAndFilterAsync(bool isAdd, int orderId, string trackCode, - DateTime trackTime, float trackTimezone, DateTime? newTrackTime, string? extra, string? newExtra, - string? compareFields, bool isRepeatable) + DateTime trackTime, float trackTimezone, string? extra, bool isRepeatable) { var tracks = await _trackDetailsEsRepository.GetListAsync(new[] { orderId }, trackCode, true); - // 如果不允许重复,则直接返回 if (!isRepeatable) return tracks; + if (tracks.Count == 0) return tracks; - // 没有任何轨迹,也直接返回 - if (tracks.Count == 0) - return tracks; + tracks = tracks.Where(x => + x.GetUtc0TrackTime() == trackTime.Subtract(TimeSpan.FromHours(trackTimezone))).ToList(); - tracks = isAdd - ? tracks.Where(x => - x.GetUtc0TrackTime() == (newTrackTime ?? trackTime).Subtract(TimeSpan.FromHours(trackTimezone))) - .ToList() - : tracks.Where(x => - x.GetUtc0TrackTime() == trackTime.Subtract(TimeSpan.FromHours(trackTimezone)) - // 修改了时间,要校验修改后的时间是否存在 - || (newTrackTime != null && x.GetUtc0TrackTime() == - newTrackTime.Value.Subtract(TimeSpan.FromHours(trackTimezone))) - ).ToList(); - - return isAdd - ? tracks.Where(x => x.Extra == (newExtra ?? extra)).ToList() - : tracks.Where(x => - x.Extra == extra - // 修改了extra,要校验修改后的extra是否存在 - || (!string.IsNullOrWhiteSpace(newExtra) && x.Extra == newExtra) - ) - .ToList(); + return tracks.Where(x => x.Extra == extra).ToList(); } } diff --git a/src/TrackCenter.Application/Services/TrackOrders/TrackOrderService.cs b/src/TrackCenter.Application/Services/TrackOrders/TrackOrderService.cs index ec08f3bf..2dff335a 100644 --- a/src/TrackCenter.Application/Services/TrackOrders/TrackOrderService.cs +++ b/src/TrackCenter.Application/Services/TrackOrders/TrackOrderService.cs @@ -249,25 +249,6 @@ public class TrackOrderService : ApplicationServiceBase // 按轨迹发生时间正序排序 res = res.OrderBy(m => m.TrackTime).ToList(); - if (!order.IsCompleted) //如果没完结则显示签收节点 - { - var eddUnixTimestamp = order.EstimatedDeliveryDate.HasValue ? ToUnixTimestamp(DateTime.Parse(order.EstimatedDeliveryDate.ToString()!)) : 0; - var isShowTime = eddUnixTimestamp > 0 && res.All(m => m.TrackTime < eddUnixTimestamp); - - //当存在需要显示的轨迹才需要补充签收节点 - if (res.Count != 0) - { - res.Add(new GetDetailTimelineOutput() - { - Content = "签收", - IsHighlight = false, - TrackTimeZone = isShowTime ? $"GMT{(order.DestinationTimeZone >= 0 ? "+" : "-")}{order.DestinationTimeZone}" : "", - TrackTime = isShowTime ? eddUnixTimestamp : 0, - TrackTimePrefix = isShowTime ? "预计签收时间:" : "", - }); - } - } - return res; } diff --git a/src/TrackCenter.Application/Services/Warning/Handlers/TransportWarningHandler.cs b/src/TrackCenter.Application/Services/Warning/Handlers/TransportWarningHandler.cs index 90bef12c..2fec38d4 100644 --- a/src/TrackCenter.Application/Services/Warning/Handlers/TransportWarningHandler.cs +++ b/src/TrackCenter.Application/Services/Warning/Handlers/TransportWarningHandler.cs @@ -1,11 +1,9 @@ using System.Linq.Expressions; -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.TrackDetailsAggregate; using TrackCenter.Domain.Aggregates.TrackStatusAggregate; using TrackCenter.Domain.Aggregates.WarningAggregate; using TrackCenter.Domain.Aggregates.WarningAggregate.Transport; -using TrackCenter.Domain.Dtos.Forewarning; using TrackCenter.Domain.Dtos.TrackDetails; using TrackCenter.Domain.Dtos.Warning; using SqlSugar; @@ -16,7 +14,6 @@ namespace TrackCenter.Application.Services.Warning.Handlers; public class TransportWarningHandler : ITransportWarningHandler, ITransientDependency { - private readonly IKpiAreaRepository _kpiAreaRepository; private readonly IOrderRepository _orderRepository; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; private readonly IWarningRuleRepository _warningRuleRepository; @@ -24,14 +21,19 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; private readonly ITrackStatusRepository _trackStatusRepository; - public TransportWarningHandler(IWarningTaskRepository warningTaskRepository, IWarningDetailsEsRepository warningDetailsEsRepository, IWarningRuleRepository warningRuleRepository, IOrderRepository orderRepository, ITrackDetailsEsRepository trackDetailsEsRepository, IKpiAreaRepository kpiAreaRepository, ITrackStatusRepository trackStatusRepository) + public TransportWarningHandler( + IWarningTaskRepository warningTaskRepository, + IWarningDetailsEsRepository warningDetailsEsRepository, + IWarningRuleRepository warningRuleRepository, + IOrderRepository orderRepository, + ITrackDetailsEsRepository trackDetailsEsRepository, + ITrackStatusRepository trackStatusRepository) { _warningTaskRepository = warningTaskRepository; _warningDetailsEsRepository = warningDetailsEsRepository; _warningRuleRepository = warningRuleRepository; _orderRepository = orderRepository; _trackDetailsEsRepository = trackDetailsEsRepository; - _kpiAreaRepository = kpiAreaRepository; _trackStatusRepository = trackStatusRepository; } @@ -223,9 +225,9 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen orders = orders.Where(x => earliestTrackStatusTrackDict.ContainsKey(x.Id)).ToList(); if (orders.Count == 0) return; - // 3.查规则主体 - var conditions = orders.Select(x => (AppKey: x.AppKey, x.Customer)).ToHashSet(); - var ruleDict = await _warningRuleRepository.GetWarningRuleForEnabledAsync(conditions, WarningRuleType.运输时效预警); + // 3.查规则主体(运输时效:AppKey 方案级,不按客户) + var appKeys = orders.Select(x => x.AppKey).ToHashSet(); + var ruleDict = await _warningRuleRepository.GetWarningRuleForEnabledByAppKeysAsync(appKeys, WarningRuleType.运输时效预警); if (ruleDict.Count == 0) return; // 4.查规则场景 @@ -233,11 +235,7 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen var scenes = await _warningRuleRepository.GetTransportSceneAsync(x => ruleIds.Contains(x.WarningId)); if (scenes.Count == 0) return; - // 5.查区域信息 - var areaIds = scenes.Where(x => x.AreaId > 0).Select(x => x.AreaId!.Value).ToHashSet(); - var ruleAreaInfosDict = await _kpiAreaRepository.GetAreaInfosDictByAreaId(areaIds); - - // 选出订单命中的场景 + // 选出订单命中的场景(仅产品 + 国家) var hitSceneIds = new HashSet(); var orderIdHitSceneIdsDict = new Dictionary>(); var sceneBos = TransportWarningSceneBO.From(scenes); @@ -246,7 +244,10 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen var sceneSet = new HashSet(); foreach (var scene in sceneBos) { - if (!scene.CanHitScene(order, ruleAreaInfosDict.GetValueOrDefault(scene.AreaId ?? 0))) continue; + // 仅匹配同 AppKey 下方案的场景 + if (!ruleDict.TryGetValue(scene.WarningId, out var rule) || rule.AppKey != order.AppKey) + continue; + if (!scene.CanHitScene(order)) continue; sceneSet.Add(scene.Id); hitSceneIds.Add(scene.Id); } @@ -576,14 +577,11 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen return; // 3.重新验证订单是否命中新场景 - var areas = postScene.AreaId.HasValue - ? _kpiAreaRepository.GetAreaInfosByAreaId(postScene.AreaId.Value) - : new List(); var noHitSceneOrderIds = new HashSet(); var reHitSceneOrderIds = new HashSet(); foreach (var order in orders) { - if (postScene.CanHitScene(order, areas)) + if (postScene.CanHitScene(order)) reHitSceneOrderIds.Add(order.Id); else noHitSceneOrderIds.Add(order.Id); @@ -599,15 +597,11 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen var updateRuleItems = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(x => updateRuleItemIds.Contains(x.Id)); var ruleItemDict = updateRuleItems.ToDictionary(x => x.Id); - // 场景->区域 - var ruleArea = postScene.AreaId.HasValue - ? await _kpiAreaRepository.GetFirstAsync(x => x.Id == postScene.AreaId.Value) - : null; foreach (var detail in updateDetails) { var ruleItem = ruleItemDict[detail.WarningSceneRuleItemId]; detail.WarningName = rule.Name; - detail.WarningRuleDescription = detail.GetWarningRuleDescription(postScene.ToEntity(), ruleItem, ruleArea); + detail.WarningRuleDescription = detail.GetWarningRuleDescription(postScene.ToEntity(), ruleItem); needUpdateDetails.Add(detail); } } @@ -679,12 +673,8 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen private static bool CanSceneChange(TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene) { - return oldScene.CargoType != postScene.CargoType || //多个 普货,危险品 - oldScene.Months != postScene.Months || //多个 约束提交的月份组从小到大 1/3/4,不能出现 4/1/3这种 (如果出现则SequenceEqual) - oldScene.AreaId != postScene.AreaId || // 单一,area1 - oldScene.IsTransit != postScene.IsTransit || //单一,true/false - oldScene.LeavePort != postScene.LeavePort || //多个 - oldScene.ProductCode != postScene.ProductCode; //多个 + return !string.Equals(oldScene.ProductCode, postScene.ProductCode, StringComparison.OrdinalIgnoreCase) + || !string.Equals(oldScene.CountryCode, postScene.CountryCode, StringComparison.OrdinalIgnoreCase); } #endregion todo: 规则场景变更 @@ -847,9 +837,6 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen .ToDictionary(x => x.Key, y => y.ToList()); // 3.检查是否重新命中规则 - var ruleArea = scene.AreaId.HasValue - ? await _kpiAreaRepository.GetFirstAsync(x => x.Id == scene.AreaId.Value) - : null; var updateFollowRuleItemIdDict = updateFollowRuleItems.ToDictionary(x => x.Id); foreach (var group in handleBlocks.GroupBy(x => (x.OrderId, x.RuleItemId))) { @@ -877,7 +864,7 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen { // 配置相关信息 block.Detail.WarningName = rule.Name; - block.Detail.WarningRuleDescription = block.Detail.GetWarningRuleDescription(scene.ToEntity(), ruleItem.ToEntity(), ruleArea); + block.Detail.WarningRuleDescription = block.Detail.GetWarningRuleDescription(scene.ToEntity(), ruleItem.ToEntity()); block.Detail.TransportTimeSpan = ruleItem.TransportTimeSpan; block.Detail.StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan; // 起始轨迹相关信息 @@ -1185,15 +1172,6 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen ); var sceneDict = scenes.ToDictionary(x => x.Id); - // 获取区域字典 - Dictionary areaDict = null; - var areaIds = scenes.Where(x => x.AreaId.HasValue).Select(x => x.AreaId.Value!).ToHashSet(); - if (areaIds.Count > 0) - { - var areas = await _kpiAreaRepository.GetListAsync(x => areaIds.Contains(x.Id)); - areaDict = areas.ToDictionary(x => x.Id); - } - // 6.生成预警明细,手动赋予EsId,规则版本号,截止轨迹状态名称 addDetails.ForEach(x => { @@ -1205,7 +1183,7 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen var scene = sceneDict[x.WarningSceneId]; //一定存在 x.Version = rule.Version; x.WarningName = rule.Name; - x.WarningRuleDescription = x.GetWarningRuleDescription(scene, ruleItemDict[x.WarningSceneRuleItemId], areaDict?.GetValueOrDefault(scene.AreaId ?? 0)); + x.WarningRuleDescription = x.GetWarningRuleDescription(scene, ruleItemDict[x.WarningSceneRuleItemId]); }); await _warningDetailsEsRepository.BulkInsertTransportWarningDetailsAsync(addDetails); diff --git a/src/TrackCenter.Application/Services/Warning/Model/TransportWarningSceneBO.cs b/src/TrackCenter.Application/Services/Warning/Model/TransportWarningSceneBO.cs index 0b89666d..4e2e0586 100644 --- a/src/TrackCenter.Application/Services/Warning/Model/TransportWarningSceneBO.cs +++ b/src/TrackCenter.Application/Services/Warning/Model/TransportWarningSceneBO.cs @@ -1,128 +1,52 @@ using TrackCenter.Application.Dtos.Warning.Transport; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.WarningAggregate.Transport; -using TrackCenter.Domain.Dtos.Forewarning; namespace TrackCenter.Application.Services.Warning.Model; public sealed class TransportWarningSceneBO { - /// - /// 场景唯一id (传0视为新增) - /// public int Id { get; } - - /// - /// 预警规则主体id - /// public int WarningId { get; } - /// - /// 货物类型(多个,分割) - /// - public string? CargoType { get; } - - /// - /// 月份(多个/分割) - /// - public string? Months { get; } - - /// - /// 区域Id(单一) - /// - public int? AreaId { get; } - - /// - /// 是否中转(单一) - /// - public bool? IsTransit { get; } - - /// - /// 起运港口(多个,分割) - /// - public string? LeavePort { get; } - - /// - /// 产品编码(多个,分割) - /// + /// 销售产品编码(单一) public string? ProductCode { get; } - public TransportWarningSceneBO(int id, int warningId, string? cargoType, string? months, int? areaId, bool? isTransit, string? leavePort, string? productCode) + /// 到达国家二字码(单一) + public string? CountryCode { get; } + + /// 考核总时效(小时) + public float? AssessmentTotalTimeSpan { get; } + + public TransportWarningSceneBO( + int id, + int warningId, + string? productCode, + string? countryCode = null, + float? assessmentTotalTimeSpan = null) { Id = id; WarningId = warningId; - CargoType = cargoType; - - // 如果有月份则排序之后在写入 - if (!string.IsNullOrWhiteSpace(months)) - { - var sorted = months.Split('/').Select(int.Parse).OrderBy(x => x).ToList(); - Months = string.Join('/', sorted); - } - - AreaId = areaId; - IsTransit = isTransit; - LeavePort = leavePort; - ProductCode = productCode; + ProductCode = string.IsNullOrWhiteSpace(productCode) ? null : productCode.Trim(); + CountryCode = NormalizeSingleCountryCode(countryCode); + AssessmentTotalTimeSpan = assessmentTotalTimeSpan; } - public bool CanHitScene(OrderEntity order, List? ruleAreas) + /// + /// 命中:订单产品 = 配置产品;到达国家 = 配置国家(一对一)。 + /// + public bool CanHitScene(OrderEntity order) { - // 是否中转 - if (IsTransit != null && IsTransit != order.IsTransit) + if (string.IsNullOrWhiteSpace(ProductCode) || string.IsNullOrWhiteSpace(order.ProductCode)) return false; - // 货物类型 - if (!string.IsNullOrWhiteSpace(CargoType) && order.CargoType != CargoType) + if (!string.Equals(order.ProductCode.Trim(), ProductCode, StringComparison.OrdinalIgnoreCase)) return false; - // 启运港口 - if (!string.IsNullOrWhiteSpace(LeavePort) && order.LeavePort != LeavePort) + if (string.IsNullOrWhiteSpace(CountryCode) || string.IsNullOrWhiteSpace(order.ArrivalCountry)) return false; - // 产品编码 - if (!string.IsNullOrWhiteSpace(ProductCode) && order.ProductCode != ProductCode) - return false; - - // 月份 - if (!string.IsNullOrWhiteSpace(Months)) - { - var months = string.IsNullOrEmpty(Months) - ? new HashSet() - : Months.Split('/').Select(int.Parse).ToHashSet(); - // 订单创建时间月份 - var orderCreateTimeMonth = order.CreateTime.Month; - if (!months.Contains(orderCreateTimeMonth)) - return false; - } - - // 区域 - if (ruleAreas != null && ruleAreas.Count > 0) - { - // 使用订单到达国家的二字码匹配区域 - var areas = ruleAreas.Where(area => order.ArrivalCountry == area.CountryCode).ToList(); - foreach (var area in areas) - { - // 邮编是否在关注的范围内? - if (!string.IsNullOrWhiteSpace(area.LeftPostcode) && !string.IsNullOrWhiteSpace(area.RightPostcode)) - { - // tips: 参考小包的邮编匹配,字典序 - if (string.CompareOrdinal(order.ArrivalPostCode, area.LeftPostcode) < 0 || - string.CompareOrdinal(order.ArrivalPostCode, area.RightPostcode) > 0) - return false; - } - - // 城市是否匹配? - if (!string.IsNullOrWhiteSpace(area.City) && order.ArrivalCity != area.City) - return false; - - // 省份是否匹配? - if (!string.IsNullOrWhiteSpace(area.Province) && order.ArrivalProvince != area.Province) - return false; - } - } - - return true; + return string.Equals(order.ArrivalCountry.Trim(), CountryCode, StringComparison.OrdinalIgnoreCase); } public static TransportWarningSceneEntity ToEntity(TransportWarningSceneBO bo) @@ -130,80 +54,62 @@ public sealed class TransportWarningSceneBO return new TransportWarningSceneEntity( bo.Id, bo.WarningId, - bo.CargoType, - bo.Months, - bo.AreaId, - bo.IsTransit, - bo.LeavePort, - bo.ProductCode + bo.ProductCode, + bo.CountryCode, + bo.AssessmentTotalTimeSpan ); } - public TransportWarningSceneEntity ToEntity() - { - return new TransportWarningSceneEntity( - Id, - WarningId, - CargoType, - Months, - AreaId, - IsTransit, - LeavePort, - ProductCode - ); - } + public TransportWarningSceneEntity ToEntity() => ToEntity(this); public static TransportWarningSceneBO From(TransportWarningSceneEntity entity) { return new TransportWarningSceneBO( entity.Id, entity.WarningId, - entity.CargoType, - entity.Months, - entity.AreaId, - entity.IsTransit, - entity.LeavePort, - entity.ProductCode + entity.ProductCode, + entity.CountryCode, + entity.AssessmentTotalTimeSpan ); } public static List From(List entities) - { - return entities.Select(From).ToList(); - } + => entities.Select(From).ToList(); public static TransportWarningSceneBO From(UpsertTransportWarningSceneInput input) { return new TransportWarningSceneBO( input.Id, input.WarningId, - input.CargoType, - input.Months, - input.AreaId, - input.IsTransit, - input.LeavePort, - input.ProductCode + input.ProductCode, + input.CountryCode, + input.AssessmentTotalTimeSpan ); } private bool Equals(TransportWarningSceneBO other) { return WarningId == other.WarningId - && IsTransit == other.IsTransit - && CargoType == other.CargoType - && LeavePort == other.LeavePort - && ProductCode == other.ProductCode - && Months == other.Months - && AreaId == other.AreaId; + && string.Equals(ProductCode, other.ProductCode, StringComparison.OrdinalIgnoreCase) + && string.Equals(CountryCode, other.CountryCode, StringComparison.OrdinalIgnoreCase); } public override bool Equals(object? obj) - { - return ReferenceEquals(this, obj) || obj is TransportWarningSceneBO other && Equals(other); - } + => ReferenceEquals(this, obj) || obj is TransportWarningSceneBO other && Equals(other); public override int GetHashCode() + => HashCode.Combine(WarningId, ProductCode?.ToUpperInvariant(), CountryCode?.ToUpperInvariant()); + + private static string? NormalizeSingleCountryCode(string? countryCode) { - return HashCode.Combine(WarningId, IsTransit, CargoType, LeavePort, ProductCode, Months, AreaId); + if (string.IsNullOrWhiteSpace(countryCode)) + return null; + + var code = countryCode + .Split([',', '/', ';', '|', ' '], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(x => x.ToUpperInvariant()) + .FirstOrDefault(x => x.Length > 0); + + return string.IsNullOrWhiteSpace(code) ? null : code; } } diff --git a/src/TrackCenter.Application/Services/Warning/TransportWarningService.cs b/src/TrackCenter.Application/Services/Warning/TransportWarningService.cs index 9e50875f..db556d91 100644 --- a/src/TrackCenter.Application/Services/Warning/TransportWarningService.cs +++ b/src/TrackCenter.Application/Services/Warning/TransportWarningService.cs @@ -6,7 +6,6 @@ using TrackCenter.Application.Caching; using TrackCenter.Application.Helpers; using TrackCenter.Application.Services.Warning.Handlers; using TrackCenter.Application.Services.Warning.Model; -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.TrackDetailsAggregate; using TrackCenter.Domain.Aggregates.WarningAggregate; @@ -23,7 +22,6 @@ namespace TrackCenter.Application.Services.Warning; public class TransportWarningService : ApplicationServiceBase { private readonly IOrderRepository _orderRepository; - private readonly IKpiAreaRepository _kpiAreaRepository; private readonly IWarningRuleRepository _warningRuleRepository; private readonly IWarningDetailsEsRepository _warningDetailsEsRepository; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; @@ -33,7 +31,6 @@ public class TransportWarningService : ApplicationServiceBase public TransportWarningService( IOrderRepository orderRepository, - IKpiAreaRepository kpiAreaRepository, IWarningRuleRepository warningRuleRepository, IWarningDetailsEsRepository warningDetailsEsRepository, ITrackDetailsEsRepository trackDetailsEsRepository, @@ -43,7 +40,6 @@ public class TransportWarningService : ApplicationServiceBase ) { _orderRepository = orderRepository; - _kpiAreaRepository = kpiAreaRepository; _warningRuleRepository = warningRuleRepository; _warningDetailsEsRepository = warningDetailsEsRepository; _trackDetailsEsRepository = trackDetailsEsRepository; @@ -63,70 +59,29 @@ public class TransportWarningService : ApplicationServiceBase var entities = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId); if (entities.Count == 0) return Empty.List; - var areaIds = entities - .Select(x => x.AreaId) - .Where(x => x > 0) - .Select(x => x!.Value) - .ToHashSet(); - - Dictionary? 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 + CountryCode = x.CountryCode, + AssessmentTotalTimeSpan = x.AssessmentTotalTimeSpan }); return map.ToList(); - // 构建场景摘要 + // 构建场景摘要:产品 / 国家 / 总时效Xh string BuildSummary(TransportWarningSceneEntity entity) { - const string separator = "/"; - // var sb = new StringBuilder(); - var list = new List(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}"); - // 产品编码 + var list = new List(3); if (!string.IsNullOrWhiteSpace(entity.ProductCode)) - list.Add($"{entity.ProductCode}"); - - return string.Join(separator, list); + list.Add(entity.ProductCode); + if (!string.IsNullOrWhiteSpace(entity.CountryCode)) + list.Add(entity.CountryCode); + if (entity.AssessmentTotalTimeSpan is > 0) + list.Add($"{entity.AssessmentTotalTimeSpan.Value / 24f:0.##}天"); + return list.Count == 0 ? "-" : string.Join(" / ", list); } } @@ -282,14 +237,31 @@ public class TransportWarningService : ApplicationServiceBase var postBo = TransportWarningSceneBO.From(input); + if (string.IsNullOrWhiteSpace(postBo.ProductCode)) + throw new BusinessException(message: "请配置销售产品"); + if (postBo.ProductCode.Contains(',') || postBo.ProductCode.Contains('/')) + throw new BusinessException(message: "运输时效仅支持单一销售产品"); + if (string.IsNullOrWhiteSpace(postBo.CountryCode)) + throw new BusinessException(message: "请配置国家"); + if (input.CountryCode != null && + input.CountryCode.IndexOfAny([',', '/', ';', '|', ' ']) >= 0) + throw new BusinessException(message: "运输时效仅支持单一国家"); + if (postBo.AssessmentTotalTimeSpan is null or <= 0) + throw new BusinessException(message: "请配置有效的考核总时效"); + // 前端按整数天录入后转小时(天×24) + var totalHours = postBo.AssessmentTotalTimeSpan.Value; + if (Math.Abs(totalHours - MathF.Round(totalHours)) > 0.001f || + Math.Abs(totalHours % 24f) > 0.001f) + throw new BusinessException(message: "考核总时效须为整天对应的小时数(天×24)"); + // 查出这个规则主体下的所有场景 var oldScenes = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId); var oldBos = oldScenes.Count > 0 ? TransportWarningSceneBO.From(oldScenes) : Empty.List; - // 验证规则场景是否重复 + // 验证规则场景是否重复(同一方案下:产品+国家唯一) if (oldBos.Any(old => old.Equals(postBo) && old.Id != postBo.Id)) { - throw new BusinessException(message: "同一规则主体下,不允许出现因素完全相同的场景!"); + throw new BusinessException(message: "同一方案下,不允许出现产品与国家完全相同的配置!"); } // 更新或者新增 diff --git a/src/TrackCenter.Application/Services/Warning/WarningService.cs b/src/TrackCenter.Application/Services/Warning/WarningService.cs index 6eceb864..bfac5b12 100644 --- a/src/TrackCenter.Application/Services/Warning/WarningService.cs +++ b/src/TrackCenter.Application/Services/Warning/WarningService.cs @@ -84,10 +84,13 @@ public class WarningService : ApplicationServiceBase throw new BusinessException(message: $"预警规则主体启用状态未发生变化,不允许重复操作"); } - // 如果设置为启用则检查同一个应用同一客户下有多个启用的规则 + // 启用查重:运输时效按 AppKey+类型;其它类型按 AppKey+客户+类型 if (input.IsEnabled && await _warningRuleRepository.CheckEnableDuplicateAsync(rule.Id, rule.AppKey, rule.Customer, (WarningRuleType) rule.WarningType)) { - throw new BusinessException(message: $"同一【应用+客户+类型】下不能有多个启用的预警规则主体,AppKey=[{rule.AppKey},Customer=[{rule.Customer}]]"); + var scope = (WarningRuleType) rule.WarningType == WarningRuleType.运输时效预警 + ? $"同一【应用+类型】下不能有多个启用的预警规则主体,AppKey=[{rule.AppKey}]" + : $"同一【应用+客户+类型】下不能有多个启用的预警规则主体,AppKey=[{rule.AppKey},Customer=[{rule.Customer}]]"; + throw new BusinessException(message: scope); } // 更新规则主体状态 @@ -120,17 +123,28 @@ public class WarningService : ApplicationServiceBase throw new BusinessException(message: $"WarningId=[{input.Id}],WarningType=[{input.WarningType}]预警规则主体不存在,不允许修改"); } - // 要求 AppKey+Customer+Name+WarningType 联合不能重复 - if (await _warningRuleRepository.CheckNameDuplicateAsync(input.Id, input.AppKey, input.Customer, input.Name, (WarningRuleType) input.WarningType)) + // 运输时效方案级:客户固定为空,不参与匹配 + var customer = (WarningRuleType) input.WarningType == WarningRuleType.运输时效预警 + ? string.Empty + : (input.Customer ?? string.Empty); + + if ((WarningRuleType) input.WarningType != WarningRuleType.运输时效预警 && string.IsNullOrWhiteSpace(customer)) + throw new BusinessException(message: "客户不能为空"); + + // 名称查重:运输时效按 AppKey+Name+Type;其它按 AppKey+Customer+Name+Type + if (await _warningRuleRepository.CheckNameDuplicateAsync(input.Id, input.AppKey, customer, input.Name, (WarningRuleType) input.WarningType)) { - throw new BusinessException(message: $"AppKey:[{input.AppKey}],Customer:[{input.Customer}]," + + throw new BusinessException(message: $"AppKey:[{input.AppKey}],Customer:[{customer}]," + $"WarningType:[{input.WarningType.ToString()}]已存在名称为[{input.Name}]规则,不允许重复创建/修改"); } // 如果创建/修改的主体为启用状态,则要求通过检查 - if (input.IsEnabled && await _warningRuleRepository.CheckEnableDuplicateAsync(input.Id, input.AppKey, input.Customer, (WarningRuleType) input.WarningType)) + if (input.IsEnabled && await _warningRuleRepository.CheckEnableDuplicateAsync(input.Id, input.AppKey, customer, (WarningRuleType) input.WarningType)) { - throw new BusinessException(message: $"同一【应用+客户+预警类型】下不能有多个启用的预警规则主体,不允许创建/修改"); + var scope = (WarningRuleType) input.WarningType == WarningRuleType.运输时效预警 + ? "同一【应用+预警类型】下不能有多个启用的预警规则主体,不允许创建/修改" + : "同一【应用+客户+预警类型】下不能有多个启用的预警规则主体,不允许创建/修改"; + throw new BusinessException(message: scope); } var enabledChanged = false; @@ -140,7 +154,7 @@ public class WarningService : ApplicationServiceBase input.Id, input.Name, input.AppKey, - input.Customer, + customer, input.IsEnabled, input.WarningType ); @@ -150,7 +164,7 @@ public class WarningService : ApplicationServiceBase enabledChanged = ruleEntity.IsEnabled != input.IsEnabled; ruleEntity.Name = input.Name; ruleEntity.AppKey = input.AppKey; - ruleEntity.Customer = input.Customer; + ruleEntity.Customer = customer; ruleEntity.IsEnabled = input.IsEnabled; ruleEntity.WarningType = input.WarningType; } diff --git a/src/TrackCenter.Application/Track2InterfaceRelay/InterfaceForwardService.cs b/src/TrackCenter.Application/Track2InterfaceRelay/InterfaceForwardService.cs index 5018fab7..fee6ab3a 100644 --- a/src/TrackCenter.Application/Track2InterfaceRelay/InterfaceForwardService.cs +++ b/src/TrackCenter.Application/Track2InterfaceRelay/InterfaceForwardService.cs @@ -55,9 +55,16 @@ public class InterfaceForwardService : ITransientDependency var res = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { - throw new BusinessException(message: $"返回的状态码有问题: {response.StatusCode}, {response.ReasonPhrase}"); + throw new BusinessException( + message: $"调用接口通失败:HTTP {(int)response.StatusCode} {response.ReasonPhrase};响应:{Truncate(res, 300)}"); } return res.ToObject()!; } + + private static string Truncate(string? text, int maxLen) + { + if (string.IsNullOrEmpty(text)) return string.Empty; + return text.Length <= maxLen ? text : text[..maxLen] + "..."; + } } diff --git a/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/IKpiAreaRepository.cs b/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/IKpiAreaRepository.cs deleted file mode 100644 index 21f29a0d..00000000 --- a/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/IKpiAreaRepository.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Fake.Application.Dtos; -using TrackCenter.Domain.Dtos.Forewarning; - -namespace TrackCenter.Domain.Aggregates.KPIAreaAggregate; - -public interface IKpiAreaRepository : IBasicRepository -{ - /// - /// 创建分区 - /// - /// - /// - ValueTask CreateAreaAsync(RuleAreaEntity entity); - - /// - /// 添加分区信息 - /// - /// - /// - ValueTask AddAreaInfoAsync(RuleAreaInfoEntity entity); - - /// - /// 删除分区信息 - /// - /// 分区信息id - /// - Task DeleteAreaInfo(int areaInfoId); - - /// - /// 删除分区 - /// - /// 分区id - /// - ValueTask DeleteArea(int areaId); - - /// - /// 更新区域信息 - /// - /// - /// - Task UpdateAreaInfoAsync(RuleAreaInfoEntity entity); - - /// - /// 获得分区 - /// - /// - /// - Task> GetAreas(QueryAreaRequest query); - - /// - /// 获取分区信息 - /// - /// - /// - Task> GetAreaInfos(QueryAreaInfoRequest query); - - /// - /// 获取分区信息通过id - /// - /// - /// - Task GetAreaInfoById(int id); - - /// - /// 获取所有区域信息 - /// - /// - List? GetAreaInfosByAreaId(int areaId); - - /// - /// 获取所有区域信息 - /// - /// - Task>> GetAreaInfosDictByAreaId(HashSet areaIds); -} diff --git a/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/RuleAreaEntity.cs b/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/RuleAreaEntity.cs deleted file mode 100644 index e1d520dd..00000000 --- a/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/RuleAreaEntity.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Fake.Domain.Entities; -using Fake.Domain.Entities.Auditing; - -namespace TrackCenter.Domain.Aggregates.KPIAreaAggregate; - -[SugarTable("t_area", TableDescription = "KPI分区主表")] -public class RuleAreaEntity : AggregateRoot, IHasCreateUserId, IHasCreateTime, IHasUpdateUserId, IHasUpdateTime -{ - /// - /// 自增主键 - /// - [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")] - public int Id { get; set; } - - /// - /// 应用 - /// - [SugarColumn(ColumnName = "AppKey", ColumnDescription = "应用")] - public string AppKey { get; set; } - - /// - /// 分区名称KPI名称 - /// - [SugarColumn(ColumnName = "Name", Length = 64, ColumnDescription = "分区名称KPI名称")] - public string Name { get; set; } - - /// - /// 创建人 - /// - [SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")] - public int CreateUserId { get; set; } - - /// - /// 创建时间 - /// - [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")] - public DateTime CreateTime { get; set; } - - /// - /// 更新人 - /// - [SugarColumn(ColumnName = "UpdateUserId", ColumnDescription = "更新人")] - public int UpdateUserId { get; set; } - - /// - /// 更新时间 - /// - [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间")] - public DateTime UpdateTime { get; set; } - - [SugarColumn(IsIgnore = true)] public List AreaInfos { get; set; } -} diff --git a/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/RuleAreaInfoEntity.cs b/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/RuleAreaInfoEntity.cs deleted file mode 100644 index 24798f45..00000000 --- a/src/TrackCenter.Domain/Aggregates/KPIAreaAggregate/RuleAreaInfoEntity.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Fake.Domain.Entities; -using Fake.Domain.Entities.Auditing; - -namespace TrackCenter.Domain.Aggregates.KPIAreaAggregate; - -[SugarTable("t_area_details", TableDescription = "kpi 区域子表")] -public class RuleAreaInfoEntity : Entity, IHasCreateUserId, IHasCreateTime, IHasUpdateUserId, IHasUpdateTime -{ - /// - /// 自增主键 - /// - [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")] - public int Id { get; set; } - - /// - /// t_rule_area 表id - /// - [SugarColumn(ColumnName = "AreaId", ColumnDescription = "t_rule_area 表id")] - public int AreaId { get; set; } - - /// - /// 国家或地区二字码 - /// - [SugarColumn(ColumnName = "CountryCode", Length = 128, ColumnDescription = "国家或地区二字码")] - public string CountryCode { get; set; } - - /// - /// 国家或地区名称 - /// - [SugarColumn(ColumnName = "CountryName", Length = 128, ColumnDescription = "国家或地区名称")] - public string CountryName { get; set; } - - /// - /// 州或者省份 - /// - [SugarColumn(ColumnName = "Province", Length = 128, IsNullable = true, ColumnDescription = "州或者省份")] - public string Province { get; set; } - - /// - /// 城市 - /// - [SugarColumn(ColumnName = "City", Length = 128, IsNullable = true, ColumnDescription = "城市")] - public string City { get; set; } - - /// - /// 起始邮编 - /// - [SugarColumn(ColumnName = "LeftPostcode", Length = 10, IsNullable = true, ColumnDescription = "起始邮编")] - public string LeftPostcode { get; set; } - - /// - /// 截止邮编 - /// - [SugarColumn(ColumnName = "RightPostcode", Length = 10, IsNullable = true, ColumnDescription = "截止邮编")] - public string RightPostcode { get; set; } - - /// - /// 创建人 - /// - [SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")] - public int CreateUserId { get; set; } - - /// - /// 创建时间 - /// - [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")] - public DateTime CreateTime { get; set; } - - /// - /// 更新人 - /// - [SugarColumn(ColumnName = "UpdateUserId", IsNullable = false, ColumnDescription = "更新人")] - public int UpdateUserId { get; set; } - - /// - /// 更新时间 - /// - [SugarColumn(ColumnName = "UpdateTime", IsNullable = false, ColumnDescription = "更新时间")] - public DateTime UpdateTime { get; set; } -} diff --git a/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs b/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs index 75c63a7e..7c040109 100644 --- a/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs +++ b/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningRuleRepository.cs @@ -32,6 +32,11 @@ public interface IWarningRuleRepository : IBasicRepository // 获取启用的预警规则主体 Task> GetWarningRuleForEnabledAsync(HashSet<(string AppKey, string Customer)> conditions, WarningRuleType warningType); + /// + /// 按 AppKey 获取启用的预警规则主体(运输时效:方案级,不按客户) + /// + Task> GetWarningRuleForEnabledByAppKeysAsync(HashSet appKeys, WarningRuleType warningType); + // 获取预警规则主体根据id Task GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null); diff --git a/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs b/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs index f87fd24f..d65be78c 100644 --- a/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs +++ b/src/TrackCenter.Domain/Aggregates/WarningAggregate/IWarningTaskRepository.cs @@ -54,9 +54,6 @@ public interface IWarningTaskRepository #region 运输预警_预警明细生成任务 - // 扫描预警明细生成任务 - Task> ScanTransportWarningDetailsGenerateTaskAsync(); - // 条件获取运输预警明细生成任务 Task> GetTransportWarningDetailsGenerateTaskAsync(Expression> expression); diff --git a/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs b/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs index 550378e1..6e1ea408 100644 --- a/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs +++ b/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningDetailsIndex.cs @@ -1,6 +1,5 @@ using System.Text; using Nest; -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; using TrackCenter.Domain.Aggregates.TrackDetailsAggregate; using TrackCenter.Domain.Consts; using TrackCenter.Domain.SeedWork; @@ -129,32 +128,22 @@ public sealed class TransportWarningDetailsIndex : IdBaseIndex return Status; } - // 预警规则描述,创建预警明细,更新场景(需要重新匹配),更新规则项目(需要重新匹配),更新规则区域名称 - // todo:如果更新了规则区域名称不会同步到预警明细的描述中,出现预警明细和对于的规则区域名称不一致,考虑规则存缓存动态生成预警描述 - public string GetWarningRuleDescription(TransportWarningSceneEntity scene, TransportWarningSceneRuleItemEntity ruleItem, RuleAreaEntity? ruleArea) + // 预警规则描述,创建预警明细,更新场景(需要重新匹配),更新规则项目(需要重新匹配) + public string GetWarningRuleDescription(TransportWarningSceneEntity scene, TransportWarningSceneRuleItemEntity ruleItem) { var res = new StringBuilder("{"); // 起始轨迹状态-截止轨迹状态 res.Append($"{StartTrackStatus}-{EndTrackStatus}"); - // 货物类型 (普货/危险品/...) - if (!string.IsNullOrWhiteSpace(scene.CargoType)) - res.Append($",{scene.CargoType}"); - // 月份 (1/3/5/...) - if (!string.IsNullOrWhiteSpace(scene.Months)) - res.Append($",{scene.Months}月份"); - // 区域名称 (美国xxx区域) - if (ruleArea != null) - res.Append($",{ruleArea.Name}"); - // 是否中转(中转/直飞) - if (scene.IsTransit != null) - res.Append($",{(scene.IsTransit ?? false ? "中转" : "直飞")}"); - // 出发港(xxx港) - if (!string.IsNullOrWhiteSpace(scene.LeavePort)) - res.Append($",{scene.LeavePort}"); // 产品代码(xxx) if (!string.IsNullOrWhiteSpace(scene.ProductCode)) res.Append($",{scene.ProductCode}"); + // 到达国家(US) + if (!string.IsNullOrWhiteSpace(scene.CountryCode)) + res.Append($",{scene.CountryCode}"); + // 考核总时效 + if (scene.AssessmentTotalTimeSpan is > 0) + res.Append($",考核总时效{scene.AssessmentTotalTimeSpan.Value / 24f:0.##}天"); // 规则配置信息 res.Append($",生成预警时间{ruleItem.StartTrackWarningTimeSpan}小时" + diff --git a/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs b/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs index a8c25568..bc422780 100644 --- a/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs +++ b/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneEntity.cs @@ -3,47 +3,42 @@ using TrackCenter.Domain.SeedWork; namespace TrackCenter.Domain.Aggregates.WarningAggregate.Transport; -// 运输时效场景 -[SugarTable("t_warning_transport_scene", TableDescription = "运输时效规则场景")] +/// +/// 运输时效场景:一销售产品 + 一到达国家 + 考核总时效。 +/// +[SugarTable("t_warning_transport_scene", TableDescription = "运输时效场景(产品+国家+考核总时效)")] public sealed class TransportWarningSceneEntity : FullAuditedEntity { [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } - [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则id")] + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则主体Id(t_warning.Id,运输时效为 AppKey 级方案)")] public int WarningId { get; set; } - [SugarColumn(ColumnName = "IsTransit", ColumnDescription = "是否中转")] - public bool? IsTransit { get; set; } //是否中转 + [SugarColumn(ColumnName = "ProductCode", ColumnDescription = "销售产品编码(单一,命中订单产品)", IsNullable = true)] + public string? ProductCode { get; set; } - [SugarColumn(ColumnName = "CargoType", ColumnDescription = "货物类型")] - public string? CargoType { get; set; } //货物类型, + [SugarColumn(ColumnName = "CountryCode", ColumnDescription = "到达国家二字码(单一,命中订单到达国家)", IsNullable = true, Length = 16)] + public string? CountryCode { get; set; } - [SugarColumn(ColumnName = "LeavePort", ColumnDescription = "起运港口")] - public string? LeavePort { get; set; } //起运港口 - - [SugarColumn(ColumnName = "ProductCode", ColumnDescription = "产品编码")] - public string? ProductCode { get; set; } //产品编码 - - [SugarColumn(ColumnName = "Months", ColumnDescription = "月份")] - public string? Months { get; set; } //月份, 1/2/3 - - [SugarColumn(ColumnName = "AreaId", ColumnDescription = "区域id")] - public int? AreaId { get; set; } //区域Id,单分区 + [SugarColumn(ColumnName = "AssessmentTotalTimeSpan", ColumnDescription = "考核总时效(小时;前端按天录入后×24入库)", IsNullable = true)] + public float? AssessmentTotalTimeSpan { get; set; } public TransportWarningSceneEntity() { } - public TransportWarningSceneEntity(int id, int warningId, string? cargoType, string? months, int? areaId, bool? isTransit, string? leavePort, string? productCode) + public TransportWarningSceneEntity( + int id, + int warningId, + string? productCode, + string? countryCode = null, + float? assessmentTotalTimeSpan = null) { Id = id; WarningId = warningId; - CargoType = cargoType; - Months = months; - AreaId = areaId; - IsTransit = isTransit; - LeavePort = leavePort; ProductCode = productCode; + CountryCode = countryCode; + AssessmentTotalTimeSpan = assessmentTotalTimeSpan; } } diff --git a/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs b/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs index 896c59e4..e1fe8730 100644 --- a/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs +++ b/src/TrackCenter.Domain/Aggregates/WarningAggregate/Transport/TransportWarningSceneRuleItemEntity.cs @@ -4,16 +4,16 @@ using TrackCenter.Domain.SeedWork; namespace TrackCenter.Domain.Aggregates.WarningAggregate.Transport; // 运输时效场景规则项 -[SugarTable("t_warning_transport_scene_rule_item", TableDescription = "运输时效规则场景规则项")] +[SugarTable("t_warning_transport_scene_rule_item", TableDescription = "运输时效场景分段规则项")] public sealed class TransportWarningSceneRuleItemEntity : FullAuditedEntity { [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } - [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则id")] + [SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则主体Id(t_warning.Id)")] public int WarningId { get; set; } - [SugarColumn(ColumnName = "TransportWarningSceneId", ColumnDescription = "运输场景id")] + [SugarColumn(ColumnName = "TransportWarningSceneId", ColumnDescription = "运输时效场景Id(t_warning_transport_scene.Id)")] public int TransportWarningSceneId { get; set; } [SugarColumn(ColumnName = "StartTrackStatusId", ColumnDescription = "起始轨迹状态Id")] diff --git a/src/TrackCenter.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs b/src/TrackCenter.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs index b01a9193..ab70bd1d 100644 --- a/src/TrackCenter.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs +++ b/src/TrackCenter.Domain/Aggregates/WarningAggregate/WarningRuleEntity.cs @@ -14,8 +14,8 @@ public sealed class WarningRuleEntity : FullAuditedAggregateRoot [SugarColumn(ColumnName = "AppKey", ColumnDescription = "应用")] public string AppKey { get; set; } //应用 - [SugarColumn(ColumnName = "Customer", ColumnDescription = "客户")] - public string Customer { get; set; } //客户 + [SugarColumn(ColumnName = "Customer", ColumnDescription = "客户(运输时效固定空串;回传/完整预警按客户匹配)")] + public string Customer { get; set; } //客户(运输时效固定空串;回传/完整预警按客户匹配) // [SugarColumn(ColumnName = "CustomName", ColumnDescription = "客户名称")] // public string CustomName { get; set; } //冗余客户简称,用于查询 diff --git a/src/TrackCenter.Domain/Consts/GlobalConst.Redis.cs b/src/TrackCenter.Domain/Consts/GlobalConst.Redis.cs index c1f688e6..82d58e30 100644 --- a/src/TrackCenter.Domain/Consts/GlobalConst.Redis.cs +++ b/src/TrackCenter.Domain/Consts/GlobalConst.Redis.cs @@ -32,11 +32,6 @@ public partial class GlobalConst /// public const string OrderPullStatusScanCheckKey = "OrderPullStatusScanCheck"; - /// - /// kpi区域redis key前缀 - /// - public const string KpiAreaKeyPrefix = "KpiArea"; - /// /// 城市时区redis key前缀 /// diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/AddRuleAreaInfoRequest.cs b/src/TrackCenter.Domain/Dtos/Forewarning/AddRuleAreaInfoRequest.cs deleted file mode 100644 index 29c655c0..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/AddRuleAreaInfoRequest.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class AddRuleAreaInfoRequest -{ - /// - /// 国家二字码,必填项。 - /// - [Required] - public string CountryCode { get; set; } - - /// - /// 国家名称 - /// - [Required] - public string CountryName { get; set; } - - /// - /// 省份名称,可为空。 - /// - public string? Province { get; set; } - - /// - /// 城市名称,可为空。 - /// - public string? City { get; set; } - - /// - /// 左侧邮政编码,可为空。 - /// - public string? LeftPostCode { get; set; } - - /// - /// 右侧邮政编码,可为空。 - /// - public string? RightPostCode { get; set; } - - /// - /// 区域ID,必填项。 - /// - [Required] - public int AreaId { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/AreaDto.cs b/src/TrackCenter.Domain/Dtos/Forewarning/AreaDto.cs deleted file mode 100644 index 09f14ccd..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/AreaDto.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class AreaDto -{ - /// - /// 区域id - /// - public int Id { get; set; } - - /// - /// 应用 - /// - public string AppKey { get; set; } - - /// - /// 系统名称 - /// - public string SystemName { get; set; } - - /// - /// 区域名称 - /// - public string Name { get; set; } - - /// - /// 更新时间 - /// - public DateTime UpdateTime { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/AreaInfoDto.cs b/src/TrackCenter.Domain/Dtos/Forewarning/AreaInfoDto.cs deleted file mode 100644 index f6fc8dbf..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/AreaInfoDto.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class RuleAreaInfoDto -{ - /// - /// 分区信息id - /// - public int Id { get; set; } - - /// - /// 分区id - /// - public int AreaId { get; set; } - - /// - /// 国家或地区 二字码 - /// - public string CountryCode { get; set; } - - /// - /// 国家名称 - /// - public string CountryName { get; set; } - - /// - /// 国家id - /// - public int CountryId { get; set; } - - /// - /// 省份 - /// - public string Province { get; set; } - - /// - /// 城市 - /// - public string City { get; set; } - - /// - /// 开始邮编 - /// - public string LeftPostcode { get; set; } - - public string RightPostcode { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/CreateRuleAreaRequest.cs b/src/TrackCenter.Domain/Dtos/Forewarning/CreateRuleAreaRequest.cs deleted file mode 100644 index da540b15..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/CreateRuleAreaRequest.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class CreateRuleAreaRequest -{ - /// - /// 系统id - /// - [Required] - public string AppKey { get; set; } - - /// - /// 名称 - /// - [Required] - public string Name { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/KpiAreaInfo.cs b/src/TrackCenter.Domain/Dtos/Forewarning/KpiAreaInfo.cs deleted file mode 100644 index ddb313e4..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/KpiAreaInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class KpiAreaInfo -{ - public int AreaId { get; set; } - - /// - /// 国家或地区 - /// - public string CountryCode { get; set; } = null!; - - /// - /// 省份 - /// - public string? Province { get; set; } - - /// - /// 城市 - /// - public string? City { get; set; } - - /// - /// 开始邮编 - /// - public string? LeftPostcode { get; set; } - - /// - /// 截止邮编 - /// - public string? RightPostcode { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/QueryAreaInfoRequest.cs b/src/TrackCenter.Domain/Dtos/Forewarning/QueryAreaInfoRequest.cs deleted file mode 100644 index e8b4c553..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/QueryAreaInfoRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class QueryAreaInfoRequest : PageInfoInput -{ - /// - /// 区域id - /// - public int AreaId { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/QueryAreaRequest.cs b/src/TrackCenter.Domain/Dtos/Forewarning/QueryAreaRequest.cs deleted file mode 100644 index c921b4c4..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/QueryAreaRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class QueryAreaRequest : PageInfoInput -{ - /// - /// 分区名称 - /// - public string? Name { get; set; } - - /// - /// 应用 - /// - public string? AppKey { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/UpdateRuleAreaInfoRequest.cs b/src/TrackCenter.Domain/Dtos/Forewarning/UpdateRuleAreaInfoRequest.cs deleted file mode 100644 index 42497fa1..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/UpdateRuleAreaInfoRequest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace TrackCenter.Domain.Dtos.Forewarning; - -/// -/// 更新规则区域信息请求 -/// -public class UpdateRuleAreaInfoRequest -{ - /// - /// 区域信息ID - /// - public int Id { get; set; } - - /// - /// 国家二字码,必填项。 - /// - [Required] - public string CountryCode { get; set; } - - /// - /// 国家名称,必填项。 - /// - [Required] - public string CountryName { get; set; } - - /// - /// 省份名称,可为空。 - /// - public string? Province { get; set; } - - /// - /// 城市名称,可为空。 - /// - public string? City { get; set; } - - /// - /// 左侧邮政编码,可为空。 - /// - public string? LeftPostCode { get; set; } - - /// - /// 右侧邮政编码,可为空。 - /// - public string? RightPostCode { get; set; } -} diff --git a/src/TrackCenter.Domain/Dtos/Forewarning/UpdateRuleAreaRequest.cs b/src/TrackCenter.Domain/Dtos/Forewarning/UpdateRuleAreaRequest.cs deleted file mode 100644 index 14e8b8bc..00000000 --- a/src/TrackCenter.Domain/Dtos/Forewarning/UpdateRuleAreaRequest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace TrackCenter.Domain.Dtos.Forewarning; - -public class UpdateRuleAreaRequest -{ - /// - /// 名称 - /// - [Required] - public string Name { get; set; } - - /// - /// 区域id - /// - public int Id { get; set; } -} diff --git a/src/TrackCenter.Repository/Repositories/KpiAreaInfoRepository.cs b/src/TrackCenter.Repository/Repositories/KpiAreaInfoRepository.cs deleted file mode 100644 index 329c1ace..00000000 --- a/src/TrackCenter.Repository/Repositories/KpiAreaInfoRepository.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace TrackCenter.Repository.Repositories; - -public class KpiAreaInfoRepository -{ -} diff --git a/src/TrackCenter.Repository/Repositories/KpiAreaRepository.cs b/src/TrackCenter.Repository/Repositories/KpiAreaRepository.cs deleted file mode 100644 index 07206f46..00000000 --- a/src/TrackCenter.Repository/Repositories/KpiAreaRepository.cs +++ /dev/null @@ -1,130 +0,0 @@ -using TrackCenter.Domain; -using TrackCenter.Domain.Aggregates.KPIAreaAggregate; -using TrackCenter.Domain.Dtos.Forewarning; -using TrackCenter.Domain.VEntities; - -namespace TrackCenter.Repository.Repositories; - -public class KpiAreaRepository : BasicRepository, IKpiAreaRepository, ITransientDependency -{ - public async ValueTask CreateAreaAsync(RuleAreaEntity entity) - { - return await InsertReturnIdentityAsync(entity); - } - - public async ValueTask AddAreaInfoAsync(RuleAreaInfoEntity entity) - { - return await Context.Insertable(entity).ExecuteReturnIdentityAsync(); - } - - public async ValueTask DeleteArea(int areaId) - { - using var transaction = TransactionHelper.GetReadCommitted(); - await DeleteAsync(x => x.Id == areaId); - await Context.Deleteable().Where(x => x.AreaId == areaId).ExecuteCommandAsync(); - transaction.Complete(); - } - - public Task DeleteAreaInfo(int areaInfoId) - { - return Context.Deleteable().Where(val => val.Id == areaInfoId).ExecuteCommandAsync(); - } - - public Task UpdateAreaInfoAsync(RuleAreaInfoEntity entity) - { - return Context.Updateable(entity).ExecuteCommandAsync(); - } - - /// - /// 获取分区 - /// - /// - /// - public async Task> GetAreas(QueryAreaRequest query) - { - return await Context.Queryable() - .WhereIF(!string.IsNullOrEmpty(query.Name), area => area.Name.Contains(query.Name!)) - .OrderBy(area => area.UpdateTime, OrderByType.Desc) - .Select(area => new AreaDto() - { - Id = area.Id, - Name = area.Name, - AppKey = area.AppKey, - UpdateTime = area.CreateTime - }) - .ToPagedListAsync(query.PageIndex, query.PageSize); - } - - /// - /// 获得分区信息 - /// - /// - /// - public async Task> GetAreaInfos(QueryAreaInfoRequest query) - { - return await Context.Queryable() - //.InnerJoin((x, c) => x.CountryCode == c.Digit2Code) - .Where(x => x.AreaId == query.AreaId) - .OrderBy(x => x.UpdateTime, OrderByType.Desc) - .Select((x) => new RuleAreaInfoDto() - { - Id = x.Id, - AreaId = x.AreaId, - City = x.City, - //CountryId = c.Id, - CountryCode = x.CountryCode, - CountryName = x.CountryName, - LeftPostcode = x.LeftPostcode, - RightPostcode = x.RightPostcode, - Province = x.Province - }) - .ToPagedListAsync(query.PageIndex, query.PageSize); - } - - public Task GetAreaInfoById(int id) - { - return Context.Queryable() - .FirstAsync(val => val.Id == id)!; - } - - /// - /// 获取所有区域信息 - /// - /// - public List GetAreaInfosByAreaId(int areaId) - { - return Context.Queryable() - .InnerJoin((r, i) => r.AreaId == i.Id) - .Where((r, i) => r.AreaId == areaId) - .Select((r, i) => new KpiAreaInfo - { - AreaId = r.AreaId, - CountryCode = r.CountryCode, - LeftPostcode = r.LeftPostcode, - City = r.City, - Province = r.Province, - RightPostcode = r.RightPostcode - }) - .ToList(); - } - - public async Task>> GetAreaInfosDictByAreaId(HashSet areaIds) - { - var list = await Context.Queryable() - .InnerJoin((r, i) => r.AreaId == i.Id) - .Where((r, i) => areaIds.Contains(i.Id)) - .Select((r, i) => new KpiAreaInfo - { - AreaId = r.AreaId, - CountryCode = r.CountryCode, - LeftPostcode = r.LeftPostcode, - City = r.City, - Province = r.Province, - RightPostcode = r.RightPostcode - }) - .ToListAsync(); - - return list.GroupBy(x => x.AreaId) - .ToDictionary(x => x.Key, y => y.ToList()); - } -} diff --git a/src/TrackCenter.Repository/Repositories/WarningRuleRepository.cs b/src/TrackCenter.Repository/Repositories/WarningRuleRepository.cs index a8e5a24e..ee2540a7 100644 --- a/src/TrackCenter.Repository/Repositories/WarningRuleRepository.cs +++ b/src/TrackCenter.Repository/Repositories/WarningRuleRepository.cs @@ -15,12 +15,16 @@ public class WarningRuleRepository : BasicRepository, IWarnin public async Task CheckNameDuplicateAsync(int id, string appKey, string customer, string name, WarningRuleType warningType) { - var entity = await Context.Queryable() + // 运输时效:AppKey + Name + Type(方案级,不按客户) + var query = Context.Queryable() .Where(x => x.AppKey == appKey - && x.Customer == customer && x.WarningType == (int) warningType - && string.Equals(x.Name, name)) - .FirstAsync(); + && string.Equals(x.Name, name)); + + if (warningType != WarningRuleType.运输时效预警) + query = query.Where(x => x.Customer == customer); + + var entity = await query.FirstAsync(); // 不重复 if (entity == null) @@ -32,14 +36,18 @@ public class WarningRuleRepository : BasicRepository, IWarnin public async Task CheckEnableDuplicateAsync(int warningId, string appKey, string customer, WarningRuleType warningType) { - var entity = await Context.Queryable() + // 运输时效:同一 AppKey + 类型仅允许一个启用方案 + var query = Context.Queryable() .Where(x => x.AppKey == appKey - && x.Customer == customer && x.WarningType == (int) warningType - && x.IsEnabled) - .FirstAsync(); + && x.IsEnabled); - // 没有【应用+客户+类型】启用的规则 + if (warningType != WarningRuleType.运输时效预警) + query = query.Where(x => x.Customer == customer); + + var entity = await query.FirstAsync(); + + // 没有启用的规则 if (entity == null) return false; // 已经启用和当前规则不同,视为启用重复,不允许操作 @@ -117,6 +125,22 @@ public class WarningRuleRepository : BasicRepository, IWarnin : list.ToDictionary(x => x.Id); } + public async Task> GetWarningRuleForEnabledByAppKeysAsync(HashSet appKeys, WarningRuleType warningType) + { + if (appKeys.Count == 0) + return Empty.Dict; + + var list = await Context.Queryable() + .Where(x => x.WarningType == (int) warningType + && x.IsEnabled + && appKeys.Contains(x.AppKey)) + .ToListAsync(); + + return list.Count == 0 + ? Empty.Dict + : list.ToDictionary(x => x.Id); + } + public async Task GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null) { var result = await Context.Queryable() diff --git a/src/TrackCenter.Repository/Repositories/WarningTaskRepository.cs b/src/TrackCenter.Repository/Repositories/WarningTaskRepository.cs index d2f61e70..924c24fa 100644 --- a/src/TrackCenter.Repository/Repositories/WarningTaskRepository.cs +++ b/src/TrackCenter.Repository/Repositories/WarningTaskRepository.cs @@ -110,13 +110,6 @@ public class WarningTaskRepository : BasicRepository, I #region 运输预警_预警明细生成任务 - public Task> ScanTransportWarningDetailsGenerateTaskAsync() - { - return Context.Queryable() - .Where(x => DateTime.Now >= x.PlanExecutionTime && x.IsClosed == false) - .ToListAsync(); - } - public Task> GetTransportWarningDetailsGenerateTaskAsync(Expression> expression) { return Context.Queryable()