更新项目配置,新增设备同步模块,优化WebSocket和Swagger配置,增强SCADA系统的免登录接口,支持数据字典项和登录日志的免登录查询与记录。调整Java编译设置,确保更好的开发体验。
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user