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

View File

@ -21,11 +21,6 @@ public class EditTrackPullPriorityInput
/// </summary>
public string Customer { get; set; }
/// <summary>
/// 订单类型
/// </summary>
public TrackOrderType OrderType { get; set; }
/// <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>
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.TrackContentAggregate;
using TrackCenter.Domain.Aggregates.TrackFlagAggregate;
using TrackCenter.Domain.Aggregates.TrackPullAggregate;
using TrackCenter.Domain.Aggregates.TrackStatusAggregate;
using TrackCenter.Domain.Dtos.Order;
using TrackCenter.Domain.Dtos.TrackContents;
@ -19,7 +18,8 @@ namespace TrackCenter.Application.Queries;
public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>, ITransientDependency
{
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
return await Context.Queryable<TrackContentMapEntity>()
@ -29,7 +29,8 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
.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
return await Context.Queryable<TrackContentMapEntity>()
@ -66,22 +67,23 @@ public class ProvideQueries : SqlSugarRootlessRepository<TrackCenterDbContext>,
}
public async Task<List<OrderWithServiceProviderVO>> FindOrderWithServiceProviderAsync(
string serviceProviderCode,
string appKey,
TrackPullConfigVo config,
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.AppKey == appKey
.Where((a, b) => b.ServiceProviderCode == config.ServiceProviderCode
&& a.AppKey == config.AppKey
&& !a.IsCompleted
&& b.StartTime <= DateTime.Now
&& b.EndTime >= DateTime.Now
)
.WhereIF(config.Customer != GlobalConst.All, a => a.Customer == config.Customer)
.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,
AppKey = a.AppKey,
ServiceProviderCode = b.ServiceProviderCode

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,4 +21,11 @@ public class TrackPullConfigVo
/// 客户
/// </summary>
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,
Customer = x.Customer,
OrderType = x.OrderType,
OrderNoType = x.OrderNoType,
Priority = x.Priority,
PullInterval = x.PullInterval,
@ -154,7 +153,6 @@ public class TrackPullConfigRepository : BasicRepository<TrackPullConfigEntity>,
Id = x.Id,
TrackPullConfigId = x.TrackPullConfigId,
Customer = x.Customer,
OrderType = x.OrderType,
OrderNoType = x.OrderNoType,
Priority = x.Priority,
PullInterval = x.PullInterval