This commit is contained in:
xiaolipro 2025-07-30 22:00:02 +08:00
parent c66fe8b7dc
commit 1683aba0c5
72 changed files with 1111 additions and 1131 deletions

View File

@ -8,12 +8,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrackCenter.Application", "
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DAC93270-5A25-40E5-B851-E7CA398DBC31}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0E2537C3-6EF3-4085-9BD2-CF923E53F82A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrackCenter.Repository", "src\TrackCenter.Repository\TrackCenter.Repository.csproj", "{D9CF5706-1972-414F-821E-AA32C13B8570}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SJZY.Track.Test", "test\SJZY.Track.Test\SJZY.Track.Test.csproj", "{3A53539C-4A85-4E5D-A071-7B4B193AB0E0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrackCenter.Domain", "src\TrackCenter.Domain\TrackCenter.Domain.csproj", "{073EB297-E752-49AC-BED8-B4AEBAB518F6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution items", "solution items", "{086C7812-46F9-49FD-B490-A769E2C0729E}"
@ -43,10 +39,6 @@ Global
{D9CF5706-1972-414F-821E-AA32C13B8570}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9CF5706-1972-414F-821E-AA32C13B8570}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9CF5706-1972-414F-821E-AA32C13B8570}.Release|Any CPU.Build.0 = Release|Any CPU
{3A53539C-4A85-4E5D-A071-7B4B193AB0E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A53539C-4A85-4E5D-A071-7B4B193AB0E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A53539C-4A85-4E5D-A071-7B4B193AB0E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A53539C-4A85-4E5D-A071-7B4B193AB0E0}.Release|Any CPU.Build.0 = Release|Any CPU
{073EB297-E752-49AC-BED8-B4AEBAB518F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{073EB297-E752-49AC-BED8-B4AEBAB518F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{073EB297-E752-49AC-BED8-B4AEBAB518F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -59,7 +51,6 @@ Global
{5F1A9619-A5A3-4F5F-AEFF-880707A251E1} = {DAC93270-5A25-40E5-B851-E7CA398DBC31}
{E052AEB3-A86F-431E-A685-769D3E59CB8A} = {DAC93270-5A25-40E5-B851-E7CA398DBC31}
{D9CF5706-1972-414F-821E-AA32C13B8570} = {DAC93270-5A25-40E5-B851-E7CA398DBC31}
{3A53539C-4A85-4E5D-A071-7B4B193AB0E0} = {0E2537C3-6EF3-4085-9BD2-CF923E53F82A}
{073EB297-E752-49AC-BED8-B4AEBAB518F6} = {DAC93270-5A25-40E5-B851-E7CA398DBC31}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

View File

@ -19,7 +19,7 @@
<ItemGroup>
<ProjectReference Include="..\TrackCenter.Application\TrackCenter.Application.csproj" />
<ProjectReference Include="..\TrackCenter.Repository\TrackCenter.Repository.csproj" />
<PackageReference Include="Fake.Autofac" Version="8.0.0-preview11" />
<PackageReference Include="Fake.Autofac" Version="8.0.0-preview12" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />

View File

@ -1,5 +1,4 @@
using DotNetCore.CAP;
using Fake.DependencyInjection;
using TrackCenter.Application.Events;
using TrackCenter.Application.Services.Warning.Handlers;
using TrackCenter.Domain.Aggregates.OrderAggregate;

View File

@ -1,7 +1,5 @@
using System.Collections.Immutable;
using DotNetCore.CAP;
using Fake.Application;
using Fake.DependencyInjection;
using InterfaceForward.Domain.Shared.Helpers;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Application.Events;

View File

@ -1,8 +1,6 @@
using System.Collections.Immutable;
using System.Text.Json;
using DotNetCore.CAP;
using Fake.Application;
using Fake.DependencyInjection;
using InterfaceForward.Domain.Shared.Helpers;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
@ -59,7 +57,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
if (pushConfigs == null || pushConfigs.Count == 0) return;
if (pushConfigs.First().ServiceProviderCode.IsNullOrWhiteSpace())
{
throw new CapNoRetryException($"该客户{@event.CustomId}未关联服务商,无法推送");
throw new BusinessException(GlobalConst.CapNoRetryErrorCode, $"该客户{@event.CustomId}未关联服务商,无法推送");
}
_pushConfigs = pushConfigs;
@ -70,7 +68,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
.ToDictionary(x => x.Key, y => y.OrderBy(item => item.Sort).ToList());
// 查找所有订单
var orderIds = @event.OrderIdEsIdVersionDict.Keys.ToHashSet();
var orderIds = @event.OrderIdTrackMetadataDict.Keys.ToHashSet();
var orders = await _orderRepository.GetListAsync(x => orderIds.Contains(x.Id), x =>
new OrderEntity
{
@ -130,20 +128,20 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
{
var asciiString = Base64Helper.SerializeObject(OptionType.新增.ToString());
var serviceProviderCode = _pushConfigs.First().ServiceProviderCode;
// var r = await _httpClientProxy.Service.BatchForwardThenPushToQueueAsync(
// _options.CurrentValue.PushTrackInterfaceCode,
// serviceProviderCode,
// items.Cast<object>().ToList(),
// GlobalConst.TrackPushCompletedSubscribe,
// attach: asciiString);
//
// if (r.Code != GlobalConst.SuccessCode)
// {
// TrackPushFailedPublicTopic($"轨迹中台调用接口通失败,RequestId: {r.RequestId}; Msg: {r.Msg}", @event, r.RequestId,
// items.Cast<dynamic>().ToList());
// throw new BusinessException(
// message: $"requestId: {r.RequestId}; serviceProviderCode:{serviceProviderCode} Msg: {r.Msg}");
// }
var r = await _httpClientProxy.Service.BatchForwardThenPushToQueueAsync(
_options.CurrentValue.PushTrackInterfaceCode,
serviceProviderCode,
items.Cast<object>().ToList(),
GlobalConst.TrackPushCompletedSubscribe,
attach: asciiString);
if (r.Code != GlobalConst.SuccessCode)
{
TrackPushFailedPublicTopic($"轨迹中台调用接口通失败,RequestId: {r.RequestId}; Msg: {r.Msg}", @event, r.RequestId,
items.Cast<dynamic>().ToList());
throw new BusinessException(
message: $"requestId: {r.RequestId}; serviceProviderCode:{serviceProviderCode} Msg: {r.Msg}");
}
}
}
@ -179,7 +177,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
return;
}
var esIdVersion = @event.OrderIdEsIdVersionDict[order.Id];
var trackMetadata = @event.OrderIdTrackMetadataDict[order.Id];
var log = new TrackPushRecordIndex
{
// 订单信息
@ -188,8 +186,9 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
SubSystemCode = @event.SubSystemCode,
// 轨迹信息
TrackEsId = esIdVersion.Key,
TrackVersion = esIdVersion.Value,
TrackEsId = trackMetadata.EsId,
TrackVersion = trackMetadata.Version,
TrackCreateTime = trackMetadata.TrackCreateTime,
TrackStatusId = @event.SystemPushInfo.TrackStatusId,
TrackStatus = @event.SystemPushInfo.TrackStatus,
TrackContentId = @event.SystemPushInfo.TrackContentId,
@ -200,6 +199,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
TrackTimeZone = @event.SystemPushInfo.TrackTimeZone,
CompareFields = @event.SystemPushInfo.CompareFields,
Extra = @event.SystemPushInfo.Extra,
ServiceProviderCode = config.ServiceProviderCode,
// 操作信息
OptionType = @event.OptionType.ToString(),
@ -258,7 +258,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
continue;
}
var esIdVersion = @event.OrderIdEsIdVersionDict[order.Id];
var trackMetadata = @event.OrderIdTrackMetadataDict[order.Id];
var log = new TrackPushRecordIndex
{
//订单信息
@ -267,8 +267,9 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
SubSystemCode = @event.SubSystemCode,
//轨迹信息
TrackEsId = esIdVersion.Key,
TrackVersion = esIdVersion.Value,
TrackEsId = trackMetadata.EsId,
TrackVersion = trackMetadata.Version,
TrackCreateTime = trackMetadata.TrackCreateTime,
TrackStatusId = @event.SystemPushInfo.TrackStatusId,
TrackStatus = @event.SystemPushInfo.TrackStatus,
TrackContentId = @event.SystemPushInfo.TrackContentId,
@ -279,6 +280,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
TrackTimeZone = @event.SystemPushInfo.TrackTimeZone,
CompareFields = @event.SystemPushInfo.CompareFields,
Extra = @event.SystemPushInfo.Extra,
ServiceProviderCode = config.ServiceProviderCode,
//操作信息
OptionType = @event.OptionType.ToString(),
@ -314,7 +316,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
continue;
}
var esIdVersion = @event.OrderIdEsIdVersionDict[order.Id];
var trackMetadata = @event.OrderIdTrackMetadataDict[order.Id];
var log = new TrackPushRecordIndex
{
// 订单信息
@ -323,8 +325,9 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
SubSystemCode = @event.SubSystemCode,
// 轨迹信息
TrackEsId = esIdVersion.Key,
TrackVersion = esIdVersion.Value,
TrackEsId = trackMetadata.EsId,
TrackVersion = trackMetadata.Version,
TrackCreateTime = trackMetadata.TrackCreateTime,
TrackStatusId = @event.SystemPushInfo.TrackStatusId,
TrackStatus = @event.SystemPushInfo.TrackStatus,
TrackContentId = @event.SystemPushInfo.TrackContentId,
@ -335,6 +338,7 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
TrackTimeZone = @event.SystemPushInfo.TrackTimeZone,
CompareFields = @event.SystemPushInfo.CompareFields,
Extra = @event.SystemPushInfo.Extra,
ServiceProviderCode = config.ServiceProviderCode,
//操作信息
OptionType = @event.OptionType.ToString(),
@ -352,20 +356,20 @@ public class TrackPushEventHandler : ITransientDependency, ICapSubscribe
{
var asciiString = Base64Helper.SerializeObject(OptionType.修改.ToString());
var serviceProviderCode = _pushConfigs.First().ServiceProviderCode;
// var r = await _httpClientProxy.Service.BatchForwardThenPushToQueueAsync(
// _options.CurrentValue.TrackUpdateInterfaceCode,
// serviceProviderCode,
// items.Cast<object>().ToList(),
// GlobalConst.TrackPushCompletedSubscribe,
// attach: asciiString);
//
// if (r.Code != GlobalConst.SuccessCode)
// {
// TrackPushFailedPublicTopic($"轨迹中台调用接口通失败,RequestId: {r.RequestId}; Msg: {r.Msg}", @event, r.RequestId,
// items.Cast<dynamic>().ToList());
// throw new BusinessException(
// message: $"requestId: {r.RequestId}; serviceProviderCode:{serviceProviderCode} Msg: {r.Msg}");
// }
var r = await _httpClientProxy.Service.BatchForwardThenPushToQueueAsync(
_options.CurrentValue.TrackUpdateInterfaceCode,
serviceProviderCode,
items.Cast<object>().ToList(),
GlobalConst.TrackPushCompletedSubscribe,
attach: asciiString);
if (r.Code != GlobalConst.SuccessCode)
{
TrackPushFailedPublicTopic($"轨迹中台调用接口通失败,RequestId: {r.RequestId}; Msg: {r.Msg}", @event, r.RequestId,
items.Cast<dynamic>().ToList());
throw new BusinessException(
message: $"requestId: {r.RequestId}; serviceProviderCode:{serviceProviderCode} Msg: {r.Msg}");
}
}
}

View File

@ -1,5 +1,4 @@
using DotNetCore.CAP;
using Fake.DependencyInjection;
using InterfaceForward.Domain.Shared.Helpers;
using Microsoft.Extensions.Logging;
using TrackCenter.Application.Events;
@ -58,11 +57,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
var canNextOrderIds = new List<int>();
var content = await HandleTrackAsync(@event, canNextOrderIds);
// 处理广播事件
if (content != null && (content.IsToMq || @event.IsToMq))
_ = EnsureWriteAfterPublicTopicAsync(@event, content.IsExternal, content.SubSystemCode, content.TrackStatusCode);
await HandleTrackAsync(@event, canNextOrderIds);
if (canNextOrderIds.Count == 0) return;
// 根据订单关联关系异步向下刷(异步)
@ -147,32 +142,46 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
return true;
}
private async Task<TrackContentVO?> HandleTrackAsync(TrackUpsertEvent @event, List<int> canNextOrderIds)
private async Task HandleTrackAsync(TrackUpsertEvent @event, List<int> canNextOrderIds)
{
//#7001:未来潜在需求,当轨迹内容匹配预设,记录一个标记,保证下刷过程都是使用预设内容
var trackContent = await _provideQueries.GetTrackContentAsync(@event.SubSystemId, @event.TrackCode);
if (trackContent == null)
{
// 找不到轨迹内容,直接结束
LogHelper.Warn($"找不到轨迹内容:{@event}", true);
return null;
return;
}
//#7001:未来潜在需求,当轨迹内容匹配预设,记录一个标记,保证下刷过程都是使用预设内容
//虽然在引发事件之前做了判断,引发之后并不一定立即处理,在此过程中轨迹内容仍然可能被修改,所以要二次进行判断。
//通过来源指示是否为手动操作
// 根据轨迹内容标识,确定操作权限
var manualOperation = @event.Source == (int) TrackSourceEnum.Artificial;
//轨迹内容标识不允许手动创建的
if (manualOperation && @event.IsAdd && trackContent.IsAllowManuallyCreate == false) return trackContent;
//轨迹内容标识不允许手动更新的
if (manualOperation && !@event.IsAdd && trackContent.IsAllowManuallyUpdate == false) return trackContent;
// 不允许手动创建
if (manualOperation && @event.IsAdd && trackContent.IsAllowManuallyCreate == false)
{
PublicTopicAllFailed(@event, trackContent, "轨迹内容标识不允许手动创建");
return;
}
// k:[orderId] v:[k:EsId,v:Version]
var orderIdEsIdVersionDict = new Dictionary<int, KeyValuePair<string, long>>();
// 不允许手动更新
if (manualOperation && !@event.IsAdd && trackContent.IsAllowManuallyUpdate == false)
{
PublicTopicAllFailed(@event, trackContent, "轨迹标识内容不允许手动更新");
return;
}
var insertTracks = await UpsertTrackAsync(@event, canNextOrderIds, trackContent, orderIdEsIdVersionDict);
// k:[orderId] v:{EsId,Version,TrackCreateTime}
var orderIdTrackMetadataDict = new Dictionary<int, TrackMetadata>();
if (canNextOrderIds.Count == 0) return trackContent;
var trackChangeEtos = new List<TrackChangeEto>();
var insertTracks = await UpsertTrackAsync(@event, canNextOrderIds, trackContent, orderIdTrackMetadataDict, trackChangeEtos);
if (canNextOrderIds.Count == 0)
{
await TrackChangePublicTopicAsync(trackContent, trackChangeEtos);
return;
}
if (insertTracks.Any())
{
@ -180,7 +189,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
await _trackDetailsEsRepository.AddManyAsync(insertTracks);
// es新增后填充版本号字典
insertTracks.ForEach(t => orderIdEsIdVersionDict.Add(t.OrderId, KeyValuePair.Create(t.Id, t.Version)));
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
@ -193,20 +202,24 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
await _orderRepository.UpdatePullStatusToSilenceAsync(insertTracks.Select(x => x.OrderId).ToList(),
@event.TrackCode);
// todo:引发预警计算 改MQ批量+分流
WarningCalc_AddedTrack(insertTracks);
// 发布预警信号,稍后将进行预警计算
_warningSignalPublisher.PublishSignal(WarningSignalType.轨迹新增, insertTracks);
// 添加轨迹触发预警计算(异步)todo:弃用
// await _capPublisher.PublishAsync(GlobalConst.TrackWarningCalcSubscribe, new TrackWarningCalcEvent
// {
// SubSystemId = @event.SubSystemId,
// CustomId = @event.CustomId,
// OrderIds = insertTracks.Select(x => x.OrderId).ToArray(),
// TrackStatusId = trackContent.TrackStatusId,
// TrackTime = @event.TrackTime,
// });
// 如果存在轨迹标记,则触发订单pod拉取
if (!string.IsNullOrWhiteSpace(trackContent.TrackFlagId))
{
await _capPublisher.PublishAsync(GlobalConst.OrderPodPullSubscribe, new OrderPodPullEvent
{
SubSystemCode = trackContent.SubSystemCode,
OrderIdEsIdDict = insertTracks.ToDictionary(x => x.OrderId, y => y.Id),
TrackFlagIds = trackContent.TrackFlagId.Split(',').Select(int.Parse).ToHashSet()
});
}
}
// 触发轨迹变更广播
await TrackChangePublicTopicAsync(trackContent, trackChangeEtos, true);
// 触发订单主表更新事件(异步)
await _capPublisher.PublishAsync(GlobalConst.TrackChangeAfterUpdateOrderSubscribe,
new TrackChangeAfterUpdateOrderEvent
@ -231,7 +244,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
SubSystemId = @event.SubSystemId,
SubSystemCode = trackContent.SubSystemCode,
CustomId = @event.CustomId,
OrderIdEsIdVersionDict = orderIdEsIdVersionDict,
OrderIdTrackMetadataDict = orderIdTrackMetadataDict,
TrackCode = @event.TrackCode,
SystemPushInfo = new SystemPushStruct
{
@ -249,15 +262,14 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
IsToMq = (trackContent.IsToMq || @event.IsToMq)
};
await _capPublisher.PublishAsync(GlobalConst.TrackPushSubscribe, trackPushEvent);
return trackContent;
}
private async Task<List<TrackDetailsIndex>> UpsertTrackAsync(
TrackUpsertEvent @event,
List<int> canNextOrderIds,
TrackContentVO trackContent,
Dictionary<int, KeyValuePair<string, long>> orderIdEsIdVersionDict
Dictionary<int, TrackMetadata> orderIdTrackMetadataDict,
List<TrackChangeEto> trackChangeEtos
)
{
var orderIds = @event.OrderIdentifiers.Select(x => x.OrderId).ToArray();
@ -280,7 +292,14 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
var trackById = tracks.FirstOrDefault(x => x.Id == @event.EsId);
if (trackById != null)
{
updateTracks.Add(BuiltUpdateTrack(@event, trackById, order, trackContent));
var updateTrack = BuiltUpdateTrack(@event, trackById, order, trackContent);
updateTracks.Add(updateTrack);
AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, updateTrack.Id);
}
else
{
var message = $"没有通过传入的EsId:[{@event.EsId}]查询到轨迹信息,更新失败";
AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message);
}
continue;
@ -327,36 +346,42 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
{
if (existTracks.Count > 0)
{
_logger.LogInformation("订单已经存在不允许重复的轨迹,不允许重复添加:{@event}", @event);
traceLog.Add($"OrderNo-{order.OrderNo}", "订单已经存在不允许重复的轨迹,不允许重复添加");
const string message = "订单已经存在不允许重复的轨迹,不允许重复添加";
traceLog.Add($"OrderNo-{order.OrderNo}", message);
AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message);
continue;
}
insertTracks.Add(BuiltNewTrack(@event, order, trackContent));
var addTrack = BuiltNewTrack(@event, order, trackContent);
insertTracks.Add(addTrack);
AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, addTrack.Id);
}
else
{
// 不存在不让改
if (existTracks.Count == 0)
{
_logger.LogInformation("订单不存在轨迹,不允许修改:{@event}", @event);
traceLog.Add($"OrderNo-{order.OrderNo}", "订单不存在轨迹,不允许修改");
const string message = "订单不存在轨迹,不允许修改";
traceLog.Add($"OrderNo-{order.OrderNo}", message);
AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message);
continue;
}
if (existTracks.Count > 1)
{
_logger.LogInformation("订单存在多条轨迹不允许修改:{@event}", @event);
traceLog.Add($"OrderNo-{order.OrderNo}", "订单存在多条轨迹不允许修改");
const string message = "订单存在多条轨迹不允许修改";
traceLog.Add($"OrderNo-{order.OrderNo}", message);
AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, null, message);
continue;
}
var track = existTracks.First();
updateTracks.Add(BuiltUpdateTrack(@event, track, order, trackContent));
var updateTrack = BuiltUpdateTrack(@event, track, order, trackContent);
updateTracks.Add(updateTrack);
operationRecords.Add(BuildOrderOperationRecord(@event, track, order));
AddTrackChangeEto(trackChangeEtos, @event, trackContent, order.OrderNo, updateTrack.Id);
}
canNextOrderIds.Add(order.OrderId);
}
@ -366,23 +391,21 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
{
// 批量差异化更新轨迹
await _trackDetailsEsRepository.BulkUpdateAsync(updateTracks);
// 获取更新后的版本号
updateTracks.ForEach(x => orderIdEsIdVersionDict.Add(x.OrderId, KeyValuePair.Create(x.Id, x.Version)));
// 填充轨迹元数据
updateTracks.ForEach(x => orderIdTrackMetadataDict.Add(x.OrderId, TrackMetadata.Build(x.Id, x.Version, x.CreateTime)));
// 批量写入操作日志
_ = _orderOperationRecordEsRepository.AddManyAsync(operationRecords);
// todo:引发预警计算 改MQ批量+分流
WarningCalc_UpdatedTrack(updateTracks);
// 发布预警信号稍后将进行预警计算
_warningSignalPublisher.PublishSignal(WarningSignalType.轨迹更新, tracks);
}
return insertTracks;
}
private static TrackDetailsIndex BuiltUpdateTrack(TrackUpsertEvent @event, TrackDetailsIndex track, OrderIdentifier order,
TrackContentVO trackContent)
private static TrackDetailsIndex BuiltUpdateTrack(TrackUpsertEvent @event, TrackDetailsIndex track, OrderIdentifier order, TrackContentVO trackContent)
{
//外部轨迹,轨迹发生地,轨迹发生时间,轨迹发生时区,不允许更改
// 非外部轨迹才允许修改 【发生地】【时间】【时区】
if (track.Source != (int) TrackSourceEnum.ExternalServiceProvide)
{
track.TrackPlace = @event.TrackPlace;
@ -390,24 +413,11 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
track.TrackTimeZone = @event.TrackTimeZone;
}
track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
? track.TrackTime
: track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone));
track.UpdateUser = @event.OperationMan ?? GlobalConst.SystemUser;
track.UpdateTime = DateTime.Now;
//允许二次修改,在修改了轨迹内容后进行修改轨迹操作,如果输入的轨迹内容则以输入为准
//跨子系统?
//允许二次修改轨迹内容
track.TrackContent = string.IsNullOrWhiteSpace(@event.Content) ? trackContent.TrackContent : @event.Content;
track.TrackEnContent = string.IsNullOrWhiteSpace(@event.EnStatus) ? trackContent.TrackEnContent : @event.EnStatus;
//当传入的IsExternal为空,则使用轨迹内容中的IsExternal
track.IsExternal = @event.IsExternal ?? trackContent.IsExternal;
track.Remarks = @event.Remarks ?? GlobalConst.EmptyString;
track.Files = @event.Files?.Select(x => new FileItem
{
OssKey = x.OssKey,
FileName = x.FileName
}).ToList();
// 轨迹发生地的更多信息
track.TrackPlaceCity = @event.TrackPlaceCity ?? GlobalConst.EmptyString;
track.TrackPlaceCityId = @event.TrackPlaceCityId ?? GlobalConst.NotExistId;
track.TrackPlaceProvince = @event.TrackPlaceProvince ?? GlobalConst.EmptyString;
@ -415,9 +425,31 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
track.TrackPlaceNation = @event.TrackPlaceNation ?? GlobalConst.EmptyString;
track.TrackPlaceNationId = @event.TrackPlaceNationId ?? GlobalConst.NotExistId;
track.TrackPlacePostcode = @event.TrackPlacePostcode ?? GlobalConst.EmptyString;
// 签收地时区,签收地时间
track.DestinationTimeZone = @event.DestinationTimeZone;
track.EstimatedDeliveryDate = @event.EstimatedDeliveryDate;
// 转换为utc0的时间
track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
? track.TrackTime
: track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone));
// 轨迹标记ids,是否对外,备注,扩展信息,附件
track.TrackFlagIds = trackContent.TrackFlagId;
track.IsExternal = @event.IsExternal ?? trackContent.IsExternal;
track.Remarks = @event.Remarks ?? GlobalConst.EmptyString;
track.Extra = @event.NewExtra ?? @event.Extra;
track.Files = @event.Files?.Select(x => new FileItem
{
OssKey = x.OssKey,
FileName = x.FileName
}).ToList();
// 审计属性
track.UpdateUser = @event.OperationMan ?? GlobalConst.SystemUser;
track.UpdateTime = DateTime.Now;
return track;
}
@ -426,36 +458,30 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
{
var track = new TrackDetailsIndex
{
// 订单信息
SubSystemId = @event.SubSystemId,
OrderId = order.OrderId,
OrderNo = order.OrderNo,
OrderType = order.OrderType,
// 轨迹信息
TrackCode = @event.TrackCode,
TrackPlace = @event.TrackPlace,
TrackTime = @event.NewTrackTime ?? @event.TrackTime,
TrackTimeZone = @event.TrackTimeZone,
ServiceProviderCode = @event.TrackRefreshData.ServiceProviderCode ?? GlobalConst.EmptyString,
OriginTrackContent = @event.TrackRefreshData.OriginTrackContent ?? GlobalConst.EmptyString,
OriginTrackDataHash = @event.TrackRefreshData.OriginTrackDataHash ?? GlobalConst.EmptyString,
CreateUser = @event.OperationMan ?? GlobalConst.SystemUser,
CreateTime = DateTime.Now,
//来源轨迹内容,第一次新增的时候使用轨迹内容表提供的
TrackStatusId = trackContent.TrackStatusId,
TrackStatus = trackContent.TrackStatus,
TrackStatusCode = trackContent.TrackStatusCode,
//跨子系统?
TrackContentId = trackContent.TrackContentId,
TrackContent = string.IsNullOrWhiteSpace(@event.Content) ? trackContent.TrackContent : @event.Content,
TrackEnContent = string.IsNullOrWhiteSpace(@event.EnStatus) ? trackContent.TrackEnContent : @event.EnStatus,
//当传入的IsExternal为空,则使用轨迹内容中的IsExternal
IsExternal = @event.IsExternal ?? trackContent.IsExternal,
Source = @event.Source,
Remarks = @event.Remarks ?? GlobalConst.EmptyString,
Files = @event.Files?.Select(x => new FileItem
{
OssKey = x.OssKey,
FileName = x.FileName
}).ToList(),
TrackStatusId = trackContent.TrackStatusId,
TrackStatus = trackContent.TrackStatus,
TrackStatusCode = trackContent.TrackStatusCode,
TrackTime = @event.NewTrackTime ?? @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,
@ -465,9 +491,25 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
TrackPlacePostcode = @event.TrackPlacePostcode ?? GlobalConst.EmptyString,
DestinationTimeZone = @event.DestinationTimeZone,
EstimatedDeliveryDate = @event.EstimatedDeliveryDate,
// 轨迹标记ids,轨迹来源,是否对外,备注,扩展信息,附件
TrackFlagIds = trackContent.TrackFlagId,
Source = @event.Source,
IsExternal = @event.IsExternal ?? trackContent.IsExternal,
Remarks = @event.Remarks ?? GlobalConst.EmptyString,
Extra = @event.NewExtra ?? @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的时间
track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
? track.TrackTime
: track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone));
@ -489,26 +531,72 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
};
}
private async Task EnsureWriteAfterPublicTopicAsync(TrackUpsertEvent @event, bool contentIsExternal, string subSystemCode, string statusCode)
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:采用一个较大延时,不查询直接发
await Task.Delay(1000);
_broadcastingService.TrackChangePublicTopic(@event, contentIsExternal, subSystemCode, statusCode);
if (isDelay)
await Task.Delay(1000);
foreach (var eto in list)
_broadcastingService.TrackChangePublicTopic(eto, trackContent.SubSystemCode);
}
private void WarningCalc_AddedTrack(List<TrackDetailsIndex> tracks)
private void PublicTopicAllFailed(TrackUpsertEvent @event, TrackContentVO trackContent, string failedReason)
{
_warningSignalPublisher.PublishSignal(WarningSignalType.轨迹新增, tracks);
if (trackContent.IsToMq == false && @event.IsToMq == false) return;
foreach (var orderNo in @event.OrderIdentifiers.Select(x => x.OrderNo))
{
var eto = BuildEto(@event, trackContent, orderNo, null, failedReason);
_broadcastingService.TrackChangePublicTopic(eto, trackContent.SubSystemCode);
}
}
private void WarningCalc_UpdatedTrack(List<TrackDetailsIndex> tracks)
private static void AddTrackChangeEto(List<TrackChangeEto> list, TrackUpsertEvent @event, TrackContentVO trackContent, string orderNo, string? trackEsId = null, string? failedReason = null)
{
_warningSignalPublisher.PublishSignal(WarningSignalType.轨迹更新, tracks);
if (trackContent.IsToMq == false && @event.IsToMq == false) return;
list.Add(BuildEto(@event, trackContent, orderNo, trackEsId, failedReason));
}
private static TrackChangeEto BuildEto(TrackUpsertEvent @event, TrackContentVO trackContent, string orderNo, string? trackEsId = null, string? failedReason = null)
{
return new TrackChangeEto
{
TrackEsId = trackEsId,
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,
Files = @event.Files,
Remarks = @event.Remarks,
Source = @event.Source,
IsExternal = @event.IsExternal ?? trackContent.IsExternal,
TrackTimeZone = @event.TrackTimeZone,
TrackTime = @event.NewTrackTime ?? @event.TrackTime,
TrackPlace = @event.TrackPlace,
TrackStatusCode = trackContent.TrackStatusCode,
OrderNo = orderNo,
EnStatus = @event.EnStatus,
Content = @event.Content,
TrackCode = @event.TrackCode,
OperationMan = @event.OperationMan,
OrderIdentifiers = @event.OrderIdentifiers,
CustomId = @event.CustomId,
SubSystemId = @event.SubSystemId,
Extra = @event.NewExtra ?? @event.Extra
};
}
}

