feat: 轨迹拉取定时任务

This commit is contained in:
xiaolipro 2026-03-05 15:41:39 +08:00
parent a431ec7ec7
commit cb8edf4cbb
14 changed files with 110 additions and 77 deletions

View File

@ -139,6 +139,7 @@ public class TrackCenterApiModule : FakeModule
public override void ConfigureApplication(ApplicationConfigureContext context) public override void ConfigureApplication(ApplicationConfigureContext context)
{ {
var app = context.GetWebApplication(); var app = context.GetWebApplication();
var configuration = context.GetConfiguration();
app.Use(async (httpContext, next) => app.Use(async (httpContext, next) =>
{ {
httpContext.Request.EnableBuffering(); httpContext.Request.EnableBuffering();
@ -158,13 +159,12 @@ public class TrackCenterApiModule : FakeModule
app.UseCors(DefaultCorsPolicyName); app.UseCors(DefaultCorsPolicyName);
// todo: 缺乏配置
app.UseHangfireDashboard("/hangfire", new DashboardOptions app.UseHangfireDashboard("/hangfire", new DashboardOptions
{ {
DisplayStorageConnectionString = false, DisplayStorageConnectionString = false,
IgnoreAntiforgeryToken = true, IgnoreAntiforgeryToken = true,
AppPath = "", AppPath = configuration["ASPNETCORE_PATHBASE"],
PrefixPath = "", PrefixPath = configuration["ASPNETCORE_PATHBASE"],
DashboardTitle = "轨迹预警任务", DashboardTitle = "轨迹预警任务",
Authorization = [new AnonymousHangfireAuthorizationFilter()] Authorization = [new AnonymousHangfireAuthorizationFilter()]
}); });

View File

@ -21,11 +21,6 @@ public class EditTrackPullPriorityInput
/// </summary> /// </summary>
public string Customer { get; set; } public string Customer { get; set; }
/// <summary>
/// 订单类型
/// </summary>
public TrackOrderType OrderType { get; set; }
/// <summary> /// <summary>
/// 订单号类型 /// 订单号类型
/// </summary> /// </summary>

View File

@ -0,0 +1,54 @@
using Hangfire;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TrackCenter.Application.Helpers;
using TrackCenter.Application.Options;
using TrackCenter.Application.Services.TimerTaskss;
namespace TrackCenter.Application.HostServices;
public class TrackTaskComponentHostedService : IHostedService
{
private readonly IOptionsMonitor<TrackAppOptions> _optionsMonitor;
private TrackAppOptions? _oldOptions;
private readonly ILogger<TrackTaskComponentHostedService> _logger;
public TrackTaskComponentHostedService(IOptionsMonitor<TrackAppOptions> optionsMonitor, ILogger<TrackTaskComponentHostedService> logger)
{
_optionsMonitor = optionsMonitor;
_logger = logger;
var onChanged = DebounceHelper.WrapMethod(TimeSpan.FromSeconds(1), () => { ReloadConfig(optionsMonitor); });
optionsMonitor.OnChange(_ => onChanged.Debounce());
}
public Task StartAsync(CancellationToken cancellationToken)
{
ReloadConfig(_optionsMonitor);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private void ReloadConfig(IOptionsMonitor<TrackAppOptions> optionsMonitor)
{
var newVal = optionsMonitor.CurrentValue;
_logger.LogInformation("轨迹任务配置,轨迹刷新执行周期{a}min",
newVal.TrackPullTaskMinuteInterval
);
// 轨迹刷新任务
if ((_oldOptions?.TrackPullTaskMinuteInterval ?? 0) != newVal.TrackPullTaskMinuteInterval)
{
var q = GlobalConst.TaskQueueLevelDict[_optionsMonitor.CurrentValue.TrackPullTaskLevel];
RecurringJob.AddOrUpdate<TrackPullTask>("Execute_TrackPullTask",
q,
x => x.RefreshForTaskAsync(),
$"*/{newVal.TrackPullTaskMinuteInterval} * * * *");
}
_oldOptions = newVal;
}
}

View File

@ -26,4 +26,9 @@ public class TrackAppOptions
/// 轨迹拉取任务间隔(分钟) /// 轨迹拉取任务间隔(分钟)
/// </summary> /// </summary>
public int TrackPullTaskMinuteInterval { get; set; } public int TrackPullTaskMinuteInterval { get; set; }
/// <summary>
/// 拉取优先级
/// </summary>
public int TrackPullTaskLevel { get; set; }
} }

View File

@ -3,7 +3,6 @@ using SqlSugar;
using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.OrderAggregate;
using TrackCenter.Domain.Aggregates.TrackContentAggregate; using TrackCenter.Domain.Aggregates.TrackContentAggregate;
using TrackCenter.Domain.Aggregates.TrackFlagAggregate; using TrackCenter.Domain.Aggregates.TrackFlagAggregate;
using TrackCenter.Domain.Aggregates.TrackPullAggregate;
using TrackCenter.Domain.Aggregates.TrackStatusAggregate; using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
using TrackCenter.Domain.Dtos.Order; using TrackCenter.Domain.Dtos.Order;
using TrackCenter.Domain.Dtos.TrackContents; using TrackCenter.Domain.Dtos.TrackContents;
@ -19,7 +18,8 @@ namespace TrackCenter.Application.Queries;
public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>, ITransientDependency public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>, ITransientDependency
{ {
public ISqlSugarClient Context => GetDbContextAsync().Result; public ISqlSugarClient Context => GetDbContextAsync().Result;
public async Task<List<string>> MapTrackCodesAsync(string upAppKey, string appKey, params string[] trackCodes)
public async Task<List<string>> MapTrackCodesAsync(string upAppKey, string appKey, params string[] trackCodes)
{ {
// 根据上游code得到下游code // 根据上游code得到下游code
return await Context.Queryable<TrackContentMapEntity>() return await Context.Queryable<TrackContentMapEntity>()
@ -29,7 +29,8 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
.ToListAsync(); .ToListAsync();
} }
public async Task<List<TrackCodeMapVO>> GetTrackCodeMapsAsync(string upAppKey, string appKey, params string[] trackCodes) public async Task<List<TrackCodeMapVO>> GetTrackCodeMapsAsync(string upAppKey, string appKey,
params string[] trackCodes)
{ {
// 根据上游code得到下游code // 根据上游code得到下游code
return await Context.Queryable<TrackContentMapEntity>() return await Context.Queryable<TrackContentMapEntity>()
@ -66,22 +67,23 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
} }
public async Task<List<OrderWithServiceProviderVO>> FindOrderWithServiceProviderAsync( public async Task<List<OrderWithServiceProviderVO>> FindOrderWithServiceProviderAsync(
string serviceProviderCode, TrackPullConfigVo config,
string appKey,
int pageIndex, int pageSize = 1000) int pageIndex, int pageSize = 1000)
{ {
return await Context.Queryable<OrderEntity>() return await Context.Queryable<OrderEntity>()
.InnerJoin<OrderPullStatusEntity>((a, b) => a.Id == b.OrderId) .InnerJoin<OrderPullStatusEntity>((a, b) => a.Id == b.OrderId)
.Where((a, b) => b.ServiceProviderCode == serviceProviderCode .Where((a, b) => b.ServiceProviderCode == config.ServiceProviderCode
&& a.AppKey == appKey && a.AppKey == config.AppKey
&& !a.IsCompleted && !a.IsCompleted
&& b.StartTime <= DateTime.Now && b.StartTime <= DateTime.Now
&& b.EndTime >= DateTime.Now && b.EndTime >= DateTime.Now
) )
.WhereIF(config.Customer != GlobalConst.All, a => a.Customer == config.Customer)
.Select((a, b) => new OrderWithServiceProviderVO .Select((a, b) => new OrderWithServiceProviderVO
{ {
//优先使用第三方的订单号,为空时才使用主单号 //优先使用第三方的订单号,为空时才使用主单号
OrderNo = string.IsNullOrWhiteSpace(b.OrderNo) ? a.OrderNo : b.OrderNo, OrderNo = config.OrderNoType == TrackOrderNoType.客户单号 ? a.OrderNo :
config.OrderNoType == TrackOrderNoType.跟踪单号 ? a.TrackNo : a.ThirdPartyOrderNumber,
OrderType = a.OrderType, OrderType = a.OrderType,
AppKey = a.AppKey, AppKey = a.AppKey,
ServiceProviderCode = b.ServiceProviderCode ServiceProviderCode = b.ServiceProviderCode

View File

@ -36,18 +36,20 @@ public class TrackPullTask : ApplicationServiceBase, ISingletonDependency
using var serviceScope = _serviceScopeFactory.CreateScope(); using var serviceScope = _serviceScopeFactory.CreateScope();
var trackPullConfigRepository = serviceScope.ServiceProvider.GetRequiredService<ITrackPullConfigRepository>(); var trackPullConfigRepository = serviceScope.ServiceProvider.GetRequiredService<ITrackPullConfigRepository>();
// var configs = await trackPullConfigRepository.AsQueryable()
// .LeftJoin<TrackPullPriorityEntity>((a, b) => a.Id == b.TrackPullConfigId)
// .Where()
var configs = await trackPullConfigRepository.AsQueryable()
var configs = await trackPullConfigRepository.GetListAsync(x => true, x => new TrackPullConfigVo .LeftJoin<TrackPullPriorityEntity>((a, b) => a.Id == b.TrackPullConfigId)
{ .Select((a, b) => new TrackPullConfigVo
// todo:去读priority表,按照客户、订单类型、优先级拉 {
PullInterval = 60, // tips:去读priority表,根据客户看用哪个订单号类型拉
ServiceProviderCode = x.ServiceProviderCode, PullInterval = b.PullInterval,
AppKey = x.AppKey Customer = b.Customer,
}); Priority = b.Priority,
OrderNoType = b.OrderNoType,
ServiceProviderCode = a.ServiceProviderCode,
AppKey = a.AppKey
})
.ToListAsync();
foreach (var config in configs) foreach (var config in configs)
{ {
@ -58,11 +60,10 @@ public class TrackPullTask : ApplicationServiceBase, ISingletonDependency
return true; return true;
} }
private async Task RefreshForConfigAsync(IServiceScope serviceScope, TrackPullConfigVo config) private async Task RefreshForConfigAsync(IServiceScope serviceScope, TrackPullConfigVo config)
{ {
var trackPullCacheService = LazyServiceProvider.GetRequiredService<TrackPullCacheService>(); var trackPullCacheService = LazyServiceProvider.GetRequiredService<TrackPullCacheService>();
var redisKey = trackPullCacheService.TrackPullKey(config.AppKey, config.ServiceProviderCode); var redisKey = trackPullCacheService.TrackPullKey(config);
if (await RedisHelper.Client.ExistsAsync(redisKey)) if (await RedisHelper.Client.ExistsAsync(redisKey))
{ {
return; return;
@ -97,7 +98,7 @@ public class TrackPullTask : ApplicationServiceBase, ISingletonDependency
{ {
var page = 1; var page = 1;
var orders = var orders =
await provideQueries.FindOrderWithServiceProviderAsync(config.ServiceProviderCode, config.AppKey, page); await provideQueries.FindOrderWithServiceProviderAsync(config, page);
while (orders.Any()) while (orders.Any())
{ {
var serviceProviderCode = orders.First().ServiceProviderCode; var serviceProviderCode = orders.First().ServiceProviderCode;
@ -120,8 +121,7 @@ public class TrackPullTask : ApplicationServiceBase, ISingletonDependency
} }
} }
orders = await provideQueries.FindOrderWithServiceProviderAsync(config.ServiceProviderCode, orders = await provideQueries.FindOrderWithServiceProviderAsync(config, ++page);
config.AppKey, ++page);
} }
} }
} }

View File

@ -16,8 +16,8 @@ public class TrackPullCacheService : ITransientDependency
_objectMapper = objectMapper; _objectMapper = objectMapper;
} }
public string TrackPullKey(string appKey, string serviceProviderCode) => public string TrackPullKey(TrackPullConfigVo config) =>
$"{GlobalConst.TrackPullKeyPrefix}:{appKey}:{serviceProviderCode}"; $"{GlobalConst.TrackPullKeyPrefix}:{config.AppKey}:{config.ServiceProviderCode}:{config.Customer}";
public async Task<TrackPullParamsWithContentDto?> GetPullParamsWithContentAsync(string appKey, public async Task<TrackPullParamsWithContentDto?> GetPullParamsWithContentAsync(string appKey,
string serviceProviderCode) string serviceProviderCode)

View File

@ -107,7 +107,6 @@ public class TrackPullService(
{ {
TrackPullConfigId = id, TrackPullConfigId = id,
Customer = GlobalConst.All, Customer = GlobalConst.All,
OrderType = TrackOrderType.小包,
OrderNoType = TrackOrderNoType.跟踪单号, OrderNoType = TrackOrderNoType.跟踪单号,
Priority = 999, Priority = 999,
PullInterval = 12 * 60 // 12小时 PullInterval = 12 * 60 // 12小时
@ -282,7 +281,6 @@ public class TrackPullService(
Expression<Func<TrackPullPriorityEntity, bool>> isPriorityAnyExp = x => Expression<Func<TrackPullPriorityEntity, bool>> isPriorityAnyExp = x =>
x.TrackPullConfigId == input.TrackPullConfigId && x.TrackPullConfigId == input.TrackPullConfigId &&
x.Customer == input.Customer && x.Customer == input.Customer &&
x.OrderType == input.OrderType &&
x.OrderNoType == input.OrderNoType; x.OrderNoType == input.OrderNoType;
if (input.Options == OptionTypeNoNull.修改) if (input.Options == OptionTypeNoNull.修改)

View File

@ -68,10 +68,7 @@ public class TrackCenterApplicationModule : FakeModule
options.FallbackWindowLookbackSeconds = options.FallbackWindowLookbackSeconds =
configuration.GetValue<int>("CAP:FallbackWindowLookbackSeconds"); configuration.GetValue<int>("CAP:FallbackWindowLookbackSeconds");
options.UseDashboard(dashboardOptions => options.UseDashboard(dashboardOptions => { dashboardOptions.PathBase = ""; });
{
dashboardOptions.PathBase = "";
});
options.FailedThresholdCallback = faileCalback => options.FailedThresholdCallback = faileCalback =>
{ {
try try
@ -102,10 +99,10 @@ public class TrackCenterApplicationModule : FakeModule
#endregion #endregion
var options = configuration.GetSection("Redis").Get<ConnectionStringBuilder>(); var redisOptions = configuration.GetSection("Redis").Get<ConnectionStringBuilder>();
if (options == null) if (redisOptions == null)
throw new FakeException("未找到Redis相关配置,请检查配置文件中是否包含名为Redis的节点"); throw new FakeException("未找到Redis相关配置,请检查配置文件中是否包含名为Redis的节点");
RedisHelper.Init(options, showLogInfo: Debugger.IsAttached); RedisHelper.Init(redisOptions, showLogInfo: Debugger.IsAttached);
context.Services.AddHostedService<RabbitClient>(); context.Services.AddHostedService<RabbitClient>();
context.Services.Configure<RabbitOptions>(options => context.Services.Configure<RabbitOptions>(options =>
@ -194,17 +191,20 @@ public class TrackCenterApplicationModule : FakeModule
context.Services.AddHangfireServer(o => context.Services.AddHangfireServer(o =>
{ {
// 索引越小优先级越高 // 索引越小优先级越高
o.Queues = new[] { GlobalConst.WarningJobQueueLevel0, GlobalConst.WarningJobQueueLevel1, GlobalConst.WarningJobQueueLevel2, "default" }; o.Queues =
[
GlobalConst.WarningJobQueueLevel0,
GlobalConst.WarningJobQueueLevel1,
GlobalConst.WarningJobQueueLevel2,
"default"
];
o.ServerName = Dns.GetHostName(); o.ServerName = Dns.GetHostName();
o.ShutdownTimeout = TimeSpan.FromSeconds(10); o.ShutdownTimeout = TimeSpan.FromSeconds(10);
o.WorkerCount = 1; o.WorkerCount = 1;
}); });
// 轨迹拉取定时任务 // 注册轨迹业务定时任务服务
RecurringJob.AddOrUpdate<TrackPullTask>("Execute_TrackPullTask", context.Services.AddHostedService<TrackTaskComponentHostedService>();
x => x.RefreshForTaskAsync(),
$"*/{configuration["TrackApp:TrackPullTaskMinuteInterval"]} * * * *");
context.Services.AddHttpClient("itfx", client => context.Services.AddHttpClient("itfx", client =>
{ {

View File

@ -20,12 +20,6 @@ public class TrackPullPriorityEntity: FullAuditedEntity<int, int>
[SugarColumn(ColumnName = "Customer")] [SugarColumn(ColumnName = "Customer")]
public string Customer { get; set; } public string Customer { get; set; }
/// <summary>
/// 订单类型
/// </summary>
[SugarColumn(ColumnName = "OrderType")]
public TrackOrderType OrderType { get; set; }
/// <summary> /// <summary>
/// 订单号类型 /// 订单号类型
/// </summary> /// </summary>

View File

@ -16,16 +16,6 @@ public class GetTrackPullPriorityDetailOutputVO
/// </summary> /// </summary>
public string Customer { get; set; } public string Customer { get; set; }
/// <summary>
/// 订单类型
/// </summary>
public TrackOrderType OrderType { get; set; }
/// <summary>
/// 订单类型
/// </summary>
public string OrderTypeName => OrderType.ToString();
/// <summary> /// <summary>
/// 订单号类型 /// 订单号类型
/// </summary> /// </summary>

View File

@ -11,16 +11,6 @@ public class GetTrackPullPriorityPageOutputVO
/// </summary> /// </summary>
public string Customer { get; set; } public string Customer { get; set; }
/// <summary>
/// 订单类型
/// </summary>
public TrackOrderType OrderType { get; set; }
/// <summary>
/// 订单类型
/// </summary>
public string OrderTypeName => OrderType.ToString();
/// <summary> /// <summary>
/// 订单号类型 /// 订单号类型
/// </summary> /// </summary>

View File

@ -21,4 +21,11 @@ public class TrackPullConfigVo
/// 客户 /// 客户
/// </summary> /// </summary>
public string Customer { get; set; } public string Customer { get; set; }
/// <summary>
/// 优先级
/// </summary>
public int Priority { get; set; }
public TrackOrderNoType OrderNoType { get; set; }
} }

View File

@ -133,7 +133,6 @@ public class TrackPullConfigRepository : BasicRepository<TrackPullConfigEntity>,
{ {
Id = x.Id, Id = x.Id,
Customer = x.Customer, Customer = x.Customer,
OrderType = x.OrderType,
OrderNoType = x.OrderNoType, OrderNoType = x.OrderNoType,
Priority = x.Priority, Priority = x.Priority,
PullInterval = x.PullInterval, PullInterval = x.PullInterval,
@ -154,7 +153,6 @@ public class TrackPullConfigRepository : BasicRepository<TrackPullConfigEntity>,
Id = x.Id, Id = x.Id,
TrackPullConfigId = x.TrackPullConfigId, TrackPullConfigId = x.TrackPullConfigId,
Customer = x.Customer, Customer = x.Customer,
OrderType = x.OrderType,
OrderNoType = x.OrderNoType, OrderNoType = x.OrderNoType,
Priority = x.Priority, Priority = x.Priority,
PullInterval = x.PullInterval PullInterval = x.PullInterval