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

158 lines
8.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
using YY.Admin.Services.Service.Category;
using YY.Admin.Services.Service.Customer;
using YY.Admin.Services.Service.Dict;
using YY.Admin.Services.Service.MixerMaterial;
using YY.Admin.Services.Service.Supplier;
using YY.Admin.Services.Service.RawMaterialEntry;
using YY.Admin.Services.Service.Vehicle;
using YY.Admin.Services.Service.WeightRecord;
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>();
// 磅单管理:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<IWeightRecordService, WeightRecordService>();
containerRegistry.RegisterSingleton<WeightRecordSyncCoordinator>();
// 密炼物料信息API直连 + STOMP实时通知
containerRegistry.RegisterSingleton<IMixerMaterialService, MixerMaterialService>();
containerRegistry.RegisterSingleton<MixerMaterialSyncCoordinator>();
// 原料入场记录:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<IRawMaterialEntryService, RawMaterialEntryService>();
containerRegistry.RegisterSingleton<RawMaterialEntrySyncCoordinator>();
// 分类字典:启动同步 + 断线重连补刷
containerRegistry.RegisterSingleton<CategorySyncCoordinator>();
// 数据字典:启动同步 + 断线重连补刷
containerRegistry.RegisterSingleton<DictSyncCoordinator>();
// 统一轮询管理器(修改 SyncPollManager.PollInterval 即可调整所有模块的轮询间隔)
containerRegistry.RegisterSingleton<SyncPollManager>();
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>();
// 统一轮询管理器必须在各协调器之前实例化,确保 Register() 能正常接收注册
_ = containerProvider.Resolve<SyncPollManager>();
// 强制实例化车辆同步协调器(构造函数内订阅 STOMP 车辆变更事件)
_ = containerProvider.Resolve<VehicleSyncCoordinator>();
// 强制实例化客户同步协调器
_ = containerProvider.Resolve<CustomerSyncCoordinator>();
// 强制实例化供应商同步协调器
_ = containerProvider.Resolve<SupplierSyncCoordinator>();
// 强制实例化磅单同步协调器
_ = containerProvider.Resolve<WeightRecordSyncCoordinator>();
// 强制实例化密炼物料同步协调器
_ = containerProvider.Resolve<MixerMaterialSyncCoordinator>();
// 强制实例化原料入场记录同步协调器
_ = containerProvider.Resolve<RawMaterialEntrySyncCoordinator>();
// 强制实例化分类字典同步协调器
_ = containerProvider.Resolve<CategorySyncCoordinator>();
// 强制实例化数据字典同步协调器
_ = containerProvider.Resolve<DictSyncCoordinator>();
}
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;
}
}