This commit is contained in:
xiaolipro 2025-07-14 15:01:31 +08:00
parent 4ecced34a2
commit e1b2eef5f2
40 changed files with 447 additions and 425 deletions

View File

@ -1,5 +1,4 @@
using DotNetCore.CAP.Filter;
using Fake.Application;
using InterfaceForward.Domain.Shared.Helpers;
using TrackCenter.Application.Helpers;
using TrackCenter.Domain.Extensions;
@ -32,7 +31,7 @@ public class CapFilter : SubscribeFilter
if (await RedisHelper.Client.ExistsAsync($"CapExecuteFilter:{hash}{DateTime.Now:G}"))
{
throw new BusinessException(GlobalConst.CapNoRetryErrorCode, $"检测到重复消费,消费参数:{argumentJsonStr}");
throw new CapNoRetryException($"检测到重复消费,消费参数:{argumentJsonStr}");
}
}
}
@ -55,7 +54,7 @@ public class CapFilter : SubscribeFilter
{
context.DeliverMessage.Headers.TryGetValue("cap-msg-id", out var messageId);
if (context.Exception is BusinessException ex && ex.Code == GlobalConst.CapNoRetryErrorCode)
if (context.Exception is CapNoRetryException ex)
{
context.ExceptionHandled = true;
LogHelper.Warn(ex.ToString(), true);

View File

@ -0,0 +1,12 @@
using Fake;
using Fake.ExceptionHandling;
using Fake.Logging;
using Microsoft.Extensions.Logging;
namespace TrackCenter.Application.CAP;
public class CapNoRetryException(string? message = null, Exception? innerException = null)
: FakeException(message, innerException), IHasLogLevel
{
public LogLevel LogLevel { get; set; } = LogLevel.Warning;
}

View File

@ -6,12 +6,8 @@ using Fake.DependencyInjection;
using InterfaceForward.Domain.Shared.Helpers;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using SJZY.Expand.ABP.Core.PublicMethords;
using SJZY.InterfaceRelay.Application.Contracts;
using SJZY.OTel;
using TrackCenter.Application.Dtos.TrackPush;
using TrackCenter.Application.Events;
using TrackCenter.Application.Services;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Options;
using TrackCenter.Application.Services.Broadcasting;
@ -22,8 +18,7 @@ using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
using TrackCenter.Domain.Aggregates.TrackPushAggregate;
using TrackCenter.Domain.Enums;
using TrackCenter.Domain.Extensions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.DynamicProxying;
using TrackCenter.Repository.Otel;
namespace TrackCenter.Application.CAP.EventHandlers;

View File

@ -13,6 +13,7 @@ using TrackCenter.Domain.Dtos.TrackContents;
using TrackCenter.Domain.Enums;
using TrackCenter.Domain.Extensions;
using TrackCenter.Repository.Aggregates.OrderAggregate;
using TrackCenter.Repository.Otel;
using OrderIdentifier = TrackCenter.Application.Events.OrderIdentifier;
namespace TrackCenter.Application.CAP.EventHandlers;

View File

@ -399,6 +399,10 @@ public abstract class BasicCacheForPeriodicRefresh<TKey, TValue>
private async ValueTask<CacheEntry?[]> MGetRedisAsync(List<TKey> keys)
{
// tips: freeRedis单key的MGet当key不存在时,则返回长度为0的集合
if(keys.Count == 1)
return new[] { await GetRedisAsync(keys[0]) };
var redisKeys = keys.Select(BuildRedisKey).ToArray();
return await _redis.MGetAsync<CacheEntry?>(redisKeys);
}

View File

@ -1,44 +0,0 @@
using Fake.DependencyInjection;
using TrackCenter.Domain.Extensions;
using TrackCenter.Repository.Redis;
namespace TrackCenter.Application.Caching;
/// <summary>
/// 客户id简称缓存
/// </summary>
public class CustomAbbreviationDictCache : BasicCacheForPeriodicRefresh<int, string>, ISingletonDependency
{
private readonly IVExternalSubjectQueries _vExternalSubjectQueries;
public CustomAbbreviationDictCache(IVExternalSubjectQueries vExternalSubjectQueries) : base(RedisHelper.Client)
{
_vExternalSubjectQueries = vExternalSubjectQueries;
}
protected override string BuildRedisKey(int key) => $"{GlobalConst.TrackDataDictKeyPrefix}:CustomAbbreviation:{key}";
protected override TimeSpan RedisExpire => TimeSpan.FromSeconds(-1); // 设置缓存不过期,指定id客户被删除时仍能兜底提供简称
protected override TimeSpan LocalExpire => TimeSpan.FromMinutes(5); // 本地缓存5分钟过期后在次请求会刷新数据
protected override TimeSpan MissingKeySetNullRedisExpire => TimeSpan.FromMinutes(1); // 缓存不存在时设置redisNull
protected override async ValueTask<string?> LoadAsync(int key)
{
if (key == 0) return null;
return await _vExternalSubjectQueries.GetFirstAsync(x => x.Id == key, x => x.Abbreviation);
}
protected override async ValueTask<Dictionary<int, string>> LoadAsync(HashSet<int> keys)
{
if (keys.Count == 0) return Empty<int, string>.Dict;
var result = await _vExternalSubjectQueries.GetListAsync(x => keys.Contains(x.Id), x => new
{
x.Id,
x.Abbreviation
});
return result.Count == 0
? Empty<int, string>.Dict
: result.ToDictionary(x => x.Id, y => y.Abbreviation);
}
}

View File

@ -73,12 +73,6 @@ public class TrackUpsertEvent
public TrackRefreshStruct TrackRefreshData { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
[Obsolete("废弃的,请使用IsExternal")]
public bool IsVisibility { get; set; }
/// <summary>
/// 是否对外
/// </summary>

View File

@ -1,10 +1,5 @@
global using TrackCenter.Domain.Consts;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.Extensions.DependencyInjection;
global using SJZY.Expand.ABP.Common.Enums;
global using SJZY.Expand.ABP.Core.Common.Dto;
global using TrackCenter.Domain;
global using TrackCenter.Domain.Dtos.Common;
global using Volo.Abp;
global using Volo.Abp.Application.Services;
global using Volo.Abp.Validation;

View File

@ -1,3 +1,4 @@
using Fake.DependencyInjection;
using Microsoft.Extensions.Options;
using TrackCenter.Application.Dtos.Warning;
using TrackCenter.Application.Options;
@ -10,7 +11,6 @@ using TrackCenter.Domain.Dtos.Forewarning;
using TrackCenter.Domain.Dtos.Warning;
using TrackCenter.Domain.Enums;
using TrackCenter.Domain.Extensions;
using Volo.Abp.DependencyInjection;
namespace TrackCenter.Application.Hangfire;

View File

@ -1,8 +1,6 @@
using FeishuNetSdk;
using FeishuNetSdk.Im;
using FeishuNetSdk.Im.Dtos;
using SJZY.UserCenter.SharedModel.Dtos.Message;
using SJZY.UserCenter.SharedModel.Enums;
namespace TrackCenter.Application.Hangfire;

View File

@ -1,9 +1,9 @@
using Fake.DependencyInjection;
using Hangfire;
using Microsoft.Extensions.Options;
using TrackCenter.Application.Options;
using TrackCenter.Application.Services.Warning.Handlers;
using TrackCenter.Domain.Aggregates.WarningAggregate;
using Volo.Abp.DependencyInjection;
namespace TrackCenter.Application.Hangfire;

View File

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

View File

@ -0,0 +1,24 @@
using System.Net;
using System.Net.Sockets;
namespace TrackCenter.Application.Helpers;
public class NetUtil
{
/// <summary>
/// 得到局域网IP地址
/// </summary>
/// <returns></returns>
public static string GetLanIp()
{
foreach (var hostAddress in Dns.GetHostAddresses(Dns.GetHostName()))
{
if (hostAddress.AddressFamily == AddressFamily.InterNetwork)
{
return hostAddress.ToString();
}
}
return string.Empty;
}
}

View File

@ -6,6 +6,7 @@ using TrackCenter.Application.Options;
using TrackCenter.Application.Services.Warning.Handlers;
using TrackCenter.Application.Services.Warning.Signals;
using TrackCenter.Domain.Extensions;
using TrackCenter.Repository.Otel;
namespace TrackCenter.Application.Kafka;
@ -161,8 +162,8 @@ public sealed class WarningCalcBackgroundService : BackgroundService
// 3.需要重试的消息,重新投递到原队列
if (needRetryMetadata.Count > 0)
{
_activity?.SetTag("StartPublicRetryMessage", $"开始重新投递[{needRetryMetadata.Count}]条消息");
LogHelper.Warn($"开始重新投递[{needRetryMetadata.Count}]条消息");
_activity?.SetTag("StartPublicRetryMessage", $"[预警计算] 开始重新投递[{needRetryMetadata.Count}]条消息");
LogHelper.Warn($"[预警计算] 开始重新投递[{needRetryMetadata.Count}]条消息");
var tasks = needRetryMetadata.Select(async metadata =>
{
@ -171,15 +172,15 @@ public sealed class WarningCalcBackgroundService : BackgroundService
}).ToArray();
await Task.WhenAll(tasks);
_activity?.SetTag("EndPublicRetryMessage", $"已重新投递[{needRetryMetadata.Count}]条消息");
LogHelper.Warn($"已重新投递[{needRetryMetadata.Count}]条消息");
_activity?.SetTag("EndPublicRetryMessage", $"[预警计算] 已重新投递[{needRetryMetadata.Count}]条消息");
LogHelper.Warn($"[预警计算] 已重新投递[{needRetryMetadata.Count}]条消息", true);
}
// 4.进入死信的消息,投入到死信队列,等待人工处理
if (needEnqueueDlxMetadata.Count > 0)
{
_activity?.SetTag("StartPublicDLXMessage", $"开始投递死信[{needEnqueueDlxMetadata.Count}]条消息");
LogHelper.Warn($"开始投递死信[{needEnqueueDlxMetadata.Count}]条消息");
_activity?.SetTag("StartPublicDLXMessage", $"[预警计算] 开始投递死信[{needEnqueueDlxMetadata.Count}]条消息");
LogHelper.Warn($"[预警计算] 开始投递死信[{needEnqueueDlxMetadata.Count}]条消息");
var tasks = needEnqueueDlxMetadata.Select(async metadata =>
{
@ -194,8 +195,8 @@ public sealed class WarningCalcBackgroundService : BackgroundService
}).ToArray();
await Task.WhenAll(tasks);
_activity?.SetTag("StartPublicDLXMessage", $"已进入死信队列[{needEnqueueDlxMetadata.Count}]条消息");
LogHelper.Warn($"已进入死信队列[{needEnqueueDlxMetadata.Count}]条消息");
_activity?.SetTag("StartPublicDLXMessage", $"[预警计算] 已进入死信队列[{needEnqueueDlxMetadata.Count}]条消息");
LogHelper.Warn($"[预警计算] 已进入死信队列[{needEnqueueDlxMetadata.Count}]条消息", true);
}
}

View File

@ -4,9 +4,9 @@ using System.Text.Json;
using Confluent.Kafka;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using SJZY.Expand.ABP.Core.Logger;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Services.Warning.Signals;
using TrackCenter.Domain.Extensions;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace TrackCenter.Application.Kafka;

View File

@ -1,8 +1,8 @@
using Confluent.Kafka;
using Fake.DependencyInjection;
using Microsoft.Extensions.Configuration;
using TrackCenter.Application.Services.Warning.Signals;
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
using Volo.Abp.DependencyInjection;
namespace TrackCenter.Application.Kafka;

View File

@ -1,16 +0,0 @@
{
"culture": "en",
"texts": {
"NotNull1": "{0} is not null",
"NotFindData": "No data found under the specified number",
"ParameterDemo1": "{0} in {1} to do {2}",
"TemplateExist": "The template code already exists under the currently selected subsystem",
"TrackContentExist": "The trace code or trace content or trace content in the current subsystem is repeated in English",
"TrackStatusExist": "The trajectory status code or trajectory status or trajectory status in English is repeated under the current subsystem",
"TrackFlagExist": "Duplicate code or flag in the current subsystem",
"IsExistTrackPullConfig": "Track Pull The template has been bound. Delete the track pull before deleting the template",
"IsExistTrackStatus": "The trajectory content has been bound to the trajectory status. Delete the trajectory content before deleting the trajectory status",
"IsExistTrackFlag": "The track content has been bound to the track mark. Delete the track content before deleting the track mark",
"IsTrackPushConfigAny": "This subsystem already has the push mapping of this customer, and it cannot be added repeatedly"
}
}

View File

@ -1,16 +0,0 @@
{
"culture": "zh",
"texts": {
"NotFindData": "未找到指定编号下的任何数据!",
"ParameterDemo": "{0}在{1}地方做了{2}动作",
"ParameterDemo1": "{0}在{1}地方做了{2}动作",
"TemplateExist": "模板代码已存在于当前选择的子系统下",
"TrackContentExist": "当前子系统下轨迹代码或轨迹内容或轨迹内容英文重复",
"TrackStatusExist": "当前子系统下轨迹状态代码或轨迹状态或轨迹状态英文重复",
"TrackFlagExist": "当前子系统下代码或标记重复",
"IsExistTrackPullConfig": "轨迹拉取已绑定了该模版,请先删除轨迹拉取再删除该模版",
"IsExistTrackStatus": "轨迹内容已经绑定了该轨迹状态,请先删除轨迹内容后再删除该轨迹状态",
"IsExistTrackFlag": "轨迹内容已经绑定了该轨迹标记,请先删除轨迹内容后再删除该轨迹标记",
"IsTrackPushConfigAny": "这个子系统已经存在这个客户的推送映射,无法重复添加"
}
}

View File

@ -1,9 +0,0 @@
using SJZY.Expand.ABP.Core.Localization;
using Volo.Abp.Localization;
namespace TrackCenter.Application.Localization;
[InheritResource(typeof(SjzyBasicLocalization))]
public class TrackLocalizationResource
{
}

View File

@ -57,7 +57,7 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
.Select((a, b) => new OrderWithServiceProviderVO
{
//优先使用第三方的订单号,为空时才使用主单号
OrderNo = b.OrderNo ?? a.OrderNo,
OrderNo = string.IsNullOrWhiteSpace(b.OrderNo) ? a.OrderNo : b.OrderNo,
OrderType = a.OrderType,
SubSystemId = a.SubSystemId,
ServiceProviderCode = b.ServiceProviderCode,
@ -77,7 +77,7 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
.Select((a, b) => new OrderWithServiceProviderVO
{
//优先使用第三方的订单号,为空时才使用主单号
OrderNo = b.OrderNo ?? a.OrderNo,
OrderNo = string.IsNullOrWhiteSpace(b.OrderNo) ? a.OrderNo : b.OrderNo,
OrderType = a.OrderType,
SubSystemId = a.SubSystemId,
ServiceProviderCode = b.ServiceProviderCode

View File

@ -9,7 +9,6 @@ using Newtonsoft.Json.Linq;
using RabbitMQ.Client.Events;
using TrackCenter.Application.Dtos.TrackPull;
using TrackCenter.Application.Events;
using TrackCenter.Application.Services;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Services.TrackPull;
using TrackCenter.Domain.Aggregates.OrderAggregate;
@ -261,8 +260,6 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler
TrackTime = trackTime ?? DateTime.Now,
TrackTimeZone = trackTimeZone ?? GlobalConst.NoneTrackTimeZone,
IsAdd = true,
//S06新增
IsVisibility = false,
//服务商轨迹默认不对外
IsExternal = false,
Source = (int) TrackSourceEnum.ExternalServiceProvide,
@ -310,8 +307,6 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler
TrackContentId = GlobalConst.NotExistId,
TrackContent = GlobalConst.EmptyString,
TrackEnContent = GlobalConst.EmptyString,
//S06新增轨迹明细字段
IsVisibility = false,
//服务商轨迹默认不对外
IsExternal = false,
Source = (int) TrackSourceEnum.ExternalServiceProvide,

View File

@ -3,7 +3,6 @@ using Fake.Application;
using InterfaceForward.Domain.Shared.Helpers;
using Microsoft.AspNetCore.Http;
using SJZY.Expand.ABP.Core.PublicMethords;
using SJZY.OTel;
using TrackCenter.Application.Dtos.ProvideServices;
using TrackCenter.Application.Events;
using TrackCenter.Application.Services.ProvideServices;
@ -17,6 +16,7 @@ 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;

View File

@ -0,0 +1,10 @@
namespace TrackCenter.Application.Track2InterfaceRelay;
public class InterfaceForwardOptions
{
public string AppKey { get; set; }
public string AppSecret { get; set; }
public string Token { get; set; }
}

View File

@ -1,6 +1,7 @@
using Fake.Application;
using Fake.DependencyInjection;
using Microsoft.Extensions.Options;
using SJZY.InterfaceRelay.Application.Contracts;
using Volo.Abp.DependencyInjection;
using TrackCenter.Application.Helpers;
namespace TrackCenter.Application.Track2InterfaceRelay;
@ -28,8 +29,12 @@ public class Track2InterfaceRelayHttpMessageHandler : DelegatingHandler, ITransi
throw new BusinessException(message: $"子系统{subSystemCode}未在轨迹中台配置,请联系管理员");
}
var lanIp = NetUtil.GetLanIp();
request.Headers.TryAddWithoutValidation(nameof(InterfaceForwardOptions.AppKey), config.AppKey);
request.Headers.TryAddWithoutValidation(nameof(InterfaceForwardOptions.AppSecret), config.AppSecret);
request.Headers.TryAddWithoutValidation(GlobalConst.HTTP_HEADER_XRealIp, lanIp);
request.Headers.TryAddWithoutValidation(GlobalConst.HTTP_HEADER_XForwardedFor, lanIp);
return await base.SendAsync(request, cancellationToken);
}

View File

@ -1,3 +1,6 @@
using Fake;
using Fake.Application;
namespace TrackCenter.Application.Track2InterfaceRelay;
public class Track2InterfaceRelaySubSystemContext
@ -21,6 +24,6 @@ public class Track2InterfaceRelaySubSystemContext
{
var previousValue = Current;
LocalContext.Value = context;
return new DisposeAction(() => LocalContext.Value = previousValue);
return new DisposableWrapper(() => LocalContext.Value = previousValue);
}
}

View File

@ -1,25 +0,0 @@
using FluentValidation;
using TrackCenter.Application.Dtos.TrackContents;
namespace TrackCenter.Application.Validations.TrackContents;
public class EditTrackContentInputValidation : AbstractValidator<EditTrackContentInput>, IValidationEnabled
{
public EditTrackContentInputValidation()
{
//条件分支
When(x => x.Options == OptionType.修改 || x.Options == OptionType.删除,
() => { RuleFor(x => x.Id).GreaterThan(0).WithMessage("修改或删除时Id必须大于0!"); });
When(x => x.Options == OptionType.新增, () => { RuleFor(x => x.Id).Equal(0).WithMessage("新增时Id必须等于0!"); });
When(x => x.Options == OptionType.修改 || x.Options == OptionType.新增, () =>
{
RuleFor(x => x.SubSystemId).GreaterThan(0).WithMessage("子系统的id必须大于0!");
RuleFor(x => x.TrackStatusId).GreaterThan(0).WithMessage("所属状态的id必须大于0!");
RuleFor(x => x.Code).NotNull().WithMessage("轨迹内容代码必填!").NotEmpty().WithMessage("轨迹内容代码不能为空!");
RuleFor(x => x.Content).NotNull().WithMessage("轨迹内容必填!").NotEmpty().WithMessage("轨迹内容不能为空!");
RuleFor(x => x.EnContent).NotNull().WithMessage("轨迹内容英文必填!").NotEmpty().WithMessage("轨迹内容英文不能为空!");
});
}
}

View File

@ -1,59 +0,0 @@
using FluentValidation;
using Microsoft.Extensions.Localization;
using SJZY.Expand.ABP.Core.Localization;
using TrackCenter.Application.Dtos.TrackContents;
using TrackCenter.Application.Localization;
namespace TrackCenter.Application.Validations.TrackContents;
public class EditTrackContentMapInputValidation : AbstractValidator<EditTrackContentMapInput>, IValidationEnabled
{
public EditTrackContentMapInputValidation(IStringLocalizer<TrackLocalizationResource> stringLocalizer)
{
RuleFor(x => x.Options).IsInEnum().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_IsInEnum,
nameof(EditTrackContentMapInput.Options)]);
RuleFor(x => x.Id).GreaterThan(0).WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_GreaterThan,
nameof(EditTrackContentMapInput.Id), 0]);
RuleFor(x => x.MapSubSystemId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackContentMapInput.MapSubSystemId), 0]);
RuleFor(x => x.MapTrackContentId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackContentMapInput.MapTrackContentId), 0]);
RuleFor(x => x.SubSystemId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackContentMapInput.SubSystemId), 0]);
RuleFor(x => x.TrackContentId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackContentMapInput.TrackContentId), 0]);
When(x => x.Options != OptionTypeNoNull.新增,
() =>
{
RuleFor(x => x.Id).NotNull().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_NotNull,
nameof(EditTrackContentMapInput.Id)]);
});
When(x => x.Options != OptionTypeNoNull.删除, () =>
{
RuleFor(x => x.MapSubSystemId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackContentMapInput.MapSubSystemId)]);
RuleFor(x => x.MapTrackContentCode).NotEmpty().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotEmpty, nameof(EditTrackContentMapInput.MapTrackContentCode)]);
RuleFor(x => x.MapTrackContentId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackContentMapInput.MapTrackContentId)]);
RuleFor(x => x.SubSystemId).NotNull().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_NotNull,
nameof(EditTrackContentMapInput.SubSystemId)]);
RuleFor(x => x.TrackContentCode).NotEmpty().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotEmpty, nameof(EditTrackContentMapInput.TrackContentCode)]);
RuleFor(x => x.TrackContentId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackContentMapInput.TrackContentId)]);
});
}
}