View File

@ -1,4 +1,3 @@
using Fake.DependencyInjection;
using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
using TrackCenter.Domain.Extensions;
using TrackCenter.Repository.Redis;

View File

@ -33,18 +33,6 @@ public class TrackDetail
/// </summary>
public float? TimeZone { get; set; }
/// <summary>
/// 是否对外
/// </summary>
[Obsolete("废弃的,请使用IsExternal")]
public bool IsDisplay { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
[Obsolete("废弃的,请使用IsExternal")]
public bool IsVisibility { get; set; }
/// <summary>
/// 是否对外
/// </summary>

View File

@ -0,0 +1,12 @@
namespace TrackCenter.Application.Events;
public class OrderPodPullEvent
{
// 确定子系统
public string SubSystemCode { get; set; }
// 订单Id esId
public Dictionary<int, string> OrderIdEsIdDict { get; set; }
public HashSet<int> TrackFlagIds { get; set; }
}

View File

@ -4,11 +4,26 @@ namespace TrackCenter.Application.Events;
public class TrackChangeEto
{
/// <summary>
/// 轨迹唯一id,如果操作失败则为null
/// </summary>
public string? TrackEsId { get; set; }
/// <summary>
/// 操作类型:1新增,2删除,3修改
/// </summary>
public int Option { get; set; }
/// <summary>
/// 操作是否成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 失败原因
/// </summary>
public string? FailedReason { get; set; }
/// <summary>
/// 子系统id
/// </summary>

View File

@ -54,6 +54,11 @@ public class TrackDeleteEvent
/// </summary>
public List<int> Visited { get; set; } = new();
/// <summary>
/// 标记是否广播
/// </summary>
public bool IsToMq { get; set; }
public override string ToString()
{
return $"[TrackDelete by {OperationMan}] SubSystemId: {SubSystemId}, OrderIds: {string.Join(",",

View File

@ -34,7 +34,7 @@ public class TrackPushEvent
/// <summary>
/// 订单id与EsId+Version的映射字典
/// </summary>
public Dictionary<int, KeyValuePair<string, long>> OrderIdEsIdVersionDict { get; set; }
public Dictionary<int, TrackMetadata> OrderIdTrackMetadataDict { get; set; }
/// <summary>
/// 轨迹代码(我们系统的)

View File

@ -1,3 +1,5 @@
global using Fake.Application;
global using Fake.DependencyInjection;
global using TrackCenter.Domain.Consts;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.Extensions.DependencyInjection;

View File

@ -1,4 +1,3 @@
using Fake.DependencyInjection;
using Microsoft.Extensions.Options;
using TrackCenter.Application.Dtos.Warning;
using TrackCenter.Application.Options;

View File

@ -1,4 +1,3 @@
using Fake.DependencyInjection;
using Hangfire;
using Microsoft.Extensions.Options;
using TrackCenter.Application.Options;

View File

@ -1,5 +1,3 @@
using Fake.Application;
using Fake.DependencyInjection;
using FeishuNetSdk;
using Hangfire;
using Microsoft.Extensions.Options;

View File

@ -111,21 +111,6 @@ public static class DateTimeHelper
return str;
}
/// <summary>
/// 如果<paramref name="condition"/> is true,按照<paramref name="predicate"/>过滤 <see cref="IEnumerable{T}"/>
/// </summary>
/// <param name="source">被过滤的enumerable</param>
/// <param name="condition">过滤条件</param>
/// <param name="predicate">过滤表达式</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, bool condition, Func<T, bool> predicate)
{
return condition
? source.Where(predicate)
: source;
}
/// <summary>
/// 转换为utc0时区的时间转化进行Compare
/// </summary>

View File

@ -0,0 +1,263 @@
using System.Security.Cryptography;
using Aliyun.OSS;
namespace TrackCenter.Application.Helpers.OSS
{
/// <summary>
/// 阿里云OSS帮助类
/// </summary>
public static class OSSHelper
{
#region 初始化
private static OSSOptions _OSSOptions;
internal static void Init(OSSOptions OSSOptions)
{
_OSSOptions = OSSOptions;
}
#endregion
#region 获取client
private static OssClient GetClient()
{
string errorMessage = null;
if (string.IsNullOrEmpty(_OSSOptions?.Endpoint))
{
errorMessage += $"未找到核心配置参数【{nameof(_OSSOptions.Endpoint)}】!";
}
if (string.IsNullOrEmpty(_OSSOptions?.AccessKeyId))
{
errorMessage += $"未找到核心配置参数【{nameof(_OSSOptions.AccessKeyId)}】!";
}
if (string.IsNullOrEmpty(_OSSOptions?.AccessKeySecret))
{
errorMessage += $"未找到核心配置参数【{nameof(_OSSOptions.AccessKeySecret)}】!";
}
if (!string.IsNullOrEmpty(errorMessage))
{
throw new Exception(errorMessage);
}
var client = new OssClient(_OSSOptions.Endpoint, _OSSOptions.AccessKeyId, _OSSOptions.AccessKeySecret);
return client;
}
#endregion
#region 上传
/// <summary>
/// 上传文件
/// </summary>
/// <param name="data">文档流</param>
/// <param name="fileName">文件名称</param>
/// <param name="usCustomKey">是否使用自定义ossKey,如果为true,会以fileName为key</param>
/// <returns></returns>
public static UploadResultDto Upload(Stream data, string fileName = "", bool usCustomKey = false, bool isPublicBucket = false)
{
if (data == null || data.Length == 0)
return new UploadResultDto(false);
var result = Private_Upload(fileName, data, usCustomKey, isPublicBucket);
return result;
}
private static UploadResultDto Private_Upload(string fileName, Stream data, bool usCustomKey = false, bool isPublicBucket = false)
{
var client = GetClient();
var ossKey = "";
if (usCustomKey)
{
ossKey = fileName;
}
else
{
var md5 = ComputeDataMd5(data).ToUpper();// 计算文件MD5值
var ext = Path.GetExtension(fileName);
ossKey = $"{md5}{ext}";
}
// 文件已经存在直接返回
var existingObject = client.DoesObjectExist(_OSSOptions.BucketName, ossKey);
if (existingObject)
{
var res = new UploadResultDto(true);
res.TempUrl = ossKey.GetUrlByKey(isPublicBucket);
res.Key = ossKey;
res.FileName = fileName;
return res;
}
// 上传文件
bool putResult = false;
using (var pubObj = client.PutObject(_OSSOptions.BucketName, ossKey, data))
{
putResult = pubObj.HttpStatusCode == System.Net.HttpStatusCode.OK;
}
if (putResult)//上传成功
{
var result = new UploadResultDto(true);
result.FileName = fileName;
result.TempUrl = ossKey.GetUrlByKey(isPublicBucket);
result.Key = ossKey;
return result;
}
else
{
// 上传失败
var result = new UploadResultDto(false);
result.FileName = fileName;
return result;
}
}
public static string ComputeDataMd5(Stream data)
{
using (var md5 = MD5.Create())
{
var hash = BitConverter.ToString(md5.ComputeHash(data)).Replace("-", "").Replace("=", "").ToLower();
data.Position = 0;
return hash;
}
}
#endregion
#region 下载
/// <summary>
/// 通过Key获取文件的URL - 同步
/// </summary>
/// <param name="key">Key</param>
/// <returns>下载Url</returns>
public static string GetUrlByKey(this string key, bool isPublicBucket = false)
{
if (string.IsNullOrEmpty(key))
{
throw new Exception($"指定的key不能为空");
}
try
{
var client = GetClient();
Uri url = client.GeneratePresignedUri(_OSSOptions.BucketName, key);
if (isPublicBucket)
{
var urlStr = url?.GetLeftPart(UriPartial.Path);
return (_OSSOptions.ReturnHttpsUrl && urlStr?.ToString()?.StartsWith("http://") == true) ? "https://" + urlStr.ToString().Substring(7) : urlStr.ToString();
}
return (_OSSOptions.ReturnHttpsUrl && url?.ToString()?.StartsWith("http://") == true) ? "https://" + url.ToString().Substring(7) : url.ToString();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
/// <summary>
/// 根据一组key返回url
/// </summary>
/// <param name="keys"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static Dictionary<string, string> GetUrlByKeys(params string[] keys)
{
var resDic = new Dictionary<string, string>();
if (keys == null || keys.Count() <= 0)
return resDic;
var returnHttps = _OSSOptions.ReturnHttpsUrl;
var client = GetClient();
foreach (var item in keys)
{
if (resDic.ContainsKey(item))
continue;
Uri url = client.GeneratePresignedUri(_OSSOptions.BucketName, item);
if (url == null)
continue;
resDic.Add(item, returnHttps ? url.ToString().Replace("http", "https") : url.ToString());
}
return resDic;
}
/// <summary>
/// 通过Key获取文件流
/// </summary>
/// <param name="key">Key</param>
/// <returns></returns>
public static Stream GetStreamByKey(this string key)
{
if (string.IsNullOrEmpty(key))
{
throw new Exception($"指定的key不能为空");
}
try
{
var client = GetClient();
var obj = client.GetObject(_OSSOptions.BucketName, key);
if (obj != null)
{
return obj.Content;
}
else
{
throw new Exception($"文件获取异常");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
#endregion
#region 删除
/// <summary>
/// 删除文件
/// </summary>
/// <param name="keys"></param>
/// <returns></returns>
public static DeleteObjectsResult DeleteObjectsByKeys(params string[] keys)
{
if (keys == null || keys.Length <= 0)
return default(DeleteObjectsResult);
var client = GetClient();
var request = new DeleteObjectsRequest(_OSSOptions.BucketName, keys);
return client.DeleteObjects(request);
}
#endregion
#region 查看数据信息
/// <summary>
/// 查看文档信息
/// </summary>
/// <param name="ossKey"></param>
/// <returns></returns>
public static ObjectMetadata GetObjectMetadata(string ossKey)
{
var client = GetClient().GetObjectMetadata(_OSSOptions.BucketName, ossKey);
return client;
}
#endregion
}
}

View File

@ -0,0 +1,32 @@
namespace TrackCenter.Application.Helpers.OSS;
public class OSSOptions
{
/// <summary>
/// 秘钥Id(类似于公钥)
/// </summary>
public string AccessKeyId { get; set; }
/// <summary>
/// 访问秘钥
/// </summary>
public string AccessKeySecret { get; set; }
/// <summary>
/// OSS端点
/// </summary>
public string Endpoint { get; set; }
/// <summary>
/// 存储空间名称
/// 注意,如果有多个BucketName的话,可以直接使用代码的方式设置这个值
/// </summary>
public string BucketName { get; set; }
/// <summary>
/// 返回https的url,默认false返回http
/// 阿里OSS默认支持http,https,不需要额外配置
/// </summary>
public bool ReturnHttpsUrl { get; set; }
}

View File

@ -0,0 +1,30 @@
namespace TrackCenter.Application.Helpers.OSS;
public class UploadResultDto
{
/// <summary>
/// 标识本次操作是否成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 临时URL,过期时间15min
/// </summary>
public string TempUrl { get; set; }
/// <summary>
/// 文件唯一标识Key,通常保存在数据
/// 后期所有的文件操作都是通过这个Key
/// </summary>
public string Key { get; set; }
/// <summary>
/// 文件原始名称
/// </summary>
public string FileName { get; set; }
public UploadResultDto(bool isSuccess)
{
IsSuccess = isSuccess;
}
}

View File

@ -1,5 +1,4 @@
using Confluent.Kafka;
using Fake.DependencyInjection;
using Microsoft.Extensions.Configuration;
using TrackCenter.Application.Services.Warning.Signals;
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;

View File

@ -20,7 +20,7 @@ namespace TrackCenter.Application.Queries;
public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>, ITransientDependency
{
public ISqlSugarClient Context => GetDbContextAsync().Result;
public async Task<List<string>> MapTrackCodesAsync(int upSubSystemId, int subSystemId, params string[] trackCodes)
public async Task<List<string>> MapTrackCodesAsync(int upSubSystemId, int subSystemId, params string[] trackCodes)
{
// 根据上游code得到下游code
return await Context.Queryable<TrackContentMapEntity>()
@ -46,34 +46,41 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
.ToListAsync();
}
/// <inheritdoc />
public async Task<List<OrderWithServiceProviderVO>> FindOrderWithServiceProviderAsync(int subSystemId,
List<string> orderNos)
{
return await Context.Queryable<OrderEntity>()
.InnerJoin<OrderPullStatusEntity>((a, b) => a.Id == b.OrderId)
.LeftJoin<VSystemEntity>((a, b, c) => a.SubSystemId == c.Id)
.WhereIF(subSystemId > 0, a => a.SubSystemId == subSystemId)
.Where((a, b) => !a.IsCompleted && orderNos.Contains(a.OrderNo)
&& b.StartTime <= DateTime.Now && b.EndTime >= DateTime.Now)
.Select((a, b) => new OrderWithServiceProviderVO
.Select((a, b, c) => new OrderWithServiceProviderVO
{
//优先使用第三方的订单号,为空时才使用主单号
OrderNo = string.IsNullOrWhiteSpace(b.OrderNo) ? a.OrderNo : b.OrderNo,
OrderType = a.OrderType,
SubSystemId = a.SubSystemId,
ServiceProviderCode = b.ServiceProviderCode,
SubSystemCode = c.Key
})
.ToListAsync();
}
public async Task<List<OrderWithServiceProviderVO>> FindOrderWithServiceProviderAsync(
string serviceProviderCode,
int subSystemId,
int pageIndex, int pageSize = 1000)
{
return await Context.Queryable<OrderEntity>()
.InnerJoin<OrderPullStatusEntity>((a, b) => a.Id == b.OrderId)
.Where((a, b) => b.ServiceProviderCode == serviceProviderCode && !a.IsCompleted
&& b.StartTime <= DateTime.Now &&
b.EndTime >= DateTime.Now)
.Where((a, b) => b.ServiceProviderCode == serviceProviderCode
&& a.SubSystemId == subSystemId
&& !a.IsCompleted
&& b.StartTime <= DateTime.Now
&& b.EndTime >= DateTime.Now
)
.Select((a, b) => new OrderWithServiceProviderVO
{
//优先使用第三方的订单号,为空时才使用主单号
@ -87,15 +94,18 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
.ToListAsync();
}
/// <inheritdoc />
public async Task<TrackContentVO?> GetTrackContentAsync(int subSystemId, string trackCode)
{
return await Context.Queryable<TrackContentEntity>()
var content = await Context.Queryable<TrackContentEntity>()
.LeftJoin<TrackStatusEntity>((x, y) => x.TrackStatusId == y.Id)
.LeftJoin<TrackFlagEntity>((x, y, k) => x.SubSystemId == k.SubSystemId && k.Code == x.Code)
.LeftJoin<VSystemEntity>((x, y, z) => x.SubSystemId == z.Id)
.Where(x => x.SubSystemId == subSystemId && x.Code == trackCode)
.Select((x, y, k) => new TrackContentVO
.Select((x, y, z) => new TrackContentVO
{
Id = x.Id,
SubSystemId = x.SubSystemId,
SubSystemCode = z.Key,
TrackStatusId = x.TrackStatusId,
TrackStatus = y.Status,
TrackStatusCode = y.Code,
@ -108,20 +118,33 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
IsExternal = x.IsExternal,
IsAllowManuallyUpdate = x.IsAllowManuallyUpdate,
IsAllowManuallyCreate = x.IsAllowManuallyCreate,
IsToMq = y.IsToMq || k.IsToMq,
IsToMq = y.IsToMq,
TrackFlagId = x.TrackFlagId
})
.FirstAsync();
if (content == null) return null;
// 填充轨迹标记
await FillTrackContentFlagsAsync(new List<TrackContentVO>(1)
{
content
});
return content;
}
public async Task<List<TrackContentVO>> GetTrackContentsAsync(int[] subSystemIds, string[] trackCodes)
{
return await Context.Queryable<TrackContentEntity>()
var contents = await Context.Queryable<TrackContentEntity>()
.LeftJoin<TrackStatusEntity>((x, y) => x.TrackStatusId == y.Id)
.LeftJoin<TrackFlagEntity>((x, y, k) => x.SubSystemId == k.SubSystemId && k.Code == x.Code)
.LeftJoin<VSystemEntity>((x, y, z) => x.SubSystemId == z.Id)
.Where(x => subSystemIds.Contains(x.SubSystemId) && trackCodes.Contains(x.Code))
.Select((x, y, k) => new TrackContentVO
.Select((x, y, z) => new TrackContentVO
{
Id = x.Id,
SubSystemId = x.SubSystemId,
SubSystemCode = z.Key,
TrackStatusId = x.TrackStatusId,
TrackStatus = y.Status,
TrackStatusCode = y.Code,
@ -134,21 +157,110 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
IsExternal = x.IsExternal,
IsAllowManuallyUpdate = x.IsAllowManuallyUpdate,
IsAllowManuallyCreate = x.IsAllowManuallyCreate,
IsToMq = y.IsToMq || k.IsToMq,
IsToMq = y.IsToMq,
TrackFlagId = x.TrackFlagId
})
.ToListAsync();
// 填充轨迹标记
await FillTrackContentFlagsAsync(contents);
return contents;
}
public async Task<List<TrackContentVO>> GetTrackContentAsync(HashSet<int> contentIds)
{
var contents = await Context.Queryable<TrackContentEntity>()
.Where(x => contentIds.Contains(x.Id))
.LeftJoin<TrackStatusEntity>((x, y) => x.TrackStatusId == y.Id)
.LeftJoin<VSystemEntity>((x, y, z) => x.SubSystemId == z.Id)
.Select((x, y, z) => new TrackContentVO
{
Id = x.Id,
SubSystemId = x.SubSystemId,
SubSystemCode = z.Key,
TrackStatusId = x.TrackStatusId,
TrackStatus = y.Status,
TrackStatusCode = y.Code,
TrackContentCode = x.Code,
TrackContentId = x.Id,
TrackContent = x.Content,
TrackEnContent = x.EnContent,
IsCompleted = y.IsCompleted,
IsRepeatable = x.IsRepeatable,
IsExternal = x.IsExternal,
IsAllowManuallyUpdate = x.IsAllowManuallyUpdate,
IsAllowManuallyCreate = x.IsAllowManuallyCreate,
IsToMq = y.IsToMq,
TrackFlagId = x.TrackFlagId
})
.ToListAsync();
// 在轨迹内容填充轨迹标记
await FillTrackContentFlagsAsync(contents);
return contents;
}
private async Task FillTrackContentFlagsAsync(List<TrackContentVO> contents)
{
// 1.构建轨迹内容和轨迹标记Ids的映射关系
var contentFlagIdsDict = contents
.Where(x => !string.IsNullOrWhiteSpace(x.TrackFlagId))
.ToDictionary(x => x, y => y.TrackFlagId!.Split(",").Select(int.Parse).ToHashSet());
if (contentFlagIdsDict.Count == 0) return;
// 2.查轨迹标记
var flagIds = contentFlagIdsDict.SelectMany(x => x.Value).ToHashSet();
var flags = await Context.Queryable<TrackFlagEntity>()
.Where(x => flagIds.Contains(x.Id))
.Select(x => new TrackFlagVO
{
Id = x.Id,
SubSystemId = x.SubSystemId,
Code = x.Code,
Name = x.Name,
IsToMq = x.IsToMq
})
.ToListAsync();
if (flags.Count == 0) return;
// 3.填充轨迹标记
var flagDict = flags.ToDictionary(x => x.Id);
foreach (var (content, flagsIds1) in contentFlagIdsDict)
{
content.TrackFlags = flagsIds1
.Where(x => flagDict.ContainsKey(x))
.Select(x => flagDict[x])
.ToList();
// 轨迹状态为不广播,根据轨迹标记是否为广播
if (!content.IsToMq)
content.IsToMq = content.TrackFlags.Any(x => x.IsToMq);
}
}
public async Task<List<TrackPullConfigVo>> GetAllPullConfigsAsync()
{
return await Context.Queryable<TrackPullConfigEntity>()
.LeftJoin<VServiceProviderEntity>((a, b) => a.ServiceProviderId == b.Id)
.Select((a, b) => new TrackPullConfigVo
.LeftJoin<VSystemEntity>((a, b, c) => a.SubSystemId == c.Id)
.Select((a, b, c) => new TrackPullConfigVo
{
PullInterval = a.PullInterval,
ServiceProviderId = a.ServiceProviderId,
ServiceProviderCode = b.Code,
SubSystemCode = c.Key,
SubSystemId = c.Id
})
.ToListAsync();
}
public async Task<string> SubSystemId2CodeAsync(int subSystemId)
{
return await Context.Queryable<VSystemEntity>()
.Where(x => x.Id == subSystemId)
.Select(x => x.Key)
.FirstAsync();
}
}

View File

@ -1,6 +1,7 @@
using Fake.DependencyInjection;
using Fake.RabbitMQ;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
@ -9,17 +10,18 @@ namespace TrackCenter.Application.Rabbit;
public class RabbitClient(
IRabbitMqChannelPool channelPool,
IServiceScopeFactory serviceScopeFactory,
ILogger<RabbitClient> logger) : ISingletonDependency
ILogger<RabbitClient> logger,
IOptions<RabbitOptions> options) : ISingletonDependency, IHostedService
{
public void Subscribe(ConsumeOptions consumeOptions)
{
using var channelAccessor = channelPool.Acquire(consumeOptions.Queue);
if (!channelAccessor.IsNew) return;
var channel = channelAccessor.Channel;
var decl = new DefaultDeclaration(channel);
consumeOptions.Declaration.Invoke(channel);
consumeOptions.Declaration.Invoke(decl);
if (consumeOptions.FetchCount > 0)
{
@ -105,6 +107,7 @@ public class RabbitClient(
var readOnlyMemory = (ReadOnlyMemory<byte>) data;
model.BasicPublish(exchange, routingKey, basicProperties, readOnlyMemory);
}
model.WaitForConfirmsOrDie();
}
@ -122,15 +125,28 @@ public class RabbitClient(
channelPool.Release(queueName);
}
}
public class ConsumeOptions
{
public string Queue { get; set; }
public ushort FetchCount { get; set; }
public bool FailedRequeue { get; set; }
public Action<IModel> Declaration { get; set; }
public Task StartAsync(CancellationToken cancellationToken)
{
if (options.Value.Declarations.Count > 0)
{
using var channelAccessor = channelPool.Acquire();
var channel = channelAccessor.Channel;
var decl = new DefaultDeclaration(channel);
options.Value.Declarations.ForEach(q => q.Invoke(decl));
}
foreach (var opt in options.Value.Consumes)
{
Subscribe(opt);
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
channelPool.Dispose();
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,86 @@
using RabbitMQ.Client;
namespace TrackCenter.Application.Rabbit;
public class RabbitOptions
{
public string ConnectionString { get; set; }
public List<ConsumeOptions> Consumes { get; set; } = new();
public List<Action<IDeclaration>> Declarations { get; set; } = new();
}
public class ConsumeOptions
{
/// <summary>
/// 队列名
/// </summary>
public string Queue { get; set; }
/// <summary>
/// 并行度
/// </summary>
public ushort FetchCount { get; set; } = 1;
/// <summary>
/// handle发生异常,是否重回队列
/// </summary>
public bool FailedRequeue { get; set; }
public Action<IDeclaration> Declaration { get; set; }
}
public interface IDeclaration
{
void QueueDeclare(string queue, bool durable, bool autoDelete = false, bool exclusive = false,
IDictionary<string, object> arguments = null);
void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete = false,
IDictionary<string, object> arguments = null);
void QueueBind(string queue, string exchange, string routingKey,
IDictionary<string, object> arguments = null);
}
public class DefaultDeclaration : IDeclaration
{
private readonly IModel _chnl;
// ReSharper disable once ConvertToPrimaryConstructor
public DefaultDeclaration(IModel chnl)
{
_chnl = chnl;
}
public void QueueDeclare(string queue, bool durable, bool autoDelete = false, bool exclusive = false,
IDictionary<string, object> arguments = null)
{
_chnl.QueueDeclare(
queue: queue,
durable: durable,
autoDelete: autoDelete,
exclusive: exclusive,
arguments: arguments);
}
public void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete = false,
IDictionary<string, object> arguments = null)
{
_chnl.ExchangeDeclare(
exchange: exchange,
type: type,
durable: durable,
autoDelete: autoDelete,
arguments: arguments);
}
public void QueueBind(string queue, string exchange, string routingKey, IDictionary<string, object> arguments)
{
_chnl.QueueBind(
queue: queue,
exchange: exchange,
routingKey: routingKey,
arguments: arguments);
}
}

View File

@ -1,6 +1,4 @@
using System.Text;
using Fake.Application;
using Fake.DependencyInjection;
using Newtonsoft.Json;
using RabbitMQ.Client.Events;
using TrackCenter.Application.Dtos.TrackPush;

View File

@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Text;
using DotNetCore.CAP;
using Fake.DependencyInjection;
using InterfaceForward.Domain.Shared.Helpers;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

View File

@ -1,5 +1,3 @@
using Fake.Application;
namespace TrackCenter.Application.Services;
public class ApplicationServiceBase: ApplicationService

View File

@ -1,5 +1,4 @@
using System.Text;
using Fake.DependencyInjection;
using InterfaceForward.Domain.Shared.Helpers;
using TrackCenter.Application.Dtos.TrackPush;
using TrackCenter.Application.Events;
@ -19,60 +18,14 @@ public class BroadcastingService : ITransientDependency
_rabbitClient = rabbitClient;
}
/// <summary>
/// 轨迹变更广播
/// </summary>
/// <param name="eto"></param>
/// <param name="contentIsExternal">轨迹内容中的是否对外</param>
/// <param name="subSystemCode">子系统代码</param>
/// <param name="trackStatusCode">轨迹状态代码</param>
/// <returns></returns>
public void TrackChangePublicTopic(TrackUpsertEvent eto, bool contentIsExternal, string subSystemCode, string trackStatusCode)
public void TrackChangePublicTopic(TrackChangeEto eto, string subSystemCode)
{
var data = new TrackChangeEto
{
EstimatedDeliveryDate = eto.EstimatedDeliveryDate,
DestinationTimeZone = eto.DestinationTimeZone,
TrackPlacePostcode = eto.TrackPlacePostcode,
TrackPlaceNation = eto.TrackPlaceNation,
TrackPlaceNationId = eto.TrackPlaceNationId,
TrackPlaceProvince = eto.TrackPlaceProvince,
TrackPlaceProvinceId = eto.TrackPlaceProvinceId,
TrackPlaceCity = eto.TrackPlaceCity,
TrackPlaceCityId = eto.TrackPlaceCityId,
Files = eto.Files,
Remarks = eto.Remarks,
Source = eto.Source,
IsExternal = eto.IsExternal ?? contentIsExternal,
TrackTimeZone = eto.TrackTimeZone,
TrackTime = eto.NewTrackTime ?? eto.TrackTime,
TrackPlace = eto.TrackPlace,
TrackStatusCode = trackStatusCode,
OrderNo = eto.OrderIdentifiers.First().OrderNo,
EnStatus = eto.EnStatus,
Content = eto.Content,
TrackCode = eto.TrackCode,
OperationMan = eto.OperationMan,
OrderIdentifiers = eto.OrderIdentifiers,
CustomId = eto.CustomId,
SubSystemId = eto.SubSystemId,
Extra = eto.NewExtra ?? eto.Extra
};
if (eto.IsAdd)
data.Option = 1;
else
data.Option = 3;
_rabbitClient.Publish(GlobalConst.TrackChangeExchange, $"TrackChange.{subSystemCode}",
Encoding.UTF8.GetBytes(data.ToJson()), prop => { prop.Persistent = true; });
}
public void TrackChangePublicTopic(TrackChangeEto eto, string subSystemCode)
{
eto.Option = 2;
_rabbitClient.Publish(GlobalConst.TrackChangeExchange, $"TrackChange.{subSystemCode}",
Encoding.UTF8.GetBytes(eto.ToJson()), prop => { prop.Persistent = true; });
_rabbitClient.PublishByConfirmSelect(
GlobalConst.TrackChangeExchange,
$"TrackChange.{subSystemCode}",
Encoding.UTF8.GetBytes(eto.ToJson()),
prop => { prop.Persistent = true; }
);
}
/// <summary>
@ -82,11 +35,16 @@ public class BroadcastingService : ITransientDependency
/// <returns></returns>
public void TrackPushCompletedPublicTopic(List<TrackPushRecordDto> list)
{
var datas = list
var data = list
.Select(TrackPushRecordDtoToEvent)
.Select(x => Encoding.UTF8.GetBytes(x.ToJson()));
var log = list.First();
_rabbitClient.Publish(GlobalConst.TrackPushExchange, $"TrackPush.{log.SubSystemCode}", datas, prop => { prop.Persistent = true; });
_rabbitClient.PublishByConfirmSelect(
GlobalConst.TrackPushExchange,
$"TrackPush.{log.SubSystemCode}",
data,
prop => { prop.Persistent = true; }
);
}
/// <summary>

View File

@ -1,10 +1,8 @@
using TrackCenter.Application.Services;
using TrackCenter.Domain.Aggregates.KPIAreaAggregate;
using Volo.Abp.DependencyInjection;
namespace TrackCenter.Application.Services.ForewarningServices;
public class KpiAreaCacheService : IKpiAreaCacheService, ITransientDependency
public class KpiAreaCacheService : ITransientDependency
{
const string GlobalKpiAreaLockKey = "KpiAreaCacheService_GlobalLock";
IKpiAreaRepository _kpiAreaRepository;

View File

@ -1,6 +1,4 @@
using Fake.Application;
using Fake.Application.Dtos;
using TrackCenter.Application.Services;
using TrackCenter.Domain.Aggregates.KPIAreaAggregate;
using TrackCenter.Domain.Dtos.Forewarning;
@ -11,9 +9,9 @@ namespace TrackCenter.Application.Services.ForewarningServices;
public class KpiAreaService : ApplicationServiceBase
{
private readonly IKpiAreaRepository _ruleAreaRepository;
private readonly IKpiAreaCacheService _kpiAreaCacheService;
private readonly KpiAreaCacheService _kpiAreaCacheService;
public KpiAreaService(IKpiAreaRepository ruleAreaRepository, IKpiAreaCacheService kpiAreaCacheService)
public KpiAreaService(IKpiAreaRepository ruleAreaRepository, KpiAreaCacheService kpiAreaCacheService)
{
_ruleAreaRepository = ruleAreaRepository;
_kpiAreaCacheService = kpiAreaCacheService;
@ -25,18 +23,21 @@ public class KpiAreaService : ApplicationServiceBase
/// <param name="input"></param>
/// <returns>分区id</returns>
[HttpPost("KpiArea/CreateKpiArea")]
public async ValueTask<UnifyResultDto> CreateKpiArea(CreateRuleAreaRequest input)
public async ValueTask<IdBaseDto> CreateKpiArea(CreateRuleAreaRequest input)
{
var ruleAreaEntity = ObjectMapper.Map<CreateRuleAreaRequest, RuleAreaEntity>(input);
var hasSameName =
await _ruleAreaRepository.IsAnyAsync(val => val.SubSystemId == input.SystemId && val.Name == input.Name);
if (hasSameName)
{
return new UnifyResultDto(false, "分区名称重复,请重新修改提交");
throw new BusinessException("分区名称重复,请重新修改提交");
}
var areaId = await _ruleAreaRepository.CreateAreaAsync(ruleAreaEntity);
return new UnifyResultDto(true, areaId, "创建成功");
return new IdBaseDto
{
Id = areaId,
};
}
/// <summary>
@ -45,12 +46,15 @@ public class KpiAreaService : ApplicationServiceBase
/// <param name="input"></param>
/// <returns>区域id</returns>
[HttpPost("KpiArea/AddAreaInfo")]
public async ValueTask<UnifyResultDto> AddAreaInfoAsync(AddRuleAreaInfoRequest input)
public async ValueTask<IdBaseDto> AddAreaInfoAsync(AddRuleAreaInfoRequest input)
{
var ruleAreaEntity = ObjectMapper.Map<AddRuleAreaInfoRequest, RuleAreaInfoEntity>(input);
var ruleInfoId = await _ruleAreaRepository.AddAreaInfoAsync(ruleAreaEntity);
// todo: 删除 kpi区域缓存
return new UnifyResultDto(true, ruleInfoId, "添加成功");
return new IdBaseDto
{
Id = ruleInfoId,
};
}
/// <summary>
@ -59,11 +63,10 @@ public class KpiAreaService : ApplicationServiceBase
/// <param name="id">分区id</param>
/// <returns></returns>
[HttpDelete("KpiArea/DeleteKpiArea")]
public async ValueTask<UnifyResultDto> DeleteKpiArea(int id)
public async ValueTask DeleteKpiArea(int id)
{
await _ruleAreaRepository.DeleteArea(id);
// todo: 删除 kpi区域缓存
return new UnifyResultDto(true, "删除成功");
}
/// <summary>
@ -72,13 +75,12 @@ public class KpiAreaService : ApplicationServiceBase
/// <param name="id">分区信息id</param>
/// <returns></returns>
[HttpDelete("KpiArea/DeleteKpiAreaInfo")]
public async ValueTask<UnifyResultDto> DeleteKpiAreaInfo(int id)
public async ValueTask DeleteKpiAreaInfo(int id)
{
var info = await _ruleAreaRepository.GetAreaInfoById(id);
if (info == null) return new UnifyResultDto(false, "未找到该区域信息");
if (info == null) throw new BusinessException("未找到该区域信息");
await _ruleAreaRepository.DeleteAreaInfo(id);
// todo: 删除 kpi区域缓存
return new UnifyResultDto(true, "删除成功");
}
/// <summary>
@ -87,18 +89,17 @@ public class KpiAreaService : ApplicationServiceBase
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("KpiArea/ModifyKpiArea")]
public async Task<UnifyResultDto> ModifyKpiArea(UpdateRuleAreaRequest input)
public async Task ModifyKpiArea(UpdateRuleAreaRequest input)
{
if (input.Id <= 0)
return new UnifyResultDto(false, "id不能为空");
throw new BusinessException("id不能为空");
var ruleArea = await _ruleAreaRepository.GetFirstAsync(val => val.Id == input.Id);
if (ruleArea == null)
return new UnifyResultDto(false, "未找到该区域");
throw new BusinessException("未找到该区域");
ruleArea.Name = input.Name;
await _ruleAreaRepository.UpdateAsync(ruleArea);
return new UnifyResultDto(true, "更新成功");
}
/// <summary>
@ -107,20 +108,19 @@ public class KpiAreaService : ApplicationServiceBase
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("KpiArea/ModifyKpiAreaInfo")]
public async Task<UnifyResultDto> ModifyKpiAreaInfo(UpdateRuleAreaInfoRequest input)
public async Task ModifyKpiAreaInfo(UpdateRuleAreaInfoRequest input)
{
if (input.Id <= 0)
return new UnifyResultDto(false, "id不能为空");
throw new BusinessException("id不能为空");
var areaInfo = await _ruleAreaRepository.GetAreaInfoById(input.Id);
if (areaInfo == null)
return new UnifyResultDto(false, "未找到该区域信息");
throw new BusinessException("未找到该区域信息");
var ruleAreaEntity = ObjectMapper.Map(input, areaInfo);
await _ruleAreaRepository.UpdateAreaInfoAsync(ruleAreaEntity);
// todo: 删除 kpi区域缓存
return new UnifyResultDto(true, "更新成功");
}
/// <summary>

View File

@ -21,7 +21,7 @@ public partial class OrderService
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Order/Link")]
public async Task<UnifyResultDto> LinkOrderAsync(LinkOrderInput input)
public async Task LinkOrderAsync(LinkOrderInput input)
{
var orderId = await _orderRepository.GetFirstAsync(x =>
x.SubSystemId == input.SubSystemId && x.OrderType == input.OrderType && x.OrderNo == input.OrderNo,
@ -37,7 +37,7 @@ public partial class OrderService
throw new BusinessException(message: "不能同级关联");
}
if (input.LinkOrderNos.Count < 1 || input.LinkOrderType == 0) return new UnifyResultDto(true);
if (input.LinkOrderNos.Count < 1 || input.LinkOrderType == 0) return;
// 单号去重,去空,去自己
input.LinkOrderNos = input.LinkOrderNos
@ -54,8 +54,6 @@ public partial class OrderService
// 关联订单
await DoLinkOrderAsync(input, orderId);
}
return new UnifyResultDto(true, true);
}
private async Task DoLinkOrderAsync(LinkOrderInput input, int orderId)

View File

@ -1,8 +1,7 @@
using SJZY.Expand.ABP.Core.Logger;
using SJZY.Expand.ABP.Core.PublicMethords;
using InterfaceForward.Domain.Shared.Helpers;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Domain.Dtos.Order;
using SJZY.UserCenter.HttpApi.Services;
using TrackCenter.Domain.Extensions;
namespace TrackCenter.Application.Services.ProvideServices;
@ -14,21 +13,19 @@ public partial class OrderService
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("Order/UpdateOrderPullConfigs")]
public async Task<UnifyResultDto> UpdateOrderPullConfigsAsync(UpdateOrderPullConfigsInput input)
public async Task UpdateOrderPullConfigsAsync(UpdateOrderPullConfigsInput input)
{
var orderIds = await _orderRepository.GetArrayAsync(
var orderIds = await _orderRepository.GetListAsync(
x => x.SubSystemId == input.SubSystemId && x.OrderType == input.OrderType &&
input.OrderNos.Contains(x.OrderNo),
x => x.Id);
var inputVo = ObjectMapper
.Map<PullConfigInfo, UpdateOrderPullConfigsInputVo>(input.Config);
inputVo.OrderIds = orderIds;
inputVo.OrderIds = orderIds.ToArray();
var cnt = await _orderRepository.UpdateOrderPullConfigForServiceProviderAsync(inputVo);
LogHelper.Warn(
$"{CurrentUserRequest.CurrentUser.UserId}正在修改服务商拉取信息,本次共计影响了{orderIds.Length}个订单、{cnt}段拉取,请求参数是:{input.ToJson()}",
$"{CurrentUser.UserName}正在修改服务商拉取信息,本次共计影响了{orderIds.Count}个订单、{cnt}段拉取,请求参数是:{input.ToJson()}",
true);
return new UnifyResultDto(true);
}
}

View File

@ -1,5 +1,4 @@
using DotNetCore.CAP;
using Fake.Application;
using InterfaceForward.Domain.Shared.Helpers;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Domain.Aggregates.OrderAggregate;

View File

@ -10,7 +10,7 @@ namespace TrackCenter.Application.Services.ProvideServices;
/// </summary>
[ApiExplorerSettings(GroupName = "提供接口")]
[Route("/ProviderService")]
public class TrackContentService : ApplicationServiceBase, ITrackContentService
public class TrackContentService : ApplicationServiceBase
{
private readonly ITrackContentRepository _trackContentRepository;
@ -25,10 +25,10 @@ public class TrackContentService : ApplicationServiceBase, ITrackContentService
/// <param name="subSystemId"></param>
/// <returns></returns>
[HttpPost("Track/GetTrackContents")]
public async Task<UnifyResultDto<List<TrackContentOutput>>> GetTrackContentsAsync(int subSystemId)
public async Task<List<TrackContentOutput>> GetTrackContentsAsync(int subSystemId)
{
var res = await _trackContentRepository.GetTrackContentBySyetemIdAsync(subSystemId);
var result = ObjectMapper.Map<List<TrackContentVO>, List<TrackContentOutput>>(res);
return new UnifyResultDto<List<TrackContentOutput>>(true, result);
return result;
}
}

View File

@ -1,17 +1,13 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using SJZY.Expand.ABP.Core.Logger;
using SJZY.Expand.ABP.Core.PublicMethords;
using SJZY.InterfaceRelay.Application.Contracts;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Application.Dtos.TrackPull;
using TrackCenter.Application.Services.ProvideServices;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Options;
using TrackCenter.Application.Queries;
using TrackCenter.Application.Rabbit;
using TrackCenter.Application.Track2InterfaceRelay;
using TrackCenter.Domain.Queries;
using Volo.Abp.Http.Client.DynamicProxying;
using TrackCenter.Domain.Extensions;
namespace TrackCenter.Application.Services.ProvideServices;
@ -20,13 +16,13 @@ namespace TrackCenter.Application.Services.ProvideServices;
/// </summary>
[ApiExplorerSettings(GroupName = "提供接口")]
[Route("/ProviderService")]
public class TrackPullService : ApplicationServiceBase, ITrackPullService
public class TrackPullService : ApplicationServiceBase
{
private readonly IHttpClientProxy<IInterfaceForwardServiceV2> _httpClientProxy;
private readonly IOptionsMonitor<InterfaceRelayParamsConfigOptions> _options;
private readonly IProvideQueries _provideQueries;
private readonly ProvideQueries _provideQueries;
public TrackPullService(IProvideQueries provideQueries,
public TrackPullService(ProvideQueries provideQueries,
IHttpClientProxy<IInterfaceForwardServiceV2> httpClientProxy,
IOptionsMonitor<InterfaceRelayParamsConfigOptions> options)
{

View File

@ -10,7 +10,7 @@ namespace TrackCenter.Application.Services.ProvideServices;
/// </summary>
[ApiExplorerSettings(GroupName = "提供接口")]
[Route("/ProviderService")]
public class TrackPushConfigService : ApplicationServiceBase, ITrackPushConfigService
public class TrackPushConfigService : ApplicationServiceBase
{
private readonly ITrackPushConfigRepository _trackPushConfigRepository;
@ -25,13 +25,13 @@ public class TrackPushConfigService : ApplicationServiceBase, ITrackPushConfigSe
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("Track/QueryTrackPullMappings")]
public async Task<UnifyResultDto<List<QueryTrackPullMappingsInfo>>> QueryTrackPullMappingsAsync(
public async Task<List<QueryTrackPullMappingsInfo>> QueryTrackPullMappingsAsync(
QueryTrackPullMappingsInput input)
{
var vo = ObjectMapper.Map<QueryTrackPullMappingsInput, QueryTrackPullMappingsInputVO>(input);
var result = await _trackPushConfigRepository.QueryTrackPullMappingsAsync(vo);
var output = ObjectMapper.Map<List<QueryTrackPullMappingsInfoVO>, List<QueryTrackPullMappingsInfo>>(result);
return new UnifyResultDto<List<QueryTrackPullMappingsInfo>>(true, output, "操作成功");
return output;
}
}

View File

@ -136,7 +136,7 @@ public class TrackPushService : ApplicationServiceBase, ITrackPushService
SubSystemId = input.SubSystemId,
SubSystemCode = subSystemCode,
CustomId = order.CustomId,
OrderIdEsIdVersionDict = new Dictionary<int, KeyValuePair<string, long>>
OrderIdTrackMetadataDict = new Dictionary<int, KeyValuePair<string, long>>
{
[order.Id] = KeyValuePair.Create(track.Id, track.Version)
},

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Http;
using SJZY.Expand.AliyunOSS;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Helpers.OSS;
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
using TrackCenter.Domain.Aggregates.TrackTemplateAggregate;
using TrackCenter.Domain.Dtos.TrackTemplate;
using TrackCenter.Domain.Queries;
namespace TrackCenter.Application.Services.ProvideServices;
@ -17,7 +16,6 @@ public partial class TrackService
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
private readonly ITrackTemplateRepository _trackTemplateRepository;
private readonly IHttpContextAccessor _contextAccessor;
private readonly IVSystemQueries _vSystemQueries;
/// <summary>
/// 查询轨迹(时区补正)
@ -26,7 +24,7 @@ public partial class TrackService
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Track/Query")]
public async Task<UnifyResultDto<List<GetTrackOutput>>> QueryAsync(GetTrackInput input)
public async Task<List<GetTrackOutput>> QueryAsync(GetTrackInput input)
{
var res = new List<GetTrackOutput>();
input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空
@ -68,8 +66,6 @@ public partial class TrackService
TimeZone = Math.Abs(x.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
? null
: input.TimeZone ?? 8,
IsDisplay = x.IsExternal,
IsVisibility = x.IsVisibility,
IsExternal = x.IsExternal,
Source = x.Source,
Remarks = x.Remarks,
@ -99,7 +95,7 @@ public partial class TrackService
// 排序
var sortedList = await TracksOrdered(res, input, input.SortMethod);
return new UnifyResultDto<List<GetTrackOutput>>(true, sortedList, "操作成功");
return sortedList;
}
@ -109,7 +105,7 @@ public partial class TrackService
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("Track/QueryV2")]
public async Task<UnifyResultDto<List<GetTrackOutput>>> QueryV2Async(GetTrackInputV2 input)
public async Task<List<GetTrackOutput>> QueryV2Async(GetTrackInputV2 input)
{
var res = new List<GetTrackOutput>();
input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空
@ -132,10 +128,8 @@ public partial class TrackService
var trackDetails = group
.WhereIf(isFilterExternal, x => x.IsExternal)
.ToList();
if (trackDetails.Count == 0)
continue;
if (trackDetails.Count == 0) continue;
item.OrderNo = trackDetails.First().OrderNo;
item.TrackDetails = trackDetails.Select(x => new TrackDetail
{
EsId = x.Id,
@ -145,10 +139,12 @@ public partial class TrackService
EnContent = x.TrackEnContent,
OriginContent = x.OriginTrackContent,
Location = x.TrackPlace,
Time = x.TrackTime,
TimeZone = x.TrackTimeZone,
IsDisplay = x.IsExternal,
IsVisibility = x.IsVisibility,
Time = Math.Abs(x.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
? x.TrackTime
: x.ZeroTrackTime.AddHours(input.TimeZone ?? 8),
TimeZone = Math.Abs(x.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
? null
: input.TimeZone ?? 8,
IsExternal = x.IsExternal,
Source = x.Source,
Remarks = x.Remarks,
@ -168,7 +164,10 @@ public partial class TrackService
DestinationTimeZone = x.DestinationTimeZone,
EstimatedDeliveryDate = x.EstimatedDeliveryDate,
Extra = x.Extra,
CreateUser = x.CreateUser,
CreateTime = x.CreateTime,
PodFileUrls = x.PodFileUrls?.Split(",").ToList() ?? Empty<string>.List,
TrackFlags = BuildTrackFlags(trackFlagIdsDict, flagDict, x)
}).ToList();
res.Add(item);

View File

@ -1,23 +1,16 @@
using DotNetCore.CAP;
using Fake.Application;
using InterfaceForward.Domain.Shared.Helpers;
using Microsoft.AspNetCore.Http;
using SJZY.Expand.ABP.Core.PublicMethords;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Application.Events;
using TrackCenter.Application.Services.ProvideServices;
using TrackCenter.Domain.Aggregates.OrderAggregate;
using TrackCenter.Domain.Aggregates.TrackContentAggregate;
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
using TrackCenter.Domain.Aggregates.TrackTemplateAggregate;
using TrackCenter.Domain.Enums;
using TrackCenter.Domain.Extensions;
using TrackCenter.Domain.Queries;
using SJZY.UserCenter.HttpApi.Services;
using SJZY.UserCenter.HttpApi.Services.User;
using TrackCenter.Application.Helpers;
using TrackCenter.Repository.Otel;
using Volo.Abp.Http.Client.DynamicProxying;
namespace TrackCenter.Application.Services.ProvideServices;
@ -26,7 +19,7 @@ namespace TrackCenter.Application.Services.ProvideServices;
/// </summary>
[ApiExplorerSettings(GroupName = "提供接口")]
[Route("/ProviderService")]
public partial class TrackService : ApplicationServiceBase, ITrackService
public partial class TrackService : ApplicationServiceBase
{
private readonly ICapPublisher _capPublisher;
private readonly IOrderRepository _orderRepository;
@ -37,14 +30,12 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
ICapPublisher capPublisher,
ITrackDetailsEsRepository trackDetailsEsRepository,
ITrackContentRepository trackContentRepository,
ITrackTemplateRepository trackTemplateRepository,
IHttpContextAccessor contextAccessor,
ITrackTemplateRepository trackTemplateRepository
)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_trackContentRepository = trackContentRepository;
_contextAccessor = contextAccessor;
_trackTemplateRepository = trackTemplateRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
}
@ -123,10 +114,8 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
CompareValue = input.CompareValue,
Source = input.Source,
TrackIdentifiers = input.TrackIdentifiers,
OperationMan = userInfo.Data.UserName
OperationMan = CurrentUser.UserName
});
return new UnifyResultDto(true, true);
}
/// <summary>
@ -135,43 +124,30 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("Track/BatchDeleteByEsId")]
public async Task<UnifyResultDto> BatchDeleteTrackByEsIdAsync(BatchDeleteTrackByEsIdInput input)
public async Task BatchDeleteTrackByEsIdAsync(BatchDeleteTrackByEsIdInput input)
{
var esIds = input.TrackContentEsIds.ToArray();
var tracks = await _trackDetailsEsRepository.GetDataByIds(esIds);
if (tracks.Count == 0)
return new UnifyResultDto(false, "未找到指定id下的轨迹内容");
throw new BusinessException("未找到指定id下的轨迹内容");
if (tracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode()))
return new UnifyResultDto(false, "存在来源于外部服务商的轨迹,不允许删除");
throw new BusinessException("存在来源于外部服务商的轨迹,不允许删除");
if (tracks.GroupBy(m => m.SubSystemId).Count() > 1)
return new UnifyResultDto(false, "暂不支持跨子系统批量删除");
throw new BusinessException("暂不支持跨子系统批量删除");
// 获取用户name
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
// 删除轨迹
var currentUser = CurrentUserRequest.CurrentUser;
var userName = userInfo.Data.UserName;
if (string.IsNullOrEmpty(userName))
userName = (await _orderRepository.GetUserById(currentUser.UserId))?.Name;
var @event = new TrackDeleteEvent
{
EsIds = esIds,
SubSystemId = tracks.First().SubSystemId,
Source = input.Source,
OperationMan = userName,
OperationManId = currentUser.UserId
OperationMan = CurrentUser.UserName,
OperationManId = CurrentUser.Id?? Guid.Empty,
};
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event);
return new UnifyResultDto(true);
}
/// <summary>
@ -181,7 +157,7 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Track/BatchDelete")]
public async Task<UnifyResultDto> BatchDeleteTrackAsync(BatchDeleteTrackInput input)
public async Task BatchDeleteTrackAsync(BatchDeleteTrackInput input)
{
input.OrderNos = input.OrderNos.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToList(); // 单号去重、去空
var orderIds = await _orderRepository.GetListAsync(x =>
@ -222,13 +198,6 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除");
}
// 获取用户name
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
// 删除轨迹
var @event = new TrackDeleteEvent
{
@ -238,11 +207,9 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
CompareFields = input.CompareFields,
CompareValue = input.CompareValue,
SubSystemId = input.SubSystemId,
OperationMan = userInfo.Data.UserName,
OperationMan = CurrentUser.UserName
};
await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event);
return new UnifyResultDto(true, true);
}
/// <summary>
@ -252,7 +219,7 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Track/Upsert")]
public async Task<UnifyResultDto> UpsertTrackAsync(UpsertTrackInput input)
public async Task UpsertTrackAsync(UpsertTrackInput input)
{
using var activity = ActivityHelper.CreateSpanActivity("UpsertTrack");
activity.SetTag("UpsertTrackInput", input.ToJson());
@ -290,18 +257,6 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
input.NewExtra,
input.CompareFields);
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
//todo:保留之前的方式(FBA在用) 后续可能去除
userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync(input.OptionUserId);
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
var name = userInfo.Data.UserName;
// 新增/更新轨迹
input.TrackTimeZone ??= GlobalConst.NoneTrackTimeZone; //时区为空使用一个特殊值填充
var @event = ObjectMapper.Map<UpsertTrackInput, TrackUpsertEvent>(input);
@ -316,9 +271,8 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
OrderType = input.OrderType,
}
};
@event.OperationMan = name;
@event.OperationMan = CurrentUser.UserName;
await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, @event);
return new UnifyResultDto(true, true);
}
/// <summary>
@ -328,7 +282,7 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
/// <returns></returns>
/// <exception cref="BusinessException"></exception>
[HttpPost("Track/BatchUpsert")]
public async Task<UnifyResultDto> BatchUpsertTrackAsync(BatchUpsertTrackInput input)
public async Task BatchUpsertTrackAsync(BatchUpsertTrackInput input)
{
using var activity = ActivityHelper.CreateSpanActivity("BatchUpsert");
activity.SetTag("BatchUpsertTrackInput", input.ToJson());
@ -373,13 +327,6 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
input.NewExtra,
input.CompareFields);
// 获取用户name
var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync();
if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null)
{
throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}");
}
// 按客户新增/更新轨迹(为了推送轨迹时方便)
foreach (var group in orders.GroupBy(x => x.CustomId))
{
@ -399,9 +346,8 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
TrackTime = input.TrackTime,
NewTrackTime = input.NewTrackTime,
TrackTimeZone = input.TrackTimeZone ?? GlobalConst.NoneTrackTimeZone, //时区为空使用一个特殊值填充
OperationMan = userInfo.Data.UserName,
OperationManId = userInfo.Data.UserId,
IsVisibility = input.IsVisibility,
OperationMan = CurrentUser.UserName,
OperationManId = CurrentUser.Id?? Guid.Empty,
IsExternal = input.IsExternal,
Source = input.Source,
Remarks = input.Remarks,
@ -425,8 +371,6 @@ public partial class TrackService : ApplicationServiceBase, ITrackService
};
await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, @event);
}
return new UnifyResultDto(true, true);
}
//在轨迹更新主流程之前的前置检查

View File

@ -1,3 +1,4 @@
using Fake.Application.Dtos;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Application.Services.ProvideServices;
using TrackCenter.Domain.Aggregates.TrackTemplateAggregate;
@ -10,7 +11,7 @@ namespace TrackCenter.Application.Services.ProvideServices;
/// </summary>
[ApiExplorerSettings(GroupName = "提供接口")]
[Route("/ProviderService")]
public class TrackTemplateService : ApplicationServiceBase, ITrackTemplateService
public class TrackTemplateService : ApplicationServiceBase
{
private readonly ITrackTemplateRepository _trackTemplateRepository;

View File

@ -1,17 +1,14 @@
using System.Linq.Expressions;
using Fake.Application;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using SJZY.Expand.ABP.Core;
using SJZY.Expand.ABP.Core.Logger;
using TrackCenter.Application.Dtos.TrackPull;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Options;
using TrackCenter.Domain.Aggregates.OrderAggregate;
using TrackCenter.Domain.Enums;
using TrackCenter.Domain.Extensions;
using TrackCenter.Domain.Queries;
using TrackCenter.Repository.Queries;
using TrackCenter.Repository.Redis;
namespace TrackCenter.Application.Services.TimerTaskss;
@ -24,10 +21,10 @@ public class TrackButtomUpScanTask : ApplicationServiceBase
{
private readonly IOptionsSnapshot<TrackAppOptions> _options;
private readonly IOrderRepository _orderRepository;
private readonly ITimezoneQueries _timezoneQueries;
private readonly TimezoneQueries _timezoneQueries;
public TrackButtomUpScanTask(IOptionsSnapshot<TrackAppOptions> options, IOrderRepository orderRepository,
ITimezoneQueries timezoneQueries)
TimezoneQueries timezoneQueries)
{
_options = options;
_orderRepository = orderRepository;

View File

@ -1,10 +1,10 @@
using Fake.Application.Dtos;
using TrackCenter.Application.Dtos.TrackNode;
using TrackCenter.Application.Dtos.TrackStatus;
using TrackCenter.Application.Helpers;
using TrackCenter.Domain.Aggregates.TrackNodeAggregate;
using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
using TrackCenter.Domain.Dtos.TrackNode;
using Volo.Abp.DependencyInjection;
namespace TrackCenter.Application.Services.TrackNode;
@ -13,7 +13,7 @@ namespace TrackCenter.Application.Services.TrackNode;
/// </summary>
[ApiExplorerSettings(GroupName = "轨迹节点")]
[Route("/Track/Node")]
public class TrackNodeService : ApplicationServiceBase, ITransientDependency
public class TrackNodeService : ApplicationServiceBase
{
private readonly ITrackNodeRepository _trackNodeRepository;
private readonly ITrackStatusRepository _trackStatusRepository;
@ -30,15 +30,24 @@ public class TrackNodeService : ApplicationServiceBase, ITransientDependency
/// </summary>
/// <returns></returns>
[HttpPost("Edit")]
public async Task<UnifyResultDto> EditAsync(EditTrackNodeInput input)
public async Task EditAsync(EditTrackNodeInput input)
{
return input.Options switch
switch (input.Options)
{
OptionType.新增 => await AddAsync(input),
OptionType.修改 => await UpdateAsync(input),
OptionType.删除 => await DeleteAsync(input),
_ => throw new BusinessException(message: "当前操作动作不合法")
};
case OptionType.无:
break;
case OptionType.新增:
await AddAsync(input);
break;
case OptionType.删除:
await DeleteAsync(input);
break;
case OptionType.修改:
await UpdateAsync(input);
break;
default:
throw new BusinessException(message: "当前操作动作不合法");
}
}
/// <summary>
@ -60,10 +69,10 @@ public class TrackNodeService : ApplicationServiceBase, ITransientDependency
/// <param name="nodeid">节点id</param>
/// <returns></returns>
[HttpPost("GetDetail")]
public async Task<UnifyResultDto> GetDetail(int nodeid)
public async Task<TrackNodeDto> GetDetail(int nodeid)
{
var resultDto = await _trackNodeRepository.GetDetail(nodeid);
return new UnifyResultDto(true, resultDto);
return resultDto;
}
/// <summary>
@ -81,14 +90,14 @@ public class TrackNodeService : ApplicationServiceBase, ITransientDependency
#region 私有方法
private async Task<UnifyResultDto> AddAsync(EditTrackNodeInput input)
private async Task AddAsync(EditTrackNodeInput input)
{
#region 业务校验
if (string.IsNullOrEmpty(input.NodeName) || string.IsNullOrEmpty(input.NodeEnName) ||
!input.SubSystemId.HasValue)
{
return new UnifyResultDto(GlobalConst.SuccessCode, "NodeName,NodeEnName,SubSystemId 必填", false);
throw new BusinessException("NodeName,NodeEnName,SubSystemId 必填");
}
var trackNodesEntitys = await _trackNodeRepository.GetListAsync(a =>
@ -113,44 +122,40 @@ public class TrackNodeService : ApplicationServiceBase, ITransientDependency
if (!StringHelper.IsValidEnglishString(input.NodeEnName))
{
return new UnifyResultDto(GlobalConst.SuccessCode, "节点英文名称必须是英文和常用英文符号", false);
throw new BusinessException("节点英文名称必须是英文和常用英文符号");
}
var entity = ObjectMapper.Map<EditTrackNodeInput, TrackNodeEntity>(input);
var isTrue = await _trackNodeRepository.InsertAsync(entity);
return new UnifyResultDto(GlobalConst.SuccessCode, "新增成功", isTrue);
await _trackNodeRepository.InsertAsync(entity);
}
private async Task<UnifyResultDto> UpdateAsync(EditTrackNodeInput input)
private async Task UpdateAsync(EditTrackNodeInput input)
{
#region 业务校验
if (string.IsNullOrEmpty(input.NodeName) || string.IsNullOrEmpty(input.NodeEnName))
{
return new UnifyResultDto(GlobalConst.SuccessCode, "NodeName,NodeEnName 必填", false);
throw new BusinessException("NodeName,NodeEnName 必填");
}
var trackNodesEntitys = await _trackNodeRepository.GetFirstAsync(a => a.Id == input.Id);
if (trackNodesEntitys == null)
{
return new UnifyResultDto(GlobalConst.SuccessCode, "没有找到数据,更新失败", false);
throw new BusinessException("没有找到数据,更新失败");
}
#endregion
trackNodesEntitys.NodeEnName = input.NodeEnName;
trackNodesEntitys.NodeName = input.NodeName;
var isTrue = await _trackNodeRepository.UpdateAsync(trackNodesEntitys);
return new UnifyResultDto(GlobalConst.SuccessCode, "修改成功", isTrue);
await _trackNodeRepository.UpdateAsync(trackNodesEntitys);
}
private async Task<UnifyResultDto> DeleteAsync(EditTrackNodeInput input)
private async Task DeleteAsync(EditTrackNodeInput input)
{
if (input.Id <= 0 || !input.SubSystemId.HasValue)
{
return new UnifyResultDto(GlobalConst.SuccessCode, "SubSystemId 必填", false);
throw new BusinessException("SubSystemId 必填");
}
var trackStatus =
@ -158,11 +163,10 @@ public class TrackNodeService : ApplicationServiceBase, ITransientDependency
val.Node == input.Id && val.SubSystemId == input.SubSystemId);
if (trackStatus != null)
{
return new UnifyResultDto(GlobalConst.SuccessCode, "删除失败,该节点已被使用", false);
throw new BusinessException("删除失败,该节点已被使用");
}
var rows = await _trackNodeRepository.DeleteById(input.Id);
return new UnifyResultDto(GlobalConst.SuccessCode, "删除成功", rows > 0);
await _trackNodeRepository.DeleteById(input.Id);
}
#endregion

View File

@ -1,281 +0,0 @@
using System.Linq.Expressions;
using Microsoft.Extensions.Localization;
using SJZY.Expand.ABP.Core.Localization;
using SJZY.Expand.ABP.Core.PublicMethords;
using TrackCenter.Application.Dtos.TrackTemplates;
using TrackCenter.Application.Localization;
using TrackCenter.Domain.Aggregates.TrackTemplateAggregate;
using TrackCenter.Domain.Dtos.TrackTemplate;
namespace TrackCenter.Application.Services.TrackTemplates;
/// <summary>
/// 轨迹模版
/// </summary>
[ApiExplorerSettings(GroupName = "轨迹模版")]
[Route("/Track/Template")]
public class TrackTemplateService : ApplicationServiceBase
{
private readonly IStringLocalizer<TrackLocalizationResource> _stringLocalizer;
private readonly ITrackTemplateRepository _trackTemplateRepository;
/// <inheritdoc/>
public TrackTemplateService(
IStringLocalizer<TrackLocalizationResource> stringLocalizer,
ITrackTemplateRepository trackTemplateRepository
)
{
_stringLocalizer = stringLocalizer;
_trackTemplateRepository = trackTemplateRepository;
}
/// <summary>
/// 轨迹模版编辑
/// </summary>
/// <returns></returns>
[HttpPost("Edit")]
public async Task<UnifyResultDto> EditAsync(EditTrackTemplateInput input)
{
return input.Options switch
{
OptionType.新增 => await AddAsync(input),
OptionType.修改 => await UpdateAsync(input),
OptionType.删除 => await DeleteAsync(input),
_ => throw new BusinessException(message: "当前操作动作不合法")
};
}
/// <summary>
/// 根据模版id获取轨迹模版
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("GetDetail")]
public async Task<GetTrackTemplateDetailVO?> GetDetailAsync(int id)
{
return await _trackTemplateRepository.GetDetailAsync(id);
}
/// <summary>
/// 获取分页轨迹模版
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetList")]
public async Task<PagedResult<GetTrackTemplateListDto>> GetPageTrackTemplate(GetPageTrackTemplateInput input)
{
var vo = ObjectMapper.Map<GetPageTrackTemplateInput, GetPageTrackTemplateVO>(input);
var resultDto = await _trackTemplateRepository.GetPageTrackTemplate(vo);
var pageResultDto =
ObjectMapper.Map<PagedResult<GetTrackTemplateListVO>, PagedResult<GetTrackTemplateListDto>>(
resultDto);
return pageResultDto;
}
/// <summary>
/// 轨迹模板内容列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetContentPage")]
public async Task<PagedResult<GetTrackTemplateContentPageOutputVO>> GetContentPageAsync(
[FromBody] GetTrackTemplateContentPageInputVO input)
{
return await _trackTemplateRepository.GetContentPageAsync(input);
}
/// <summary>
/// 轨迹模板内容详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("GetContentDetail")]
public async Task<GetTrackTemplateContentDetailOutputVo> GetContentDetailAsync(int id)
{
return await _trackTemplateRepository.GetContentDetailAsync(id);
}
/// <summary>
/// 轨迹模板内容编辑
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("EditContent")]
public async Task<bool> EditContentAsync([FromBody] EditTrackTemplateContentInput input)
{
if (input.Options != OptionTypeNoNull.删除)
{
Expression<Func<TrackTemplateContentEntity, bool>> isContentAnyExp = x =>
x.TrackTemplateId == input.TrackTemplateId && x.TrackContentId == input.TrackContentId;
if (input.Options == OptionTypeNoNull.修改) isContentAnyExp = isContentAnyExp.And(x => x.Id != input.Id);
if (await _trackTemplateRepository.IsContentAnyAsync(isContentAnyExp))
throw new BusinessException(message: _stringLocalizer["IsTrackTemplateContentAny"]);
}
switch (input.Options)
{
case OptionTypeNoNull.新增:
{
int maxSort = await _trackTemplateRepository.ContentMaxAsync(x => x.Sort,
x => x.TrackTemplateId == input.TrackTemplateId);
var entity = ObjectMapper.Map<EditTrackTemplateContentInput, TrackTemplateContentEntity>(input);
entity.Sort = maxSort + 1;
await _trackTemplateRepository.InsertContentAsync(entity);
break;
}
case OptionTypeNoNull.修改:
{
var entity = await _trackTemplateRepository.GetContentByIdAsync(input.Id!.Value) ??
throw new BusinessException(message: _stringLocalizer["NotFindData"]);
ObjectMapper.Map(input, entity);
await _trackTemplateRepository.UpdateContentAsync(entity);
break;
}
case OptionTypeNoNull.删除:
{
var entity = await _trackTemplateRepository.GetContentByIdAsync(input.Id!.Value) ??
throw new BusinessException(message: _stringLocalizer["NotFindData"]);
using var ts = TransactionScopeHelper.Create();
await _trackTemplateRepository.SoftDeleteContentAsync(input.Id!.Value);
await _trackTemplateRepository.UpdateContentAsync(x => x.Sort == x.Sort - 1,
x => x.TrackTemplateId == entity.TrackTemplateId && x.Sort > entity.Sort);
ts.Complete();
break;
}
default:
throw new BusinessException(message: _stringLocalizer[SjzyBasicLocalization.Validation_IsInEnum,
nameof(EditTrackTemplateContentInput.Options)]);
}
return true;
}
/// <summary>
/// 轨迹模板内容排序
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("SortContent")]
public async Task<bool> SortContentAsync([FromBody] SortTrackTemplateContentInput input)
{
List<int> ids = input.SortInfo.Select(x => int.Parse(x.Key)).ToList();
var trackTemplateContentEntities = await _trackTemplateRepository.GetContentListAsync(x => ids.Contains(x.Id));
trackTemplateContentEntities.ForEach(item =>
{
item.Sort = input.SortInfo.Single(x => x.Key == item.Id.ToString()).Value;
});
await _trackTemplateRepository.UpdateContentRangeAsync(trackTemplateContentEntities);
return true;
}
#region 私有方法
private async Task<UnifyResultDto> AddAsync(EditTrackTemplateInput input)
{
#region 业务校验
if (await _trackTemplateRepository.IsAnyAsync(a =>
a.SubSystemId == input.SubSystemId && a.Code == input.Code && a.Name == input.Name &&
a.CategoryId == input.CategoryId && a.CustomerId == input.CustomerId))
{
throw new BusinessException(message: "该子系统下该分类的客户模版已存在");
}
//校验模版代码唯一性
var templateEntitys = await _trackTemplateRepository.GetListAsync(a =>
a.SubSystemId == input.SubSystemId && (a.Code == input.Code || a.Name == input.Name));
if (templateEntitys.Any())
{
var trackTemplateCode = templateEntitys.FirstOrDefault(t => t.Code == input.Code);
if (trackTemplateCode != null)
{
throw new BusinessException(message: "该子系统下模板代码已存在");
}
var trackTemplateName = templateEntitys.FirstOrDefault(t => t.Name == input.Name);
if (trackTemplateName != null)
{
throw new BusinessException(message: "该子系统下模板名称已存在");
}
}
#endregion 业务校验
var entity = ObjectMapper.Map<EditTrackTemplateInput, TrackTemplateEntity>(input);
var isTrue = await _trackTemplateRepository.InsertAsync(entity);
return new UnifyResultDto(GlobalConst.SuccessCode, "新增成功", isTrue);
}
private async Task<UnifyResultDto> UpdateAsync(EditTrackTemplateInput input)
{
#region 业务校验
if (await _trackTemplateRepository.IsAnyAsync(a =>
a.Id != input.Id &&
a.SubSystemId == input.SubSystemId && a.Code == input.Code && a.Name == input.Name &&
a.CategoryId == input.CategoryId && a.CustomerId == input.CustomerId))
{
throw new BusinessException(message: "该子系统下该分类的客户模版已存在");
}
var templateEntitys = await _trackTemplateRepository.GetListAsync(a =>
a.Id != input.Id && a.SubSystemId == input.SubSystemId && (a.Code == input.Code || a.Name == input.Name));
if (templateEntitys.Any())
{
//throw new BusinessException(message: "模板代码已存在于当前选择的子系统下");
var trackTemplateCode = templateEntitys.FirstOrDefault(t => t.Code == input.Code);
if (trackTemplateCode != null)
{
throw new BusinessException(message: "该子系统下模板代码已存在");
}
var trackTemplateName = templateEntitys.FirstOrDefault(t => t.Name == input.Name);
if (trackTemplateName != null)
{
throw new BusinessException(message: "该子系统下模板名称已存在");
}
}
#endregion 业务校验
var templateEntity = await _trackTemplateRepository.GetFirstAsync(a => a.Id == input.Id);
if (templateEntity == null)
{
throw new BusinessException(message: "模版不存在");
}
ObjectMapper.Map(input, templateEntity);
var isTrue = await _trackTemplateRepository.UpdateAsync(templateEntity);
return new UnifyResultDto(GlobalConst.SuccessCode, "修改成功", isTrue);
}
private async Task<UnifyResultDto> DeleteAsync(EditTrackTemplateInput input)
{
var isTrue = await _trackTemplateRepository.SoftDeleteAsync(x => x.Id == input.Id);
return new UnifyResultDto(GlobalConst.SuccessCode, "删除成功", isTrue);
}
#endregion 私有方法
}

