From 1358cd397df05bfe388f788276cad267668eee88 Mon Sep 17 00:00:00 2001 From: zuohuaijun Date: Sun, 23 Feb 2025 03:10:34 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=98=8E1=E3=80=81=E4=BC=98=E5=8C=96MQTT?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=20=20=20=202=E3=80=81=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Configuration/Mqtt.json | 13 +- .../Admin.NET.Core/Admin.NET.Core.csproj | 12 +- .../Admin.NET.Core/Job/MqttHostedService.cs | 141 ++++++------ ...nNet.cs => DefaultMqttEventInterceptor.cs} | 90 ++++---- ...ventHandler.cs => MqttEventInterceptor.cs} | 210 +++++++++-------- .../Admin.NET.Core/Option/MqttOptions.cs | 35 ++- .../Dto/{SysMqttDto.cs => MessageInput.cs} | 42 ++-- .../Service/Mqtt/SysMqttService.cs | 11 +- Admin.NET/Admin.NET.Web.Core/Startup.cs | 21 +- Web/package.json | 18 +- Web/src/api-services/api.ts | 1 + Web/src/api-services/apis/sys-enum-api.ts | 75 ++++++ Web/src/api-services/apis/sys-mqtt-api.ts | 214 ++++++++++++++++++ .../api-services/apis/sys-online-user-api.ts | 75 ++++++ Web/src/api-services/models/address-family.ts | 53 +++++ ...dmin-netresult-ilist-mqtt-client-status.ts | 71 ++++++ Web/src/api-services/models/end-point.ts | 29 +++ Web/src/api-services/models/index.ts | 7 + .../api-services/models/mqtt-client-status.ts | 115 ++++++++++ .../models/mqtt-protocol-version.ts | 26 +++ .../models/mqtt-session-status.ts | 58 +++++ .../models/public-message-input.ts | 38 ++++ Web/src/api-services/models/sys-file.ts | 8 + 23 files changed, 1085 insertions(+), 278 deletions(-) rename Admin.NET/Admin.NET.Core/Mqtt/{MqttEventHandlerForAdminNet.cs => DefaultMqttEventInterceptor.cs} (64%) rename Admin.NET/Admin.NET.Core/Mqtt/{MqttEventHandler.cs => MqttEventInterceptor.cs} (51%) rename Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/{SysMqttDto.cs => MessageInput.cs} (81%) create mode 100644 Web/src/api-services/apis/sys-mqtt-api.ts create mode 100644 Web/src/api-services/models/address-family.ts create mode 100644 Web/src/api-services/models/admin-netresult-ilist-mqtt-client-status.ts create mode 100644 Web/src/api-services/models/end-point.ts create mode 100644 Web/src/api-services/models/mqtt-client-status.ts create mode 100644 Web/src/api-services/models/mqtt-protocol-version.ts create mode 100644 Web/src/api-services/models/mqtt-session-status.ts create mode 100644 Web/src/api-services/models/public-message-input.ts diff --git a/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json b/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json index c9afa21a..18d14166 100644 --- a/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json +++ b/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json @@ -3,12 +3,11 @@ // MQTT 配置 "Mqtt": { - "MqttServerId": "MqttServer", // 服务器主动发布时用的ClientId - "Enabled": true, // 是否开启 - "Logging": true, // 记录文件日志 - "ConsoleOutput": true, // 输出控制台日志 - "ConnectionBacklog": 1000, - "Port": "1883", - "IPAddress": "" + "Enabled": false, // 是否开启 + "Port": "1883", // 端口 + "IPAddress": "", // IP地址 + "ConnectionBacklog": 1000, // 最大连接数 + "MqttServerId": "Admin.NET.MQTT", // 服务器主动发消息时的ClientId + "Logging": false // 记录日志 } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj b/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj index d6f747bb..c4570d6a 100644 --- a/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj +++ b/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj @@ -14,20 +14,20 @@ - + - - - + + + - + @@ -48,7 +48,7 @@ - + diff --git a/Admin.NET/Admin.NET.Core/Job/MqttHostedService.cs b/Admin.NET/Admin.NET.Core/Job/MqttHostedService.cs index b132df76..250952ed 100644 --- a/Admin.NET/Admin.NET.Core/Job/MqttHostedService.cs +++ b/Admin.NET/Admin.NET.Core/Job/MqttHostedService.cs @@ -6,7 +6,6 @@ namespace Admin.NET.Core; -using Furion.Logging.Extensions; using Microsoft.Extensions.Hosting; using MQTTnet; using MQTTnet.Protocol; @@ -19,50 +18,35 @@ using System.Threading.Tasks; /// /// MQTT 服务 /// -public class MqttHostedService(IOptions mqttOptions, ISqlSugarClient db) : IHostedService, ISingleton -{ - private const string ServerClientId = "Admin.NET.MQTT"; - public static MqttServer MqttServer { get; set; } +public class MqttHostedService(IOptions mqttOptions) : IHostedService, ISingleton +{ private readonly MqttOptions _mqttOptions = mqttOptions.Value; - private readonly ISqlSugarClient _db = db; - private bool _isLogging = false; - private bool _consoleOutput = false; + public static MqttServer MqttServer { get; set; } + public static readonly List MqttEventInterceptors = []; // MQTT 事件拦截器集合 - static List mqttEventHandlers = new List(); - - public static void RegistMqttEventHandler(MqttEventHandler eh, int order) + /// + /// 注册 MQTT 事件拦截器 + /// + /// + /// + public static void AddMqttEventInterceptor(MqttEventInterceptor mqttEventInterceptor, int order = 0) { - eh.Order = order; - mqttEventHandlers.Add(eh); - mqttEventHandlers.Sort((a,b) => b.Order - a.Order); - } - - public async void PublicMessage(string topic, string message) - { - // 创建一个 MQTT 应用消息 - var applicationMessage = new MqttApplicationMessageBuilder() - .WithTopic(topic) - .WithPayload(message) - .Build(); - - // 记录日志 - Log($"服务器发布主题: {topic}, 内容:{message}"); - await MqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(applicationMessage) - { - SenderClientId = _mqttOptions.MqttServerId - }); + mqttEventInterceptor.Order = order; + MqttEventInterceptors.Add(mqttEventInterceptor); + MqttEventInterceptors.Sort((a, b) => b.Order - a.Order); } public async Task StartAsync(CancellationToken cancellationToken) { - if (!_mqttOptions.Enabled) return; - _isLogging = _mqttOptions.Logging; - _consoleOutput = _mqttOptions.ConsoleOutput; + if (!_mqttOptions.Enabled) return; + + // 注册 MQTT 自定义客户端验证事件拦截器 + AddMqttEventInterceptor(new DefaultMqttEventInterceptor()); var options = new MqttServerOptionsBuilder() .WithDefaultEndpoint() // 默认地址127.0.0.1 - .WithDefaultEndpointPort(_mqttOptions.Port) // 端口号 - //.WithDefaultEndpointBoundIPAddress(_mqttOptions.IPAddress) // IP地址 + .WithDefaultEndpointPort(_mqttOptions.Port) // 端口号 + //.WithDefaultEndpointBoundIPAddress(_mqttOptions.IPAddress) // IP地址 .WithConnectionBacklog(_mqttOptions.ConnectionBacklog) // 最大连接数 .WithPersistentSessions() .Build(); @@ -102,7 +86,7 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien private async Task MqttServer_StoppedAsync(EventArgs arg) { Console.WriteLine($"【MQTT】服务已关闭...... {DateTime.Now}"); - foreach (var eh in mqttEventHandlers) + foreach (var eh in MqttEventInterceptors) { await eh.StoppedAsync(arg); } @@ -115,7 +99,7 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien /// private async Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs arg) { - foreach (var eh in mqttEventHandlers) + foreach (var eh in MqttEventInterceptors) { await eh.ValidatingConnectionAsync(arg); if (arg.ReasonCode != MqttConnectReasonCode.Success) @@ -129,12 +113,13 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien /// /// private async Task MqttServer_ClientConnectedAsync(ClientConnectedEventArgs arg) - { - Log($"客户端连接:客户端ID=【{arg.ClientId}】已连接:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}"); - foreach (var eh in mqttEventHandlers) + { + foreach (var eh in MqttEventInterceptors) { await eh.ClientConnectedAsync(arg); - } + } + + Logging($"客户端连接:客户端ID=【{arg.ClientId}】已连接:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}"); } /// @@ -145,11 +130,12 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien /// private async Task MqttServer_ClientDisconnectedAsync(ClientDisconnectedEventArgs arg) { - Log($"客户端断开:客户端ID=【{arg.ClientId}】已断开:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}"); - foreach (var eh in mqttEventHandlers) + foreach (var eh in MqttEventInterceptors) { await eh.ClientDisconnectedAsync(arg); - } + } + + Logging($"客户端断开:客户端ID=【{arg.ClientId}】已断开:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}"); } /// @@ -159,11 +145,12 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien /// private async Task MqttServer_ClientSubscribedTopicAsync(ClientSubscribedTopicEventArgs arg) { - Log($"订阅主题:客户端ID=【{arg.ClientId}】订阅主题=【{arg.TopicFilter}】 {DateTime.Now}"); - foreach (var eh in mqttEventHandlers) + foreach (var eh in MqttEventInterceptors) { await eh.ClientSubscribedTopicAsync(arg); - } + } + + Logging($"订阅主题:客户端ID=【{arg.ClientId}】订阅主题=【{arg.TopicFilter}】 {DateTime.Now}"); } /// @@ -173,15 +160,16 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien /// private async Task MqttServer_ClientUnsubscribedTopicAsync(ClientUnsubscribedTopicEventArgs arg) { - Log($"取消订阅:客户端ID=【{arg.ClientId}】取消订阅主题=【{arg.TopicFilter}】 {DateTime.Now}"); - foreach (var eh in mqttEventHandlers) + foreach (var eh in MqttEventInterceptors) { await eh.ClientUnsubscribedTopicAsync(arg); - } + } + + Logging($"取消订阅:客户端ID=【{arg.ClientId}】取消订阅主题=【{arg.TopicFilter}】 {DateTime.Now}"); } /// - /// 拦截接收消息 + /// 拦截发布的消息事件 /// /// /// @@ -190,25 +178,48 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien if (string.Equals(arg.ClientId, _mqttOptions.MqttServerId)) return; - Log($"拦截消息:客户端ID=【{arg.ClientId}】 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】 {DateTime.Now}"); - foreach (var eh in mqttEventHandlers) + foreach (var eh in MqttEventInterceptors) { await eh.InterceptingPublishAsync(arg); - } + } + + Logging($"拦截消息:客户端ID=【{arg.ClientId}】 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】 {DateTime.Now}"); } /// - /// 消息未被消费 + /// 未被消费的消息事件 /// /// /// private async Task MqttServer_ApplicationMessageNotConsumedAsync(ApplicationMessageNotConsumedEventArgs arg) - { - Console.WriteLine($"接收消息:发送端ID=【{arg.SenderId}】 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】 {DateTime.Now}"); - foreach (var eh in mqttEventHandlers) + { + foreach (var eh in MqttEventInterceptors) { await eh.ApplicationMessageNotConsumedAsync(arg); - } + } + + Logging($"接收消息:发送端ID=【{arg.SenderId}】 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】 {DateTime.Now}"); + } + + /// + /// 发布主题消息 + /// + /// + /// + public async Task PublicMessageAsync(string topic, string message) + { + var applicationMessage = new MqttApplicationMessageBuilder() + .WithTopic(topic) + .WithPayload(message) + .Build(); + + await MqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(applicationMessage) + { + SenderClientId = _mqttOptions.MqttServerId, + SenderUserName = _mqttOptions.MqttServerId, + }); + + Logging($"服务器发布主题:{topic}, 内容:{message}"); } public Task StopAsync(CancellationToken cancellationToken) @@ -216,11 +227,13 @@ public class MqttHostedService(IOptions mqttOptions, ISqlSugarClien return Task.CompletedTask; } - protected void Log(string msg) + /// + /// 输出日志 + /// + /// + protected void Logging(string msg) { - if (_consoleOutput) - Console.WriteLine(msg); - if (_isLogging) - msg.LogDebug(); + if (!_mqttOptions.Logging) return; + LoggingWriter.LogInformation(msg); } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Mqtt/MqttEventHandlerForAdminNet.cs b/Admin.NET/Admin.NET.Core/Mqtt/DefaultMqttEventInterceptor.cs similarity index 64% rename from Admin.NET/Admin.NET.Core/Mqtt/MqttEventHandlerForAdminNet.cs rename to Admin.NET/Admin.NET.Core/Mqtt/DefaultMqttEventInterceptor.cs index b11e3062..f77a0283 100644 --- a/Admin.NET/Admin.NET.Core/Mqtt/MqttEventHandlerForAdminNet.cs +++ b/Admin.NET/Admin.NET.Core/Mqtt/DefaultMqttEventInterceptor.cs @@ -1,43 +1,47 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -using MQTTnet.Protocol; -using MQTTnet.Server; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Admin.NET.Core; -public class MqttEventHandlerForAdminNet : MqttEventHandler -{ - public override async Task ValidatingConnectionAsync(ValidatingConnectionEventArgs arg) - { - ISqlSugarClient _db = App.GetRequiredService(); - // 验证账号 - var user = _db.Queryable().First(u => u.Account == arg.UserName); - if (user == null) - { - arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; - Log($"客户端验证:客户端ID=【{arg.ClientId}】用户名不存在 {DateTime.Now} "); - return; - } - - // 验证密码 - var password = arg.Password; - if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString()) - { - if (user.Password.Equals(MD5Encryption.Encrypt(password))) return; - } - else - { - if (CryptogramUtil.Decrypt(user.Password).Equals(password)) return; - } - arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; - Log($"客户端验证:客户端ID=【{arg.ClientId}】用户名或密码验证错误 {DateTime.Now} "); - } -} +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +using MQTTnet.Protocol; +using MQTTnet.Server; + +namespace Admin.NET.Core; + +/// +/// 默认 MQTT 事件拦截器 +/// +public class DefaultMqttEventInterceptor : MqttEventInterceptor +{ + public new int Order = int.MinValue; + + public override Task ValidatingConnectionAsync(ValidatingConnectionEventArgs arg) + { + var _db = App.GetRequiredService(); + // 验证账号 + var user = _db.Queryable().First(u => u.Account == arg.UserName); + if (user == null) + { + arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; + Logging($"客户端验证:客户端ID=【{arg.ClientId}】用户名不存在 {DateTime.Now} "); + return Task.CompletedTask; + } + + // 验证密码 + var password = arg.Password; + if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString()) + { + if (user.Password.Equals(MD5Encryption.Encrypt(password))) + return Task.CompletedTask; + } + else + { + if (CryptogramUtil.Decrypt(user.Password).Equals(password)) + return Task.CompletedTask; + } + arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; + Logging($"客户端验证:客户端ID=【{arg.ClientId}】用户名或密码验证错误 {DateTime.Now} "); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Mqtt/MqttEventHandler.cs b/Admin.NET/Admin.NET.Core/Mqtt/MqttEventInterceptor.cs similarity index 51% rename from Admin.NET/Admin.NET.Core/Mqtt/MqttEventHandler.cs rename to Admin.NET/Admin.NET.Core/Mqtt/MqttEventInterceptor.cs index ea3146f3..1442ddd0 100644 --- a/Admin.NET/Admin.NET.Core/Mqtt/MqttEventHandler.cs +++ b/Admin.NET/Admin.NET.Core/Mqtt/MqttEventInterceptor.cs @@ -1,90 +1,120 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -using MQTTnet.Server; -using MQTTnet; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using MQTTnet.Protocol; -using Furion.Logging.Extensions; -using Furion.HttpRemote; - -namespace Admin.NET.Core; - -/// -/// Mqtt事件拦截器 -/// -/// -/// 为了让底层的 Mqtt 代码与业务层代码分离,应该不同的业务继函 MqttEventHandler 类,实现自己的代码后 -/// 在 Startup.cs 的 Configure 函数中,调用 MqttHostedService 对象的 RegistMqttEventHandler 函数,来注册自己的事件处理器 -/// “业务相关的代码千万不要写在 MqttHostedService 中” -/// - -public class MqttEventHandler -{ - /// - /// 排序,数据越大越先执行 - /// - public int Order = 0; - - protected void Log(string msg) - { - var mqttOptions = App.GetOptions(); - if (mqttOptions.ConsoleOutput) - Console.WriteLine(msg); - if (mqttOptions.Logging) - msg.LogDebug(); - } - - public virtual async Task ValidatingConnectionAsync(ValidatingConnectionEventArgs arg) - { - } - - public virtual async Task StartedAsync(EventArgs arg) - { - } - - public virtual async Task StoppedAsync(EventArgs arg) - { - } - - /// - /// 客户端发布的数据 - /// - /// - /// - public virtual async Task InterceptingPublishAsync(InterceptingPublishEventArgs arg) - { - } - - /// - /// 没有客户端接收的数据 - /// - /// - /// - public virtual async Task ApplicationMessageNotConsumedAsync(ApplicationMessageNotConsumedEventArgs arg) - { - } - - public virtual async Task ClientSubscribedTopicAsync(ClientSubscribedTopicEventArgs arg) - { - } - public virtual async Task ClientUnsubscribedTopicAsync(ClientUnsubscribedTopicEventArgs arg) - { - } - - public virtual async Task ClientDisconnectedAsync(ClientDisconnectedEventArgs arg) - { - } - - public virtual async Task ClientConnectedAsync(ClientConnectedEventArgs arg) - { - } -} +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +using MQTTnet.Server; + +namespace Admin.NET.Core; + +/// +/// MQTT 事件拦截器 +/// +public class MqttEventInterceptor +{ + /// + /// 数值越大越先执行 + /// + public int Order = 0; + + /// + /// 启动后事件 + /// + /// + /// + public virtual async Task StartedAsync(EventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 关闭后事件 + /// + /// + /// + public virtual async Task StoppedAsync(EventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 客户端验证事件 + /// + /// + /// + public virtual async Task ValidatingConnectionAsync(ValidatingConnectionEventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 客户端连接事件 + /// + /// + /// + public virtual async Task ClientConnectedAsync(ClientConnectedEventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 客户端断开事件 + /// + /// + /// + public virtual async Task ClientDisconnectedAsync(ClientDisconnectedEventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 订阅主题事件 + /// + /// + /// + public virtual async Task ClientSubscribedTopicAsync(ClientSubscribedTopicEventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 取消订阅事件 + /// + /// + /// + public virtual async Task ClientUnsubscribedTopicAsync(ClientUnsubscribedTopicEventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 拦截发布的消息事件 + /// + /// + /// + public virtual async Task InterceptingPublishAsync(InterceptingPublishEventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 未被消费的消息事件 + /// + /// + /// + public virtual async Task ApplicationMessageNotConsumedAsync(ApplicationMessageNotConsumedEventArgs arg) + { + await Task.CompletedTask; + } + + /// + /// 输出日志事件 + /// + /// + protected static void Logging(string msg) + { + if (!App.GetOptions().Logging) return; + LoggingWriter.LogInformation(msg); + } +} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Option/MqttOptions.cs b/Admin.NET/Admin.NET.Core/Option/MqttOptions.cs index 60078e6a..873a5d6a 100644 --- a/Admin.NET/Admin.NET.Core/Option/MqttOptions.cs +++ b/Admin.NET/Admin.NET.Core/Option/MqttOptions.cs @@ -11,31 +11,11 @@ namespace Admin.NET.Core; /// public sealed class MqttOptions : IConfigurableOptions { - /// - /// 服务器主动发布时用的ClientId - /// - public string MqttServerId { get; set; } - /// /// 是否启用 /// public bool Enabled { get; set; } - /// - /// 输出文件日志 - /// - public bool Logging { get; set; } - - /// - /// 控制台输出 - /// - public bool ConsoleOutput { get; set; } - - /// - /// ConnectionBacklog - /// - public int ConnectionBacklog { get; set; } - /// /// 端口 /// @@ -45,4 +25,19 @@ public sealed class MqttOptions : IConfigurableOptions /// IP地址 /// public string IPAddress { get; set; } + + /// + /// 最大连接数 + /// + public int ConnectionBacklog { get; set; } + + /// + /// 服务器主动发消息时的ClientId + /// + public string MqttServerId { get; set; } + + /// + /// 输出日志 + /// + public bool Logging { get; set; } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/SysMqttDto.cs b/Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/MessageInput.cs similarity index 81% rename from Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/SysMqttDto.cs rename to Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/MessageInput.cs index 6df5ed55..3d105392 100644 --- a/Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/SysMqttDto.cs +++ b/Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/MessageInput.cs @@ -1,19 +1,23 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Admin.NET.Core; -public class PublicMessageInput -{ - public string Topic { get; set; } - - public string Message { get; set; } -} +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Core; + +/// +/// 发布主题消息 +/// +public class PublicMessageInput +{ + /// + /// 主题名称 + /// + public string Topic { get; set; } + + /// + /// 消息内容 + /// + public string Message { get; set; } +} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Service/Mqtt/SysMqttService.cs b/Admin.NET/Admin.NET.Core/Service/Mqtt/SysMqttService.cs index 85dbdfb0..bde16576 100644 --- a/Admin.NET/Admin.NET.Core/Service/Mqtt/SysMqttService.cs +++ b/Admin.NET/Admin.NET.Core/Service/Mqtt/SysMqttService.cs @@ -18,7 +18,7 @@ public class SysMqttService() : IDynamicApiController, ITransient /// 获取客户端列表 🔖 /// /// - [DisplayName("获取客户端列表")] + [DisplayName("获取客户端列表")] public async Task> GetClients() { if (MqttHostedService.MqttServer == null) @@ -28,15 +28,14 @@ public class SysMqttService() : IDynamicApiController, ITransient } /// - /// 发布主题 🔖 + /// 发布主题消息 🔖 /// /// /// - [AllowAnonymous] - [DisplayName("发布主题")] + [DisplayName("发布主题消息")] public async Task PublicMessage(PublicMessageInput input) { - MqttHostedService mqttHostedService = App.GetRequiredService(); - mqttHostedService.PublicMessage(input.Topic, input.Message); + var mqttHostedService = App.GetRequiredService(); + await mqttHostedService.PublicMessageAsync(input.Topic, input.Message); } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Core/Startup.cs b/Admin.NET/Admin.NET.Web.Core/Startup.cs index f637206c..b7b2ad8e 100644 --- a/Admin.NET/Admin.NET.Web.Core/Startup.cs +++ b/Admin.NET/Admin.NET.Web.Core/Startup.cs @@ -22,7 +22,6 @@ using Microsoft.AspNetCore.ResponseCompression; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; using MQTTnet.AspNetCore; using Newtonsoft.Json; using OnceMi.AspNetCore.OSS; @@ -108,7 +107,8 @@ public class Startup : AppStartup // setting.MetadataPropertyHandling = MetadataPropertyHandling.Ignore; // 解决DateTimeOffset异常 // setting.DateParseHandling = DateParseHandling.None; // 解决DateTimeOffset异常 // setting.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }); // 解决DateTimeOffset异常 - }; + } + ; services.AddControllersWithViews() .AddAppLocalization() @@ -412,29 +412,22 @@ public class Startup : AppStartup } }); - IOptions mqttOptions = App.GetRequiredService>(); + var mqttOptions = App.GetConfig("Mqtt", true); app.UseEndpoints(endpoints => { // 注册集线器 endpoints.MapHubs(); - + // 注册路由 endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); - if (mqttOptions.Value.Enabled) + // 注册 MQTT 支持 WebSocket + if (mqttOptions.Enabled) { - endpoints.MapConnectionHandler( - "/mqtt", + endpoints.MapConnectionHandler("/mqtt", httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector = protocolList => protocolList.FirstOrDefault() ?? string.Empty); } }); - - - if (mqttOptions.Value.Enabled) - { - // [以下是验证的示例] 注册自己业务的 Mqtt 处理器,如果有不同的认证方式,要把以下这句注释掉 - //MqttHostedService.RegistMqttEventHandler(new MqttEventHandlerForAdminNet(), int.MaxValue); - } } } \ No newline at end of file diff --git a/Web/package.json b/Web/package.json index 001b344e..1953e0ec 100644 --- a/Web/package.json +++ b/Web/package.json @@ -2,7 +2,7 @@ "name": "admin.net.pro", "type": "module", "version": "2.4.33", - "lastBuildTime": "2025.02.20", + "lastBuildTime": "2025.02.23", "description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架", "author": "zuohuaijun", "license": "MIT", @@ -18,8 +18,8 @@ }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", - "@logicflow/core": "^2.0.10", - "@logicflow/extension": "^2.0.14", + "@logicflow/core": "^2.0.11", + "@logicflow/extension": "^2.0.15", "@microsoft/signalr": "^8.0.7", "@vue-office/docx": "^1.6.2", "@vue-office/excel": "^1.7.14", @@ -36,7 +36,7 @@ "echarts": "^5.6.0", "echarts-gl": "^2.0.9", "echarts-wordcloud": "^2.1.0", - "element-plus": "^2.9.4", + "element-plus": "^2.9.5", "exceljs": "^4.4.0", "ezuikit-js": "^8.1.6", "gcoord": "^1.0.7", @@ -45,7 +45,7 @@ "jsplumb": "^2.15.6", "jwchat": "^2.0.3", "lodash-es": "^4.17.21", - "md-editor-v3": "^5.2.3", + "md-editor-v3": "^5.3.2", "mitt": "^3.0.1", "monaco-editor": "^0.52.2", "mqtt": "^5.10.3", @@ -74,7 +74,7 @@ "vue-router": "^4.5.0", "vue-signature-pad": "^3.0.2", "vue3-tree-org": "^4.2.2", - "vxe-pc-ui": "^4.3.91", + "vxe-pc-ui": "^4.3.95", "vxe-table": "^4.10.0", "vxe-table-plugin-element": "^4.0.4", "vxe-table-plugin-export-xlsx": "^4.0.7", @@ -94,11 +94,11 @@ "@vitejs/plugin-vue-jsx": "^4.1.1", "@vue/compiler-sfc": "^3.5.13", "code-inspector-plugin": "^0.20.0", - "eslint": "^9.20.1", + "eslint": "^9.21.0", "eslint-plugin-vue": "^9.32.0", - "globals": "^15.15.0", + "globals": "^16.0.0", "less": "^4.2.2", - "prettier": "^3.5.1", + "prettier": "^3.5.2", "rollup-plugin-visualizer": "^5.14.0", "sass": "^1.85.0", "terser": "^5.39.0", diff --git a/Web/src/api-services/api.ts b/Web/src/api-services/api.ts index ef08566b..0944a5c1 100644 --- a/Web/src/api-services/api.ts +++ b/Web/src/api-services/api.ts @@ -38,6 +38,7 @@ export * from './apis/sys-log-op-api'; export * from './apis/sys-log-vis-api'; export * from './apis/sys-menu-api'; export * from './apis/sys-message-api'; +export * from './apis/sys-mqtt-api'; export * from './apis/sys-notice-api'; export * from './apis/sys-oauth-api'; export * from './apis/sys-oauth-user-api'; diff --git a/Web/src/api-services/apis/sys-enum-api.ts b/Web/src/api-services/apis/sys-enum-api.ts index f7bf078c..4f65d065 100644 --- a/Web/src/api-services/apis/sys-enum-api.ts +++ b/Web/src/api-services/apis/sys-enum-api.ts @@ -77,6 +77,49 @@ export const SysEnumApiAxiosParamCreator = function (configuration?: Configurati options: localVarRequestOptions, }; }, + /** + * + * @summary 枚举转字典 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiSysEnumEnumToDictPost: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/sysEnum/enumToDict`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, /** * * @summary 获取所有枚举类型 🔖 @@ -143,6 +186,19 @@ export const SysEnumApiFp = function(configuration?: Configuration) { return axios.request(axiosRequestArgs); }; }, + /** + * + * @summary 枚举转字典 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysEnumEnumToDictPost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await SysEnumApiAxiosParamCreator(configuration).apiSysEnumEnumToDictPost(options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, /** * * @summary 获取所有枚举类型 🔖 @@ -175,6 +231,15 @@ export const SysEnumApiFactory = function (configuration?: Configuration, basePa async apiSysEnumEnumDataListGet(enumName: string, options?: AxiosRequestConfig): Promise> { return SysEnumApiFp(configuration).apiSysEnumEnumDataListGet(enumName, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary 枚举转字典 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysEnumEnumToDictPost(options?: AxiosRequestConfig): Promise> { + return SysEnumApiFp(configuration).apiSysEnumEnumToDictPost(options).then((request) => request(axios, basePath)); + }, /** * * @summary 获取所有枚举类型 🔖 @@ -205,6 +270,16 @@ export class SysEnumApi extends BaseAPI { public async apiSysEnumEnumDataListGet(enumName: string, options?: AxiosRequestConfig) : Promise> { return SysEnumApiFp(this.configuration).apiSysEnumEnumDataListGet(enumName, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary 枚举转字典 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SysEnumApi + */ + public async apiSysEnumEnumToDictPost(options?: AxiosRequestConfig) : Promise> { + return SysEnumApiFp(this.configuration).apiSysEnumEnumToDictPost(options).then((request) => request(this.axios, this.basePath)); + } /** * * @summary 获取所有枚举类型 🔖 diff --git a/Web/src/api-services/apis/sys-mqtt-api.ts b/Web/src/api-services/apis/sys-mqtt-api.ts new file mode 100644 index 00000000..ae673eb4 --- /dev/null +++ b/Web/src/api-services/apis/sys-mqtt-api.ts @@ -0,0 +1,214 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import globalAxios, { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios'; +import { Configuration } from '../configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base'; +import { AdminNETResultIListMqttClientStatus } from '../models'; +import { PublicMessageInput } from '../models'; +/** + * SysMqttApi - axios parameter creator + * @export + */ +export const SysMqttApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary 获取客户端列表 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiSysMqttClientsGet: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/sysMqtt/clients`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + /** + * + * @summary 发布主题消息 🔖 + * @param {PublicMessageInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiSysMqttPublicMessagePost: async (body?: PublicMessageInput, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/sysMqtt/publicMessage`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + localVarHeaderParameter['Content-Type'] = 'application/json-patch+json'; + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json'; + localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || ""); + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * SysMqttApi - functional programming interface + * @export + */ +export const SysMqttApiFp = function(configuration?: Configuration) { + return { + /** + * + * @summary 获取客户端列表 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysMqttClientsGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await SysMqttApiAxiosParamCreator(configuration).apiSysMqttClientsGet(options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + /** + * + * @summary 发布主题消息 🔖 + * @param {PublicMessageInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysMqttPublicMessagePost(body?: PublicMessageInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await SysMqttApiAxiosParamCreator(configuration).apiSysMqttPublicMessagePost(body, options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + } +}; + +/** + * SysMqttApi - factory interface + * @export + */ +export const SysMqttApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + return { + /** + * + * @summary 获取客户端列表 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysMqttClientsGet(options?: AxiosRequestConfig): Promise> { + return SysMqttApiFp(configuration).apiSysMqttClientsGet(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary 发布主题消息 🔖 + * @param {PublicMessageInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysMqttPublicMessagePost(body?: PublicMessageInput, options?: AxiosRequestConfig): Promise> { + return SysMqttApiFp(configuration).apiSysMqttPublicMessagePost(body, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * SysMqttApi - object-oriented interface + * @export + * @class SysMqttApi + * @extends {BaseAPI} + */ +export class SysMqttApi extends BaseAPI { + /** + * + * @summary 获取客户端列表 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SysMqttApi + */ + public async apiSysMqttClientsGet(options?: AxiosRequestConfig) : Promise> { + return SysMqttApiFp(this.configuration).apiSysMqttClientsGet(options).then((request) => request(this.axios, this.basePath)); + } + /** + * + * @summary 发布主题消息 🔖 + * @param {PublicMessageInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SysMqttApi + */ + public async apiSysMqttPublicMessagePost(body?: PublicMessageInput, options?: AxiosRequestConfig) : Promise> { + return SysMqttApiFp(this.configuration).apiSysMqttPublicMessagePost(body, options).then((request) => request(this.axios, this.basePath)); + } +} diff --git a/Web/src/api-services/apis/sys-online-user-api.ts b/Web/src/api-services/apis/sys-online-user-api.ts index 5eb8c50c..bacc81fa 100644 --- a/Web/src/api-services/apis/sys-online-user-api.ts +++ b/Web/src/api-services/apis/sys-online-user-api.ts @@ -74,6 +74,49 @@ export const SysOnlineUserApiAxiosParamCreator = function (configuration?: Confi options: localVarRequestOptions, }; }, + /** + * + * @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的) + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiSysOnlineUserOnlinePost: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/sysOnlineUser/online`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, /** * * @summary 获取在线用户分页列表 🔖 @@ -145,6 +188,19 @@ export const SysOnlineUserApiFp = function(configuration?: Configuration) { return axios.request(axiosRequestArgs); }; }, + /** + * + * @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的) + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysOnlineUserOnlinePost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await SysOnlineUserApiAxiosParamCreator(configuration).apiSysOnlineUserOnlinePost(options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, /** * * @summary 获取在线用户分页列表 🔖 @@ -178,6 +234,15 @@ export const SysOnlineUserApiFactory = function (configuration?: Configuration, async apiSysOnlineUserForceOfflinePost(body?: SysOnlineUser, options?: AxiosRequestConfig): Promise> { return SysOnlineUserApiFp(configuration).apiSysOnlineUserForceOfflinePost(body, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的) + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiSysOnlineUserOnlinePost(options?: AxiosRequestConfig): Promise> { + return SysOnlineUserApiFp(configuration).apiSysOnlineUserOnlinePost(options).then((request) => request(axios, basePath)); + }, /** * * @summary 获取在线用户分页列表 🔖 @@ -209,6 +274,16 @@ export class SysOnlineUserApi extends BaseAPI { public async apiSysOnlineUserForceOfflinePost(body?: SysOnlineUser, options?: AxiosRequestConfig) : Promise> { return SysOnlineUserApiFp(this.configuration).apiSysOnlineUserForceOfflinePost(body, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的) + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SysOnlineUserApi + */ + public async apiSysOnlineUserOnlinePost(options?: AxiosRequestConfig) : Promise> { + return SysOnlineUserApiFp(this.configuration).apiSysOnlineUserOnlinePost(options).then((request) => request(this.axios, this.basePath)); + } /** * * @summary 获取在线用户分页列表 🔖 diff --git a/Web/src/api-services/models/address-family.ts b/Web/src/api-services/models/address-family.ts new file mode 100644 index 00000000..654715f1 --- /dev/null +++ b/Web/src/api-services/models/address-family.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/** + * + * @export + * @enum {string} + */ +export enum AddressFamily { + NUMBER_0 = 0, + NUMBER_1 = 1, + NUMBER_2 = 2, + NUMBER_3 = 3, + NUMBER_4 = 4, + NUMBER_5 = 5, + NUMBER_6 = 6, + NUMBER_7 = 7, + NUMBER_8 = 8, + NUMBER_9 = 9, + NUMBER_10 = 10, + NUMBER_11 = 11, + NUMBER_12 = 12, + NUMBER_13 = 13, + NUMBER_14 = 14, + NUMBER_15 = 15, + NUMBER_16 = 16, + NUMBER_17 = 17, + NUMBER_18 = 18, + NUMBER_19 = 19, + NUMBER_21 = 21, + NUMBER_22 = 22, + NUMBER_23 = 23, + NUMBER_24 = 24, + NUMBER_25 = 25, + NUMBER_26 = 26, + NUMBER_28 = 28, + NUMBER_29 = 29, + NUMBER_65536 = 65536, + NUMBER_65537 = 65537, + NUMBER_MINUS_1 = -1 +} + diff --git a/Web/src/api-services/models/admin-netresult-ilist-mqtt-client-status.ts b/Web/src/api-services/models/admin-netresult-ilist-mqtt-client-status.ts new file mode 100644 index 00000000..2f6c29da --- /dev/null +++ b/Web/src/api-services/models/admin-netresult-ilist-mqtt-client-status.ts @@ -0,0 +1,71 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import { MqttClientStatus } from './mqtt-client-status'; + /** + * 全局返回结果 + * + * @export + * @interface AdminNETResultIListMqttClientStatus + */ +export interface AdminNETResultIListMqttClientStatus { + + /** + * 状态码 + * + * @type {number} + * @memberof AdminNETResultIListMqttClientStatus + */ + code?: number; + + /** + * 类型success、warning、error + * + * @type {string} + * @memberof AdminNETResultIListMqttClientStatus + */ + type?: string | null; + + /** + * 错误信息 + * + * @type {string} + * @memberof AdminNETResultIListMqttClientStatus + */ + message?: string | null; + + /** + * 数据 + * + * @type {Array} + * @memberof AdminNETResultIListMqttClientStatus + */ + result?: Array | null; + + /** + * 附加数据 + * + * @type {any} + * @memberof AdminNETResultIListMqttClientStatus + */ + extras?: any | null; + + /** + * 时间 + * + * @type {Date} + * @memberof AdminNETResultIListMqttClientStatus + */ + time?: Date; +} diff --git a/Web/src/api-services/models/end-point.ts b/Web/src/api-services/models/end-point.ts new file mode 100644 index 00000000..d8fb2621 --- /dev/null +++ b/Web/src/api-services/models/end-point.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import { AddressFamily } from './address-family'; + /** + * + * + * @export + * @interface EndPoint + */ +export interface EndPoint { + + /** + * @type {AddressFamily} + * @memberof EndPoint + */ + addressFamily?: AddressFamily; +} diff --git a/Web/src/api-services/models/index.ts b/Web/src/api-services/models/index.ts index ac63806c..77de8f9a 100644 --- a/Web/src/api-services/models/index.ts +++ b/Web/src/api-services/models/index.ts @@ -21,6 +21,7 @@ export * from './add-sys-ldap-input'; export * from './add-tenant-input'; export * from './add-upgrade-input'; export * from './add-user-input'; +export * from './address-family'; export * from './admin-netresult-boolean'; export * from './admin-netresult-captcha-output'; export * from './admin-netresult-create-pay-transaction-native-output'; @@ -34,6 +35,7 @@ export * from './admin-netresult-grant-role-output'; export * from './admin-netresult-iaction-result'; export * from './admin-netresult-idisposable'; export * from './admin-netresult-ienumerable-entity-info'; +export * from './admin-netresult-ilist-mqtt-client-status'; export * from './admin-netresult-int32'; export * from './admin-netresult-int64'; export * from './admin-netresult-jobject'; @@ -210,6 +212,7 @@ export * from './delete-user-input'; export * from './dict-data-input'; export * from './dict-type-input'; export * from './digit-shapes'; +export * from './end-point'; export * from './entity-column-info'; export * from './entity-info'; export * from './enum-entity'; @@ -276,6 +279,9 @@ export * from './method-impl-attributes'; export * from './method-info'; export * from './module'; export * from './module-handle'; +export * from './mqtt-client-status'; +export * from './mqtt-protocol-version'; +export * from './mqtt-session-status'; export * from './navigate'; export * from './notice-input'; export * from './notice-status-enum'; @@ -322,6 +328,7 @@ export * from './print-type-enum'; export * from './promotion'; export * from './property-attributes'; export * from './property-info'; +export * from './public-message-input'; export * from './query-region-input'; export * from './refund-request-input'; export * from './reset-pwd-user-input'; diff --git a/Web/src/api-services/models/mqtt-client-status.ts b/Web/src/api-services/models/mqtt-client-status.ts new file mode 100644 index 00000000..49acff86 --- /dev/null +++ b/Web/src/api-services/models/mqtt-client-status.ts @@ -0,0 +1,115 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import { EndPoint } from './end-point'; +import { MqttProtocolVersion } from './mqtt-protocol-version'; +import { MqttSessionStatus } from './mqtt-session-status'; + /** + * + * + * @export + * @interface MqttClientStatus + */ +export interface MqttClientStatus { + + /** + * @type {number} + * @memberof MqttClientStatus + */ + bytesReceived?: number; + + /** + * @type {number} + * @memberof MqttClientStatus + */ + bytesSent?: number; + + /** + * @type {Date} + * @memberof MqttClientStatus + */ + connectedTimestamp?: Date; + + /** + * @type {EndPoint} + * @memberof MqttClientStatus + */ + remoteEndPoint?: EndPoint; + + /** + * @type {string} + * @memberof MqttClientStatus + */ + endpoint?: string | null; + + /** + * @type {string} + * @memberof MqttClientStatus + */ + id?: string | null; + + /** + * @type {Date} + * @memberof MqttClientStatus + */ + lastNonKeepAlivePacketReceivedTimestamp?: Date; + + /** + * @type {Date} + * @memberof MqttClientStatus + */ + lastPacketReceivedTimestamp?: Date; + + /** + * @type {Date} + * @memberof MqttClientStatus + */ + lastPacketSentTimestamp?: Date; + + /** + * @type {MqttProtocolVersion} + * @memberof MqttClientStatus + */ + protocolVersion?: MqttProtocolVersion; + + /** + * @type {number} + * @memberof MqttClientStatus + */ + receivedApplicationMessagesCount?: number; + + /** + * @type {number} + * @memberof MqttClientStatus + */ + receivedPacketsCount?: number; + + /** + * @type {number} + * @memberof MqttClientStatus + */ + sentApplicationMessagesCount?: number; + + /** + * @type {number} + * @memberof MqttClientStatus + */ + sentPacketsCount?: number; + + /** + * @type {MqttSessionStatus} + * @memberof MqttClientStatus + */ + session?: MqttSessionStatus; +} diff --git a/Web/src/api-services/models/mqtt-protocol-version.ts b/Web/src/api-services/models/mqtt-protocol-version.ts new file mode 100644 index 00000000..22fcb5c0 --- /dev/null +++ b/Web/src/api-services/models/mqtt-protocol-version.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/** + * + * @export + * @enum {string} + */ +export enum MqttProtocolVersion { + NUMBER_0 = 0, + NUMBER_3 = 3, + NUMBER_4 = 4, + NUMBER_5 = 5 +} + diff --git a/Web/src/api-services/models/mqtt-session-status.ts b/Web/src/api-services/models/mqtt-session-status.ts new file mode 100644 index 00000000..e83e0538 --- /dev/null +++ b/Web/src/api-services/models/mqtt-session-status.ts @@ -0,0 +1,58 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + /** + * + * + * @export + * @interface MqttSessionStatus + */ +export interface MqttSessionStatus { + + /** + * @type {Date} + * @memberof MqttSessionStatus + */ + createdTimestamp?: Date; + + /** + * @type {Date} + * @memberof MqttSessionStatus + */ + disconnectedTimestamp?: Date | null; + + /** + * @type {number} + * @memberof MqttSessionStatus + */ + expiryInterval?: number; + + /** + * @type {string} + * @memberof MqttSessionStatus + */ + id?: string | null; + + /** + * @type {{ [key: string]: any; }} + * @memberof MqttSessionStatus + */ + items?: { [key: string]: any; } | null; + + /** + * @type {number} + * @memberof MqttSessionStatus + */ + pendingApplicationMessagesCount?: number; +} diff --git a/Web/src/api-services/models/public-message-input.ts b/Web/src/api-services/models/public-message-input.ts new file mode 100644 index 00000000..13fedfa0 --- /dev/null +++ b/Web/src/api-services/models/public-message-input.ts @@ -0,0 +1,38 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + /** + * 发布主题消息 + * + * @export + * @interface PublicMessageInput + */ +export interface PublicMessageInput { + + /** + * 主题名称 + * + * @type {string} + * @memberof PublicMessageInput + */ + topic?: string | null; + + /** + * 消息内容 + * + * @type {string} + * @memberof PublicMessageInput + */ + message?: string | null; +} diff --git a/Web/src/api-services/models/sys-file.ts b/Web/src/api-services/models/sys-file.ts index 72381d10..e0ca5143 100644 --- a/Web/src/api-services/models/sys-file.ts +++ b/Web/src/api-services/models/sys-file.ts @@ -140,6 +140,14 @@ export interface SysFile { */ suffix?: string | null; + /** + * MIME类型 + * + * @type {string} + * @memberof SysFile + */ + contentType?: string | null; + /** * 存储路径 *