View File

@ -1,23 +0,0 @@
using FluentValidation;
using TrackCenter.Application.Dtos.TrackFlags;
namespace TrackCenter.Application.Validations.TrackFlags;
public class EditTrackFlagInputValidation : AbstractValidator<EditTrackFlagInput>, IValidationEnabled
{
public EditTrackFlagInputValidation()
{
//条件分支
When(x => x.Options == OptionType.修改 || x.Options == OptionType.删除,
() => { RuleFor(x => x.Id).GreaterThan(0).WithMessage("修改或删除时Id必须大于0!"); });
When(x => x.Options == OptionType.新增, () => { RuleFor(x => x.Id).Equal(0).WithMessage("新增时Id必须等于0!"); });
When(x => x.Options == OptionType.修改 || x.Options == OptionType.新增, () =>
{
RuleFor(x => x.SubSystemId).GreaterThan(0).WithMessage("子系统的id必须大于0!");
RuleFor(x => x.Code).NotNull().WithMessage("轨迹标记代码必填!").NotEmpty().WithMessage("轨迹标记代码不能为空!");
RuleFor(x => x.Flag).NotNull().WithMessage("轨迹标记必填!").NotEmpty().WithMessage("轨迹标记不能为空!");
});
}
}

View File

@ -1,53 +0,0 @@
using FluentValidation;
using Microsoft.Extensions.Localization;
using SJZY.Expand.ABP.Core.Localization;
using TrackCenter.Application.Dtos.TrackPull;
using TrackCenter.Application.Localization;
namespace TrackCenter.Application.Validations.TrackPull;
public class EditTrackPullConfigInputValidation : AbstractValidator<EditTrackPullConfigInput>, IValidationEnabled
{
public EditTrackPullConfigInputValidation(IStringLocalizer<TrackLocalizationResource> stringLocalizer)
{
RuleFor(x => x.Options).IsInEnum().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_IsInEnum,
nameof(EditTrackPullConfigInput.Options)]);
When(x => x.Options != OptionTypeNoNull.新增,
() =>
{
RuleFor(x => x.Id).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan,
nameof(EditTrackPullConfigInput.Id), 0]);
});
When(x => x.Options != OptionTypeNoNull.删除, () =>
{
RuleFor(x => x.SubSystemId).NotNull().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_NotNull,
nameof(EditTrackPullConfigInput.SubSystemId)]);
RuleFor(x => x.ServiceProviderId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackPullConfigInput.ServiceProviderId)]);
RuleFor(x => x.PullInterval).NotNull().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_NotNull,
nameof(EditTrackPullConfigInput.PullInterval)]);
RuleFor(x => x.ParseMethod).NotNull().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_NotNull,
nameof(EditTrackPullConfigInput.ParseMethod)]);
When(x => x.ParseMethod == 2, () =>
{
RuleFor(x => x.OrderNoName).NotEmpty().WithMessage(
stringLocalizer[SjzyBasicLocalization.Validation_NotEmpty,
nameof(EditTrackPullConfigInput.OrderNoName)]);
RuleFor(x => x.OrderPath).NotEmpty().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotEmpty,
nameof(EditTrackPullConfigInput.OrderPath)]);
RuleFor(x => x.TrackDetailsPath).NotEmpty().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotEmpty, nameof(EditTrackPullConfigInput.TrackDetailsPath)]);
});
});
}
}

