Files
qhmes/yy-admin-master/YY.Admin.Services/Service/Jeecg/JeecgUserSyncCoordinator.cs

192 lines
6.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.Configuration;
using Prism.Events;
using System.Text.Json;
using YY.Admin.Core;
using YY.Admin.Core.Events;
using YY.Admin.Core.Services;
using YY.Admin.Core.Sync;
namespace YY.Admin.Services.Service.Jeecg;
/// <summary>
/// 用户镜像同步统一走设备同步规范线路STOMP 收信号 → Outbox → REST 拉取 SCADA不再使用独立 Jeecg 原生 WebSocket 收包循环。
/// </summary>
public class JeecgUserSyncCoordinator : IJeecgUserSyncCoordinator, ISingletonDependency
{
private readonly IConfiguration _configuration;
private readonly IEventAggregator _eventAggregator;
private readonly IJeecgUserMirrorPullOutbox _mirrorOutbox;
private readonly ILoggerService _logger;
private CancellationTokenSource? _cts;
private readonly object _lifecycleLock = new();
private SubscriptionToken? _remoteCommandSubscription;
public JeecgUserSyncCoordinator(
IConfiguration configuration,
IEventAggregator eventAggregator,
IJeecgUserMirrorPullOutbox mirrorOutbox,
ILoggerService logger)
{
_configuration = configuration;
_eventAggregator = eventAggregator;
_mirrorOutbox = mirrorOutbox;
_logger = logger;
}
/// <inheritdoc />
public void Start()
{
var enabled = _configuration.GetValue("JeecgIntegration:Enabled", false);
var baseUrl = _configuration.GetValue<string>("JeecgIntegration:BaseUrl") ?? string.Empty;
var stompPath = "/ws/device/websocket";
_logger.Information($"Jeecg用户同步协调器启动统一设备通道Enabled={enabled}, BaseUrl={baseUrl}, Stomp={stompPath}");
if (!enabled)
{
_logger.Warning("Jeecg用户同步协调器未启动JeecgIntegration:Enabled=false");
return;
}
CancellationToken token;
lock (_lifecycleLock)
{
CancelAndDisposeCts();
UnsubscribeRemoteCommand();
_remoteCommandSubscription = _eventAggregator.GetEvent<RemoteCommandReceivedEvent>().Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
_cts = new CancellationTokenSource();
token = _cts.Token;
}
// 进入主窗口后稍延迟再入队一次全量拉取,避免与登录同步抢带宽
_ = Task.Run(async () =>
{
try
{
await Task.Delay(3000, token).ConfigureAwait(false);
await _mirrorOutbox.EnqueuePullAsync(JeecgUserMirrorOutbox.EventBoot, null, token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// 忽略
}
catch (Exception ex)
{
_logger.Warning($"Jeecg 启动后入队同步失败: {ex.Message}");
}
}, token);
}
/// <inheritdoc />
public void Stop()
{
lock (_lifecycleLock)
{
UnsubscribeRemoteCommand();
CancelAndDisposeCts();
}
}
private void OnRemoteCommand(RemoteCommandPayload payload)
{
try
{
var json = payload.CommandJson ?? string.Empty;
if (!ShouldTriggerUserSync(json))
{
return;
}
_logger.Information($"收到设备统一通道(STOMP)用户变更信号,入队 Outbox: {json}");
_ = _mirrorOutbox.EnqueuePullAsync(JeecgUserMirrorOutbox.EventSignal, json, CancellationToken.None);
}
catch (Exception ex)
{
_logger.Warning($"处理 STOMP 用户变更信号失败: {ex.Message}");
}
}
private void UnsubscribeRemoteCommand()
{
if (_remoteCommandSubscription != null)
{
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>().Unsubscribe(_remoteCommandSubscription);
_remoteCommandSubscription = null;
}
}
private void CancelAndDisposeCts()
{
try
{
_cts?.Cancel();
}
catch
{
// 忽略
}
finally
{
_cts?.Dispose();
_cts = null;
}
}
private static bool ShouldTriggerUserSync(string message)
{
if (string.IsNullOrWhiteSpace(message))
{
return false;
}
try
{
using var doc = JsonDocument.Parse(message);
var root = doc.RootElement;
if (TryMatchCmd(root))
{
return true;
}
// 设备模块 REST 下发的 commandJson 包裹
if (root.TryGetProperty("commandJson", out var innerEl) && innerEl.ValueKind == JsonValueKind.String)
{
var rawInner = innerEl.GetString();
if (!string.IsNullOrWhiteSpace(rawInner))
{
using var innerDoc = JsonDocument.Parse(rawInner);
return TryMatchCmd(innerDoc.RootElement);
}
}
if (root.TryGetProperty("message", out var innerMessage)
&& innerMessage.ValueKind == JsonValueKind.String)
{
var rawInner = innerMessage.GetString();
if (!string.IsNullOrWhiteSpace(rawInner))
{
using var innerDoc = JsonDocument.Parse(rawInner);
return TryMatchCmd(innerDoc.RootElement);
}
}
return false;
}
catch
{
return false;
}
}
private static bool TryMatchCmd(JsonElement element)
{
if (element.ValueKind != JsonValueKind.Object)
{
return false;
}
if (!element.TryGetProperty("cmd", out var cmd))
{
return false;
}
var cmdValue = cmd.GetString();
return string.Equals(cmdValue, "SCADA_USER_CHANGED", StringComparison.OrdinalIgnoreCase)
|| string.Equals(cmdValue, "SCADA_USERS_CHANGED", StringComparison.OrdinalIgnoreCase);
}
}