diff --git a/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json b/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json
index 427640ef..18d14166 100644
--- a/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json
+++ b/Admin.NET/Admin.NET.Application/Configuration/Mqtt.json
@@ -1,10 +1,13 @@
-{
+{
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
// MQTT 配置
"Mqtt": {
"Enabled": false, // 是否开启
- "Port": "5001",
- "IPAddress": ""
+ "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 e349147c..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 @@
-
+
-
-
-
+
+
+
-
+
@@ -40,7 +40,7 @@
-
+
@@ -48,7 +48,7 @@
-
+
diff --git a/Admin.NET/Admin.NET.Core/Job/MqttHostedService.cs b/Admin.NET/Admin.NET.Core/Job/MqttHostedService.cs
new file mode 100644
index 00000000..250952ed
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Job/MqttHostedService.cs
@@ -0,0 +1,239 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+using Microsoft.Extensions.Hosting;
+using MQTTnet;
+using MQTTnet.Protocol;
+using MQTTnet.Server;
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+///
+/// MQTT 服务
+///
+public class MqttHostedService(IOptions mqttOptions) : IHostedService, ISingleton
+{
+ private readonly MqttOptions _mqttOptions = mqttOptions.Value;
+ public static MqttServer MqttServer { get; set; }
+ public static readonly List MqttEventInterceptors = []; // MQTT 事件拦截器集合
+
+ ///
+ /// 注册 MQTT 事件拦截器
+ ///
+ ///
+ ///
+ public static void AddMqttEventInterceptor(MqttEventInterceptor mqttEventInterceptor, int order = 0)
+ {
+ mqttEventInterceptor.Order = order;
+ MqttEventInterceptors.Add(mqttEventInterceptor);
+ MqttEventInterceptors.Sort((a, b) => b.Order - a.Order);
+ }
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ if (!_mqttOptions.Enabled) return;
+
+ // 注册 MQTT 自定义客户端验证事件拦截器
+ AddMqttEventInterceptor(new DefaultMqttEventInterceptor());
+
+ var options = new MqttServerOptionsBuilder()
+ .WithDefaultEndpoint() // 默认地址127.0.0.1
+ .WithDefaultEndpointPort(_mqttOptions.Port) // 端口号
+ //.WithDefaultEndpointBoundIPAddress(_mqttOptions.IPAddress) // IP地址
+ .WithConnectionBacklog(_mqttOptions.ConnectionBacklog) // 最大连接数
+ .WithPersistentSessions()
+ .Build();
+
+ MqttServer = new MqttServerFactory().CreateMqttServer(options);
+ MqttServer.StartedAsync += MqttServer_StartedAsync; // 启动后事件
+ MqttServer.StoppedAsync += MqttServer_StoppedAsync; // 关闭后事件
+
+ MqttServer.ValidatingConnectionAsync += MqttServer_ValidatingConnectionAsync; // 客户端验证事件
+ MqttServer.ClientConnectedAsync += MqttServer_ClientConnectedAsync; // 客户端连接事件
+ MqttServer.ClientDisconnectedAsync += MqttServer_ClientDisconnectedAsync; // 客户端断开事件
+
+ MqttServer.ClientSubscribedTopicAsync += MqttServer_ClientSubscribedTopicAsync; // 订阅主题事件
+ MqttServer.ClientUnsubscribedTopicAsync += MqttServer_ClientUnsubscribedTopicAsync; // 取消订阅事件
+ MqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync; // 拦截接收消息
+ MqttServer.ApplicationMessageNotConsumedAsync += MqttServer_ApplicationMessageNotConsumedAsync; // 消息未被消费
+
+ await MqttServer.StartAsync();
+ }
+
+ ///
+ /// 启动后事件
+ ///
+ ///
+ ///
+ private Task MqttServer_StartedAsync(EventArgs arg)
+ {
+ Console.WriteLine($"【MQTT】服务已启动,端口:{_mqttOptions.Port}...... {DateTime.Now}");
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// 关闭后事件
+ ///
+ ///
+ ///
+ private async Task MqttServer_StoppedAsync(EventArgs arg)
+ {
+ Console.WriteLine($"【MQTT】服务已关闭...... {DateTime.Now}");
+ foreach (var eh in MqttEventInterceptors)
+ {
+ await eh.StoppedAsync(arg);
+ }
+ }
+
+ ///
+ /// 客户端验证事件
+ ///
+ ///
+ ///
+ private async Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs arg)
+ {
+ foreach (var eh in MqttEventInterceptors)
+ {
+ await eh.ValidatingConnectionAsync(arg);
+ if (arg.ReasonCode != MqttConnectReasonCode.Success)
+ break;
+ }
+ }
+
+ ///
+ /// 客户端连接事件
+ ///
+ ///
+ ///
+ private async Task MqttServer_ClientConnectedAsync(ClientConnectedEventArgs arg)
+ {
+ foreach (var eh in MqttEventInterceptors)
+ {
+ await eh.ClientConnectedAsync(arg);
+ }
+
+ Logging($"客户端连接:客户端ID=【{arg.ClientId}】已连接:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}");
+ }
+
+ ///
+ /// 客户端断开事件
+ ///
+ ///
+ ///
+ ///
+ private async Task MqttServer_ClientDisconnectedAsync(ClientDisconnectedEventArgs arg)
+ {
+ foreach (var eh in MqttEventInterceptors)
+ {
+ await eh.ClientDisconnectedAsync(arg);
+ }
+
+ Logging($"客户端断开:客户端ID=【{arg.ClientId}】已断开:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}");
+ }
+
+ ///
+ /// 订阅主题事件
+ ///
+ ///
+ ///
+ private async Task MqttServer_ClientSubscribedTopicAsync(ClientSubscribedTopicEventArgs arg)
+ {
+ foreach (var eh in MqttEventInterceptors)
+ {
+ await eh.ClientSubscribedTopicAsync(arg);
+ }
+
+ Logging($"订阅主题:客户端ID=【{arg.ClientId}】订阅主题=【{arg.TopicFilter}】 {DateTime.Now}");
+ }
+
+ ///
+ /// 取消订阅事件
+ ///
+ ///
+ ///
+ private async Task MqttServer_ClientUnsubscribedTopicAsync(ClientUnsubscribedTopicEventArgs arg)
+ {
+ foreach (var eh in MqttEventInterceptors)
+ {
+ await eh.ClientUnsubscribedTopicAsync(arg);
+ }
+
+ Logging($"取消订阅:客户端ID=【{arg.ClientId}】取消订阅主题=【{arg.TopicFilter}】 {DateTime.Now}");
+ }
+
+ ///
+ /// 拦截发布的消息事件
+ ///
+ ///
+ ///
+ private async Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs arg)
+ {
+ if (string.Equals(arg.ClientId, _mqttOptions.MqttServerId))
+ return;
+
+ 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)
+ {
+ 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)
+ {
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// 输出日志
+ ///
+ ///
+ protected void Logging(string msg)
+ {
+ if (!_mqttOptions.Logging) return;
+ LoggingWriter.LogInformation(msg);
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Mqtt/DefaultMqttEventInterceptor.cs b/Admin.NET/Admin.NET.Core/Mqtt/DefaultMqttEventInterceptor.cs
new file mode 100644
index 00000000..f77a0283
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Mqtt/DefaultMqttEventInterceptor.cs
@@ -0,0 +1,47 @@
+// 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/MqttEventInterceptor.cs b/Admin.NET/Admin.NET.Core/Mqtt/MqttEventInterceptor.cs
new file mode 100644
index 00000000..1442ddd0
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Mqtt/MqttEventInterceptor.cs
@@ -0,0 +1,120 @@
+// 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/Mqtt/MqttHostedService.cs b/Admin.NET/Admin.NET.Core/Mqtt/MqttHostedService.cs
deleted file mode 100644
index 2fc64a1d..00000000
--- a/Admin.NET/Admin.NET.Core/Mqtt/MqttHostedService.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-using Microsoft.Extensions.Hosting;
-using MQTTnet.Protocol;
-using MQTTnet.Server;
-using System;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-///
-/// MQTT 服务
-///
-public class MqttHostedService(IOptions mqttOptions, ISqlSugarClient db) : IHostedService, IDisposable
-{
- private const string ServerClientId = "Admin.NET.MQTT";
- public MqttServer MqttServer { get; set; }
- private readonly MqttOptions _mqttOptions = mqttOptions.Value;
- private readonly ISqlSugarClient _db = db;
-
- public async Task StartAsync(CancellationToken cancellationToken)
- {
- if (!_mqttOptions.Enabled) return;
-
- var options = new MqttServerOptionsBuilder()
- .WithDefaultEndpoint() // 默认地址127.0.0.1
- .WithDefaultEndpointPort(_mqttOptions.Port) // 端口号
- //.WithDefaultEndpointBoundIPAddress(_mqttOptions.IPAddress) // IP地址
- .WithConnectionBacklog(1000) // 最大连接数
- .WithPersistentSessions()
- .Build();
-
- MqttServer = new MqttServerFactory().CreateMqttServer(options);
- MqttServer.StartedAsync += MqttServer_StartedAsync; // 启动后事件
- MqttServer.StoppedAsync += MqttServer_StoppedAsync; // 关闭后事件
-
- MqttServer.ValidatingConnectionAsync += MqttServer_ValidatingConnectionAsync; // 客户端验证事件
- MqttServer.ClientConnectedAsync += MqttServer_ClientConnectedAsync; // 客户端连接事件
- MqttServer.ClientDisconnectedAsync += MqttServer_ClientDisconnectedAsync; // 客户端断开事件
-
- MqttServer.ClientSubscribedTopicAsync += MqttServer_ClientSubscribedTopicAsync; // 订阅主题事件
- MqttServer.ClientUnsubscribedTopicAsync += MqttServer_ClientUnsubscribedTopicAsync; // 取消订阅事件
- MqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync; // 拦截接收消息
- MqttServer.ApplicationMessageNotConsumedAsync += MqttServer_ApplicationMessageNotConsumedAsync; // 消息未被消费
-
- await MqttServer.StartAsync();
- }
-
- ///
- /// 启动后事件
- ///
- ///
- ///
- private Task MqttServer_StartedAsync(EventArgs arg)
- {
- Console.WriteLine($"【MQTT】服务已启动,端口:{_mqttOptions.Port}...... {DateTime.Now}");
- return Task.CompletedTask;
- }
-
- ///
- /// 关闭后事件
- ///
- ///
- ///
- private Task MqttServer_StoppedAsync(EventArgs arg)
- {
- Console.WriteLine($"【MQTT】服务已关闭...... {DateTime.Now}");
- return Task.CompletedTask;
- }
-
- ///
- /// 客户端验证事件
- ///
- ///
- ///
- private Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs arg)
- {
- arg.ReasonCode = MqttConnectReasonCode.Success;
-
- // 验证账号
- var user = _db.Queryable().First(u => u.Account == arg.UserName);
- if (user == null)
- {
- arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
- Console.WriteLine($"客户端验证:客户端ID=【{arg.ClientId}】用户名或密码验证错误 {DateTime.Now} ");
- throw Oops.Oh("MQTT客户端验证:账号不能为空或不存在!");
- }
-
- // 验证密码
- 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;
- Console.WriteLine($"客户端验证:客户端ID=【{arg.ClientId}】用户名或密码验证错误 {DateTime.Now} ");
- throw Oops.Oh("MQTT客户端验证:密码错误!");
- }
-
- ///
- /// 客户端连接事件
- ///
- ///
- ///
- private Task MqttServer_ClientConnectedAsync(ClientConnectedEventArgs arg)
- {
- Console.WriteLine($"客户端连接:客户端ID=【{arg.ClientId}】已连接:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}");
- return Task.CompletedTask;
- }
-
- ///
- /// 客户端断开事件
- ///
- ///
- ///
- ///
- private Task MqttServer_ClientDisconnectedAsync(ClientDisconnectedEventArgs arg)
- {
- Console.WriteLine($"客户端断开:客户端ID=【{arg.ClientId}】已断开:用户名=【{arg.UserName}】地址=【{arg.RemoteEndPoint}】 {DateTime.Now}");
- return Task.CompletedTask;
- }
-
- ///
- /// 订阅主题事件
- ///
- ///
- ///
- private Task MqttServer_ClientSubscribedTopicAsync(ClientSubscribedTopicEventArgs arg)
- {
- Console.WriteLine($"订阅主题:客户端ID=【{arg.ClientId}】订阅主题=【{arg.TopicFilter}】 {DateTime.Now}");
- return Task.CompletedTask;
- }
-
- ///
- /// 取消订阅事件
- ///
- ///
- ///
- private Task MqttServer_ClientUnsubscribedTopicAsync(ClientUnsubscribedTopicEventArgs arg)
- {
- Console.WriteLine($"取消订阅:客户端ID=【{arg.ClientId}】取消订阅主题=【{arg.TopicFilter}】 {DateTime.Now}");
- return Task.CompletedTask;
- }
-
- ///
- /// 拦截接收消息
- ///
- ///
- ///
- private Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs arg)
- {
- if (string.Equals(arg.ClientId, ServerClientId))
- return Task.CompletedTask;
-
- Console.WriteLine($"拦截消息:客户端ID=【{arg.ClientId}】 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】 {DateTime.Now}");
- return Task.CompletedTask;
- }
-
- ///
- /// 消息未被消费
- ///
- ///
- ///
- private 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}");
- return Task.CompletedTask;
- }
-
- public Task StopAsync(CancellationToken cancellationToken)
- {
- return Task.CompletedTask;
- }
-
- public void Dispose()
- {
- throw new NotImplementedException();
- }
-}
\ 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 69a4fa2f..873a5d6a 100644
--- a/Admin.NET/Admin.NET.Core/Option/MqttOptions.cs
+++ b/Admin.NET/Admin.NET.Core/Option/MqttOptions.cs
@@ -1,28 +1,43 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-///
-/// MQTT 配置选项
-///
-public sealed class MqttOptions : IConfigurableOptions
-{
- ///
- /// 是否启用
- ///
- public bool Enabled { get; set; }
-
- ///
- /// 端口
- ///
- public int Port { get; set; }
-
- ///
- /// IP地址
- ///
- public string IPAddress { get; set; }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// MQTT 配置选项
+///
+public sealed class MqttOptions : IConfigurableOptions
+{
+ ///
+ /// 是否启用
+ ///
+ public bool Enabled { get; set; }
+
+ ///
+ /// 端口
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// 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/MessageInput.cs b/Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/MessageInput.cs
new file mode 100644
index 00000000..3d105392
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/Mqtt/Dto/MessageInput.cs
@@ -0,0 +1,23 @@
+// 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
new file mode 100644
index 00000000..bde16576
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/Mqtt/SysMqttService.cs
@@ -0,0 +1,41 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using MQTTnet.Server;
+
+namespace Admin.NET.Core.Service;
+
+///
+/// 系统 MQTT 服务 🧩
+///
+[ApiDescriptionSettings(Order = 90, Description = "MQTT 服务")]
+public class SysMqttService() : IDynamicApiController, ITransient
+{
+ ///
+ /// 获取客户端列表 🔖
+ ///
+ ///
+ [DisplayName("获取客户端列表")]
+ public async Task> GetClients()
+ {
+ if (MqttHostedService.MqttServer == null)
+ throw Oops.Oh("【MQTT】服务未启动");
+
+ return await MqttHostedService.MqttServer.GetClientsAsync();
+ }
+
+ ///
+ /// 发布主题消息 🔖
+ ///
+ ///
+ ///
+ [DisplayName("发布主题消息")]
+ public async Task PublicMessage(PublicMessageInput input)
+ {
+ var mqttHostedService = App.GetRequiredService();
+ await mqttHostedService.PublicMessageAsync(input.Topic, input.Message);
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/OnlineUser/SysOnlineUserService.cs b/Admin.NET/Admin.NET.Core/Service/OnlineUser/SysOnlineUserService.cs
index 44a3da5b..c6441b0f 100644
--- a/Admin.NET/Admin.NET.Core/Service/OnlineUser/SysOnlineUserService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/OnlineUser/SysOnlineUserService.cs
@@ -119,8 +119,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
if (await _sysConfigService.GetConfigValueByCode(ConfigConst.SysSingleLogin)) return;
// 相同账号最后登录的用户Id集合
- var onlineUserRecords = await _sysOnlineUerRep.AsQueryable()
- .GroupBy(u => u.UserId)
+ var onlineUsers = await _sysOnlineUerRep.AsQueryable().GroupBy(u => u.UserId)
.Select(u => new
{
UserId = u.UserId,
@@ -128,9 +127,10 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
Id = SqlFunc.AggregateMax(u.Id)
})
.ToListAsync();
- var onlineUserIds = onlineUserRecords.Select(x => x.Id).ToList();
+ if (onlineUsers.Count < 1) return;
// 无效登录用户集合
+ var onlineUserIds = onlineUsers.Select(u => u.Id).ToList();
var offlineUsers = await _sysOnlineUerRep.AsQueryable().Where(u => !onlineUserIds.Contains(u.Id)).ToListAsync();
foreach (var user in offlineUsers)
{
diff --git a/Admin.NET/Admin.NET.Web.Core/Admin.NET.Web.Core.csproj b/Admin.NET/Admin.NET.Web.Core/Admin.NET.Web.Core.csproj
index 9a5475c5..4bb365c3 100644
--- a/Admin.NET/Admin.NET.Web.Core/Admin.NET.Web.Core.csproj
+++ b/Admin.NET/Admin.NET.Web.Core/Admin.NET.Web.Core.csproj
@@ -11,6 +11,7 @@
+
diff --git a/Admin.NET/Admin.NET.Web.Core/Startup.cs b/Admin.NET/Admin.NET.Web.Core/Startup.cs
index 487ffc0f..b7b2ad8e 100644
--- a/Admin.NET/Admin.NET.Web.Core/Startup.cs
+++ b/Admin.NET/Admin.NET.Web.Core/Startup.cs
@@ -22,6 +22,7 @@ using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
+using MQTTnet.AspNetCore;
using Newtonsoft.Json;
using OnceMi.AspNetCore.OSS;
using RabbitMQ.Client;
@@ -106,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()
@@ -410,14 +412,22 @@ public class Startup : AppStartup
}
});
+ var mqttOptions = App.GetConfig("Mqtt", true);
app.UseEndpoints(endpoints =>
{
// 注册集线器
endpoints.MapHubs();
-
+ // 注册路由
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
+ // 注册 MQTT 支持 WebSocket
+ if (mqttOptions.Enabled)
+ {
+ endpoints.MapConnectionHandler("/mqtt",
+ httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector =
+ protocolList => protocolList.FirstOrDefault() ?? string.Empty);
+ }
});
}
}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_List.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_List.vue.vm
index 19957ec7..171b618e 100644
--- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_List.vue.vm
+++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_List.vue.vm
@@ -165,7 +165,7 @@
@:
} else if(@column.EffectType == "DictSelector") {
@:
- @: {{dc('@(@column.DictTypeCode)', row.@column.LowerPropertyName)?.value}}
+ @: {{dc('@(@column.DictTypeCode)', row.@column.LowerPropertyName)?.label}}
@:
} else if(@column.EffectType == "EnumSelector") {
@:
@@ -258,8 +258,8 @@ const userStore = useUserInfo();
@:const codeToName = userStore.codeToName;
}
@if(@Model.TableField.Any(x=>x.EffectType == "DictSelector") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
-@:const dc = userStore.getDictItemByCode;
-@:const dv = userStore.getDictLabelByVal;
+@:const dc = userStore.getDictItemByValue;
+@:const dv = userStore.getDictItemByLabel;
@:const dl = userStore.getDictDataByCode;
}
diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_editDialog.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_editDialog.vue.vm
index 692c0950..80f207a8 100644
--- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_editDialog.vue.vm
+++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_editDialog.vue.vm
@@ -224,8 +224,6 @@ const userStore = useUserInfo();
@:const getConstType = userStore.getConstDataByTypeCode;
}
@if(@Model.TableField.Any(x=>x.EffectType == "DictSelector") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
-//@:const dc = userStore.getDictItemByCode;
-//@:const dv = userStore.getDictLabelByVal;
@:const dl = userStore.getDictDataByCode;
}
diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_el_table_index.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_el_table_index.vue.vm
index 9270788d..a0f30f8e 100644
--- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_el_table_index.vue.vm
+++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_el_table_index.vue.vm
@@ -158,7 +158,7 @@
else if(@column.EffectType == "DictSelector"){
@:
@:
- @: {{dc("@(@column.DictTypeCode)", scope.row.@(@column.LowerPropertyName))?.value}}
+ @: {{dc("@(@column.DictTypeCode)", scope.row.@(@column.LowerPropertyName))?.label}}
@:
@:
}
@@ -251,8 +251,8 @@
@:const codeToName = userStore.codeToName;
}
@if(@Model.TableField.Any(x=>x.EffectType == "DictSelector") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
- @:const dc = userStore.getDictItemByCode;
- @:const dv = userStore.getDictLabelByVal;
+ @:const dc = userStore.getDictItemByValue;
+ @:const dv = userStore.getDictItemByLabel;
@:const dl = userStore.getDictDataByCode;
}
diff --git a/Web/package.json b/Web/package.json
index afa7570d..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.18",
+ "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.90",
+ "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,16 +94,16 @@
"@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",
"typescript": "^5.7.3",
- "vite": "^6.1.0",
+ "vite": "^6.1.1",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-compression2": "^1.3.3",
"vite-plugin-vue-setup-extend": "^0.4.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;
+
/**
* 存储路径
*
diff --git a/Web/src/stores/userInfo.ts b/Web/src/stores/userInfo.ts
index 00454b97..d64b7f8b 100644
--- a/Web/src/stores/userInfo.ts
+++ b/Web/src/stores/userInfo.ts
@@ -139,18 +139,23 @@ export const useUserInfo = defineStore('userInfo', {
return data.find((item: any) => item.code === itemCode)?.name;
},
+ // 常量编码和名称转换
+ codeToName(code: any, type: any) {
+ return this.constList.find((x: any) => x.code === type).data.result.find((x: any) => x.code === code)?.name;
+ },
+
// 根据字典类型获取字典数据
getDictDataByCode(dictTypeCode: string) {
return this.dictList[dictTypeCode] || [];
},
- // 根据字典类型和代码取字典项
- getDictItemByCode(typePCode: string, code: string) {
- if (code != undefined && code !== '') {
- const _code = code.toString();
- const ds = this.getDictDataByCode(typePCode);
+ // 根据字典类型和值获取取字典项
+ getDictItemByValue(dictTypeCode: string, value: string) {
+ if (value != undefined && value !== '') {
+ const _value = value.toString();
+ const ds = this.dictList[dictTypeCode] || [];
for (const element of ds) {
- if (element.code == _code) {
+ if (element.value == _value) {
return element;
}
}
@@ -158,30 +163,25 @@ export const useUserInfo = defineStore('userInfo', {
return {};
},
- // 根据字典类型和值取描述
- getDictLabelByVal(typePCode: string, val: string) {
- if (val != undefined && val !== '') {
- const _val = val.toString();
- const ds = this.getDictDataByCode(typePCode);
+ // 根据字典类型和名称获取取字典项
+ getDictItemByLabel(dictTypeCode: string, label: string) {
+ if (label != undefined && label !== '') {
+ const _label = label.toString();
+ const ds = this.dictList[dictTypeCode] || [];
for (const element of ds) {
- if (element.value == _val) {
+ if (element.label == _label) {
return element;
}
}
}
return {};
},
-
- // 常量编码和名称转换
- codeToName(code: any, type: any) {
- return this.constList.find((x: any) => x.code === type).data.result.find((x: any) => x.code === code)?.name;
- },
},
});
-// 处理字典国际化, 默认显示字典中的value值
+// 处理字典国际化, 默认显示字典中的label值
const setDictLangMessageAsync = async (dict: any) => {
- dict.langMessage = `message.system.dictType.${dict.typeCode}.${dict.code}`;
- const value = t(dict.langMessage);
- dict.value = value !== dict.langMessage ? value : dict.value;
+ dict.langMessage = `message.dictType.${dict.typeCode}_${dict.value}`;
+ const text = t(dict.langMessage);
+ dict.label = text !== dict.langMessage ? text : dict.label;
};
diff --git a/Web/src/views/system/codeGen/component/genConfigDialog.vue b/Web/src/views/system/codeGen/component/genConfigDialog.vue
index b2f7a487..db307ae2 100644
--- a/Web/src/views/system/codeGen/component/genConfigDialog.vue
+++ b/Web/src/views/system/codeGen/component/genConfigDialog.vue
@@ -10,7 +10,7 @@
-
+
修改
@@ -43,7 +43,7 @@
-
+
diff --git a/Web/src/views/system/job/component/JobScriptCode.ts b/Web/src/views/system/job/component/JobScriptCode.ts
index 344f3021..b83eb832 100644
--- a/Web/src/views/system/job/component/JobScriptCode.ts
+++ b/Web/src/views/system/job/component/JobScriptCode.ts
@@ -7,8 +7,8 @@ export const JobScriptCode = `// Admin.NET 项目的版权、商标、专利和
#region using
using Furion;
+using Furion.HttpRemote;
using Furion.Logging;
-using Furion.RemoteRequest.Extensions;
using Furion.Schedule;
using Microsoft.Extensions.DependencyInjection;
using System;
@@ -45,7 +45,9 @@ public class DynamicJob : IJob
// var rep = serviceScope.ServiceProvider.GetService>();
// 请求网址
- // var result = await "http://www.baidu.com".GetAsStringAsync();
+ // var url = "http://www.baidu.com";
+ // var httpRemoteService = App.GetRequiredService();
+ // var result = await httpRemoteService.GetAsStringAsync(url);
// Console.WriteLine(result);
// 日志
diff --git a/Web/src/views/system/org/component/editOrg.vue b/Web/src/views/system/org/component/editOrg.vue
index 2ee246e2..8bc16005 100644
--- a/Web/src/views/system/org/component/editOrg.vue
+++ b/Web/src/views/system/org/component/editOrg.vue
@@ -37,7 +37,7 @@
-
+
diff --git a/Web/src/views/system/org/index.vue b/Web/src/views/system/org/index.vue
index dc95b472..078a9ca1 100644
--- a/Web/src/views/system/org/index.vue
+++ b/Web/src/views/system/org/index.vue
@@ -21,7 +21,7 @@
-
+