View File

@ -1,4 +1,3 @@
using Fake.Application;
using Fake.Application.Dtos;
using InterfaceForward.Domain.Shared.Helpers;
using SJZY.Expand.ABP.Core.PublicMethords;

View File

@ -1,4 +1,3 @@
using Fake.DependencyInjection;
using TrackCenter.Application.Services.Warning.Model;
using TrackCenter.Application.Services.Warning.Signals;
using TrackCenter.Domain.Aggregates.OrderAggregate;

View File

@ -300,7 +300,7 @@ public class ReturnWarningService : ApplicationServiceBase
SubSystemId = detail.SubSystemId,
SubSystemCode = subSystemCodeDict.GetValueOrDefault(detail.SubSystemId, $"系统代码{detail.SubSystemId}不存在"),
CustomId = detail.CustomId,
OrderIdEsIdVersionDict = new Dictionary<int, KeyValuePair<string, long>>
OrderIdTrackMetadataDict = new Dictionary<int, KeyValuePair<string, long>>
{
[detail.OrderId] = KeyValuePair.Create(detail.TrackEsId, detail.TrackVersion)
},

View File

@ -1,5 +1,3 @@
using Fake.Application;
using Fake.DependencyInjection;
using Microsoft.Extensions.Options;
using TrackCenter.Application.Helpers;