View File

@ -1,41 +0,0 @@
using FluentValidation;
using Microsoft.Extensions.Localization;
using SJZY.Expand.ABP.Core.Localization;
using TrackCenter.Application.Dtos.TrackPull;
using TrackCenter.Application.Localization;
namespace TrackCenter.Application.Validations.TrackPull;
public class EditTrackPullContentInputValidation : AbstractValidator<EditTrackPullContentInput>, IValidationEnabled
{
public EditTrackPullContentInputValidation(IStringLocalizer<TrackLocalizationResource> stringLocalizer)
{
RuleFor(x => x.Options).IsInEnum().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_IsInEnum,
nameof(EditTrackPullContentInput.Options)]);
RuleFor(x => x.Id).GreaterThan(0).WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_GreaterThan,
nameof(EditTrackPullContentInput.Id), 0]);
RuleFor(x => x.TrackContentId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackPullContentInput.TrackContentId), 0]);
RuleFor(x => x.TrackPullConfigId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackPullContentInput.TrackPullConfigId), 0]);
When(x => x.Options != OptionTypeNoNull.新增,
() =>
{
RuleFor(x => x.Id).NotNull().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_NotNull,
nameof(EditTrackPullContentInput.Id)]);
});
When(x => x.Options != OptionTypeNoNull.删除, () =>
{
RuleFor(x => x.TrackContentId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackPullContentInput.TrackContentId)]);
RuleFor(x => x.TrackPullConfigId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackPullContentInput.TrackPullConfigId)]);
});
}
}

