Files
qhmes/yy-admin-master/YY.Admin/Module/SyncModule.cs

125 lines
5.9 KiB
C#
Raw Normal View History

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Polly;
using Polly.Extensions.Http;
using Prism.Ioc;
using Prism.Modularity;
using System.Net.Http;
using System.Text;
using YY.Admin.Core.Services;
using YY.Admin.Core.Session;
using YY.Admin.EventBus;
using YY.Admin.Infrastructure.Hubs;
using YY.Admin.Infrastructure.Network;
using YY.Admin.Infrastructure.Storage;
using YY.Admin.Infrastructure.Sync;
using YY.Admin.Services.Service.Customer;
using YY.Admin.Services.Service.Supplier;
using YY.Admin.Services.Service.Vehicle;
namespace YY.Admin.Module;
public class SyncModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<TokenStore>();
containerRegistry.RegisterSingleton<HttpSyncClient>();
containerRegistry.RegisterSingleton<OutboxProcessor>();
containerRegistry.RegisterSingleton<IJeecgUserMirrorPullOutbox, JeecgUserMirrorPullOutbox>();
containerRegistry.RegisterSingleton<INetworkMonitor, NetworkMonitor>();
containerRegistry.RegisterSingleton<ISignalRService, StompWebSocketService>();
containerRegistry.RegisterSingleton<IUserSyncOutbox, UserSyncOutbox>();
// 本地用户事件订阅器:将增删改操作入 Outbox 回传后端
containerRegistry.RegisterSingleton<SysUserEventSubscriber>();
// 车辆管理:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<IVehicleService, VehicleService>();
containerRegistry.RegisterSingleton<VehicleSyncCoordinator>();
// 客户管理:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<ICustomerService, CustomerService>();
containerRegistry.RegisterSingleton<CustomerSyncCoordinator>();
// 供应商管理:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<ISupplierService, SupplierService>();
containerRegistry.RegisterSingleton<SupplierSyncCoordinator>();
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<DisconnectGuardHandler>();
serviceCollection.AddHttpClient("JeecgApi", (sp, client) =>
{
var config = containerRegistry.GetContainer().Resolve<IConfiguration>();
var baseUrl = config.GetValue<string>("JeecgIntegration:BaseUrl")?.TrimEnd('/');
if (!string.IsNullOrWhiteSpace(baseUrl))
{
client.BaseAddress = new Uri(baseUrl);
}
var tokenStore = containerRegistry.GetContainer().Resolve<TokenStore>();
var token = tokenStore.GetTokenAsync(default).ConfigureAwait(false).GetAwaiter().GetResult();
if (!string.IsNullOrWhiteSpace(token))
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
client.DefaultRequestHeaders.TryAddWithoutValidation("X-Platform", "desktop");
// .NET HttpClient 要求请求头值为 ASCII主机名/中文姓名否则会报 Request headers must contain only ASCII characters
client.DefaultRequestHeaders.TryAddWithoutValidation("X-Host-Name", ToAsciiHttpHeaderValue(Environment.MachineName));
var currentUser = AppSession.CurrentUser;
client.DefaultRequestHeaders.TryAddWithoutValidation("X-User-Name", ToAsciiHttpHeaderValue(currentUser?.Account ?? "unknown"));
client.DefaultRequestHeaders.TryAddWithoutValidation("X-Real-Name", ToAsciiHttpHeaderValue(currentUser?.RealName ?? ""));
}).AddPolicyHandler(GetRetryPolicy())
.AddHttpMessageHandler<DisconnectGuardHandler>();
var provider = serviceCollection.BuildServiceProvider();
var httpClientFactory = provider.GetRequiredService<IHttpClientFactory>();
containerRegistry.RegisterInstance(httpClientFactory);
}
public void OnInitialized(IContainerProvider containerProvider)
{
var networkMonitor = containerProvider.Resolve<INetworkMonitor>();
var outboxProcessor = containerProvider.Resolve<OutboxProcessor>();
var signalService = containerProvider.Resolve<ISignalRService>();
_ = networkMonitor.StartAsync(CancellationToken.None);
_ = outboxProcessor.StartConsumerAsync(CancellationToken.None);
// 用户镜像 + 设备指令:统一 STOMP/ws/device免密与设备 Token 模式均启动
_ = Task.Run(() => signalService.ConnectUnifiedDeviceChannelAsync(CancellationToken.None));
// 强制实例化事件订阅器(单例,构造函数内完成订阅注册)
_ = containerProvider.Resolve<SysUserEventSubscriber>();
// 强制实例化车辆同步协调器(构造函数内订阅 STOMP 车辆变更事件)
_ = containerProvider.Resolve<VehicleSyncCoordinator>();
// 强制实例化客户同步协调器
_ = containerProvider.Resolve<CustomerSyncCoordinator>();
// 强制实例化供应商同步协调器
_ = containerProvider.Resolve<SupplierSyncCoordinator>();
}
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
/// <summary>
/// 将自定义请求头转为 ASCII含非 ASCII 时整体做 UTF-8 Base64前缀 B64. 便于后端按需解码)。
/// </summary>
private static string ToAsciiHttpHeaderValue(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
foreach (var c in value)
{
if (c > 127)
{
return "B64." + Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
}
}
return value;
}
}