View File

@ -1,5 +1,4 @@
using Fake;
using Fake.Application;
namespace TrackCenter.Application.Track2InterfaceRelay;

View File

@ -8,10 +8,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fake.AspNetCore" Version="8.0.0-preview11" />
<PackageReference Include="Fake.EventBus.RabbitMQ" Version="8.0.0-preview11" />
<PackageReference Include="Fake.ObjectMapping.AutoMapper" Version="8.0.0-preview11" />
<PackageReference Include="Fake.AspNetCore" Version="8.0.0-preview12" />
<PackageReference Include="Fake.EventBus.RabbitMQ" Version="8.0.0-preview12" />
<PackageReference Include="Fake.ObjectMapping.AutoMapper" Version="8.0.0-preview12" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="Confluent.Kafka" Version="2.10.0" />
<PackageReference Include="FeishuNetSdk" Version="2.4.6" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />

View File

@ -16,6 +16,7 @@ using TrackCenter.Application.Kafka;
using TrackCenter.Application.CAP;
using TrackCenter.Application.Hangfire;
using TrackCenter.Application.Options;
using TrackCenter.Application.Rabbit;
using TrackCenter.Domain.Extensions;
namespace TrackCenter.Application;
@ -91,11 +92,11 @@ public class TrackCenterApplicationModule : FakeModule
Console.WriteLine("-- 当前为VS本地环境,用内存存储和内存队列");
}
Action<RabbitOptions> action = options =>
context.Services.AddHostedService<RabbitClient>();
context.Services.Configure<RabbitOptions>(options =>
{
options.ConnectionString =
$"amqp://{configuration["RabbitMQ:UserName"]}:{configuration["RabbitMQ:Password"]}@{configuration["RabbitMQ:HostName"]}:{configuration["RabbitMQ:Port"]}";
options.PoolSize = configuration.GetValue<int>("RabbitMQ:PoolSize");
options.Declarations = new List<Action<IDeclaration>>()
{
o => o.ExchangeDeclare(GlobalConst.TrackChangeExchange, ExchangeType.Topic, true),
@ -130,24 +131,21 @@ public class TrackCenterApplicationModule : FakeModule
}
}
};
};
context.Services.Configure(action);
context.Services.AddRabbitBoot(action, typeof(TrackCenterApplicationModule).Assembly);
});
context.Services.Configure<TrackAppOptions>(configuration.GetSection("TrackApp"));
context.Services.Configure<InterfaceRelayParamsConfigOptions>(
configuration.GetSection("InterfaceRelayParamsConfig"));
//注册阿里云OSS服务
context.Services.AddAliOSS(option =>
{
option.AccessKeyId = configuration["OSS:AccessKeyId"];
option.AccessKeySecret = configuration["OSS:AccessKeySecret"];
option.Endpoint = configuration["OSS:Endpoint"];
option.BucketName = configuration["OSS:BucketName"];
option.ReturnHttpsUrl = true;
});
// //注册阿里云OSS服务
// context.Services.AddAliOSS(option =>
// {
// option.AccessKeyId = configuration["OSS:AccessKeyId"];
// option.AccessKeySecret = configuration["OSS:AccessKeySecret"];
// option.Endpoint = configuration["OSS:Endpoint"];
// option.BucketName = configuration["OSS:BucketName"];
// option.ReturnHttpsUrl = true;
// });
var connectionString = $"{configuration["Redis:Host"]}" +
$",password={configuration["Redis:Password"]}" +
@ -192,14 +190,14 @@ public class TrackCenterApplicationModule : FakeModule
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpHttpClientBuilderOptions>(options =>
{
options.ProxyClientActions.Add((_, _, httpClient) =>
{
//如果设置Token,前端需要传入Token 否则引发异常
httpClient.SetTokenHeader();
httpClient.Timeout = TimeSpan.FromMinutes(3);
});
});
// PreConfigure<AbpHttpClientBuilderOptions>(options =>
// {
// options.ProxyClientActions.Add((_, _, httpClient) =>
// {
// //如果设置Token,前端需要传入Token 否则引发异常
// httpClient.SetTokenHeader();
// httpClient.Timeout = TimeSpan.FromMinutes(3);
// });
// });
}
}