View File

@ -1,25 +0,0 @@
using FluentValidation;
using TrackCenter.Application.Dtos.TrackStatus;
namespace TrackCenter.Application.Validations.TrackStatus;
public class EditTrackStatusInputValidation : AbstractValidator<EditTrackStatusInput>, IValidationEnabled
{
public EditTrackStatusInputValidation()
{
//条件分支
When(x => x.Options == OptionType.修改 || x.Options == OptionType.删除,
() => { RuleFor(x => x.Id).GreaterThan(0).WithMessage("修改或删除时Id必须大于0!"); });
When(x => x.Options == OptionType.新增, () => { RuleFor(x => x.Id).Equal(0).WithMessage("新增时Id必须等于0!"); });
When(x => x.Options == OptionType.修改 || x.Options == OptionType.新增, () =>
{
RuleFor(x => x.SubSystemId).GreaterThan(0).WithMessage("子系统的id必须大于0!");
RuleFor(x => x.NodeId).GreaterThan(0).WithMessage("所属节点的id必须大于0!");
RuleFor(x => x.Code).NotNull().WithMessage("轨迹状态代码必填!").NotEmpty().WithMessage("轨迹状态代码不能为空!");
RuleFor(x => x.TrackStatus).NotNull().WithMessage("轨迹状态必填!").NotEmpty().WithMessage("轨迹状态不能为空!");
RuleFor(x => x.EnTrackStatus).NotNull().WithMessage("轨迹状态英文必填!").NotEmpty().WithMessage("轨迹状态英文不能为空!");
});
}
}

