Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
48064b4cb1
@ -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;
|
||||
31
scripts/sql/20260801_transport_warning_simplify.sql
Normal file
31
scripts/sql/20260801_transport_warning_simplify.sql
Normal file
@ -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;
|
||||
@ -18,7 +18,6 @@ public class TrackContentAutoMapperProfile : Profile
|
||||
CreateMap<PagedResult<GetTrackContentListVO>, PagedResult<GetTrackContentListDto>>(
|
||||
MemberList.None);
|
||||
CreateMap<EditTrackContentMapInput, TrackContentMapEntity>(MemberList.None);
|
||||
CreateMap<TrackContentVO, TrackContentOutput>(MemberList.None);
|
||||
|
||||
CreateMap<TrackDetailsIndex, GetContentDetailOutput>(MemberList.None).ReverseMap();
|
||||
CreateMap<TrackCenter.Domain.Aggregates.TrackDetailsAggregate.FileItem, TrackCenter.Application.Dtos.TrackContents.FileItem>(MemberList.None).ReverseMap();
|
||||
|
||||
@ -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<GetPageCompleteWarningDetailsInput, GetPageCompleteWarningDetailsInputVO>(MemberList.None);
|
||||
CreateMap<GetPageReturnWarningDetailsInput, GetPageReturnWarningDetailsInputVO>(MemberList.None);
|
||||
CreateMap<GetPageTransportWarningDetailsInput, GetPageTransportWarningDetailsInputVO>(MemberList.None);
|
||||
// kpi区域
|
||||
CreateMap<UpdateRuleAreaRequest, RuleAreaEntity>(MemberList.None);
|
||||
CreateMap<AddRuleAreaInfoRequest, RuleAreaInfoEntity>(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<CreateRuleAreaRequest, RuleAreaEntity>(MemberList.None)
|
||||
.ForMember(dest => dest.AppKey, opt => opt.MapFrom(src => src.AppKey))
|
||||
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name));
|
||||
|
||||
CreateMap<UpdateRuleAreaInfoRequest, RuleAreaInfoEntity>(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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<int> 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<int> 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<TrackChangeEto> 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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
namespace TrackCenter.Application.Dtos.Forewarning;
|
||||
|
||||
public class AreaCacheEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 国家或地区
|
||||
/// </summary>
|
||||
public string CountryCode { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 省份
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始邮编
|
||||
/// </summary>
|
||||
public string? LeftPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 截止邮编
|
||||
/// </summary>
|
||||
public string? RightPostcode { get; set; }
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace TrackCenter.Application.Dtos.ProvideServices;
|
||||
|
||||
public class BatchDeleteTrackByEsIdInput
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容es id,必填
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public List<string> TrackContentEsIds { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 删除轨迹来源 1.外部服务商 2.内部系统 3.人工
|
||||
/// </summary>
|
||||
public int? Source { get; set; } = 2;
|
||||
}
|
||||
@ -29,26 +29,11 @@ public class BatchUpsertTrackInput : OrderNosInfo
|
||||
[Required]
|
||||
public DateTime TrackTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新的轨迹发生时间
|
||||
/// 如果是新增,那么优先级高于TrackTime
|
||||
/// 如果要修改TrackTime,请同时传递TrackTime(旧的用于匹配),和NewTrackTime
|
||||
/// </summary>
|
||||
public DateTime? NewTrackTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生时区
|
||||
/// </summary>
|
||||
// [Required]
|
||||
// [Range(-12, 12)]
|
||||
public float? TrackTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户是否可见
|
||||
/// </summary>
|
||||
[Obsolete("废弃的,请使用IsExternal")]
|
||||
public bool IsVisibility { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否对外(可选的)
|
||||
/// 如果未填则按照【轨迹内容】中的【是否可见】做为默认值
|
||||
@ -57,7 +42,6 @@ public class BatchUpsertTrackInput : OrderNosInfo
|
||||
|
||||
/// <summary>
|
||||
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
|
||||
/// 批量新增时使用指定轨迹来源,更新时无需指定
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int Source { get; set; }
|
||||
@ -83,68 +67,10 @@ public class BatchUpsertTrackInput : OrderNosInfo
|
||||
public string? EnStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(城市)
|
||||
/// </summary>
|
||||
public string? TrackPlaceCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(城市)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceCityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(省/州)
|
||||
/// </summary>
|
||||
public string? TrackPlaceProvince { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///轨迹发生地(省/州)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceProvinceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(国家/地区)
|
||||
/// </summary>
|
||||
public string? TrackPlaceNation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(国家/地区)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceNationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地邮编
|
||||
/// </summary>
|
||||
public string? TrackPlacePostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预计到达时间
|
||||
/// </summary>
|
||||
public DateTime? EstimatedDeliveryDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目的地时区
|
||||
/// </summary>
|
||||
[Range(-12, 12)]
|
||||
public float? DestinationTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 重复的情况下,用什么比较
|
||||
/// </summary>
|
||||
public string? CompareFields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扩展信息字段,用与轨迹推送的附加字段
|
||||
/// 扩展信息字段,用于轨迹推送的附加字段
|
||||
/// </summary>
|
||||
public string? Extra { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新的扩展信息,
|
||||
/// 如果是新增,那么优先级高于Extra
|
||||
/// 如果要修改Extra,请同时传递Extra(用于匹配旧的)和NewExtra
|
||||
/// </summary>
|
||||
public string? NewExtra { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否强制推送
|
||||
/// </summary>
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
namespace TrackCenter.Application.Dtos.ProvideServices;
|
||||
|
||||
/// <summary>
|
||||
/// ProviderService 提示文案助手(不改变接口返回结构,仅用于异常 message:结果;原因:...)
|
||||
/// </summary>
|
||||
public static class ProviderServiceActionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 抛出结构化业务异常(message = 结果;原因:...)
|
||||
/// </summary>
|
||||
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}";
|
||||
}
|
||||
@ -10,7 +10,7 @@ public class UpsertTrackInput : OrderNoInfo
|
||||
public bool IsAdd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹唯一标识(可选)
|
||||
/// 轨迹唯一标识(可选,更新时优先按 EsId 匹配)
|
||||
/// </summary>
|
||||
public string? EsId { get; set; }
|
||||
|
||||
@ -34,26 +34,11 @@ public class UpsertTrackInput : OrderNoInfo
|
||||
[Required]
|
||||
public DateTime TrackTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新的轨迹发生时间
|
||||
/// 如果是新增,那么优先级高于TrackTime
|
||||
/// 如果要修改TrackTime,请同时传递TrackTime(旧的用于匹配),和NewTrackTime
|
||||
/// </summary>
|
||||
public DateTime? NewTrackTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生时区
|
||||
/// </summary>
|
||||
// [Required]
|
||||
// [Range(-12, 12)]
|
||||
public float? TrackTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户是否可见
|
||||
/// </summary>
|
||||
[Obsolete("废弃的,请使用IsExternal")]
|
||||
public bool IsVisibility { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否对外(可选的)
|
||||
/// 如果未填则按照【轨迹内容】中的【是否可见】做为默认值
|
||||
@ -87,68 +72,10 @@ public class UpsertTrackInput : OrderNoInfo
|
||||
public string? EnStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(城市)
|
||||
/// </summary>
|
||||
public string? TrackPlaceCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(城市)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceCityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(省/州)
|
||||
/// </summary>
|
||||
public string? TrackPlaceProvince { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(省/州)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceProvinceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(国家/地区)
|
||||
/// </summary>
|
||||
public string? TrackPlaceNation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(国家/地区)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceNationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地邮编
|
||||
/// </summary>
|
||||
public string? TrackPlacePostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预计到达时间
|
||||
/// </summary>
|
||||
public DateTime? EstimatedDeliveryDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目的地时区
|
||||
/// </summary>
|
||||
[Range(-12, 12)]
|
||||
public float? DestinationTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 重复的情况下,用什么比较
|
||||
/// </summary>
|
||||
public string? CompareFields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扩展信息字段,用与轨迹推送的附加字段
|
||||
/// 扩展信息字段,用于轨迹推送的附加字段
|
||||
/// </summary>
|
||||
public string? Extra { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新的扩展信息,
|
||||
/// 如果是新增,那么优先级高于Extra
|
||||
/// 如果要修改Extra,请同时传递Extra(用于匹配旧的)和NewExtra
|
||||
/// </summary>
|
||||
public string? NewExtra { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否强制推送
|
||||
/// </summary>
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
namespace TrackCenter.Application.Dtos.TrackContents;
|
||||
|
||||
public sealed class TrackContentOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用
|
||||
/// </summary>
|
||||
public string AppKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用代码
|
||||
/// </summary>
|
||||
public string AppCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹状态Id
|
||||
/// </summary>
|
||||
public int TrackStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹状态
|
||||
/// </summary>
|
||||
public string TrackStatus { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容Id
|
||||
/// </summary>
|
||||
public int TrackContentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容
|
||||
/// </summary>
|
||||
public string TrackContent { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容英文
|
||||
/// </summary>
|
||||
public string TrackEnContent { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 是否完成态
|
||||
/// </summary>
|
||||
public bool IsCompleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹状态代码
|
||||
/// </summary>
|
||||
public string TrackStatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否可重复
|
||||
/// </summary>
|
||||
public bool IsRepeatable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许手动修改
|
||||
/// </summary>
|
||||
public bool IsAllowManuallyUpdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许手动创建
|
||||
/// </summary>
|
||||
public bool IsAllowManuallyCreate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否广播
|
||||
/// </summary>
|
||||
public bool IsToMq { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否对外
|
||||
/// </summary>
|
||||
public bool IsExternal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容code
|
||||
/// </summary>
|
||||
public string TrackContentCode { get; set; }
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
namespace TrackCenter.Application.Dtos.Warning.Transport;
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则场景
|
||||
/// 运输时效场景(一产品 + 一国家 + 考核总时效)
|
||||
/// </summary>
|
||||
public class TransportWarningSceneDto
|
||||
{
|
||||
@ -11,47 +11,27 @@ public class TransportWarningSceneDto
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则主体id
|
||||
/// 预警规则主体id(AppKey 级方案)
|
||||
/// </summary>
|
||||
public int WarningId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 场景摘要(用于展示场景tabs)
|
||||
/// 场景摘要(产品 / 国家 / 考核总时效)
|
||||
/// </summary>
|
||||
public string Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中转(单一)
|
||||
/// </summary>
|
||||
public bool? IsTransit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型(多个,分割)
|
||||
/// </summary>
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起运港口(多个,分割)
|
||||
/// </summary>
|
||||
public string? LeavePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码(多个,分割)
|
||||
/// 销售产品编码(单一)
|
||||
/// </summary>
|
||||
public string? ProductCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 月份(多个/分割)
|
||||
/// 到达国家二字码(单一)
|
||||
/// </summary>
|
||||
public string? Months { get; set; }
|
||||
public string? CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域Id(单一)
|
||||
/// 考核总时效(小时;前端按天展示)
|
||||
/// </summary>
|
||||
public int? AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域id对应的名称
|
||||
/// </summary>
|
||||
public string? AreaName { get; set; }
|
||||
public float? AssessmentTotalTimeSpan { get; set; }
|
||||
}
|
||||
|
||||
@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则主体id
|
||||
/// 预警规则主体id(AppKey 级方案)
|
||||
/// </summary>
|
||||
[IdRequired]
|
||||
public int WarningId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中转(单一)
|
||||
/// </summary>
|
||||
public bool? IsTransit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型(多个,分割)
|
||||
/// </summary>
|
||||
public string? CargoType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起运港口(多个,分割)
|
||||
/// </summary>
|
||||
public string? LeavePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码(多个,分割)
|
||||
/// </summary>
|
||||
/// <summary>销售产品编码(单一)</summary>
|
||||
[Required(ErrorMessage = "请配置销售产品")]
|
||||
public string? ProductCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 月份(多个/分割)
|
||||
/// </summary>
|
||||
public string? Months { get; set; }
|
||||
/// <summary>到达国家二字码(单一)</summary>
|
||||
[Required(ErrorMessage = "请配置国家")]
|
||||
public string? CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域Id(单一)
|
||||
/// </summary>
|
||||
public int? AreaId { get; set; }
|
||||
/// <summary>考核总时效(小时;前端按天录入后×24)</summary>
|
||||
[Required(ErrorMessage = "请配置考核总时效")]
|
||||
[Range(0.1, 99999, ErrorMessage = "考核总时效须大于0")]
|
||||
public float? AssessmentTotalTimeSpan { get; set; }
|
||||
}
|
||||
|
||||
@ -19,9 +19,9 @@ public class UpsertWarningRuleInput
|
||||
public string AppKey { get; set; } //应用
|
||||
|
||||
/// <summary>
|
||||
/// 客户
|
||||
/// 客户(运输时效可空,后端会置空;回传/完整预警必填)
|
||||
/// </summary>
|
||||
public string Customer { get; set; } //客户
|
||||
public string? Customer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则名称
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
using TrackCenter.Application.Dtos.Forewarning;
|
||||
|
||||
namespace TrackCenter.Application.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 历史区域缓存清理事件(预警分区已下线,保留空壳兼容 Redis 订阅反序列化)
|
||||
/// </summary>
|
||||
public class ClearRulCacheEvent
|
||||
{
|
||||
public List<RuleAreaBaseInfo> Items { get; set; }
|
||||
public List<int> AreaIds { get; set; } = new();
|
||||
}
|
||||
|
||||
@ -54,13 +54,6 @@ public class TrackUpsertEvent
|
||||
/// </summary>
|
||||
public DateTime TrackTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新的轨迹发生时间
|
||||
/// 如果是新增,那么优先级高于TrackTime
|
||||
/// 如果要修改TrackTime,请同时传递TrackTime(旧的用于匹配),和NewTrackTime
|
||||
/// </summary>
|
||||
public DateTime? NewTrackTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生时区
|
||||
/// </summary>
|
||||
@ -104,67 +97,10 @@ public class TrackUpsertEvent
|
||||
public string? EnStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(城市)
|
||||
/// </summary>
|
||||
public string? TrackPlaceCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(城市)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceCityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(省/州)
|
||||
/// </summary>
|
||||
public string? TrackPlaceProvince { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///轨迹发生地(省/州)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceProvinceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(国家/地区)
|
||||
/// </summary>
|
||||
public string? TrackPlaceNation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地(国家/地区)Id
|
||||
/// </summary>
|
||||
public int? TrackPlaceNationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发生地邮编
|
||||
/// </summary>
|
||||
public string? TrackPlacePostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预计到达时间
|
||||
/// </summary>
|
||||
public DateTime? EstimatedDeliveryDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目的地时区
|
||||
/// </summary>
|
||||
public float? DestinationTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 重复的情况下,用什么比较
|
||||
/// </summary>
|
||||
public string? CompareFields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扩展信息字段,用与轨迹推送的附加字段
|
||||
/// 扩展信息字段,用于轨迹推送的附加字段
|
||||
/// </summary>
|
||||
public string? Extra { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新的扩展信息,
|
||||
/// 如果是新增,那么优先级高于Extra
|
||||
/// 如果要修改Extra,请同时传递Extra(用于匹配旧的)和NewExtra
|
||||
/// </summary>
|
||||
public string? NewExtra { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否强制推送
|
||||
/// </summary>
|
||||
|
||||
@ -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<TrackWarningTaskOptions> _optionsMonitor;
|
||||
|
||||
public WarningNotifyAggregator(IWarningTaskRepository warningTaskRepository, IWarningRuleRepository warningRuleRepository, IOrderRepository orderRepository, ITrackDetailsEsRepository trackDetailsEsRepository, IWarningDetailsEsRepository warningDetailsEsRepository, IKpiAreaRepository kpiAreaRepository, IOptionsMonitor<TrackWarningTaskOptions> optionsMonitor)
|
||||
public WarningNotifyAggregator(
|
||||
IWarningTaskRepository warningTaskRepository,
|
||||
IWarningRuleRepository warningRuleRepository,
|
||||
IOrderRepository orderRepository,
|
||||
ITrackDetailsEsRepository trackDetailsEsRepository,
|
||||
IWarningDetailsEsRepository warningDetailsEsRepository,
|
||||
IOptionsMonitor<TrackWarningTaskOptions> 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<int, List<KpiAreaInfo>>(0);
|
||||
|
||||
// 2.2场景验证
|
||||
// 2.1场景验证(产品+国家)
|
||||
var validTasks = new List<WarningNotifyTaskEntity>(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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -21,23 +16,17 @@ public interface IWarningTaskScanner
|
||||
/// </summary>
|
||||
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<TrackWarningTaskOptions> _optionsMonitor;
|
||||
|
||||
public WarningTaskScanner(IWarningTaskRepository warningTaskRepository,
|
||||
ITransportWarningHandler transportWarningHandler, WarningNotifyAggregator warningNotifyAggregator,
|
||||
IWarningTaskService warningTaskService, IOptionsMonitor<TrackWarningTaskOptions> 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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,10 +47,9 @@ public class WarningTaskComponentHostedService : IHostedService
|
||||
private void ReloadConfig(IOptionsMonitor<TrackWarningTaskOptions> 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<IWarningTaskScanner>("Scan_TransportWarningDetailsGenerateTask",
|
||||
q,
|
||||
x => x.ScanTransportDetailsGenerateTaskAndSchedulingAsync(),
|
||||
$"*/{newVal.ScanTransportWarningDetailsGenerateTaskMinuteInterval} * * * *");
|
||||
}
|
||||
// 移除历史运输预警明细生成任务扫描(若仍挂在 Hangfire 上)
|
||||
_recurringJobManager.RemoveIfExists("Scan_TransportWarningDetailsGenerateTask");
|
||||
|
||||
_oldOptions = newVal;
|
||||
}
|
||||
|
||||
@ -28,10 +28,4 @@ public sealed class TrackWarningTaskOptions
|
||||
|
||||
/// <summary> 回传重试推送任务扫描间隔(分钟) </summary>
|
||||
public int ScanReturnWarningRetryPushTaskMinuteInterval { get; set; }
|
||||
|
||||
/// <summary> 运输预警明细生成任务扫描优先级 </summary>
|
||||
public int ScanTransportWarningDetailsGenerateTaskLevel { get; set; }
|
||||
|
||||
/// <summary> 预警明细生成任务扫描间隔(分钟) </summary>
|
||||
public int ScanTransportWarningDetailsGenerateTaskMinuteInterval { get; set; }
|
||||
}
|
||||
|
||||
@ -70,6 +70,84 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手工刷新诊断:区分可刷新订单与不可刷新原因
|
||||
/// </summary>
|
||||
public async Task<(List<OrderWithServiceProviderVO> CanRefresh, List<string> SkipReasons)>
|
||||
DiagnoseManualRefreshAsync(string appKey, List<string> orderNos)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var canRefresh = await FindOrderWithServiceProviderAsync(appKey, orderNos);
|
||||
|
||||
var eligibleOrderNos = await Context.Queryable<OrderEntity>()
|
||||
.InnerJoin<OrderPullStatusEntity>((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<OrderEntity>()
|
||||
.LeftJoin<OrderPullStatusEntity>((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<string>();
|
||||
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<List<OrderWithServiceProviderVO>> FindOrderWithServiceProviderAsync(
|
||||
TrackPullConfigVo config,
|
||||
int pageIndex, int pageSize = 1000)
|
||||
|
||||
@ -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<string> Hello(string name)
|
||||
{
|
||||
var order = await _orderRepository.GetFirstAsync(x => true);
|
||||
if (order == null)
|
||||
{
|
||||
throw new BusinessException(message: "订单不存在");
|
||||
}
|
||||
|
||||
return $"hello {name}";
|
||||
}
|
||||
}
|
||||
@ -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<OrderEntity> _orderRepository;
|
||||
|
||||
public OTelService(IBasicRepository<OrderEntity> orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
// [OTel]
|
||||
public async Task<string> 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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 运输预警明细生成任务
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运输预警明细生成任务立即执行,不考虑计划执行时间,执行完成任务会关闭
|
||||
///
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 模拟接口通回调
|
||||
// /// </summary>
|
||||
// [HttpPost("MockReturn")]
|
||||
// public async Task<string> MockReturn(TrackPushRecordIndex record)
|
||||
// {
|
||||
// await _returnWarningHandler.ReceiveTrackPushRecordHandleAsync(record);
|
||||
// return "Ok";
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 模拟接口通回调
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("MockReturn")]
|
||||
public async Task<string> 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<TrackUpdateInput>(0));
|
||||
return "Ok";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public record MockReturnInput(bool IsSuccess, string ReturnMessage, Guid RequestId, List<TrackPushInput> PushInputs,List<TrackPushRecordIndex> Records);
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建分区
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns>分区id</returns>
|
||||
[HttpPost("KpiArea/CreateKpiArea")]
|
||||
public async ValueTask<IdBaseDto> CreateKpiArea(CreateRuleAreaRequest input)
|
||||
{
|
||||
var ruleAreaEntity = ObjectMapper.Map<CreateRuleAreaRequest, RuleAreaEntity>(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,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加区域信息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns>区域id</returns>
|
||||
[HttpPost("KpiArea/AddAreaInfo")]
|
||||
public async ValueTask<IdBaseDto> AddAreaInfoAsync(AddRuleAreaInfoRequest input)
|
||||
{
|
||||
var ruleAreaEntity = ObjectMapper.Map<AddRuleAreaInfoRequest, RuleAreaInfoEntity>(input);
|
||||
var ruleInfoId = await _ruleAreaRepository.AddAreaInfoAsync(ruleAreaEntity);
|
||||
// todo: 删除 kpi区域缓存
|
||||
return new IdBaseDto
|
||||
{
|
||||
Id = ruleInfoId,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区
|
||||
/// </summary>
|
||||
/// <param name="id">分区id</param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("KpiArea/DeleteKpiArea")]
|
||||
public async ValueTask DeleteKpiArea(int id)
|
||||
{
|
||||
await _ruleAreaRepository.DeleteArea(id);
|
||||
// todo: 删除 kpi区域缓存
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区信息
|
||||
/// </summary>
|
||||
/// <param name="id">分区信息id</param>
|
||||
/// <returns></returns>
|
||||
[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区域缓存
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新区域信息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新区域信息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[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区域缓存
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询分区列表
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("KpiArea/GetKpiAreas")]
|
||||
public async Task<PagedResult<AreaDto>> GetKpiAreas(QueryAreaRequest input)
|
||||
{
|
||||
return await _ruleAreaRepository.GetAreas(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询分区列表
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("KpiArea/GetKpiAreaInfos")]
|
||||
public async Task<PagedResult<RuleAreaInfoDto>> GetKpiAreaInfos(QueryAreaInfoRequest query)
|
||||
{
|
||||
return await _ruleAreaRepository.GetAreaInfos(query);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 关联订单
|
||||
await DoLinkOrderAsync(input, orderId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task DoLinkOrderAsync(LinkOrderInput input, int orderId)
|
||||
await DoLinkOrderAsync(input, orderId);
|
||||
}
|
||||
|
||||
private async Task<int> 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<int> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<PullConfigInfo, UpdateOrderPullConfigsInputVo>(input.Config);
|
||||
inputVo.OrderIds = orderIds.ToArray();
|
||||
@ -69,5 +74,6 @@ public partial class OrderService
|
||||
Log.Warning(
|
||||
$"{CurrentUser.UserName}正在修改服务商拉取信息,本次共计影响了{orderIds.Count}个订单、{cnt}段拉取,请求参数是:{input.ToJson()}",
|
||||
true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
/// <summary>
|
||||
/// 订单服务 - 小包/大包/主单 Upsert(原 OrderServiceV2)
|
||||
/// </summary>
|
||||
public partial class OrderService
|
||||
{
|
||||
/// <summary>
|
||||
/// 新增、更新小包(存在就更新)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("OrderV2/UpsertSmallBag")]
|
||||
public async Task<IdBaseDto> UpsertAsync(UpsertSmallBagInput input)
|
||||
[HttpPost("Order/UpsertSmallBag")]
|
||||
[VerifySignature]
|
||||
public async Task<IdBaseDto> 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 };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增、更新大包(存在就更新)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("OrderV2/UpsertBigBag")]
|
||||
public async Task<IdBaseDto> UpsertAsync(UpsertBigBagInput input)
|
||||
[HttpPost("Order/UpsertBigBag")]
|
||||
[VerifySignature]
|
||||
public async Task<IdBaseDto> 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 };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增、更新主单(存在就更新)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("OrderV2/UpsertMasterOrder")]
|
||||
public async Task<IdBaseDto> UpsertAsync(UpsertMasterOrderInput input)
|
||||
[HttpPost("Order/UpsertMasterOrder")]
|
||||
[VerifySignature]
|
||||
public async Task<IdBaseDto> 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<OrderEntity> 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<OrderFlightEntity>();
|
||||
|
||||
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)];
|
||||
|
||||
@ -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;
|
||||
/// <summary>
|
||||
/// 订单服务
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增、更新小包(存在就更新)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("Order/Upsert")]
|
||||
public async Task<IdBaseDto> 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<UpsertOrderInput, OrderEntity>(input);
|
||||
order.OrderFlights = ObjectMapper.Map<List<OrderFlightInfo>, List<OrderFlightEntity>>(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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量创建/新增 轨迹订单
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
[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<OrderEntity>();
|
||||
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<OrderEntity>(existsOrders.Count) : Empty<OrderEntity>.List;
|
||||
var updateRecords = existsOrders.Count > 0 ? new List<OrderOperationRecordIndex>(existsOrders.Count) : Empty<OrderOperationRecordIndex>.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<OrderFlightInfo>, List<OrderFlightEntity>>(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<OrderEntity>(insertInputs.Count);
|
||||
var createRecords = new List<OrderOperationRecordIndex>(insertInputs.Count);
|
||||
foreach (var insertInput in insertInputs)
|
||||
{
|
||||
var order = ObjectMapper.Map<UpsertOrderInput, OrderEntity>(insertInput);
|
||||
order.OrderFlights = ObjectMapper.Map<List<OrderFlightInfo>, List<OrderFlightEntity>>(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除订单
|
||||
/// </summary>
|
||||
@ -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<OrderFlightInfo>, List<OrderFlightEntity>>(input.FlightInfos);
|
||||
order.OrderPullStatus = BuiltOrderPullStatus(input.PullConfigInfos);
|
||||
order.SetThirdPartyOrderNumber(order.OrderPullStatus);
|
||||
await _orderRepository.UpdateOrderAsync(order);
|
||||
}
|
||||
|
||||
|
||||
private static List<OrderPullStatusEntity> BuiltOrderPullStatus(List<PullConfigInfo>? pullConfigInfos)
|
||||
{
|
||||
if (pullConfigInfos == null) return new List<OrderPullStatusEntity>();
|
||||
|
||||
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)
|
||||
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹内容服务
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "提供接口")]
|
||||
[Route("/ProviderService")]
|
||||
public class TrackContentService : ApplicationServiceBase
|
||||
{
|
||||
private readonly ITrackContentRepository _trackContentRepository;
|
||||
|
||||
public TrackContentService(ITrackContentRepository trackContentRepository)
|
||||
{
|
||||
_trackContentRepository = trackContentRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据应用获取轨迹内容列表
|
||||
/// </summary>
|
||||
/// <param name="AppKey"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("Track/GetTrackContents")]
|
||||
public async Task<List<TrackContentOutput>> GetTrackContentsAsync(string AppKey)
|
||||
{
|
||||
var res = await _trackContentRepository.GetTrackContentByAppKeyAsync(AppKey);
|
||||
var result = ObjectMapper.Map<List<TrackContentVO>, List<TrackContentOutput>>(res);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹刷新服务
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "提供接口")]
|
||||
[ApiExplorerSettings(GroupName = "OpenApi")]
|
||||
[Route("/ProviderService")]
|
||||
public class TrackPullService : ApplicationServiceBase
|
||||
{
|
||||
@ -39,20 +38,28 @@ public class TrackPullService : ApplicationServiceBase
|
||||
[HttpPost("Track/Refresh")]
|
||||
public async Task<bool> 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
|
||||
var forwardErrors = new List<string>();
|
||||
var submittedOrderCount = 0;
|
||||
|
||||
foreach (var group in orders.GroupBy(x => new
|
||||
{
|
||||
x.ServiceProviderCode,
|
||||
x.AppKey,
|
||||
@ -63,15 +70,13 @@ public class TrackPullService : ApplicationServiceBase
|
||||
{
|
||||
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,29 +84,21 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹刷新测试
|
||||
/// </summary>
|
||||
/// <param name="trackRefreshEventHandler"></param>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("Track/TrackRefreshEventTest")]
|
||||
#if DEBUG
|
||||
[AllowAnonymous]
|
||||
#endif
|
||||
public async Task<bool> TrackRefreshEventTest([FromServices] TrackRefreshEventHandler trackRefreshEventHandler,
|
||||
InterfaceRelayForwardReplayEvent input)
|
||||
if (submittedOrderCount == 0)
|
||||
{
|
||||
await trackRefreshEventHandler.ConsumerAsync(input);
|
||||
throw ProviderServiceActionResult.Exception("刷新失败:下游转发均未成功", string.Join(";", forwardErrors));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
/// <summary>
|
||||
/// 轨迹推送配置服务
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "提供接口")]
|
||||
[ApiExplorerSettings(GroupName = "OpenApi")]
|
||||
[Route("/ProviderService")]
|
||||
public class TrackPushConfigService : ApplicationServiceBase
|
||||
{
|
||||
|
||||
@ -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;
|
||||
/// <summary>
|
||||
/// 轨迹推送服务
|
||||
/// </summary>
|
||||
[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<string>
|
||||
var tracks = await _trackDetailsEsRepository.GetListAsync(input.AppKey, input.OrderType, input.OrderNos,
|
||||
new List<string>
|
||||
{
|
||||
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<string>();
|
||||
var skippedNoTrack = new List<string>();
|
||||
|
||||
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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
/// <summary>
|
||||
/// 轨迹服务
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "提供接口")]
|
||||
[ApiExplorerSettings(GroupName = "OpenApi")]
|
||||
[Route("/ProviderService")]
|
||||
public partial class TrackService : ApplicationServiceBase
|
||||
{
|
||||
@ -38,7 +38,6 @@ public partial class TrackService : ApplicationServiceBase
|
||||
/// 删除轨迹
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
[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<Dictionary<string, object>>(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
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据esid删除轨迹内容(FBA专用)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量删除轨迹
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
[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
|
||||
/// 新增、更新轨迹(存在就更新)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
[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<UpsertTrackInput, TrackUpsertEvent>(input);
|
||||
@event.AppKey = input.AppKey;
|
||||
@event.Customer = order.Customer;
|
||||
@ -273,14 +233,13 @@ public partial class TrackService : ApplicationServiceBase
|
||||
/// 批量新增、更新轨迹
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
[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<List<TrackDetailsIndex>> 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<List<TrackDetailsIndex>> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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<int>();
|
||||
var orderIdHitSceneIdsDict = new Dictionary<int, HashSet<int>>();
|
||||
var sceneBos = TransportWarningSceneBO.From(scenes);
|
||||
@ -246,7 +244,10 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
|
||||
var sceneSet = new HashSet<int>();
|
||||
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<KpiAreaInfo>();
|
||||
var noHitSceneOrderIds = new HashSet<int>();
|
||||
var reHitSceneOrderIds = new HashSet<int>();
|
||||
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<int, RuleAreaEntity> 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);
|
||||
|
||||
|
||||
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 场景唯一id (传0视为新增)
|
||||
/// </summary>
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 预警规则主体id
|
||||
/// </summary>
|
||||
public int WarningId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 货物类型(多个,分割)
|
||||
/// </summary>
|
||||
public string? CargoType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 月份(多个/分割)
|
||||
/// </summary>
|
||||
public string? Months { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域Id(单一)
|
||||
/// </summary>
|
||||
public int? AreaId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中转(单一)
|
||||
/// </summary>
|
||||
public bool? IsTransit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 起运港口(多个,分割)
|
||||
/// </summary>
|
||||
public string? LeavePort { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品编码(多个,分割)
|
||||
/// </summary>
|
||||
/// <summary>销售产品编码(单一)</summary>
|
||||
public string? ProductCode { get; }
|
||||
|
||||
public TransportWarningSceneBO(int id, int warningId, string? cargoType, string? months, int? areaId, bool? isTransit, string? leavePort, string? productCode)
|
||||
/// <summary>到达国家二字码(单一)</summary>
|
||||
public string? CountryCode { get; }
|
||||
|
||||
/// <summary>考核总时效(小时)</summary>
|
||||
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);
|
||||
ProductCode = string.IsNullOrWhiteSpace(productCode) ? null : productCode.Trim();
|
||||
CountryCode = NormalizeSingleCountryCode(countryCode);
|
||||
AssessmentTotalTimeSpan = assessmentTotalTimeSpan;
|
||||
}
|
||||
|
||||
AreaId = areaId;
|
||||
IsTransit = isTransit;
|
||||
LeavePort = leavePort;
|
||||
ProductCode = productCode;
|
||||
}
|
||||
|
||||
public bool CanHitScene(OrderEntity order, List<KpiAreaInfo>? ruleAreas)
|
||||
/// <summary>
|
||||
/// 命中:订单产品 = 配置产品;到达国家 = 配置国家(一对一)。
|
||||
/// </summary>
|
||||
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<int>()
|
||||
: 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<TransportWarningSceneBO> From(List<TransportWarningSceneEntity> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<TransportWarningSceneDto>.List;
|
||||
|
||||
var areaIds = entities
|
||||
.Select(x => x.AreaId)
|
||||
.Where(x => x > 0)
|
||||
.Select(x => x!.Value)
|
||||
.ToHashSet();
|
||||
|
||||
Dictionary<int, string>? 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<string>(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<string>(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<TransportWarningSceneBO>.List;
|
||||
|
||||
// 验证规则场景是否重复
|
||||
// 验证规则场景是否重复(同一方案下:产品+国家唯一)
|
||||
if (oldBos.Any(old => old.Equals(postBo) && old.Id != postBo.Id))
|
||||
{
|
||||
throw new BusinessException(message: "同一规则主体下,不允许出现因素完全相同的场景!");
|
||||
throw new BusinessException(message: "同一方案下,不允许出现产品与国家完全相同的配置!");
|
||||
}
|
||||
|
||||
// 更新或者新增
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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<InterfaceRelayUnifyResultDto>()!;
|
||||
}
|
||||
|
||||
private static string Truncate(string? text, int maxLen)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return string.Empty;
|
||||
return text.Length <= maxLen ? text : text[..maxLen] + "...";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
using Fake.Application.Dtos;
|
||||
using TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
namespace TrackCenter.Domain.Aggregates.KPIAreaAggregate;
|
||||
|
||||
public interface IKpiAreaRepository : IBasicRepository<RuleAreaEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建分区
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
ValueTask<int> CreateAreaAsync(RuleAreaEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// 添加分区信息
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
ValueTask<int> AddAreaInfoAsync(RuleAreaInfoEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区信息
|
||||
/// </summary>
|
||||
/// <param name="areaInfoId">分区信息id</param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteAreaInfo(int areaInfoId);
|
||||
|
||||
/// <summary>
|
||||
/// 删除分区
|
||||
/// </summary>
|
||||
/// <param name="areaId">分区id</param>
|
||||
/// <returns></returns>
|
||||
ValueTask DeleteArea(int areaId);
|
||||
|
||||
/// <summary>
|
||||
/// 更新区域信息
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> UpdateAreaInfoAsync(RuleAreaInfoEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// 获得分区
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<PagedResult<AreaDto>> GetAreas(QueryAreaRequest query);
|
||||
|
||||
/// <summary>
|
||||
/// 获取分区信息
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<PagedResult<RuleAreaInfoDto>> GetAreaInfos(QueryAreaInfoRequest query);
|
||||
|
||||
/// <summary>
|
||||
/// 获取分区信息通过id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<RuleAreaInfoEntity?> GetAreaInfoById(int id);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有区域信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<KpiAreaInfo>? GetAreaInfosByAreaId(int areaId);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有区域信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<Dictionary<int, List<KpiAreaInfo>>> GetAreaInfosDictByAreaId(HashSet<int> areaIds);
|
||||
}
|
||||
@ -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<int>, IHasCreateUserId<int>, IHasCreateTime, IHasUpdateUserId<int>, IHasUpdateTime
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "AppKey", ColumnDescription = "应用")]
|
||||
public string AppKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 分区名称KPI名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Name", Length = 64, ColumnDescription = "分区名称KPI名称")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")]
|
||||
public int CreateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateUserId", ColumnDescription = "更新人")]
|
||||
public int UpdateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间")]
|
||||
public DateTime UpdateTime { get; set; }
|
||||
|
||||
[SugarColumn(IsIgnore = true)] public List<RuleAreaInfoEntity> AreaInfos { get; set; }
|
||||
}
|
||||
@ -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<int>, IHasCreateUserId<int>, IHasCreateTime, IHasUpdateUserId<int>, IHasUpdateTime
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增主键
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "自增主键")]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// t_rule_area 表id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "AreaId", ColumnDescription = "t_rule_area 表id")]
|
||||
public int AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区二字码
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CountryCode", Length = 128, ColumnDescription = "国家或地区二字码")]
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CountryName", Length = 128, ColumnDescription = "国家或地区名称")]
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 州或者省份
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Province", Length = 128, IsNullable = true, ColumnDescription = "州或者省份")]
|
||||
public string Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "City", Length = 128, IsNullable = true, ColumnDescription = "城市")]
|
||||
public string City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始邮编
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "LeftPostcode", Length = 10, IsNullable = true, ColumnDescription = "起始邮编")]
|
||||
public string LeftPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 截止邮编
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "RightPostcode", Length = 10, IsNullable = true, ColumnDescription = "截止邮编")]
|
||||
public string RightPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateUserId", ColumnDescription = "创建人")]
|
||||
public int CreateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateUserId", IsNullable = false, ColumnDescription = "更新人")]
|
||||
public int UpdateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "UpdateTime", IsNullable = false, ColumnDescription = "更新时间")]
|
||||
public DateTime UpdateTime { get; set; }
|
||||
}
|
||||
@ -32,6 +32,11 @@ public interface IWarningRuleRepository : IBasicRepository<WarningRuleEntity>
|
||||
// 获取启用的预警规则主体
|
||||
Task<IReadOnlyDictionary<int, WarningRuleEntity>> GetWarningRuleForEnabledAsync(HashSet<(string AppKey, string Customer)> conditions, WarningRuleType warningType);
|
||||
|
||||
/// <summary>
|
||||
/// 按 AppKey 获取启用的预警规则主体(运输时效:方案级,不按客户)
|
||||
/// </summary>
|
||||
Task<IReadOnlyDictionary<int, WarningRuleEntity>> GetWarningRuleForEnabledByAppKeysAsync(HashSet<string> appKeys, WarningRuleType warningType);
|
||||
|
||||
// 获取预警规则主体根据id
|
||||
Task<WarningRuleEntity?> GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null);
|
||||
|
||||
|
||||
@ -54,9 +54,6 @@ public interface IWarningTaskRepository
|
||||
|
||||
#region 运输预警_预警明细生成任务
|
||||
|
||||
// 扫描预警明细生成任务
|
||||
Task<List<TransportWarningDetailsGenerateTaskEntity>> ScanTransportWarningDetailsGenerateTaskAsync();
|
||||
|
||||
// 条件获取运输预警明细生成任务
|
||||
Task<List<TransportWarningDetailsGenerateTaskEntity>> GetTransportWarningDetailsGenerateTaskAsync(Expression<Func<TransportWarningDetailsGenerateTaskEntity, bool>> expression);
|
||||
|
||||
|
||||
@ -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}小时" +
|
||||
|
||||
@ -3,47 +3,42 @@ using TrackCenter.Domain.SeedWork;
|
||||
|
||||
namespace TrackCenter.Domain.Aggregates.WarningAggregate.Transport;
|
||||
|
||||
// 运输时效场景
|
||||
[SugarTable("t_warning_transport_scene", TableDescription = "运输时效规则场景")]
|
||||
/// <summary>
|
||||
/// 运输时效场景:一销售产品 + 一到达国家 + 考核总时效。
|
||||
/// </summary>
|
||||
[SugarTable("t_warning_transport_scene", TableDescription = "运输时效场景(产品+国家+考核总时效)")]
|
||||
public sealed class TransportWarningSceneEntity : FullAuditedEntity<int, int>
|
||||
{
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<int, int>
|
||||
{
|
||||
[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")]
|
||||
|
||||
@ -14,8 +14,8 @@ public sealed class WarningRuleEntity : FullAuditedAggregateRoot<int, int>
|
||||
[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; } //冗余客户简称,用于查询
|
||||
|
||||
@ -32,11 +32,6 @@ public partial class GlobalConst
|
||||
/// </summary>
|
||||
public const string OrderPullStatusScanCheckKey = "OrderPullStatusScanCheck";
|
||||
|
||||
/// <summary>
|
||||
/// kpi区域redis key前缀
|
||||
/// </summary>
|
||||
public const string KpiAreaKeyPrefix = "KpiArea";
|
||||
|
||||
/// <summary>
|
||||
/// 城市时区redis key前缀
|
||||
/// </summary>
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class AddRuleAreaInfoRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 国家二字码,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 省份名称,可为空。
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市名称,可为空。
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 左侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? LeftPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 右侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? RightPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域ID,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int AreaId { get; set; }
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class AreaDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 区域id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用
|
||||
/// </summary>
|
||||
public string AppKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 系统名称
|
||||
/// </summary>
|
||||
public string SystemName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
public DateTime UpdateTime { get; set; }
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class RuleAreaInfoDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 分区信息id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 分区id
|
||||
/// </summary>
|
||||
public int AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区 二字码
|
||||
/// </summary>
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家名称
|
||||
/// </summary>
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家id
|
||||
/// </summary>
|
||||
public int CountryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 省份
|
||||
/// </summary>
|
||||
public string Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
public string City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始邮编
|
||||
/// </summary>
|
||||
public string LeftPostcode { get; set; }
|
||||
|
||||
public string RightPostcode { get; set; }
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class CreateRuleAreaRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统id
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string AppKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class KpiAreaInfo
|
||||
{
|
||||
public int AreaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家或地区
|
||||
/// </summary>
|
||||
public string CountryCode { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 省份
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始邮编
|
||||
/// </summary>
|
||||
public string? LeftPostcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 截止邮编
|
||||
/// </summary>
|
||||
public string? RightPostcode { get; set; }
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class QueryAreaInfoRequest : PageInfoInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 区域id
|
||||
/// </summary>
|
||||
public int AreaId { get; set; }
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class QueryAreaRequest : PageInfoInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 分区名称
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用
|
||||
/// </summary>
|
||||
public string? AppKey { get; set; }
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
/// <summary>
|
||||
/// 更新规则区域信息请求
|
||||
/// </summary>
|
||||
public class UpdateRuleAreaInfoRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 区域信息ID
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家二字码,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国家名称,必填项。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CountryName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 省份名称,可为空。
|
||||
/// </summary>
|
||||
public string? Province { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 城市名称,可为空。
|
||||
/// </summary>
|
||||
public string? City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 左侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? LeftPostCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 右侧邮政编码,可为空。
|
||||
/// </summary>
|
||||
public string? RightPostCode { get; set; }
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace TrackCenter.Domain.Dtos.Forewarning;
|
||||
|
||||
public class UpdateRuleAreaRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
namespace TrackCenter.Repository.Repositories;
|
||||
|
||||
public class KpiAreaInfoRepository
|
||||
{
|
||||
}
|
||||
@ -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<RuleAreaEntity>, IKpiAreaRepository, ITransientDependency
|
||||
{
|
||||
public async ValueTask<int> CreateAreaAsync(RuleAreaEntity entity)
|
||||
{
|
||||
return await InsertReturnIdentityAsync(entity);
|
||||
}
|
||||
|
||||
public async ValueTask<int> 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<RuleAreaInfoEntity>().Where(x => x.AreaId == areaId).ExecuteCommandAsync();
|
||||
transaction.Complete();
|
||||
}
|
||||
|
||||
public Task<int> DeleteAreaInfo(int areaInfoId)
|
||||
{
|
||||
return Context.Deleteable<RuleAreaInfoEntity>().Where(val => val.Id == areaInfoId).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public Task<int> UpdateAreaInfoAsync(RuleAreaInfoEntity entity)
|
||||
{
|
||||
return Context.Updateable(entity).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取分区
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<PagedResult<AreaDto>> GetAreas(QueryAreaRequest query)
|
||||
{
|
||||
return await Context.Queryable<RuleAreaEntity>()
|
||||
.WhereIF(!string.IsNullOrEmpty(query.Name), area => area.Name.Contains(query.Name!))
|
||||
.OrderBy(area => area.UpdateTime, OrderByType.Desc)
|
||||
.Select<AreaDto>(area => new AreaDto()
|
||||
{
|
||||
Id = area.Id,
|
||||
Name = area.Name,
|
||||
AppKey = area.AppKey,
|
||||
UpdateTime = area.CreateTime
|
||||
})
|
||||
.ToPagedListAsync(query.PageIndex, query.PageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得分区信息
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<PagedResult<RuleAreaInfoDto>> GetAreaInfos(QueryAreaInfoRequest query)
|
||||
{
|
||||
return await Context.Queryable<RuleAreaInfoEntity>()
|
||||
//.InnerJoin<VCountryEntity>((x, c) => x.CountryCode == c.Digit2Code)
|
||||
.Where(x => x.AreaId == query.AreaId)
|
||||
.OrderBy(x => x.UpdateTime, OrderByType.Desc)
|
||||
.Select<RuleAreaInfoDto>((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<RuleAreaInfoEntity?> GetAreaInfoById(int id)
|
||||
{
|
||||
return Context.Queryable<RuleAreaInfoEntity>()
|
||||
.FirstAsync(val => val.Id == id)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有区域信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<KpiAreaInfo> GetAreaInfosByAreaId(int areaId)
|
||||
{
|
||||
return Context.Queryable<RuleAreaInfoEntity>()
|
||||
.InnerJoin<RuleAreaEntity>((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<Dictionary<int, List<KpiAreaInfo>>> GetAreaInfosDictByAreaId(HashSet<int> areaIds)
|
||||
{
|
||||
var list = await Context.Queryable<RuleAreaInfoEntity>()
|
||||
.InnerJoin<RuleAreaEntity>((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());
|
||||
}
|
||||
}
|
||||
@ -15,12 +15,16 @@ public class WarningRuleRepository : BasicRepository<WarningRuleEntity>, IWarnin
|
||||
|
||||
public async Task<bool> CheckNameDuplicateAsync(int id, string appKey, string customer, string name, WarningRuleType warningType)
|
||||
{
|
||||
var entity = await Context.Queryable<WarningRuleEntity>()
|
||||
// 运输时效:AppKey + Name + Type(方案级,不按客户)
|
||||
var query = Context.Queryable<WarningRuleEntity>()
|
||||
.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<WarningRuleEntity>, IWarnin
|
||||
|
||||
public async Task<bool> CheckEnableDuplicateAsync(int warningId, string appKey, string customer, WarningRuleType warningType)
|
||||
{
|
||||
var entity = await Context.Queryable<WarningRuleEntity>()
|
||||
// 运输时效:同一 AppKey + 类型仅允许一个启用方案
|
||||
var query = Context.Queryable<WarningRuleEntity>()
|
||||
.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<WarningRuleEntity>, IWarnin
|
||||
: list.ToDictionary(x => x.Id);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyDictionary<int, WarningRuleEntity>> GetWarningRuleForEnabledByAppKeysAsync(HashSet<string> appKeys, WarningRuleType warningType)
|
||||
{
|
||||
if (appKeys.Count == 0)
|
||||
return Empty<int, WarningRuleEntity>.Dict;
|
||||
|
||||
var list = await Context.Queryable<WarningRuleEntity>()
|
||||
.Where(x => x.WarningType == (int) warningType
|
||||
&& x.IsEnabled
|
||||
&& appKeys.Contains(x.AppKey))
|
||||
.ToListAsync();
|
||||
|
||||
return list.Count == 0
|
||||
? Empty<int, WarningRuleEntity>.Dict
|
||||
: list.ToDictionary(x => x.Id);
|
||||
}
|
||||
|
||||
public async Task<WarningRuleEntity?> GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null)
|
||||
{
|
||||
var result = await Context.Queryable<WarningRuleEntity>()
|
||||
|
||||
@ -110,13 +110,6 @@ public class WarningTaskRepository : BasicRepository<WarningNotifyTaskEntity>, I
|
||||
|
||||
#region 运输预警_预警明细生成任务
|
||||
|
||||
public Task<List<TransportWarningDetailsGenerateTaskEntity>> ScanTransportWarningDetailsGenerateTaskAsync()
|
||||
{
|
||||
return Context.Queryable<TransportWarningDetailsGenerateTaskEntity>()
|
||||
.Where(x => DateTime.Now >= x.PlanExecutionTime && x.IsClosed == false)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public Task<List<TransportWarningDetailsGenerateTaskEntity>> GetTransportWarningDetailsGenerateTaskAsync(Expression<Func<TransportWarningDetailsGenerateTaskEntity, bool>> expression)
|
||||
{
|
||||
return Context.Queryable<TransportWarningDetailsGenerateTaskEntity>()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user