View File

@ -1,6 +1,5 @@
using Fake.Application.Dtos;
using TrackCenter.Domain.Dtos.Order;
using TrackCenter.Domain.VEntities;
namespace TrackCenter.Domain.Aggregates.OrderAggregate;
@ -183,18 +182,4 @@ public interface IOrderRepository : IBasicRepository<OrderEntity>
Task<int> UpdateOrderPullConfigForServiceProviderAsync(UpdateOrderPullConfigsInputVo inputVo);
#endregion
/// <summary>
/// 根据id获取子系统信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<VSystemEntity> GetSystemInfoById(int id);
/// <summary>
/// 根据id获取员工姓名
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<VUserEntity> GetUserById(int id);
}

View File

@ -110,7 +110,7 @@ public class TrackDetailsIndex : IdVersionIndex
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 修改人
@ -192,6 +192,16 @@ public class TrackDetailsIndex : IdVersionIndex
/// </summary>
public string? Extra { get; set; }
/// <summary>
/// 轨迹内容标记Ids(逗号分割)
/// </summary>
public string? TrackFlagIds { get; set; }
/// <summary>
/// Pod文件地址(逗号分割)
/// </summary>
public string? PodFileUrls { get; set; }
/// <summary>
/// 如果时区为空,则兜底为utc+8
/// </summary>