View File

@ -1,42 +0,0 @@
using FluentValidation;
using Microsoft.Extensions.Localization;
using SJZY.Expand.ABP.Core.Localization;
using TrackCenter.Application.Dtos.TrackTemplates;
using TrackCenter.Application.Localization;
namespace TrackCenter.Application.Validations.TrackTemplates;
public class EditTrackTemplateContentInputValidation : AbstractValidator<EditTrackTemplateContentInput>,
IValidationEnabled
{
public EditTrackTemplateContentInputValidation(IStringLocalizer<TrackLocalizationResource> stringLocalizer)
{
RuleFor(x => x.Options).IsInEnum().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_IsInEnum,
nameof(EditTrackTemplateContentInput.Options)]);
RuleFor(x => x.Id).GreaterThan(0).WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_GreaterThan,
nameof(EditTrackTemplateContentInput.Id), 0]);
RuleFor(x => x.TrackContentId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackTemplateContentInput.TrackContentId), 0]);
RuleFor(x => x.TrackTemplateId).GreaterThan(0).WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_GreaterThan, nameof(EditTrackTemplateContentInput.TrackTemplateId), 0]);
When(x => x.Options != OptionTypeNoNull.新增,
() =>
{
RuleFor(x => x.Id).NotNull().WithMessage(stringLocalizer[SjzyBasicLocalization.Validation_NotNull,
nameof(EditTrackTemplateContentInput.Id)]);
});
When(x => x.Options != OptionTypeNoNull.删除, () =>
{
RuleFor(x => x.TrackContentId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackTemplateContentInput.TrackContentId)]);
RuleFor(x => x.TrackTemplateId).NotNull().WithMessage(stringLocalizer[
SjzyBasicLocalization.Validation_NotNull, nameof(EditTrackTemplateContentInput.TrackTemplateId)]);
});
}
}

