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;
+
/**
* 存储路径
*