View File

@ -22,6 +22,11 @@ public partial class GlobalConst
/// </summary>
public const string TrackRefreshSubscribe = "TrackCenter.TrackRefresh";
/// <summary>
/// 订单POD拉取
/// </summary>
public const string OrderPodPullSubscribe = "TrackCenter.OrderPodPull";
/// <summary>
/// 轨迹推送
/// </summary>
@ -35,7 +40,7 @@ public partial class GlobalConst
/// <summary>
/// 接口通交换机
/// </summary>
public const string InterfaceRelayExchange = "sjzy.interface.relay.direct";
public const string InterfaceRelayExchange = "interface.relay.direct";
/// <summary>
/// 轨迹推送完成(接口通回调)
@ -50,31 +55,31 @@ public partial class GlobalConst
/// <summary>
/// 轨迹变更通知交换机
/// </summary>
public const string TrackChangeExchange = "sjzy.track.on_track_change_ex";
public const string TrackChangeExchange = "track.on_track_change_ex";
/// <summary>
/// 轨迹推送通知交换机
/// </summary>
public const string TrackPushExchange = "sjzy.track.on_track_push_ex";
public const string TrackPushExchange = "track.on_track_push_ex";
// todo:以下需要在kafka创建主题
/// <summary>
/// 预警信号kafka 主题
/// </summary>
public const string WarningKafkaTopic = "sjzy_trackapi_warning";
public const string WarningKafkaTopic = "trackapi_warning";
/// <summary>
/// 预警信号消费组
/// </summary>
public const string WarningKafkaGroup = "sjzy_trackapi_warning_group";
public const string WarningKafkaGroup = "trackapi_warning_group";
/// <summary>
/// 预警信号 DLX kafka 主题
/// </summary>
public const string WarningDLXKafkaTopic = "sjzy_trackapi_warning_dlx";
public const string WarningDLXKafkaTopic = "trackapi_warning_dlx";
/// <summary>
/// 预警信号 DLX kafka消费组
/// </summary>
public const string WarningDLXKafkaGroup = "sjzy_trackapi_warning_dlx_group";
public const string WarningDLXKafkaGroup = "trackapi_warning_dlx_group";
}