View File

@ -1,26 +0,0 @@
using FluentValidation;
using TrackCenter.Application.Dtos.TrackTemplates;
namespace TrackCenter.Application.Validations.TrackTemplates;
/// <summary>
/// 轨迹模版编辑校验
/// </summary>
public class EditTrackTemplateInputValidation : AbstractValidator<EditTrackTemplateInput>, IValidationEnabled
{
public EditTrackTemplateInputValidation()
{
//条件分支
When(x => x.Options == OptionType.修改 || x.Options == OptionType.删除,
() => { RuleFor(x => x.Id).GreaterThan(0).WithMessage("修改或删除时Id必须大于0!"); });
When(x => x.Options == OptionType.新增, () => { RuleFor(x => x.Id).Equal(0).WithMessage("新增时Id必须等于0!"); });
When(x => x.Options == OptionType.修改 || x.Options == OptionType.新增, () =>
{
RuleFor(x => x.SubSystemId).GreaterThan(0).WithMessage("子系统的id必须大于0!");
RuleFor(x => x.Name).NotNull().WithMessage("模版名称必填!").NotEmpty().WithMessage("模版名称不能为空!");
RuleFor(x => x.Code).NotNull().WithMessage("模版代码必填!").NotEmpty().WithMessage("模版代码不能为空!");
});
}
}

View File

@ -0,0 +1,10 @@
namespace TrackCenter.Domain.Consts;
public partial class GlobalConst
{
public const string HTTP_HEADER_SUBSYSTEMID = "Subsystemid";
public const string HTTP_HEADER_XForwardedFor = "X-Forwarded-For";
public const string HTTP_HEADER_XRealIp = "X-Real-IP";
}

View File

@ -4,7 +4,6 @@ public partial class GlobalConst
{
public const string MainDataKey_OrderType = "TrackOrderType";
public const string MainDataKey_TrackQuestionType = "TrackQuestionType";
public const string HTTP_HEADER_SUBSYSTEMID = "Subsystemid";
public const string SystemUser = "系统";
public const string EmptyString = "--";
public const int ChunkSize = 100;

View File

@ -0,0 +1,115 @@
using System.Diagnostics;
using TrackCenter.Repository.Otel.Options;
namespace TrackCenter.Repository.Otel
{
/// <summary>
/// ActivityHelper
/// </summary>
public static class ActivityHelper
{
/// <summary>
/// 是否初始化
/// </summary>
private static bool _initialized = false;
/// <summary>
/// 获取ActivitySource
/// </summary>
public static ActivitySource ActivitySource { get; private set; }
/// <summary>
/// 获取OTelOptions
/// </summary>
public static OTelOptions OTelOptions { get; private set; }
/// <summary>
/// 初始化服务名称
/// </summary>
/// <param name="options"> OTelOptions 配置信息</param>
public static void InitActivitySource(OTelOptions options)
{
if (_initialized == false)
{
ActivitySource = new ActivitySource(options.ServiceName, options.ServiceVersion);
OTelOptions = options;
_initialized = true;
}
else
{
Debug.WriteLine("ActivityHelper has been initialized");
}
}
/// <summary>
/// 创建SpanActivity
/// </summary>
/// <param name="spanName"> span 名称 </param>
/// <param name="kind"> 类型 </param>
/// <returns></returns>
public static Activity CreateSpanActivity(string spanName, ActivityKind kind = ActivityKind.Internal)
{
return ActivitySource.StartActivity(spanName, kind);
}
/// <summary>
/// 启动SpanActivity
/// </summary>
/// <param name="spanName"> span名称 </param>
/// <param name="tags"> 标签 </param>
/// <param name="kind"> 类型 </param>
public static void StartSpanActivity(string spanName, Dictionary<string, object> tags, ActivityKind kind = ActivityKind.Internal)
{
using (var activity = ActivitySource.StartActivity(spanName, kind))
{
if (tags != null)
{
foreach (var tag in tags)
{
activity.SetTag(tag.Key, tag.Value);
}
}
}
}
/// <summary>
/// 启动SpanActivity
/// </summary>
/// <param name="spanName"> span名称 </param>
/// <param name="proxyActivity"> 代理Activity </param>
/// <param name="kind"> 类型 </param>
public static void StartSpanActivity(string spanName, Action<Activity> proxyActivity, ActivityKind kind = ActivityKind.Internal)
{
using (var activity = ActivitySource.StartActivity(spanName, kind))
{
try
{
proxyActivity?.Invoke(activity);
}
catch (Exception ex)
{
activity.SetStatus(ActivityStatusCode.Error, ex.ToString());
}
}
}
/// <summary>
/// 对象转字符串
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToString(this object obj)
{
string value;
try
{
if (OTelOptions != null && OTelOptions.Serializer != null && obj != null)
{
value = OTelOptions.Serializer(obj);
}
else
{
value = obj?.ToString();
}
}
catch
{
value = $"Type: {obj?.GetType().FullName}";
}
return value;
}
}
}

View File

@ -0,0 +1,20 @@
namespace TrackCenter.Repository.Otel.Options
{
/// <summary>
/// arguments store type
/// <para>参数存储类型</para>
/// </summary>
public enum ArgumentsStoreType
{
/// <summary>
/// store as a tag
/// <para>作为标签存储</para>
/// </summary>
Tag,
/// <summary>
/// store as a event(default)
/// <para>作为事件存储(默认)</para>
/// </summary>
Event
}
}

View File

@ -0,0 +1,47 @@
namespace TrackCenter.Repository.Otel.Options
{
/// <summary>
/// tags / events etc.. name
/// <para>用于定义tags/events等名称</para>
/// </summary>
public struct KeyNames
{
private const string TAG_KEY_PARAMETER = "parameters";
private const string TAG_KEY_RETURN = "return";
private const string EVENT_NAME_ARGUMENTS = "arguments";
private string _tagParameter;
private string _tagReturn;
private string _eventArguments;
/// <summary>
/// parameter tag name
/// <para>参数标签名称</para>
/// </summary>
public string TagParameter
{
get => _tagParameter ?? TAG_KEY_PARAMETER;
set => _tagParameter = value;
}
/// <summary>
/// return value tag name
/// <para>返回值标签名称</para>
/// </summary>
public string TagReturn
{
get => _tagReturn ?? TAG_KEY_RETURN;
set => _tagReturn = value;
}
/// <summary>
/// arguments(parameter and return value) event name
/// <para>参数和返回值事件名称</para>
/// </summary>
public string EventArguments
{
get => _eventArguments ?? EVENT_NAME_ARGUMENTS;
set => _eventArguments = value;
}
}
}

View File

@ -0,0 +1,138 @@
namespace TrackCenter.Repository.Otel.Options
{
/// <summary>
/// OTel options
/// <para>OTel 相关配置</para>
/// </summary>
public class OTelOptions
{
/// <summary>
/// OTel enabled or not
/// <para>是否启用OTel</para>
/// </summary>
public bool Enabled { get; set; } = true;
/// <summary>
/// 服务名称
/// </summary>
public string ServiceName { get; set; } = "OTel.Service";
/// <summary>
/// 服务版本
/// </summary>
public string ServiceVersion { get; set; } = "1.0.0";
/// <summary>
/// OTel Collector Exporter Protocol
/// <para>OTel Collector导出协议</para>
/// <para>Grpc</para>
/// <para>HttpProtobuf</para>
/// </summary>
public string ExporterProtocol { get; set; } = "Grpc";
/// <summary>
/// OTel Collector Exporter URL
/// <para>OTel Collector导出地址</para>
/// </summary>
public string ExporterUrl { get; set; }
/// <summary>
/// OTel Collector Exporter Timeout Seconds
/// <para>OTel Collector导出超时时间(秒)</para>
/// </summary>
public int ExporterTimeoutSeconds { get; set; } = 10;
/// <summary>
/// OTel Sampler Type
/// <para>OTel采样类型</para>
/// <para>AlwaysSample</para>
/// <para>NeverSample</para>
/// <para>ProbabilitySampler</para>
/// <para>ParentBased</para>
/// </summary>
public SamplerType SamplerType { get; set; } = SamplerType.AlwaysOn;
/// <summary>
/// OTel Sampler Probability
/// <para>OTel采样概率</para>
/// </summary>
public double SamplerProbability { get; set; } = 0.5;
/// <summary>
/// OTel ChildBasedSampler Type
/// <para>OTel子采样类型</para>
/// </summary>
public SamplerType ChildBasedSampler { get; set; } = SamplerType.AlwaysOn;
/// <summary>
/// AspNetCore enabled or not
/// <para>是否启用AspNetCore</para>
/// </summary>
public bool EnableAspNetCore { get; set; } = true;
/// <summary>
/// HttpClient enabled or not
/// <para>是否启用HttpClient</para>
/// </summary>
public bool EnableHttpClient { get; set; } = true;
/// <summary>
/// 过滤器
/// <para>过滤器,用于过滤请求,返回true则忽略该请求</para>
/// </summary>
/// <returns></returns>
public Func<HttpRequestMessage,bool> FilterHttpClient{ get; set; }
/// <summary>
/// GrpcClient enabled or not
/// <para>是否启用GrpcClient</para>
/// </summary>
public bool EnableGrpcClient { get; set; } = true;
/// <summary>
/// EFCore enabled or not
/// <para>是否启用EFCore</para>
/// </summary>
public bool EnableSql { get; set; } = true;
/// <summary>
/// Sql parameter enabled or not
/// <para>是否启用Sql参数</para>
/// </summary>
public bool EnableSqlParameters { get; set; } = false;
/// <summary>
/// Redis enabled or not
/// <para>是否启用Redis</para>
/// </summary>
public bool EnableRedis { get; set; } = true;
/// <summary>
/// CAP enabled or not
/// <para>是否启用CAP</para>
/// </summary>
public bool EnableCap { get; set; } = true;
/// <summary>
/// Enable Console Exporter or not
/// <para>是否启用Console导出器</para>
/// <para>默认启用, 如果配置了ExporterUrl则不启用</para>
/// </summary>
public bool EnableConsoleExporter { get; set; } = true;
/// <summary>
/// how to store arguments
/// <para>参数存储方式</para>
/// </summary>
public ArgumentsStoreType ArgumentsStoreType { get; set; } = ArgumentsStoreType.Event;
/// <summary>
/// 默认 false 表示当span/activity执行没有任何异常抛出时,使用 Status.Unset 状态,
/// 改为true则使用 Status.Ok 状态
/// </summary>
public bool SetOkStatusWhenSuccess { get; set; } = false;
/// <summary>
/// span using method full name by default, change to true to using short name and save full name to tag or event(depend on <see cref="ArgumentsStoreType"/>)
/// <para>默认使用方法全名作为span名称,改为true则使用短名作为span名称,并将全名保存到tag或event中(取决于<see cref="ArgumentsStoreType"/>)</para>
/// </summary>
public bool ShortName { get; set; } = false;
/// <summary>
/// tags / events etc.. name
/// <para>用于定义tags/events等名称</para>
/// </summary>
public KeyNames KeyNames { get; set; }
/// <summary>
/// 序列化参数和返回值时使用的函数
/// </summary>
public Func<object, string> Serializer { get; set; }
/// <summary>
/// 是否启用静态weaving
/// <para>默认启用,如果设置为false,则不会对静态方法进行weaving</para>
/// </summary>
public bool EnableStaticWeav { get; set; } = true;
}
}

View File

@ -0,0 +1,34 @@
namespace TrackCenter.Repository.Otel.Options
{
/// <summary>
/// sampler type
/// <para>采样类型</para>
/// </summary>
public enum SamplerType
{
/// <summary>
/// always sample
/// <para>总是采样</para>
/// </summary>
AlwaysOn = 0,
/// <summary>
/// never sample
/// <para>从不采样</para>
/// </summary>
AlwaysOff = 1,
/// <summary>
/// probability sampler
/// <para>概率采样</para>
/// </summary>
TraceIdRatio,
/// <summary>
/// Parent-based sampler
/// <para>基于父级的采样器</para>
/// </summary>
ParentBasedSampler
}
}