View File

@ -0,0 +1,34 @@
namespace TrackCenter.Domain.Dtos.Common;
public class TrackMetadata
{
/// <summary>
/// 轨迹esId
/// </summary>
public string EsId { get; set; }
/// <summary>
/// 轨迹es 文档版本
/// </summary>
public long Version { get; set; }
/// <summary>
/// 轨迹创建时间
/// </summary>
public DateTime TrackCreateTime { get; set; }
public static TrackMetadata Build(string esId, long version, DateTime trackCreateTime)
{
return new TrackMetadata
{
EsId = esId,
Version = version,
TrackCreateTime = trackCreateTime
};
}
public TrackMetadata()
{
}
}

View File

@ -1,7 +1,14 @@
using TrackCenter.Domain.Extensions;
namespace TrackCenter.Domain.Dtos.TrackContents;
public class TrackContentVO
{
/// <summary>
/// 轨迹内容Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 子系统Id
/// </summary>
@ -62,6 +69,16 @@ public class TrackContentVO
/// </summary>
public bool IsAllowManuallyCreate { get; set; }
/// <summary>
/// 轨迹标记id,多个逗号分隔
/// </summary>
public string? TrackFlagId { get; set; }
/// <summary>
/// 轨迹标记
/// </summary>
public List<TrackFlagVO> TrackFlags { get; set; } = Empty<TrackFlagVO>.List;
/// <summary>
/// 是否广播
/// </summary>
@ -77,3 +94,32 @@ public class TrackContentVO
/// </summary>
public string TrackContentCode { get; set; }
}
public class TrackFlagVO
{
/// <summary>
/// 自增主键
/// </summary>
public int Id { get; set; }
/// <summary>
/// 标记代码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 标记名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 子系统id
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// 是否广播
/// </summary>
public bool IsToMq { get; set; }
}

View File

@ -30,26 +30,11 @@ public class TrackNodeDto
/// </summary>
public string? NodeEnName { get; set; }
/// <summary>
/// 是否删除
/// </summary>
public bool IsDeleted { get; set; }
/// <summary>
/// 创建人id
/// </summary>
public int CreateUserId { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 更新人
/// </summary>
public int? UpdateUserId { get; set; }
/// <summary>
/// 更新时间
/// </summary>

View File

@ -9,11 +9,11 @@
<ItemGroup>
<PackageReference Include="NEST" Version="7.17.5" />
<PackageReference Include="Fake.SqlSugarCore" Version="8.0.0-preview11" />
<PackageReference Include="Fake.SqlSugarCore" Version="8.0.0-preview12" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="4.2.3" />
<PackageReference Include="Fake.DomainDrivenDesign" Version="8.0.0-preview11" />
<PackageReference Include="Fake.DomainDrivenDesign" Version="8.0.0-preview12" />
</ItemGroup>
</Project>

View File

@ -290,14 +290,11 @@ public class OrderRepository : BasicRepository<OrderEntity>, IOrderRepository, I
exp.Or(x => x.OrderType == order.OrderType && x.SubSystemId == order.SubSystemId && x.OrderNo == order.OrderNo);
// 查出id
var queryOrders = await Context.Queryable<OrderEntity>()
.WhereIF(exp != null, exp!.ToExpression())
.Select(x => new
{
x.Id,
x.OrderNo
})
.ToListAsync();
var queryOrders = await GetListAsync(exp.ToExpression(), x => new
{
x.Id,
x.OrderNo
});
// 填充订单关联数据中的OrderId
orderNoIdDict = queryOrders.ToDictionary(x => x.OrderNo, x => x.Id);
@ -480,16 +477,6 @@ public class OrderRepository : BasicRepository<OrderEntity>, IOrderRepository, I
.ToListAsync();
}
public Task<VSystemEntity> GetSystemInfoById(int id)
{
return Context.Queryable<VSystemEntity>().FirstAsync(m => m.IsDeleted == false && m.Id == id);
}
public Task<VUserEntity> GetUserById(int id)
{
return Context.Queryable<VUserEntity>().FirstAsync(m => m.IsDeleted == false && m.Id == id);
}
private int UpdateOrderQuestionType(int questionType, IEnumerable<int> ids)
{
var idsString = string.Join(",", ids);

View File

@ -1,10 +1,11 @@
using System.Linq.Expressions;
using Fake.Domain.Entities.Auditing;
using TrackCenter.Domain.Aggregates.TrackContentAggregate;
using TrackCenter.Domain.Aggregates.TrackFlagAggregate;
using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
using TrackCenter.Domain.Aggregates.TrackTemplateAggregate;
using TrackCenter.Domain.Dtos.TrackContents;
using TrackCenter.Repository.VEntities;
using TrackCenter.Domain.VEntities;
namespace TrackCenter.Repository.Repositories;
@ -211,7 +212,7 @@ public class TrackContentRepository : BasicRepository<TrackContentEntity>, ITrac
{
return await Context.Updateable<TrackContentMapEntity>()
.Where(whereExpression)
.SetColumns(nameof(ISoftDeleteBasicEntity.IsDeleted), true)
.SetColumns(nameof(ISoftDelete.IsDeleted), true)
.ExecuteCommandHasChangeAsync();
}

View File

@ -1,6 +1,5 @@
using InterfaceForward.Domain.Shared.Helpers;
using Nest;
using SJZY.Expand.ABP.Core.PublicMethords;
using SJZY.Expand.ElasticSearch.Services;
using TrackCenter.Domain.Aggregates.OrderAggregate;
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
using TrackCenter.Domain.Aggregates.TrackPushAggregate;
@ -8,9 +7,7 @@ using TrackCenter.Domain.Consts;
using TrackCenter.Domain.Dtos.TrackDetails;
using TrackCenter.Domain.Enums;
using TrackCenter.Domain.Extensions;
using TrackCenter.Repository.Aggregates.OrderAggregate;
using TrackCenter.Repository.ES;
using ListWrapperFactory = TrackCenter.Repository.ES.ListWrapperFactory;
namespace TrackCenter.Repository.Repositories;
@ -379,7 +376,6 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
UpdateTime = DateTime.Now,
track.TrackContent,
track.TrackEnContent,
track.IsVisibility,
track.IsExternal,
track.Remarks,
track.Files,
@ -429,7 +425,6 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
ctx._source.updateTime = params.updateTime;
ctx._source.trackContent = params.trackContent;
ctx._source.trackEnContent = params.trackEnContent;
ctx._source.isVisibility = params.isVisibility;
ctx._source.isExternal = params.isExternal;
ctx._source.remarks = params.remarks;
ctx._source.files = params.files;
@ -453,7 +448,6 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
//S06新增字段更新
.Add("trackContent", track.TrackContent)
.Add("trackEnContent", track.TrackEnContent)
.Add("isVisibility", track.IsVisibility)
.Add("isExternal", track.IsExternal)
.Add("remarks", track.Remarks)
.Add("files", track.Files)
@ -508,7 +502,6 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
ctx._source.updateTime = params.updateTime;
ctx._source.trackContent = params.trackContent;
ctx._source.trackEnContent = params.trackEnContent;
ctx._source.isVisibility = params.isVisibility;
ctx._source.isExternal = params.isExternal;
ctx._source.remarks = params.remarks;
ctx._source.files = params.files;
@ -531,7 +524,6 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
//S06新增字段更新
.Add("trackContent", track.TrackContent)
.Add("trackEnContent", track.TrackEnContent)
.Add("isVisibility", track.IsVisibility)
.Add("isExternal", track.IsExternal)
.Add("remarks", track.Remarks)
.Add("files", track.Files)

View File

@ -1,15 +1,11 @@
using TrackCenter.Domain.Aggregates.TrackFlagAggregate;
using TrackCenter.Domain.Dtos.TrackFlags;
using TrackCenter.Repository.VEntities;
using TrackCenter.Domain.VEntities;
namespace TrackCenter.Repository.Repositories;
public class TrackFlagRepository : BasicRepository<TrackFlagEntity>, ITrackFlagRepository, IScopedDependency
{
public TrackFlagRepository()
{
}
/// <summary>
/// 获取轨迹标记明细
/// </summary>

View File

@ -1,6 +1,6 @@
using TrackCenter.Domain.Aggregates.TrackNodeAggregate;
using TrackCenter.Domain.Dtos.TrackNode;
using TrackCenter.Repository.VEntities;
using TrackCenter.Domain.VEntities;
namespace TrackCenter.Repository.Repositories;
@ -24,7 +24,6 @@ public class TrackNodeRepository : BasicRepository<TrackNodeEntity>, ITrackNodeR
SubSystemId = t.SubSystemId,
UpdateTime = t.UpdateTime,
CreateTime = t.CreateTime,
UpdateUserId = t.UpdateUserId,
UpdateUserName = u2.Name,
CreateUserName = u1.Name,
SystemName = u3.CnName
@ -71,7 +70,6 @@ public class TrackNodeRepository : BasicRepository<TrackNodeEntity>, ITrackNodeR
SubSystemId = t.SubSystemId,
UpdateTime = t.UpdateTime,
CreateTime = t.CreateTime,
UpdateUserId = t.UpdateUserId,
UpdateUserName = u2.Name,
CreateUserName = u1.Name,
SystemName = u3.CnName

View File

@ -1,12 +1,13 @@
using System.Linq.Expressions;
using SJZY.Expand.ABP.Core.PublicMethords;
using Fake.Domain.Entities.Auditing;
using InterfaceForward.Domain.Shared.Helpers;
using TrackCenter.Domain.Aggregates.TrackContentAggregate;
using TrackCenter.Domain.Aggregates.TrackPullAggregate;
using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
using TrackCenter.Domain.Dtos.Common;
using TrackCenter.Domain.Dtos.TrackPull;
using TrackCenter.Domain.Enums;
using TrackCenter.Repository.VEntities;
using TrackCenter.Domain.VEntities;
namespace TrackCenter.Repository.Repositories;
@ -136,7 +137,7 @@ public class TrackPullConfigRepository : BasicRepository<TrackPullConfigEntity>,
{
return await Context.Updateable<TrackPullContentConfigEntity>()
.Where(whereExpression)
.SetColumns(nameof(ISoftDeleteBasicEntity.IsDeleted), true)
.SetColumns(nameof(ISoftDelete.IsDeleted), true)
.ExecuteCommandHasChangeAsync();
}

View File

@ -1,11 +1,12 @@
using System.Linq.Expressions;
using SJZY.Expand.ABP.Core.PublicMethords;
using Fake.Domain.Entities.Auditing;
using InterfaceForward.Domain.Shared.Helpers;
using TrackCenter.Domain.Aggregates.TrackContentAggregate;
using TrackCenter.Domain.Aggregates.TrackPushAggregate;
using TrackCenter.Domain.Dtos.Common;
using TrackCenter.Domain.Dtos.TrackPush;
using TrackCenter.Domain.Enums;
using TrackCenter.Repository.VEntities;
using TrackCenter.Domain.VEntities;
namespace TrackCenter.Repository.Repositories;
@ -98,7 +99,7 @@ public class TrackPushConfigRepository : BasicRepository<TrackPushConfigEntity>,
{
return await Context.Updateable<TrackPushContentConfigEntity>()
.Where(whereExpression)
.SetColumns(nameof(ISoftDeleteBasicEntity.IsDeleted), true)
.SetColumns(nameof(ISoftDelete.IsDeleted), true)
.ExecuteCommandHasChangeAsync();
}

View File

@ -1,11 +1,8 @@
using Nest;
using SJZY.Expand.ABP.Core.Logger;
using SJZY.Expand.ElasticSearch.Services;
using TrackCenter.Domain.Aggregates.TrackPushAggregate;
using TrackCenter.Domain.Consts;
using TrackCenter.Domain.Extensions;
using TrackCenter.Repository.ES;
using ListWrapperFactory = TrackCenter.Repository.ES.ListWrapperFactory;
namespace TrackCenter.Repository.Repositories;

View File

@ -1,45 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.TestBase" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SJZY.Track.Api\SJZY.Track.Api.csproj" />
</ItemGroup>
</Project>

View File

@ -1,45 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.TestBase" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\TrackCenter.Api\TrackCenter.Api.csproj" />
</ItemGroup>
</Project>

View File

@ -1,19 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace TrackCenter.Test;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<TestModule>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.InitializeApplication();
}
}

View File

@ -1,63 +0,0 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using TrackCenter.Repository;
using TrackCenter.Api;
using Volo.Abp;
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Authorization;
using Volo.Abp.Autofac;
using Volo.Abp.Data;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
[assembly: HostingStartup(typeof(SJZY.Expand.ABP.Core.HostingStartup))]
namespace TrackCenter.Test;
[DependsOn(
typeof(AbpAspNetCoreTestBaseModule),
typeof(AbpAutofacModule),
typeof(AbpAuthorizationModule),
typeof(SjzyTrackRepositoryModule),
typeof(SjzyTrackApplicationModule),
typeof(TrackCenterApiModule),
typeof(AbpHttpClientModule) //添加abp的http代理模块
)]
[assembly: HostingStartup(typeof(Expand.ABP.Core.HostingStartup))]
public class TestModule : FakeModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAlwaysAllowAuthorization();
//添加内网服务
//context.Services.AddHttpClientProxies(typeof(LoggerCenterHttpApiModule).Assembly, "LoggerCenter", false);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
SeedTestData(context);
}
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
//根据业务场景决定,是否需要删除测试数据
}
/// <summary>
/// 生成种子测试数据
/// </summary>
/// <param name="context"></param>
private static void SeedTestData(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(async () =>
{
using (var scope = context.ServiceProvider.CreateScope())
{
await scope.ServiceProvider
.GetRequiredService<IDataSeeder>()
.SeedAsync();
}
});
}
}

View File

@ -1,13 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Volo.Abp.AspNetCore.TestBase;
namespace TrackCenter.Test;
public class TestModuleTestBase : AbpAspNetCoreIntegratedTestBase<Startup>
{
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
base.ConfigureServices(context, services);
}
}

View File

@ -1 +0,0 @@
global using Xunit;

View File

@ -1,91 +0,0 @@
{
"Environment": "Dev",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "server=192.168.80.81;Database=sjzy_package_setting;Uid=root;Pwd=10c1fb814e7c1552;SslMode=None;Charset=utf8mb4;",
"DataCenter": "server=192.168.80.81;Database=sjzy_data_center;Uid=root;Pwd=10c1fb814e7c1552;SslMode=None;Charset=utf8mb4;"
},
"Redis": {
"ConnectionString": "192.168.80.56:6379,abortConnect=false",
"PassWord": "sjzyadmin",
"DefaultDatabaseIndex": 8,
"Prefix": ""
},
"Consul": {
"ServiceName": "Package.Setting",
"ServiceIP": "192.168.80.55",
"ServicePort": 5047,
"ServiceHealthCheck": "http://192.168.80.55:5047/HealthCheck",
"ConsulAddress": "http://192.168.80.56:8500"
},
"CorsPolicy": [
""
],
"Swagger": {
"IsEnable": true,
"ServiceUrl": "",
"JsonUrlPrefix": "", //转发目录前缀,部署服务器时需要配置 /kingmall
"ScanPrefix": [ "SJZY" ] //全局扫描程序集或xml文件时只加载以 SJZY 开头的内容
},
"RabbitMQ": {
"UserName": "sjzy",
"Password": "sjzyadmin",
"HostName": "192.168.80.56",
"Port": 5672
},
"Custome": {
"Setting1": 12,
"Setting2": 22
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"System": "Warning",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
"WriteTo": [
{
"Name": "Console",
"Args": {
//"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, SystemConsoleTheme.Literate",
"outputTemplate": "{NewLine}[{Timestamp:yyyy-MM-dd HH:mm:ss fff} {Level:u3}] {Message:lj}"
}
},
{
"Name": "File",
"Args": {
"path": "logs/.log",
"rollingInterval": "Day",
"outputTemplate": "{NewLine}[{Timestamp:yyyy-MM-dd HH:mm:ss,fff} {Level:u3}] {Message:lj}"
}
},
{
"Name": "Elasticsearch",
"Args": {
"nodeUris": "http://192.168.80.97:9200",
"indexFormat": "log_package_setting_{0:yyyy.MM.dd}",
"autoRegisterTemplate": true,
"autoRegisterTemplateVersion": "ESv7",
"connectionGlobalHeaders": "Authorization=Basic ZWxhc3RpYzpzanp5YWRtaW4=;"
}
}
]
},
"OSS": {
"AccessKeyId": "LTAI5tJWyx1kgQfk221QFknu",
"AccessKeySecret": "0AvP3vyBdwT1pwvLXeqeJ448US8QL8",
"Endpoint": "http://oss-cn-shenzhen.aliyuncs.com/",
"BucketName": "cams3",
"ReturnHttpsUrl": true
}
}