mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-29 22:53:59 +08:00 
			
		
		
		
	Compare commits
	
		
			126 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 4574596bac | ||
|   | 4d16855e36 | ||
|   | 13a0d4d282 | ||
|   | b9cd06b829 | ||
|   | 5b460e8fa2 | ||
|   | 41087edf17 | ||
|   | 2afcc38e38 | ||
|   | e59ccce25f | ||
|   | d7425890e8 | ||
|   | a989a837fb | ||
|   | db1221da50 | ||
|   | cf794569ed | ||
|   | 51e5bbab0d | ||
|   | 2c197ed2b2 | ||
|   | d8fc6665b3 | ||
|   | c671a79822 | ||
|   | 9d93ce4c41 | ||
|   | a6d99fe227 | ||
|   | 923b8bca31 | ||
|   | e2c30d1c88 | ||
|   | b6d9f2a04e | ||
|   | 57306ea664 | ||
|   | cd7f3fd02f | ||
|   | 0482e077a8 | ||
|   | 5f986a45ca | ||
|   | ca7b49c0d5 | ||
|   | 52dd555e6c | ||
|   | 579b1a59f9 | ||
|   | 5299c5c4be | ||
|   | f7756bccef | ||
|   | a6b874d160 | ||
|   | 3e5fb3ddcf | ||
|   | 5e6bcb12d3 | ||
|   | 14303f1429 | ||
|   | 96711ba022 | ||
|   | cbfc0fdbdc | ||
|   | 6e81886c0e | ||
|   | 2d976bc132 | ||
|   | 57f6a476af | ||
|   | 8491ed296e | ||
|   | cd1288afdc | ||
|   | ec6c830cb0 | ||
|   | 2f86ccc4bf | ||
|   | 8ca445aec0 | ||
|   | 1e1f27c8a5 | ||
|   | 2b84bde367 | ||
|   | b52e58551d | ||
|   | 9aceed00bf | ||
|   | 58814f7f74 | ||
|   | 6a70ef9f31 | ||
|   | 82cc4ca500 | ||
|   | 4567fa04ed | ||
|   | 8b98b5d818 | ||
|   | 176d0351af | ||
|   | d63dc3384b | ||
|   | 1ccd704e30 | ||
|   | f5d23dbe79 | ||
|   | 75bfe53ac3 | ||
|   | 3308f916dd | ||
|   | e7140279ca | ||
|   | 1034719f5e | ||
|   | 2c00043a7f | ||
|   | 65c695d9ce | ||
|   | 57253fe46a | ||
|   | 4e5c443440 | ||
|   | 0b3b73d8ec | ||
|   | 921eabc134 | ||
|   | 0faa428751 | ||
|   | f71a2fdd63 | ||
|   | 4eb9ed8aba | ||
|   | d7b549abb8 | ||
|   | 95d723c578 | ||
|   | 2fcd853e86 | ||
|   | 07eef7c812 | ||
|   | b01e0757fa | ||
|   | 32844a20c6 | ||
|   | 5b6532c601 | ||
|   | 2c5b4b4027 | ||
|   | 72d7ecf195 | ||
|   | 2cfa6b4306 | ||
|   | 6f6ffde0ab | ||
|   | 1694739a16 | ||
|   | 95d1e8bfca | ||
|   | 60dec08e3c | ||
|   | a99d71be93 | ||
|   | f1331b6a0c | ||
|   | 10d66b642b | ||
|   | cd2310e4a8 | ||
|   | 1b399cf6b0 | ||
|   | 877445bc0a | ||
|   | 9a5b345bde | ||
|   | fc9e8ea7b3 | ||
|   | 32be6fcfc1 | ||
|   | 49847236c2 | ||
|   | d8424443e6 | ||
|   | f3b571ec3f | ||
|   | 99318bb5d7 | ||
|   | 1aa154c9aa | ||
|   | c65d8a445b | ||
|   | 80f4f85570 | ||
|   | 5beee43a6b | ||
|   | 8d6ae203a0 | ||
|   | 4353479a5c | ||
|   | 34d7687f9e | ||
|   | b1dc3cf4af | ||
|   | 6a58b95933 | ||
|   | d3badfd02b | ||
|   | 0098be057b | ||
|   | 6f972aa515 | ||
|   | 7407ba6313 | ||
|   | 1c79de207b | ||
|   | 257c79db92 | ||
|   | 9d1934a308 | ||
|   | d70f959902 | ||
|   | e4d810222f | ||
|   | bc1af4ae07 | ||
|   | 6e688ef43f | ||
|   | f0fe1b23dc | ||
|   | aaf2006401 | ||
|   | b821e26935 | ||
|   | 7ae4287157 | ||
|   | c6fcc38a65 | ||
|   | ab2d5c8853 | ||
|   | 5e557ff0bc | ||
|   | 918ca449a1 | ||
|   | 8e73368008 | 
| @@ -5,12 +5,11 @@ | ||||
|  | ||||
|  **NetCore** 跨平台边缘采集网关(工业设备采集) | ||||
|  | ||||
|  **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/profiles/KimDiego) | ||||
|  **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||
|  | ||||
|  **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 [**ThingsGateway.Admin**](https://gitee.com/dotnetchina/ThingsGateway/blob/master/framework/ThingsGateway.Admin.sln) | ||||
|  | ||||
|  | ||||
|  | ||||
| ## 文档 | ||||
|  | ||||
| [ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。 | ||||
| @@ -23,7 +22,9 @@ | ||||
|  | ||||
| [ThingsGateway演示地址](http://120.24.62.140:5000/) | ||||
|  | ||||
| 账户	:  **superAdmin**	密码 : **111111** | ||||
| 账户	:  **superAdmin**	 | ||||
|  | ||||
| 密码 : **111111** | ||||
|  | ||||
| ## 赞助 | ||||
|  | ||||
|   | ||||
| @@ -81,6 +81,15 @@ dotnet_diagnostic.CA2208.severity = none | ||||
| # IDE0057: 使用范围运算符 | ||||
| dotnet_diagnostic.IDE0057.severity = none | ||||
|  | ||||
| # IDE0056: 使用索引运算符 | ||||
| dotnet_diagnostic.IDE0056.severity = none | ||||
|  | ||||
| # CA1830: 最好使用 StringBuilder 的强类型 Append 和 Insert 方法重载 | ||||
| dotnet_diagnostic.CA1830.severity = none | ||||
|  | ||||
| # CA1847: 将字符型文本用于单个字符查找 | ||||
| dotnet_diagnostic.CA1847.severity = none | ||||
|  | ||||
| [*.vb] | ||||
| #### 命名样式 #### | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFrameworks>net6.0;net7.0</TargetFrameworks> | ||||
| 		<Version>2.0.3.0</Version> | ||||
| 		<Version>2.1.0.10</Version> | ||||
| 		<Authors>Diego</Authors> | ||||
| 		<Product>ThingsGateway</Product> | ||||
| 		<Copyright>© 2023-present Diego</Copyright> | ||||
|   | ||||
| @@ -19,7 +19,7 @@ using ThingsGateway.Admin.Core; | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 后台登录控制器 | ||||
| /// 文件下载 | ||||
| /// </summary> | ||||
| [ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)] | ||||
| [Route("file")] | ||||
| @@ -51,7 +51,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadOperateLogAsync([FromQuery] OperateLogInput input) | ||||
|     { | ||||
|         var memoryStream = await _operateLogService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"operateLog{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
| @@ -66,7 +65,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadVisitLogAsync([FromQuery] VisitLogInput input) | ||||
|     { | ||||
|         var memoryStream = await _visitLogService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"operateLog{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Admin.Application.FileController"> | ||||
|             <summary> | ||||
|             后台登录控制器 | ||||
|             文件下载 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Admin.Application.FileController.#ctor(ThingsGateway.Admin.Application.IFileService,ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)"> | ||||
|   | ||||
| @@ -18,6 +18,7 @@ using System.Reflection; | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| using UAParser; | ||||
|  | ||||
|   | ||||
| @@ -20,6 +20,7 @@ using System.Globalization; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,8 @@ | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
|   | ||||
| @@ -52,8 +52,8 @@ public class OpenApiSessionService : DbRepository<OpenApiUser>, IOpenApiSessionS | ||||
|         //获取该用户的verificat信息 | ||||
|         List<VerificatInfo> verificatInfos = await _verificatService.GetOpenApiVerificatIdAsync(input.Id); | ||||
|         //当前需要踢掉用户的verificat | ||||
|         var deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList(); | ||||
|         await _verificatService.SetOpenApiVerificatIdAsync(input.Id, deleteVerificats);//如果还有verificat则更新verificat | ||||
|         var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList(); | ||||
|         await _verificatService.SetOpenApiVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -88,8 +88,7 @@ public class AuthService : IAuthService | ||||
|         } | ||||
|  | ||||
|         var password = DESCEncryption.Decrypt(input.Password, DESCKeyConst.DESCKey);  // 解密 | ||||
|         var userInfo = await _userService.GetUserByAccountAsync(input.Account);//获取用户信息 | ||||
|         if (userInfo == null) throw Oops.Bah("用户不存在");//用户不存在 | ||||
|         var userInfo = await _userService.GetUserByAccountAsync(input.Account) ?? throw Oops.Bah("用户不存在");//获取用户信息 | ||||
|         if (userInfo.Password != password) throw Oops.Bah("账号密码错误");//账号密码错误 | ||||
|         return await LoginAsync(userInfo, input.Device); | ||||
|     } | ||||
|   | ||||
| @@ -16,6 +16,7 @@ using Furion.FriendlyException; | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|   | ||||
| @@ -66,6 +66,7 @@ public class OperateLogService : DbRepository<SysOperateLog>, IOperateLogService | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ using Furion.FriendlyException; | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
|  | ||||
|   | ||||
| @@ -52,12 +52,11 @@ public class SessionService : DbRepository<SysUser>, ISessionService | ||||
|     { | ||||
|         //获取该用户的verificat信息 | ||||
|         List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(input.Id); | ||||
|         //当前需要踢掉用户的verificat | ||||
|         List<VerificatInfo> deleteVerificats = new(); | ||||
|  | ||||
|         //踢掉包含verificat列表的verificat信息 | ||||
|         deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList(); | ||||
|         await _verificatService.SetVerificatIdAsync(input.Id, deleteVerificats);//如果还有verificat则更新verificat | ||||
|         var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList(); | ||||
|         var deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList(); | ||||
|         await _verificatService.SetVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat | ||||
|  | ||||
|         var message = "您已被强制下线!"; | ||||
|         await _noticeService.LogoutAsync(input.Id, deleteVerificats, message);//通知下线 | ||||
|   | ||||
| @@ -19,6 +19,7 @@ using Mapster; | ||||
| using SqlSugar; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ using Furion.FriendlyException; | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|   | ||||
| @@ -92,6 +92,7 @@ public class VisitLogService : DbRepository<SysVisitLog>, IVisitLogService | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -30,7 +30,9 @@ public class BaseComponentBase : ComponentBase, IDisposable | ||||
|     /// </summary> | ||||
|     public virtual void Dispose() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// InvokeAsync(StateHasChanged) | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -20,19 +20,19 @@ namespace ThingsGateway.Admin.Blazor.Core; | ||||
| public partial class AppBarItems | ||||
| { | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
|     /// 链接 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_COPYRIGHT_URL { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD>Ȩ | ||||
|     /// 版权 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_COPYRIGHT { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
|     /// 标题 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_TITLE { get; set; } | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Components; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor.Core; | ||||
| /// <summary> | ||||
| /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>/ͣ<><CDA3> <20>ı<EFBFBD><C4B1><EFBFBD>ʾ | ||||
| /// 启用/停用 文本提示 | ||||
| /// </summary> | ||||
| public partial class EnableChip | ||||
| { | ||||
| @@ -46,5 +46,5 @@ public partial class EnableChip | ||||
|  | ||||
|     private string TextColor => Value ? "green" : "error"; | ||||
|     private string Color => Value ? "green-lighten" : "warning-lighten"; | ||||
|     private string Label => Value ? EnabledLabel ?? "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>" : DisabledLabel ?? "ͣ<EFBFBD><EFBFBD>"; | ||||
|     private string Label => Value ? EnabledLabel ?? "启用" : DisabledLabel ?? "停用"; | ||||
| } | ||||
| @@ -1,18 +1,18 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor.Core; | ||||
| /// <summary> | ||||
| /// <EFBFBD>ղ<EFBFBD>/<2F><><EFBFBD>ݷ<EFBFBD>ʽ | ||||
| /// 收藏/快捷方式 | ||||
| /// </summary> | ||||
| public partial class Favorite | ||||
| { | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
| <MSheet> | ||||
|     <MCard Flat Href=@CONFIG_COPYRIGHT_URL Target="_blank"> | ||||
|         <MLabel Style="background-color:inherit;" Class="text-subtltie-2">@CONFIG_COPYRIGHT</MLabel> | ||||
|         <MLabel Style="background-color:inherit;" Class="text-subtltie-2 ml-4">@Version</MLabel> | ||||
|         <MLabel Style="background-color:inherit;white-space: pre;" Class="text-subtltie-2 ml-4">@Version</MLabel> | ||||
|     </MCard> | ||||
| </MSheet> | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -14,6 +14,8 @@ using Microsoft.AspNetCore.Components; | ||||
|  | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor.Core; | ||||
| /// <summary> | ||||
| /// Foter | ||||
| @@ -22,17 +24,17 @@ public partial class Foter | ||||
| { | ||||
|     private string Version = ""; | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
|     /// 链接 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_COPYRIGHT_URL { get; set; } | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD>Ȩ | ||||
|     /// 版权 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_COPYRIGHT { get; set; } | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
|     /// 标题 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_TITLE { get; set; } | ||||
| @@ -40,7 +42,12 @@ public partial class Foter | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task OnParametersSetAsync() | ||||
|     { | ||||
|         Version = "v" + Assembly.GetExecutingAssembly().GetName().Version.ToString(); | ||||
|         var assembly = Assembly.GetEntryAssembly(); | ||||
|         if (assembly != null) | ||||
|         { | ||||
|             Version = $"v{assembly.GetName().Version}"; | ||||
|         } | ||||
|  | ||||
|         await base.OnParametersSetAsync(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -20,23 +20,23 @@ namespace ThingsGateway.Admin.Blazor.Core; | ||||
| public partial class Logo | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Logo<EFBFBD>߶<EFBFBD> | ||||
|     /// Logo高度 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public int HeightInt { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
|     /// 链接 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_COPYRIGHT_URL { get; set; } | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD>Ȩ | ||||
|     /// 版权 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_COPYRIGHT { get; set; } | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
|     /// 标题 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public string CONFIG_TITLE { get; set; } | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -34,7 +34,7 @@ public partial class PageTabs | ||||
|         return op; | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// Tabsʵ<EFBFBD><EFBFBD> | ||||
|     /// Tabs实例 | ||||
|     /// </summary> | ||||
|     public PPageTabs PPageTabs { get; private set; } | ||||
|  | ||||
| @@ -46,7 +46,7 @@ public partial class PageTabs | ||||
|     [Parameter] | ||||
|     public IEnumerable<string> SelfPatterns { get; set; } | ||||
|     /// <summary> | ||||
|     /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
|     /// 子组件 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public RenderFragment ChildContent { get; set; } | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -17,6 +17,7 @@ using Masa.Blazor; | ||||
| using Microsoft.AspNetCore.Components; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor.Core; | ||||
| /// <summary> | ||||
| @@ -49,11 +50,11 @@ public partial class UserMenu | ||||
|         var ret = str?.ToJsonWithT<UnifyResult<string>>(); | ||||
|         if (ret?.Code != 200) | ||||
|         { | ||||
|             await PopupService.EnqueueSnackbarAsync("ע<EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>", AlertTypes.Error); | ||||
|             await PopupService.EnqueueSnackbarAsync("注销失败", AlertTypes.Error); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await PopupService.EnqueueSnackbarAsync("ע<EFBFBD><EFBFBD><EFBFBD>ɹ<EFBFBD>", AlertTypes.Success); | ||||
|             await PopupService.EnqueueSnackbarAsync("注销成功", AlertTypes.Success); | ||||
|             await Task.Delay(500); | ||||
|             NavigationManager.NavigateTo(NavigationManager.Uri); | ||||
|         } | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion.DependencyInjection; | ||||
|  | ||||
| namespace BlazorComponent.I18n; | ||||
| /// <summary> | ||||
| /// CookieStorage | ||||
| /// </summary> | ||||
| public class CookieStorage : IScoped | ||||
| { | ||||
|     private readonly IJSRuntime _jsRuntime; | ||||
|     /// <summary> | ||||
|     /// CookieStorage | ||||
|     /// </summary> | ||||
|     /// <param name="jsRuntime"></param> | ||||
|     public CookieStorage(IJSRuntime jsRuntime) | ||||
|     { | ||||
|         _jsRuntime = jsRuntime; | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// GetCookieAsync | ||||
|     /// </summary> | ||||
|     /// <param name="key"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<string> GetCookieAsync(string key) | ||||
|     { | ||||
|         return await _jsRuntime.InvokeAsync<string>(JsInteropConstants.GetCookie, key); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// GetCookie | ||||
|     /// </summary> | ||||
|     /// <param name="key"></param> | ||||
|     /// <returns></returns> | ||||
|     public string GetCookie(string key) | ||||
|     { | ||||
|         if (_jsRuntime is IJSInProcessRuntime jsInProcess) | ||||
|         { | ||||
|             return jsInProcess.Invoke<string>(JsInteropConstants.GetCookie, key); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// SetItemAsync | ||||
|     /// </summary> | ||||
|     /// <typeparam name="T"></typeparam> | ||||
|     /// <param name="key"></param> | ||||
|     /// <param name="value"></param> | ||||
|     public async void SetItemAsync<T>(string key, T value) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await _jsRuntime.InvokeVoidAsync(JsInteropConstants.SetCookie, key, value?.ToString()); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -10,6 +10,8 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using BlazorComponent; | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Masa.Blazor; | ||||
| @@ -61,6 +63,8 @@ public class Startup : AppStartup | ||||
|  | ||||
|  | ||||
|             }); | ||||
|             options.Locale = new Locale("zh-CN", "en-US"); | ||||
|  | ||||
|         }); | ||||
|  | ||||
|         services.AddScoped<InitTimezone>(); | ||||
|   | ||||
| @@ -14,8 +14,8 @@ | ||||
| 	 | ||||
| 	<ItemGroup> | ||||
|  | ||||
| 		<PackageReference Include="Masa.Blazor" Version="1.0.1" /> | ||||
| 		<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.9" /> | ||||
| 		<PackageReference Include="Masa.Blazor" Version="1.0.3" /> | ||||
| 		<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.10" /> | ||||
|  | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
|   | ||||
| @@ -857,5 +857,38 @@ | ||||
|             设置深浅主题统一由这个方法为入口 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="T:BlazorComponent.I18n.CookieStorage"> | ||||
|             <summary> | ||||
|             CookieStorage | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:BlazorComponent.I18n.CookieStorage.#ctor(Microsoft.JSInterop.IJSRuntime)"> | ||||
|             <summary> | ||||
|             CookieStorage | ||||
|             </summary> | ||||
|             <param name="jsRuntime"></param> | ||||
|         </member> | ||||
|         <member name="M:BlazorComponent.I18n.CookieStorage.GetCookieAsync(System.String)"> | ||||
|             <summary> | ||||
|             GetCookieAsync | ||||
|             </summary> | ||||
|             <param name="key"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:BlazorComponent.I18n.CookieStorage.GetCookie(System.String)"> | ||||
|             <summary> | ||||
|             GetCookie | ||||
|             </summary> | ||||
|             <param name="key"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:BlazorComponent.I18n.CookieStorage.SetItemAsync``1(System.String,``0)"> | ||||
|             <summary> | ||||
|             SetItemAsync | ||||
|             </summary> | ||||
|             <typeparam name="T"></typeparam> | ||||
|             <param name="key"></param> | ||||
|             <param name="value"></param> | ||||
|         </member> | ||||
|     </members> | ||||
| </doc> | ||||
|   | ||||
| @@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Http; | ||||
|  | ||||
| using ThingsGateway.Admin.Application; | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor.Core; | ||||
|  | ||||
| @@ -153,7 +154,7 @@ public class UserResoures : IDisposable | ||||
|     { | ||||
|         if (MasaBlazor.Theme.Dark != isDark || isDark == null) | ||||
|             MasaBlazor.ToggleTheme(); | ||||
|         _cookieStorage?.SetItemAsync(BlazorResourceConst.ThemeCookieKey, isDark.ToJsonString()); | ||||
|         _cookieStorage?.SetItemAsync(BlazorResourceConst.ThemeCookieKey, MasaBlazor.Theme.Dark.ToJsonString()); | ||||
|     } | ||||
|  | ||||
|     private void InitCookie(IRequestCookieCollection cookies) | ||||
|   | ||||
| @@ -48,8 +48,7 @@ | ||||
|         var controllerTypes = App.EffectiveTypes. | ||||
|     Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass | ||||
|     && u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), false)) | ||||
|     .Where(it => it.Assembly != typeof(BlazorApp).Assembly) | ||||
|     ; | ||||
|     .Where(it => it.Assembly != typeof(BlazorApp).Assembly); | ||||
|         var assemblys = controllerTypes?.Select(it => it.Assembly)?.Distinct(); | ||||
|         return assemblys; | ||||
|     } | ||||
|   | ||||
| @@ -189,7 +189,7 @@ | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (ItemColWithDTTemplate!=null) | ||||
|                     if (ItemColWithDTTemplate != null) | ||||
|                     { | ||||
|                         @ItemColWithDTTemplate(context) | ||||
|                     } | ||||
| @@ -310,16 +310,24 @@ | ||||
|                         { | ||||
|                             string value = DetailModelPairs[item.Value]; | ||||
|                             { | ||||
|                                 <tr @key="item.Text"> | ||||
|                                     <td class="text-start table-minwidth"> | ||||
|                                         @item.Text | ||||
|                                     </td> | ||||
|                                     <td class="text-start "> | ||||
|                                         <div style="word-break:break-all; white-space:pre-wrap;"> | ||||
|                                             @value | ||||
|                                         </div> | ||||
|                                     </td> | ||||
|                                 </tr> | ||||
|                                 @if (Detailemplate != null) | ||||
|                                 { | ||||
|                                     @Detailemplate((item,value)) | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     <tr @key="item.Text"> | ||||
|                                         <td class="text-start table-minwidth"> | ||||
|                                             @item.Text | ||||
|                                         </td> | ||||
|                                         <td class="text-start "> | ||||
|                                             <div style="word-break:break-all; white-space:pre-wrap;"> | ||||
|                                                 @value | ||||
|                                             </div> | ||||
|                                         </td> | ||||
|                                     </tr> | ||||
|                                 } | ||||
|                                  | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|   | ||||
| @@ -72,12 +72,6 @@ public partial class AppDataTable<TItem, SearchItem, AddItem, EditItem> : IAppDa | ||||
|     [Parameter] | ||||
|     public bool Dense { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获得/设置 明细行模板 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public RenderFragment<TItem> DetailRowTemplate { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 编辑项委托 | ||||
|     /// </summary> | ||||
| @@ -89,6 +83,11 @@ public partial class AppDataTable<TItem, SearchItem, AddItem, EditItem> : IAppDa | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public RenderFragment<EditItem> EditTemplate { get; set; } | ||||
|     /// <summary> | ||||
|     /// 获得/设置 详情模板 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public RenderFragment<(DataTableHeader<TItem>, string)> Detailemplate { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 表头过滤,返回DataTableHeader列表,传输参数已包含全部初始表头与表头标题 | ||||
| @@ -283,6 +282,7 @@ public partial class AppDataTable<TItem, SearchItem, AddItem, EditItem> : IAppDa | ||||
|             { | ||||
|                 SearchModel.Size = PageItems.Total; | ||||
|             } | ||||
|             selectedItem = new List<TItem>(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,6 @@ | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
|   | ||||
| @inject IConfigService ConfigService | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
|   | ||||
| @@ -41,26 +41,26 @@ public partial class Config | ||||
|     MainLayout MainLayout { get; set; } | ||||
|     private Task AddCallAsync(ConfigAddInput input) | ||||
|     { | ||||
|         return ConfigService.AddAsync(input); | ||||
|         return App.GetService<ConfigService>().AddAsync(input); | ||||
|     } | ||||
|  | ||||
|     private Task DeleteCallAsync(IEnumerable<SysConfig> sysConfigs) | ||||
|     { | ||||
|         return ConfigService.DeleteAsync(sysConfigs.Select(a => a.Id).ToArray()); | ||||
|         return App.GetService<ConfigService>().DeleteAsync(sysConfigs.Select(a => a.Id).ToArray()); | ||||
|     } | ||||
|     private Task EditCallAsync(ConfigEditInput sysConfigs) | ||||
|     { | ||||
|         return ConfigService.EditAsync(sysConfigs); | ||||
|         return App.GetService<ConfigService>().EditAsync(sysConfigs); | ||||
|     } | ||||
|  | ||||
|     private async Task OnSaveAsync() | ||||
|     { | ||||
|         await ConfigService.EditBatchAsync(_sysConfig); | ||||
|         await App.GetService<ConfigService>().EditBatchAsync(_sysConfig); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|         await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success); | ||||
|     } | ||||
|     private Task<SqlSugarPagedList<SysConfig>> QueryCallAsync(ConfigPageInput input) | ||||
|     { | ||||
|         return ConfigService.PageAsync(input); | ||||
|         return App.GetService<ConfigService>().PageAsync(input); | ||||
|     } | ||||
| } | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -18,7 +18,7 @@ using ThingsGateway.Admin.Core; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor; | ||||
| /// <summary> | ||||
| /// <EFBFBD><EFBFBD>ҳ | ||||
| /// 首页 | ||||
| /// </summary> | ||||
| public partial class Index | ||||
| { | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
| @using Masa.Blazor.Presets; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
| @inject IMenuService MenuService | ||||
|  | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inherits BaseComponentBase | ||||
|   | ||||
| @@ -29,14 +29,11 @@ public partial class Menu | ||||
|     long buttonParentId; | ||||
|     bool IsShowButtonList; | ||||
|     List<SysResource> MenuCatalog = new(); | ||||
|     [Inject] | ||||
|     IButtonService ButtonService { get; set; } | ||||
|  | ||||
|  | ||||
|     [CascadingParameter] | ||||
|     MainLayout MainLayout { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     IResourceService ResourceService { get; set; } | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
| @@ -52,23 +49,23 @@ public partial class Menu | ||||
|     private async Task AddCallAsync(MenuAddInput input) | ||||
|     { | ||||
|         input.ParentId = search.ParentId; | ||||
|         await MenuService.AddAsync(input); | ||||
|         await App.GetService<MenuService>().AddAsync(input); | ||||
|         await NavChangeAsync(); | ||||
|     } | ||||
|     private async Task ButtonAddCallAsync(ButtonAddInput input) | ||||
|     { | ||||
|         input.ParentId = buttonParentId; | ||||
|         await ButtonService.AddAsync(input); | ||||
|         await App.GetService<ButtonService>().AddAsync(input); | ||||
|     } | ||||
|  | ||||
|     private Task ButtonDeleteCallAsync(IEnumerable<SysResource> input) | ||||
|     { | ||||
|         return ButtonService.DeleteAsync(input.Select(a => a.Id).ToArray()); | ||||
|         return App.GetService<ButtonService>().DeleteAsync(input.Select(a => a.Id).ToArray()); | ||||
|     } | ||||
|  | ||||
|     private Task ButtonEditCallAsync(ButtonEditInput input) | ||||
|     { | ||||
|         return ButtonService.EditAsync(input); | ||||
|         return App.GetService<ButtonService>().EditAsync(input); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -92,7 +89,7 @@ public partial class Menu | ||||
|     private async Task<SqlSugarPagedList<SysResource>> ButtonQueryCallAsync(ButtonPageInput input) | ||||
|     { | ||||
|         input.ParentId = buttonParentId; | ||||
|         var data = await ButtonService.PageAsync(input); | ||||
|         var data = await App.GetService<ButtonService>().PageAsync(input); | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
| @@ -103,13 +100,13 @@ public partial class Menu | ||||
|  | ||||
|     private async Task DeleteCallAsync(IEnumerable<SysResource> input) | ||||
|     { | ||||
|         await MenuService.DeleteAsync(input.Select(a => a.Id).ToArray()); | ||||
|         await App.GetService<MenuService>().DeleteAsync(input.Select(a => a.Id).ToArray()); | ||||
|         await NavChangeAsync(); | ||||
|  | ||||
|     } | ||||
|     private async Task EditCallAsync(MenuEditInput input) | ||||
|     { | ||||
|         await MenuService.EditAsync(input); | ||||
|         await App.GetService<MenuService>().EditAsync(input); | ||||
|         await NavChangeAsync(); | ||||
|  | ||||
|     } | ||||
| @@ -117,9 +114,9 @@ public partial class Menu | ||||
|     private async Task<List<SysResource>> GetMenuCatalogAsync() | ||||
|     { | ||||
|         //获取所有菜单 | ||||
|         List<SysResource> sysResources = await ResourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU); | ||||
|         List<SysResource> sysResources = await App.GetService<ResourceService>().GetListByCategoryAsync(ResourceCategoryEnum.MENU); | ||||
|         sysResources = sysResources.Where(it => it.TargetType == TargetTypeEnum.None).ToList(); | ||||
|         MenuCatalog = ResourceService.ResourceListToTree(sysResources); | ||||
|         MenuCatalog = App.GetService<ResourceService>().ResourceListToTree(sysResources); | ||||
|         return MenuCatalog; | ||||
|     } | ||||
|  | ||||
| @@ -130,7 +127,7 @@ public partial class Menu | ||||
|     } | ||||
|     private async Task<SqlSugarPagedList<SysResource>> QueryCallAsync(MenuPageInput input) | ||||
|     { | ||||
|         var data = await MenuService.TreeAsync(input); | ||||
|         var data = await App.GetService<MenuService>().TreeAsync(input); | ||||
|         return data.ToPagedList(input); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
| @using Masa.Blazor.Presets; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
| @inject IOpenApiSessionService SessionService | ||||
|   | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
|   | ||||
| @@ -33,13 +33,13 @@ public partial class OpenApiSession | ||||
|         var confirm = await PopupService.OpenConfirmDialogAsync("警告", "确定 ?"); | ||||
|         if (confirm) | ||||
|         { | ||||
|             await SessionService.ExitSessionAsync(id); | ||||
|             await App.GetService<OpenApiSessionService>().ExitSessionAsync(id); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Task<SqlSugarPagedList<OpenApiSessionOutput>> SessionQueryCallAsync(OpenApiSessionPageInput input) | ||||
|     { | ||||
|         return SessionService.PageAsync(input); | ||||
|         return App.GetService<OpenApiSessionService>().PageAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task ShowVerificatListAsync(List<VerificatInfo> verificatInfos) | ||||
| @@ -57,7 +57,7 @@ public partial class OpenApiSession | ||||
|             VerificatIds = verificats.Select(it => it.Id).ToList(), | ||||
|             Id = verificats.First().UserId | ||||
|         }; | ||||
|         await SessionService.ExitVerificatAsync(send); | ||||
|         await App.GetService<OpenApiSessionService>().ExitVerificatAsync(send); | ||||
|         _verificatInfos.RemoveWhere(it => send.VerificatIds.Contains(it.Id)); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
| @using Masa.Blazor.Presets; | ||||
| @inject IOpenApiUserService OpenApiUserService | ||||
|   | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
|   | ||||
| @@ -31,21 +31,20 @@ public partial class OpenApiUserR | ||||
|     bool IsShowRoles; | ||||
|     List<OpenApiPermissionTreeSelector> RolesChoice = new(); | ||||
|     string SearchName; | ||||
|     [Inject] | ||||
|     IRoleService SysRoleService { get; set; } | ||||
|  | ||||
|     private Task AddCallAsync(OpenApiUserAddInput input) | ||||
|     { | ||||
|         return OpenApiUserService.AddAsync(input); | ||||
|         return App.GetService<OpenApiUserService>().AddAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task DeleteCallAsync(IEnumerable<OpenApiUser> users) | ||||
|     { | ||||
|         await OpenApiUserService.DeleteAsync(users.Select(a => a.Id).ToArray()); | ||||
|         await App.GetService<OpenApiUserService>().DeleteAsync(users.Select(a => a.Id).ToArray()); | ||||
|     } | ||||
|  | ||||
|     private Task EditCallAsync(OpenApiUserEditInput users) | ||||
|     { | ||||
|         return OpenApiUserService.EditAsync(users); | ||||
|         return App.GetService<OpenApiUserService>().EditAsync(users); | ||||
|     } | ||||
|  | ||||
|     private List<OpenApiPermissionTreeSelector> GetRouters() | ||||
| @@ -61,7 +60,7 @@ public partial class OpenApiUserR | ||||
|             OpenApiUserGrantPermissionInput userGrantRoleInput = new(); | ||||
|             userGrantRoleInput.Id = ChoiceUserId; | ||||
|             userGrantRoleInput.PermissionList = RolesChoice.Select(it => it.ApiRoute).ToList(); | ||||
|             await OpenApiUserService.GrantRoleAsync(userGrantRoleInput); | ||||
|             await App.GetService<OpenApiUserService>().GrantRoleAsync(userGrantRoleInput); | ||||
|             IsShowRoles = false; | ||||
|             await _datatable?.QueryClickAsync(); | ||||
|         } | ||||
| @@ -73,7 +72,7 @@ public partial class OpenApiUserR | ||||
|     } | ||||
|     private Task<SqlSugarPagedList<OpenApiUser>> QueryCallAsync(OpenApiUserPageInput input) | ||||
|     { | ||||
|         return OpenApiUserService.PageAsync(input); | ||||
|         return App.GetService<OpenApiUserService>().PageAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task UserStatusChangeAsync(OpenApiUser context, bool enable) | ||||
| @@ -81,9 +80,9 @@ public partial class OpenApiUserR | ||||
|         try | ||||
|         { | ||||
|             if (enable) | ||||
|                 await OpenApiUserService.EnableUserAsync(context.Id); | ||||
|                 await App.GetService<OpenApiUserService>().EnableUserAsync(context.Id); | ||||
|             else | ||||
|                 await OpenApiUserService.DisableUserAsync(context.Id); | ||||
|                 await App.GetService<OpenApiUserService>().DisableUserAsync(context.Id); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
| @using System.Linq.Expressions; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
| @inject IOperateLogService OperateLogService | ||||
|   | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
|   | ||||
| @@ -44,7 +44,7 @@ public partial class Oplog | ||||
|         var confirm = await PopupService.OpenConfirmDialogAsync("删除", "确定 ?"); | ||||
|         if (confirm) | ||||
|         { | ||||
|             await OperateLogService.DeleteAsync(CategoryFilters.Select(it => it.Value).ToArray()); | ||||
|             await App.GetService<OperateLogService>().DeleteAsync(CategoryFilters.Select(it => it.Value).ToArray()); | ||||
|             await _datatable?.QueryClickAsync(); | ||||
|         } | ||||
|     } | ||||
| @@ -54,7 +54,7 @@ public partial class Oplog | ||||
|         input.Account = search.Account; | ||||
|         input.Category = search.Category; | ||||
|         input.ExeStatus = search.ExeStatus; | ||||
|         return OperateLogService.PageAsync(input); | ||||
|         return App.GetService<OperateLogService>().PageAsync(input); | ||||
|     } | ||||
|     [Inject] | ||||
|     AjaxService AjaxService { get; set; } | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
| @using Masa.Blazor.Presets; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
| @inject IRoleService SysRoleService | ||||
|   | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
|   | ||||
| @@ -38,27 +38,23 @@ public partial class Role | ||||
|     [CascadingParameter] | ||||
|     MainLayout MainLayout { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     IResourceService ResourceService { get; set; } | ||||
|  | ||||
|  | ||||
|     private string SearchKey { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     ISysUserService SysUserService { get; set; } | ||||
|  | ||||
|     private Task AddCallAsync(RoleAddInput input) | ||||
|     { | ||||
|         return SysRoleService.AddAsync(input); | ||||
|         return App.GetService<RoleService>().AddAsync(input); | ||||
|     } | ||||
|     private async Task DeleteCallAsync(IEnumerable<SysRole> sysRoles) | ||||
|     { | ||||
|         await SysRoleService.DeleteAsync(sysRoles.Select(a => a.Id).ToArray()); | ||||
|         await App.GetService<RoleService>().DeleteAsync(sysRoles.Select(a => a.Id).ToArray()); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task EditCallAsync(RoleEditInput input) | ||||
|     { | ||||
|         await SysRoleService.EditAsync(input); | ||||
|         await App.GetService<RoleService>().EditAsync(input); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|     private async Task OnRoleHasResuorcesSaveAsync(ModalActionEventArgs args) | ||||
| @@ -69,7 +65,7 @@ public partial class Role | ||||
|             var data = new List<SysResource>(); | ||||
|             userGrantRoleInput.Id = ChoiceRoleId; | ||||
|             userGrantRoleInput.GrantInfoList = RoleHasResuorces; | ||||
|             await SysRoleService.GrantResourceAsync(userGrantRoleInput); | ||||
|             await App.GetService<RoleService>().GrantResourceAsync(userGrantRoleInput); | ||||
|             IsShowResuorces = false; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
| @@ -86,7 +82,7 @@ public partial class Role | ||||
|             GrantUserInput userGrantRoleInput = new(); | ||||
|             userGrantRoleInput.Id = ChoiceRoleId; | ||||
|             userGrantRoleInput.GrantInfoList = UsersChoice.Select(it => it.Id).ToList(); | ||||
|             await SysRoleService.GrantUserAsync(userGrantRoleInput); | ||||
|             await App.GetService<RoleService>().GrantUserAsync(userGrantRoleInput); | ||||
|             IsShowUsers = false; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
| @@ -99,19 +95,19 @@ public partial class Role | ||||
|  | ||||
|     private Task<SqlSugarPagedList<SysRole>> QueryCallAsync(RolePageInput input) | ||||
|     { | ||||
|         return SysRoleService.PageAsync(input); | ||||
|         return App.GetService<RoleService>().PageAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task ResuorceInitAsync() | ||||
|     { | ||||
|         ResTreeSelectors = (await ResourceService.GetRoleGrantResourceMenusAsync()); | ||||
|         RoleHasResuorces = (await SysRoleService.OwnResourceAsync(ChoiceRoleId))?.GrantInfoList; | ||||
|         ResTreeSelectors = (await App.GetService<ResourceService>().GetRoleGrantResourceMenusAsync()); | ||||
|         RoleHasResuorces = (await App.GetService<RoleService>().OwnResourceAsync(ChoiceRoleId))?.GrantInfoList; | ||||
|     } | ||||
|  | ||||
|     private async Task<List<UserSelectorOutput>> UserInitAsync() | ||||
|     { | ||||
|         AllUsers = await SysUserService.UserSelectorAsync(SearchKey); | ||||
|         var data = await SysRoleService.OwnUserAsync(ChoiceRoleId); | ||||
|         AllUsers = await App.GetService<SysUserService>().UserSelectorAsync(SearchKey); | ||||
|         var data = await App.GetService<RoleService>().OwnUserAsync(ChoiceRoleId); | ||||
|         UsersChoice = AllUsers.Where(a => data.Contains(a.Id)).ToList(); | ||||
|         return AllUsers; | ||||
|     } | ||||
|   | ||||
| @@ -16,7 +16,6 @@ | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
|   | ||||
| @inject ISessionService SessionService | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
|   | ||||
| @@ -33,14 +33,14 @@ public partial class Session | ||||
|         var confirm = await PopupService.OpenConfirmDialogAsync("警告", "确定 ?"); | ||||
|         if (confirm) | ||||
|         { | ||||
|             await SessionService.ExitSessionAsync(id); | ||||
|             await App.GetService<SessionService>().ExitSessionAsync(id); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private Task<SqlSugarPagedList<SessionOutput>> SessionQueryCallAsync(SessionPageInput input) | ||||
|     { | ||||
|         return SessionService.PageAsync(input); | ||||
|         return App.GetService<SessionService>().PageAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task ShowVerificatListAsync(List<VerificatInfo> verificatInfos) | ||||
| @@ -58,7 +58,7 @@ public partial class Session | ||||
|             VerificatIds = verificats.Select(it => it.Id).ToList(), | ||||
|             Id = verificats.First().UserId | ||||
|         }; | ||||
|         await SessionService.ExitVerificatAsync(send); | ||||
|         await App.GetService<SessionService>().ExitVerificatAsync(send); | ||||
|         _verificatInfos.RemoveWhere(it => send.VerificatIds.Contains(it.Id)); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
| @using Masa.Blazor.Presets; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
| @inject ISpaService SpaService | ||||
|   | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inherits BaseComponentBase | ||||
|   | ||||
| @@ -27,23 +27,23 @@ public partial class Spa | ||||
|  | ||||
|     private async Task AddCallAsync(SpaAddInput input) | ||||
|     { | ||||
|         await SpaService.AddAsync(input); | ||||
|         await App.GetService<SpaService>().AddAsync(input); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|     private async Task DeleteCallAsync(IEnumerable<SysResource> input) | ||||
|     { | ||||
|         await SpaService.DeleteAsync(input.Select(a => a.Id).ToArray()); | ||||
|         await App.GetService<SpaService>().DeleteAsync(input.Select(a => a.Id).ToArray()); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task EditCallAsync(SpaEditInput input) | ||||
|     { | ||||
|         await SpaService.EditAsync(input); | ||||
|         await App.GetService<SpaService>().EditAsync(input); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private Task<SqlSugarPagedList<SysResource>> QueryCallAsync(SpaPageInput input) | ||||
|     { | ||||
|         return SpaService.PageAsync(input); | ||||
|         return App.GetService<SpaService>().PageAsync(input); | ||||
|     } | ||||
| } | ||||
| @@ -11,7 +11,7 @@ | ||||
| *@ | ||||
|  | ||||
| @page "/admin/user" | ||||
| @inject ISysUserService SysUserService | ||||
|   | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @using Masa.Blazor.Presets; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
|   | ||||
| @@ -34,21 +34,20 @@ public partial class User | ||||
|     [CascadingParameter] | ||||
|     MainLayout MainLayout { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     IRoleService SysRoleService { get; set; } | ||||
|  | ||||
|     private Task AddCallAsync(UserAddInput input) | ||||
|     { | ||||
|         return SysUserService.AddAsync(input); | ||||
|         return App.GetService<SysUserService>().AddAsync(input); | ||||
|     } | ||||
|     private async Task DeleteCallAsync(IEnumerable<SysUser> users) | ||||
|     { | ||||
|         await SysUserService.DeleteAsync(users.Select(a => a.Id).ToArray()); | ||||
|         await App.GetService<SysUserService>().DeleteAsync(users.Select(a => a.Id).ToArray()); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task EditCallAsync(UserEditInput users) | ||||
|     { | ||||
|         await SysUserService.EditAsync(users); | ||||
|         await App.GetService<SysUserService>().EditAsync(users); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
| @@ -59,7 +58,7 @@ public partial class User | ||||
|             UserGrantRoleInput userGrantRoleInput = new(); | ||||
|             userGrantRoleInput.Id = ChoiceUserId; | ||||
|             userGrantRoleInput.RoleIdList = RolesChoice.Select(it => it.Id).ToList(); | ||||
|             await SysUserService.GrantRoleAsync(userGrantRoleInput); | ||||
|             await App.GetService<SysUserService>().GrantRoleAsync(userGrantRoleInput); | ||||
|             IsShowRoles = false; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
| @@ -71,20 +70,20 @@ public partial class User | ||||
|     } | ||||
|     private Task<SqlSugarPagedList<SysUser>> QueryCallAsync(UserPageInput input) | ||||
|     { | ||||
|         return SysUserService.PageAsync(input); | ||||
|         return App.GetService<SysUserService>().PageAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task ResetPasswordAsync(SysUser sysUser) | ||||
|     { | ||||
|         await SysUserService.ResetPasswordAsync(sysUser.Id); | ||||
|         await App.GetService<SysUserService>().ResetPasswordAsync(sysUser.Id); | ||||
|         await PopupService.EnqueueSnackbarAsync(new("成功", AlertTypes.Success)); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task RoleInitAsync() | ||||
|     { | ||||
|         AllRoles = await SysRoleService.RoleSelectorAsync(); | ||||
|         var data = await SysRoleService.GetRoleIdListByUserIdAsync(ChoiceUserId); | ||||
|         AllRoles = await App.GetService<RoleService>().RoleSelectorAsync(); | ||||
|         var data = await App.GetService<RoleService>().GetRoleIdListByUserIdAsync(ChoiceUserId); | ||||
|         RolesChoice = AllRoles.Where(a => data.Contains(a.Id)).ToList(); | ||||
|     } | ||||
|     private async Task UserStatusChangeAsync(SysUser context, bool enable) | ||||
| @@ -92,9 +91,9 @@ public partial class User | ||||
|         try | ||||
|         { | ||||
|             if (enable) | ||||
|                 await SysUserService.EnableUserAsync(context.Id); | ||||
|                 await App.GetService<SysUserService>().EnableUserAsync(context.Id); | ||||
|             else | ||||
|                 await SysUserService.DisableUserAsync(context.Id); | ||||
|                 await App.GetService<SysUserService>().DisableUserAsync(context.Id); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊȫ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <EFBFBD>˴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><EFBFBD><EFBFBD>Diego<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><EFBFBD>Ŀ<EFBFBD>ԴЭ<EFBFBD>鼰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD> | ||||
| //  GiteeԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<EFBFBD><EFBFBD>605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| @@ -18,7 +18,7 @@ using ThingsGateway.Admin.Core; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor; | ||||
| /// <summary> | ||||
| /// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ||||
| /// 个人设置 | ||||
| /// </summary> | ||||
| public partial class UserCenter | ||||
| { | ||||
| @@ -33,8 +33,7 @@ public partial class UserCenter | ||||
|     [Inject] | ||||
|     NavigationManager NavigationManager { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     IUserCenterService UserCenterService { get; set; } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task OnParametersSetAsync() | ||||
| @@ -48,27 +47,27 @@ public partial class UserCenter | ||||
|  | ||||
|     async Task OnDefaultRazorSaveAsync() | ||||
|     { | ||||
|         await UserCenterService.UpdateUserDefaultRazorAsync(UserManager.UserId, DefaultMenuId); | ||||
|         await App.GetService<UserCenterService>().UpdateUserDefaultRazorAsync(UserManager.UserId, DefaultMenuId); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|         await PopupService.EnqueueSnackbarAsync("<EFBFBD>ɹ<EFBFBD>", AlertTypes.Success); | ||||
|         await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success); | ||||
|     } | ||||
|  | ||||
|     async Task OnShortcutSaveAsync() | ||||
|     { | ||||
|         await UserCenterService.UpdateWorkbenchAsync(_menusChoice); | ||||
|         await App.GetService<UserCenterService>().UpdateWorkbenchAsync(_menusChoice); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|         await PopupService.EnqueueSnackbarAsync("<EFBFBD>ɹ<EFBFBD>", AlertTypes.Success); | ||||
|         await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success); | ||||
|     } | ||||
|     async Task OnUpdatePasswordInfoAsync(FormContext context) | ||||
|     { | ||||
|         var success = context.Validate(); | ||||
|         if (success) | ||||
|         { | ||||
|             //<EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD> | ||||
|             //验证成功,操作业务 | ||||
|             _passwordInfoInput.Id = UserResoures.CurrentUser.Id; | ||||
|             await UserCenterService.EditPasswordAsync(_passwordInfoInput); | ||||
|             await App.GetService<UserCenterService>().EditPasswordAsync(_passwordInfoInput); | ||||
|             await MainLayout.StateHasChangedAsync(); | ||||
|             await PopupService.EnqueueSnackbarAsync("<EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD>¼", AlertTypes.Success); | ||||
|             await PopupService.EnqueueSnackbarAsync("成功,将重新登录", AlertTypes.Success); | ||||
|             await Task.Delay(2000); | ||||
|             NavigationManager.NavigateTo(NavigationManager.Uri); | ||||
|         } | ||||
| @@ -76,8 +75,8 @@ public partial class UserCenter | ||||
|  | ||||
|     async Task OnUpdateUserInfoAsync() | ||||
|     { | ||||
|         await UserCenterService.UpdateUserInfoAsync(_updateInfoInput); | ||||
|         await App.GetService<UserCenterService>().UpdateUserInfoAsync(_updateInfoInput); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|         await PopupService.EnqueueSnackbarAsync("<EFBFBD>ɹ<EFBFBD>", AlertTypes.Success); | ||||
|         await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success); | ||||
|     } | ||||
| } | ||||
| @@ -15,7 +15,6 @@ | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
|   | ||||
| @inject IVisitLogService VisitLogService | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
|   | ||||
| @@ -51,14 +51,14 @@ public partial class Vislog | ||||
|         var confirm = await PopupService.OpenConfirmDialogAsync("删除", "确定 ?"); | ||||
|         if (confirm) | ||||
|         { | ||||
|             await VisitLogService.DeleteAsync(CategoryFilters.Select(it => it.Value).ToArray()); | ||||
|             await App.GetService<VisitLogService>().DeleteAsync(CategoryFilters.Select(it => it.Value).ToArray()); | ||||
|             await _datatable?.QueryClickAsync(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<SqlSugarPagedList<SysVisitLog>> QueryCallAsync(VisitLogPageInput input) | ||||
|     { | ||||
|         var data = await VisitLogService.PageAsync(input); | ||||
|         var data = await App.GetService<VisitLogService>().PageAsync(input); | ||||
|         return data; | ||||
|     } | ||||
|     [Inject] | ||||
|   | ||||
| @@ -21,6 +21,7 @@ using Microsoft.Extensions.Hosting; | ||||
| using ThingsGateway.Admin.Application; | ||||
| using ThingsGateway.Admin.Blazor.Core; | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor; | ||||
|  | ||||
| @@ -39,10 +40,6 @@ public partial class Login | ||||
|     AjaxService AjaxService { get; set; } | ||||
|  | ||||
|  | ||||
|     [Inject] | ||||
|     IAuthService AuthService { get; set; } | ||||
|  | ||||
|  | ||||
|  | ||||
|     string UserLogoUrl { get; set; } = BlazorResourceConst.ResourceUrl + "images/defaultUser.svg"; | ||||
|  | ||||
| @@ -64,12 +61,7 @@ public partial class Login | ||||
|         } | ||||
|     } | ||||
|     private PImageCaptcha captcha; | ||||
|     [Inject] | ||||
|     IUserCenterService UserCenterService { get; set; } | ||||
|     [Inject] | ||||
|     IResourceService ResourceService { get; set; } | ||||
|     [Inject] | ||||
|     ISysUserService SysUserService { get; set; } | ||||
|  | ||||
|     private async Task LoginAsync() | ||||
|     { | ||||
|         loginModel.ValidCodeReqNo = CaptchaInfo.ValidCodeReqNo; | ||||
| @@ -101,9 +93,9 @@ public partial class Login | ||||
|             { | ||||
|                 await PopupService.EnqueueSnackbarAsync(new("登录成功", AlertTypes.Success)); | ||||
|                 await Task.Delay(500); | ||||
|                 var userId = await SysUserService.GetIdByAccountAsync(loginModel.Account); | ||||
|                 var data = await UserCenterService.GetLoginDefaultRazorAsync(userId); | ||||
|                 var sameLevelMenus = await ResourceService.GetaMenuAndSpaListAsync(); | ||||
|                 var userId = await App.GetService<SysUserService>().GetIdByAccountAsync(loginModel.Account); | ||||
|                 var data = await App.GetService<UserCenterService>().GetLoginDefaultRazorAsync(userId); | ||||
|                 var sameLevelMenus = await App.GetService<ResourceService>().GetaMenuAndSpaListAsync(); | ||||
|                 if (NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/Login" || NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/") | ||||
|                     await AjaxService.GotoAsync(sameLevelMenus.FirstOrDefault(a => a.Id == data)?.Component ?? "index"); | ||||
|                 else | ||||
| @@ -140,12 +132,12 @@ public partial class Login | ||||
|  | ||||
|     private void GetCaptchaInfo() | ||||
|     { | ||||
|         CaptchaInfo = AuthService.GetCaptchaInfo(); | ||||
|         CaptchaInfo = App.GetService<AuthService>().GetCaptchaInfo(); | ||||
|     } | ||||
|  | ||||
|     private Task<string> RefreshCode() | ||||
|     { | ||||
|         CaptchaInfo = AuthService.GetCaptchaInfo(); | ||||
|         CaptchaInfo = App.GetService<AuthService>().GetCaptchaInfo(); | ||||
|         return Task.FromResult(CaptchaInfo.CodeValue); | ||||
|     } | ||||
| } | ||||
| @@ -43,11 +43,6 @@ | ||||
|             表格紧凑 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.DetailRowTemplate"> | ||||
|             <summary> | ||||
|             获得/设置 明细行模板 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.EditCallAsync"> | ||||
|             <summary> | ||||
|             编辑项委托 | ||||
| @@ -58,6 +53,11 @@ | ||||
|             获得/设置 编辑模板 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.Detailemplate"> | ||||
|             <summary> | ||||
|             获得/设置 详情模板 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.FilterHeaders"> | ||||
|             <summary> | ||||
|             表头过滤,返回DataTableHeader列表,传输参数已包含全部初始表头与表头标题 | ||||
|   | ||||
| @@ -86,7 +86,7 @@ public class OpenApiUser : BaseEntity | ||||
|     /// <summary> | ||||
|     /// 权限码集合 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "PermissionCodeList", ColumnDescription = "权限json", IsJson = true, IsNullable = true)] | ||||
|     [SugarColumn(ColumnName = "PermissionCodeList", ColumnDescription = "权限json", ColumnDataType = StaticConfig.CodeFirst_BigString, IsJson = true, IsNullable = true)] | ||||
|     public List<string> PermissionCodeList { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -31,7 +31,7 @@ public class SysVerificat : PrimaryIdEntity | ||||
|     /// <summary> | ||||
|     /// 会话信息列表 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsJson = true)] | ||||
|     [SugarColumn(ColumnName = "VerificatInfos", ColumnDescription = "会话信息列表", ColumnDataType = StaticConfig.CodeFirst_BigString, IsJson = true, IsNullable = true)] | ||||
|     public List<VerificatInfo> VerificatInfos { get; set; } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|  | ||||
| using Furion.DependencyInjection; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Core; | ||||
| namespace ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| /// <summary> | ||||
| /// 对象拓展 | ||||
|   | ||||
| @@ -18,6 +18,8 @@ using System.Collections; | ||||
| using System.Data; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Core; | ||||
|  | ||||
| /// <summary> | ||||
|   | ||||
| @@ -332,20 +332,51 @@ public static class ObjectExtensions | ||||
|     /// <returns></returns> | ||||
|     public static bool ToBoolean(this object value, bool defaultValue = false) => value?.ToString().ToUpper() switch | ||||
|     { | ||||
|         "0" or "FALSE" => false, | ||||
|         "1" or "TRUE" => true, | ||||
|         _ => defaultValue, | ||||
|     }; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ToLong | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public static long ToLong(this object value, long defaultValue = 0) => value == null || value.ToString().IsNullOrEmpty() ? defaultValue : Int64.TryParse(value.ToString(), out var n) ? n : defaultValue; | ||||
|     public static long ToLong(this object value, long defaultValue = 0) | ||||
|     { | ||||
|         if (value == null || value.ToString().IsNullOrEmpty()) | ||||
|         { | ||||
|             return defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return Int64.TryParse(value.ToString(), out var n) ? n : defaultValue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ToInt | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public static int ToInt(this object value, int defaultValue = 0) => value == null || value.ToString().IsNullOrEmpty() ? defaultValue : Int32.TryParse(value.ToString(), out var n) ? n : defaultValue; | ||||
|     public static int ToInt(this object value, int defaultValue = 0) | ||||
|     { | ||||
|         if (value == null || value.ToString().IsNullOrEmpty()) | ||||
|         { | ||||
|             return defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return int.TryParse(value.ToString(), out int n) ? n : defaultValue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ToDecimal | ||||
|     /// </summary> | ||||
| @@ -357,7 +388,18 @@ public static class ObjectExtensions | ||||
|             return Double.IsNaN(d) ? defaultValue : (Decimal)d; | ||||
|         } | ||||
|         var str = value?.ToString(); | ||||
|         return str.IsNullOrEmpty() ? defaultValue : Decimal.TryParse(str, out var n) ? n : defaultValue; | ||||
|         if (str.IsNullOrEmpty()) | ||||
|         { | ||||
|             return defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return Decimal.TryParse(str, out var n) ? n : defaultValue; | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// ToDecimal | ||||
| @@ -370,7 +412,18 @@ public static class ObjectExtensions | ||||
|             return Double.IsNaN(d) ? defaultValue : (Double)d; | ||||
|         } | ||||
|         var str = value?.ToString(); | ||||
|         return str.IsNullOrEmpty() ? defaultValue : double.TryParse(str, out var n) ? n : defaultValue; | ||||
|         if (str.IsNullOrEmpty()) | ||||
|         { | ||||
|             return (double)defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return (double)(double.TryParse(str, out var n) ? n : defaultValue); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -12,6 +12,8 @@ | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Linq.Expressions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Core; | ||||
|  | ||||
| /// <summary> | ||||
| @@ -55,12 +57,14 @@ public static class SqlSugarPageExtension | ||||
|     /// <param name="whereExpression"></param> | ||||
|     /// <returns></returns> | ||||
|     public static async Task<SqlSugarPagedList<TEntity>> ToPagedListAsync<TEntity>(this ISugarQueryable<TEntity> queryable, | ||||
|         int pageIndex, int pageSize, Func<TEntity, bool> whereExpression = null) | ||||
|         int pageIndex, int pageSize, Expression<Func<TEntity, bool>> whereExpression = null) | ||||
|     { | ||||
|  | ||||
|         RefAsync<int> totalCount = 0; | ||||
|         if (whereExpression != null) | ||||
|             queryable = queryable.Where(whereExpression); | ||||
|         var records = await queryable.ToPageListAsync(pageIndex, pageSize, totalCount); | ||||
|         records = whereExpression != null ? records.Where(whereExpression).ToList() : records; | ||||
|         //records = whereExpression != null ? records.Where(whereExpression).ToList() : records; | ||||
|         var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize); | ||||
|         return new SqlSugarPagedList<TEntity> | ||||
|         { | ||||
|   | ||||
| @@ -9,13 +9,13 @@ | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.8.40" /> | ||||
| 		<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.8.40" /> | ||||
| 		<PackageReference Include="Furion.Pure" Version="4.8.8.40" /> | ||||
| 		<PackageReference Include="SqlSugarCore" Version="5.1.4.94" /> | ||||
| 		<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.8.42" /> | ||||
| 		<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.8.42" /> | ||||
| 		<PackageReference Include="Furion.Pure" Version="4.8.8.42" /> | ||||
| 		<PackageReference Include="SqlSugarCore" Version="5.1.4.104" /> | ||||
| 		<PackageReference Include="UAParser" Version="3.1.47" /> | ||||
| 		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> | ||||
| 		<PackageReference Include="MiniExcel" Version="1.31.0" /> | ||||
| 		<PackageReference Include="MiniExcel" Version="1.31.2" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| </Project> | ||||
| @@ -850,19 +850,19 @@ | ||||
|             <param name="this"></param> | ||||
|             <param name="values"></param> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Admin.Core.JsonExtensions"> | ||||
|         <member name="T:ThingsGateway.Admin.Core.JsonExtensions.JsonExtensions"> | ||||
|             <summary> | ||||
|             对象拓展 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Admin.Core.JsonExtensions.ToJsonString(System.Object)"> | ||||
|         <member name="M:ThingsGateway.Admin.Core.JsonExtensions.JsonExtensions.ToJsonString(System.Object)"> | ||||
|             <summary> | ||||
|             转换为Json | ||||
|             </summary> | ||||
|             <param name="item"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Admin.Core.JsonExtensions.ToJsonWithT``1(System.String)"> | ||||
|         <member name="M:ThingsGateway.Admin.Core.JsonExtensions.JsonExtensions.ToJsonWithT``1(System.String)"> | ||||
|             <summary> | ||||
|             从字符串到json | ||||
|             </summary> | ||||
| @@ -1495,7 +1495,7 @@ | ||||
|             <param name="size"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Admin.Core.SqlSugarPageExtension.ToPagedListAsync``1(SqlSugar.ISugarQueryable{``0},System.Int32,System.Int32,System.Func{``0,System.Boolean})"> | ||||
|         <member name="M:ThingsGateway.Admin.Core.SqlSugarPageExtension.ToPagedListAsync``1(SqlSugar.ISugarQueryable{``0},System.Int32,System.Int32,System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}})"> | ||||
|             <summary> | ||||
|             SqlSugar分页扩展 | ||||
|             </summary> | ||||
|   | ||||
| @@ -21,7 +21,7 @@ using ThingsGateway.Application; | ||||
| namespace ThingsGateway.ApiController; | ||||
|  | ||||
| /// <summary> | ||||
| /// 后台登录控制器 | ||||
| /// 文件下载 | ||||
| /// </summary> | ||||
| [ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)] | ||||
| [Route("gatewayFile")] | ||||
| @@ -61,7 +61,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadRpcLogAsync([FromQuery] RpcLogInput input) | ||||
|     { | ||||
|         var memoryStream = await _rpcLogService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"rpcLog{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
| @@ -76,7 +75,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadBackendLogAsync([FromQuery] BackendLogInput input) | ||||
|     { | ||||
|         var memoryStream = await _backendLogService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"backendLog{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
| @@ -93,7 +91,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadCollectDeviceAsync([FromQuery] CollectDeviceInput input) | ||||
|     { | ||||
|         var memoryStream = await _collectDeviceService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"collectDevice{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
| @@ -108,7 +105,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadUploadDeviceAsync([FromQuery] UploadDeviceInput input) | ||||
|     { | ||||
|         var memoryStream = await _uploadDeviceService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"uploadDevice{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
| @@ -123,7 +119,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadDeviceVariableAsync([FromQuery] DeviceVariableInput input) | ||||
|     { | ||||
|         var memoryStream = await _variableService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"deviceVariable{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
| @@ -138,7 +133,6 @@ public class FileController : IDynamicApiController | ||||
|     public async Task<IActionResult> DownloadMemoryVariableAsync([FromQuery] MemoryVariableInput input) | ||||
|     { | ||||
|         var memoryStream = await _variableService.ExportFileAsync(input); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
|         { | ||||
|             FileDownloadName = $"memoryVariable{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx" | ||||
|   | ||||
| @@ -89,13 +89,8 @@ public class RpcControler : IDynamicApiController | ||||
|     [Description("写入变量")] | ||||
|     public async Task<Dictionary<string, OperResult>> WriteDeviceMethods(Dictionary<string, string> objs) | ||||
|     { | ||||
|         Dictionary<string, OperResult> operResultDict = new(); | ||||
|         foreach (KeyValuePair<string, string> obj in objs) | ||||
|         { | ||||
|             var result = await RpcCore.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", obj); | ||||
|             operResultDict.Add(obj.Key, result); | ||||
|         } | ||||
|         return operResultDict; | ||||
|         var result = await RpcCore.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", objs); | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.ApiController.FileController"> | ||||
|             <summary> | ||||
|             后台登录控制器 | ||||
|             文件下载 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.ApiController.FileController.#ctor(ThingsGateway.Application.IRpcLogService,ThingsGateway.Application.IBackendLogService,ThingsGateway.Application.ICollectDeviceService,ThingsGateway.Application.IUploadDeviceService,ThingsGateway.Application.IVariableService)"> | ||||
|   | ||||
| @@ -80,7 +80,7 @@ public class StringToEncodingConverter : IConverter<string> | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             source = target.ToJson(); | ||||
|             source = target.ToJsonString(); | ||||
|             return true; | ||||
|         } | ||||
|         catch (Exception) | ||||
|   | ||||
| @@ -31,7 +31,7 @@ public class DeviceVariable : MemoryVariable | ||||
|     public virtual long DeviceId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入表达式 | ||||
|     /// 单位 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "Unit", ColumnDescription = "单位", Length = 200, IsNullable = true)] | ||||
|     [DataTable(Order = 3, IsShow = true, Sortable = true)] | ||||
| @@ -59,12 +59,7 @@ public class DeviceVariable : MemoryVariable | ||||
|     [DataTable(Order = 3, IsShow = true, Sortable = true)] | ||||
|     public string VariableAddress { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入表达式 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "WriteExpressions", ColumnDescription = "写入表达式", Length = 200, IsNullable = true)] | ||||
|     [DataTable(Order = 7, IsShow = true, Sortable = true)] | ||||
|     public string WriteExpressions { get; set; } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 是否中间变量 | ||||
|   | ||||
| @@ -51,7 +51,12 @@ public class MemoryVariable : BaseEntity | ||||
|     [SugarColumn(ColumnName = "ReadExpressions", ColumnDescription = "读取表达式", Length = 200, IsNullable = true)] | ||||
|     [DataTable(Order = 7, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")] | ||||
|     public string ReadExpressions { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入表达式 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnName = "WriteExpressions", ColumnDescription = "写入表达式", Length = 200, IsNullable = true)] | ||||
|     [DataTable(Order = 7, IsShow = true, Sortable = true)] | ||||
|     public string WriteExpressions { get; set; } | ||||
|     /// <summary> | ||||
|     /// 是否中间变量 | ||||
|     /// </summary> | ||||
| @@ -67,7 +72,7 @@ public class MemoryVariable : BaseEntity | ||||
|     /// <summary> | ||||
|     /// 变量额外属性Json,通常使用为上传设备,List属性 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsJson = true, ColumnName = "VariablePropertys", ColumnDescription = "变量属性Json", IsNullable = true)] | ||||
|     [SugarColumn(IsJson = true, ColumnName = "VariablePropertys", ColumnDataType = StaticConfig.CodeFirst_BigString, ColumnDescription = "变量属性Json", IsNullable = true)] | ||||
|     [IgnoreExcel] | ||||
|     public ConcurrentDictionary<long, List<DependencyProperty>> VariablePropertys { get; set; } = new(); | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -65,7 +65,7 @@ public class UploadDevice : BaseEntity | ||||
|     /// <summary> | ||||
|     /// 设备属性Json | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsJson = true, ColumnName = "DevicePropertys", ColumnDescription = "设备属性Json", IsNullable = true)] | ||||
|     [SugarColumn(IsJson = true, ColumnName = "DevicePropertys", ColumnDataType = StaticConfig.CodeFirst_BigString, ColumnDescription = "设备属性Json", IsNullable = true)] | ||||
|     [IgnoreExcel] | ||||
|     public List<DependencyProperty> DevicePropertys { get; set; } | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,7 @@ using Newtonsoft.Json.Converters; | ||||
| using System.Dynamic; | ||||
| using System.Text; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
|  | ||||
| namespace ThingsGateway.Application; | ||||
| /// <summary> | ||||
| @@ -76,7 +76,7 @@ public class CSharpScriptEngine : ISingleton | ||||
|         var expConverter = new ExpandoObjectConverter(); | ||||
|         dynamic obj = JsonConvert.DeserializeObject<List<ExpandoObject>>(input, expConverter); | ||||
|         object result = runscript(obj); | ||||
|         var json = result.ToJson(); | ||||
|         var json = result.ToJsonString(); | ||||
|         return json; | ||||
|     } | ||||
|  | ||||
| @@ -116,7 +116,7 @@ public class CSharpScriptEngine : ISingleton | ||||
|         var expConverter = new ExpandoObjectConverter(); | ||||
|         dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(input, expConverter); | ||||
|         object result = runscript(obj); | ||||
|         var json = result.ToJson(); | ||||
|         var json = result.ToJsonString(); | ||||
|         return json; | ||||
|     } | ||||
|  | ||||
| @@ -136,7 +136,7 @@ public static class CSharpScriptEngineExtension | ||||
|     /// </summary> | ||||
|     public static string GetSciptListValue<T>(this T datas, string script) where T : class | ||||
|     { | ||||
|         var inPut = datas.ToJson(); | ||||
|         var inPut = datas.ToJsonString(); | ||||
|         if (!string.IsNullOrEmpty(script)) | ||||
|         { | ||||
|             //执行脚本,获取新实体 | ||||
| @@ -154,7 +154,7 @@ public static class CSharpScriptEngineExtension | ||||
|     /// </summary> | ||||
|     public static string GetSciptValue<T>(this T datas, string script) where T : class | ||||
|     { | ||||
|         var inPut = datas.ToJson(); | ||||
|         var inPut = datas.ToJsonString(); | ||||
|         if (!string.IsNullOrEmpty(script)) | ||||
|         { | ||||
|             //执行脚本,获取新实体 | ||||
|   | ||||
| @@ -113,8 +113,9 @@ public class HardwareInfoService : ISingleton | ||||
|         try | ||||
|         { | ||||
|             var url = "http://myip.ipip.net"; | ||||
|             var stream = await new HttpClient().GetStreamAsync(url); | ||||
|             var streamReader = new StreamReader(stream, Encoding.UTF8); | ||||
|             using var httpClient = new HttpClient(); | ||||
|             using var stream = await httpClient.GetStreamAsync(url); | ||||
|             using var streamReader = new StreamReader(stream, Encoding.UTF8); | ||||
|             var html = streamReader.ReadToEnd(); | ||||
|             return html.Replace("当前 IP:", "").Replace("来自于:", ""); | ||||
|         } | ||||
|   | ||||
| @@ -0,0 +1,197 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
|  | ||||
| using System.ComponentModel; | ||||
|  | ||||
| using ThingsGateway.Foundation; | ||||
|  | ||||
| namespace ThingsGateway.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// ManageGatewayConfig | ||||
| /// </summary> | ||||
| public class ManageGatewayConfig | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 启用 | ||||
|     /// </summary> | ||||
|     [Description("启用")] | ||||
|     public bool Enable { get; set; } | ||||
|     /// <summary> | ||||
|     /// MqttBrokerIP | ||||
|     /// </summary> | ||||
|     [Description("Mqtt-Tcp IP")] | ||||
|     public string MqttBrokerIP { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// MqttBrokerPort | ||||
|     /// </summary> | ||||
|     [Description("Mqtt-Tcp 端口")] | ||||
|     public int MqttBrokerPort { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// UserName | ||||
|     /// </summary> | ||||
|     [Description("Mqtt用户名")] | ||||
|     public string UserName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Password | ||||
|     /// </summary> | ||||
|     [Description("Mqtt密码")] | ||||
|     public string Password { get; set; } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// WriteRpcTopic,Rpc返回为{WriteRpcTopic}/Return,只有这个topic才开放外部订阅权限 | ||||
|     /// </summary> | ||||
|     [Description("变量写入Rpc主题")] | ||||
|     public string WriteRpcTopic { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DBDownTopic | ||||
|     /// </summary> | ||||
|     [Description("配置下发Rpc主题")] | ||||
|     public string DBDownTopic { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DBUploadTopic | ||||
|     /// </summary> | ||||
|     [Description("配置上传Rpc主题")] | ||||
|     public string DBUploadTopic { get; set; } | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// ClientGatewayConfig | ||||
| /// </summary> | ||||
| public class ClientGatewayConfig : ManageGatewayConfig | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 标识 | ||||
|     /// </summary> | ||||
|     [Description("子网关标识ID")] | ||||
|     public string GatewayId { get; set; } | ||||
|  | ||||
| } | ||||
| /// <summary> | ||||
| /// 用于Mqtt Json传输,上传/下载配置信息 | ||||
| /// </summary> | ||||
| public class MqttDBUploadRpcResult | ||||
| { | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 采集设备 | ||||
|     /// </summary> | ||||
|     public List<CollectDevice> CollectDevices { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上传设备 | ||||
|     /// </summary> | ||||
|     public List<UploadDevice> UploadDevices { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 变量 | ||||
|     /// </summary> | ||||
|     public List<DeviceVariable> DeviceVariables { get; set; } = new(); | ||||
|  | ||||
| } | ||||
| /// <summary> | ||||
| /// 用于Mqtt Json传输,上传/下载配置信息 | ||||
| /// </summary> | ||||
| public class MqttDBDownRpc | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 采集设备 | ||||
|     /// </summary> | ||||
|     public byte[] CollectDevices { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上传设备 | ||||
|     /// </summary> | ||||
|     public byte[] UploadDevices { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 变量 | ||||
|     /// </summary> | ||||
|     public byte[] DeviceVariables { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// true=>删除全部后增加 | ||||
|     /// </summary> | ||||
|     [Description("是否删除原采集设备表")] | ||||
|     public bool IsCollectDevicesFullUp { get; set; } | ||||
|     /// <summary> | ||||
|     /// true=>删除全部后增加 | ||||
|     /// </summary> | ||||
|     [Description("是否删除原上传设备表")] | ||||
|     public bool IsUploadDevicesFullUp { get; set; } | ||||
|     /// <summary> | ||||
|     /// true=>删除全部后增加 | ||||
|     /// </summary> | ||||
|     [Description("是否删除原变量表")] | ||||
|     public bool IsDeviceVariablesFullUp { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 是否立即重启,使配置生效 | ||||
|     /// </summary> | ||||
|     [Description("是否重启子网关线程")] | ||||
|     public bool IsRestart { get; set; } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| /// <summary> | ||||
| /// MqttRpc传入 | ||||
| /// </summary> | ||||
| public class ManageMqttRpcFrom | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 标识 | ||||
|     /// </summary> | ||||
|     public string GatewayId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 标识 | ||||
|     /// </summary> | ||||
|     public string RpcId { get; set; } | ||||
|     /// <summary> | ||||
|     /// "WriteInfos":{"test":"1"} | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> WriteInfos { get; set; } = new(); | ||||
| } | ||||
| /// <summary> | ||||
| /// MqttRpc输出 | ||||
| /// </summary> | ||||
| public class ManageMqttRpcResult | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 标识 | ||||
|     /// </summary> | ||||
|     public string GatewayId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 标识 | ||||
|     /// </summary> | ||||
|     public string RpcId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 消息 | ||||
|     /// </summary> | ||||
|     public Dictionary<string, OperResult> Message { get; set; } = new(); | ||||
|     /// <summary> | ||||
|     /// 是否成功 | ||||
|     /// </summary> | ||||
|     public bool Success { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,811 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
| using Furion.Logging.Extensions; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using MQTTnet; | ||||
| using MQTTnet.Client; | ||||
| using MQTTnet.Internal; | ||||
| using MQTTnet.Protocol; | ||||
| using MQTTnet.Server; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.Net; | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| namespace ThingsGateway.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// ManageGatewayWorker | ||||
| /// </summary> | ||||
| public class ManageGatewayWorker : BackgroundService | ||||
| { | ||||
|     private readonly ILogger _clientLogger; | ||||
|     private readonly ILogger _logger; | ||||
|     private readonly ILogger _manageLogger; | ||||
|     /// <summary> | ||||
|     /// 全部重启锁 | ||||
|     /// </summary> | ||||
|     private readonly EasyLock restartLock = new(); | ||||
|  | ||||
|     private IMqttClient _mqttClient; | ||||
|  | ||||
|     private MqttServer _mqttServer; | ||||
|  | ||||
|     private MqttClientSubscribeOptions _mqttSubscribeOptions; | ||||
|  | ||||
|     /// <inheritdoc cref="ManageGatewayWorker"/> | ||||
|     public ManageGatewayWorker(ILoggerFactory loggerFactory) | ||||
|     { | ||||
|         _logger = loggerFactory.CreateLogger("ManageGatewayWorker"); | ||||
|         _manageLogger = loggerFactory.CreateLogger("管理网关(mqttBroker)"); | ||||
|         _clientLogger = loggerFactory.CreateLogger("子网关(mqttClient)"); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 服务状态 | ||||
|     /// </summary> | ||||
|     public OperResult ClientStatuString { get; set; } = new OperResult("初始化"); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 服务状态 | ||||
|     /// </summary> | ||||
|     public OperResult ManageStatuString { get; set; } = new OperResult("初始化"); | ||||
|     #region worker服务 | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task StartAsync(CancellationToken token) | ||||
|     { | ||||
|         _logger?.LogInformation("ManageGatewayWorker启动"); | ||||
|         await RestartAsync(); | ||||
|         await base.StartAsync(token); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task StopAsync(CancellationToken token) | ||||
|     { | ||||
|         _logger?.LogInformation("ManageGatewayWorker停止"); | ||||
|         await StopAsync(); | ||||
|         await base.StopAsync(token); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task ExecuteAsync(CancellationToken stoppingToken) | ||||
|     { | ||||
|  | ||||
|         while (!stoppingToken.IsCancellationRequested) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (_mqttClient != null) | ||||
|                 { | ||||
|                     //持续重连 | ||||
|                     var result = await TryMqttClientAsync(stoppingToken); | ||||
|                     if (result.IsSuccess) | ||||
|                     { | ||||
|                         _clientLogger.LogDebug("连接正常:" + result.Message); | ||||
|                         ClientStatuString.ResultCode = ResultCode.Success; | ||||
|                         ClientStatuString.Message = "连接正常:" + result.Message; | ||||
|  | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _clientLogger.LogWarning("连接错误:" + result.Message); | ||||
|                         ClientStatuString.ResultCode = ResultCode.Fail; | ||||
|                         ClientStatuString.Message = "连接错误:" + result.Message; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 await Task.Delay(10000, stoppingToken); | ||||
|  | ||||
|  | ||||
|                 //if (_mqttServer != null) | ||||
|                 //{ | ||||
|                 //    //TODO:test code | ||||
|                 //    var mqttClientStatuses = await _mqttServer.GetClientsAsync(); | ||||
|  | ||||
|                 //    if (mqttClientStatuses.FirstOrDefault() is MqttClientStatus mqttClientStatus) | ||||
|                 //    { | ||||
|                 //        //获取子网关信息 | ||||
|                 //        var getClientGatewayDBResult = await GetClientGatewayDBAsync(mqttClientStatus.Id); | ||||
|  | ||||
|                 //        //下发子网关配置 | ||||
|                 //        var mqttDBDownRpc = new MqttDBDownRpc | ||||
|                 //        { | ||||
|                 //            IsRestart = true | ||||
|                 //        }; | ||||
|                 //        var setClientGatewayDBResult = await SetClientGatewayDBAsync(mqttClientStatus.Id, mqttDBDownRpc); | ||||
|  | ||||
|                 //        //下发子网关配置 | ||||
|                 //        var manageMqttRpcFrom = new ManageMqttRpcFrom | ||||
|                 //        { | ||||
|                 //            WriteInfos = new Dictionary<string, string>() | ||||
|                 //            { | ||||
|                 //                { | ||||
|                 //                "test41","123" | ||||
|                 //                } | ||||
|                 //            }, | ||||
|                 //            GatewayId = "GatewayId", | ||||
|                 //            RpcId = "123456", | ||||
|                 //        }; | ||||
|  | ||||
|                 //        var WriteVariableResult = await WriteVariableAsync(manageMqttRpcFrom); | ||||
|  | ||||
|                 //    } | ||||
|  | ||||
|                 //} | ||||
|  | ||||
|  | ||||
|  | ||||
|             } | ||||
|             catch (TaskCanceledException) | ||||
|             { | ||||
|  | ||||
|             } | ||||
|             catch (ObjectDisposedException) | ||||
|             { | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 _logger.LogError(ex, ToString()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #endregion | ||||
|     /// <summary> | ||||
|     /// 获取子网关的配置信息 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult<MqttDBUploadRpcResult>> GetClientGatewayDBAsync(string gatewayId, int timeOut = 3000, CancellationToken token = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var buffer = Encoding.UTF8.GetBytes(string.Empty); | ||||
|             var response = await RpcDataExecuteAsync(gatewayId, ClientGatewayConfig.DBUploadTopic, buffer, timeOut, MqttQualityOfServiceLevel.AtMostOnce, token); | ||||
|             var data = Encoding.UTF8.GetString(response).FromJsonString<MqttDBUploadRpcResult>(); | ||||
|             return OperResult.CreateSuccessResult(data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<MqttDBUploadRpcResult>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 重启 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task RestartAsync() | ||||
|     { | ||||
|         await StopAsync(); | ||||
|         await StartAsync(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// RPC请求子网关并返回,需要传入子网关ID,作为Topic参数一部分 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task<byte[]> RpcDataExecuteAsync(string gatewayId, string topic, byte[] payload, int timeOut, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken token = default) | ||||
|     { | ||||
|         var responseTopic = GetRpcReturnTopic(gatewayId, topic); | ||||
|         var requestTopic = GetRpcTopic(gatewayId, topic); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             using WaitDataAsync<byte[]> waitDataAsync = new(); | ||||
|             if (!_waitingCalls.TryAdd(responseTopic, waitDataAsync)) | ||||
|             { | ||||
|                 throw new InvalidOperationException(); | ||||
|             } | ||||
|             waitDataAsync.SetCancellationToken(token); | ||||
|  | ||||
|             //请求子网关的数据 | ||||
|             var message = new MqttApplicationMessageBuilder().WithTopic(requestTopic).WithPayload(payload).Build(); | ||||
|             await _mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(message), token); | ||||
|  | ||||
|             var result = await waitDataAsync.WaitAsync(timeOut); | ||||
|             switch (result) | ||||
|             { | ||||
|                 case WaitDataStatus.SetRunning: | ||||
|                     return waitDataAsync.WaitResult; | ||||
|                 case WaitDataStatus.Overtime: | ||||
|                     throw new TimeoutException(); | ||||
|                 case WaitDataStatus.Canceled: | ||||
|                     { | ||||
|                         throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。"); | ||||
|                     } | ||||
|                 case WaitDataStatus.Default: | ||||
|                 case WaitDataStatus.Disposed: | ||||
|                 default: | ||||
|                     throw new Exception(ThingsGatewayStatus.UnknownError.GetDescription()); | ||||
|             } | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             _waitingCalls.Remove(responseTopic); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 下载配置信息到子网关 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult<OperResult>> SetClientGatewayDBAsync(string gatewayId, MqttDBDownRpc mqttDBRpc, int timeOut = 3000, CancellationToken token = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var buffer = Encoding.UTF8.GetBytes(mqttDBRpc?.ToJsonString() ?? string.Empty); | ||||
|             var response = await RpcDataExecuteAsync(gatewayId, ClientGatewayConfig.DBDownTopic, buffer, timeOut, MqttQualityOfServiceLevel.AtMostOnce, token); | ||||
|             var data = Encoding.UTF8.GetString(response).FromJsonString<OperResult>(); | ||||
|             return OperResult.CreateSuccessResult(data); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<OperResult>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入变量到子网关 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult<ManageMqttRpcResult>> WriteVariableAsync(ManageMqttRpcFrom manageMqttRpcFrom, int timeOut = 3000, CancellationToken token = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var payload = Encoding.UTF8.GetBytes(manageMqttRpcFrom?.ToJsonString() ?? string.Empty); | ||||
|             var requestTopic = ManageGatewayConfig.WriteRpcTopic; | ||||
|             var responseTopic = GetRpcReturnTopic(ManageGatewayConfig.WriteRpcTopic); | ||||
|             var key = GetRpcReturnIdTopic(manageMqttRpcFrom.GatewayId, requestTopic, manageMqttRpcFrom.RpcId); | ||||
|  | ||||
|             ManageMqttRpcResult result = await RpcWriteExecuteAsync(timeOut, payload, requestTopic, key, token); | ||||
|  | ||||
|             return OperResult.CreateSuccessResult(result); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<ManageMqttRpcResult>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取子网关列表 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task<List<MqttClientStatus>> GetClientGatewayAsync() | ||||
|     { | ||||
|         var data = await _mqttServer.GetClientsAsync(); | ||||
|         return data.ToList(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     internal async Task StartAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             //重启操作在未完全之前直接取消 | ||||
|             if (restartLock.IsWaitting) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             await restartLock.WaitAsync(); | ||||
|  | ||||
|             await InitAsync(); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logger.LogError(ex, "启动错误"); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             restartLock.Release(); | ||||
|         } | ||||
|     } | ||||
|     internal async Task StopAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             //重启操作在未完全之前直接取消 | ||||
|             if (restartLock.IsWaitting) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             await restartLock.WaitAsync(); | ||||
|             _mqttClient?.SafeDispose(); | ||||
|             _mqttServer?.SafeDispose(); | ||||
|             _mqttClient = null; | ||||
|             _mqttServer = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logger.LogError(ex, "停止错误"); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             restartLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #region 核心实现 | ||||
|     readonly ConcurrentDictionary<string, WaitDataAsync<byte[]>> _waitingCalls = new(); | ||||
|     readonly ConcurrentDictionary<string, WaitDataAsync<ManageMqttRpcResult>> _writerRpcResultWaitingCalls = new(); | ||||
|     private readonly EasyLock clientLock = new(); | ||||
|     /// <summary> | ||||
|     /// ClientGatewayConfig | ||||
|     /// </summary> | ||||
|     public ClientGatewayConfig ClientGatewayConfig; | ||||
|     /// <summary> | ||||
|     /// ManageGatewayConfig | ||||
|     /// </summary> | ||||
|     public ManageGatewayConfig ManageGatewayConfig; | ||||
|     private MqttClientOptions _mqttClientOptions; | ||||
|     RpcSingletonService _rpcCore; | ||||
|  | ||||
|     private async Task DBDownTopicMethod(MqttApplicationMessageReceivedEventArgs args) | ||||
|     { | ||||
|         var mqttDBRpc = args.ApplicationMessage.PayloadSegment.Count > 0 ? Encoding.UTF8.GetString(args.ApplicationMessage.PayloadSegment).FromJsonString<MqttDBDownRpc>() : null; | ||||
|         if (mqttDBRpc != null) | ||||
|         { | ||||
|             OperResult result = new(); | ||||
|             var collectDeviceService = App.GetService<CollectDeviceService>(); | ||||
|             var variableService = App.GetService<VariableService>(); | ||||
|             var uploadDeviceService = App.GetService<UploadDeviceService>(); | ||||
|  | ||||
|             collectDeviceService.Context = variableService.Context = uploadDeviceService.Context; | ||||
|             var itenant = collectDeviceService.Context.AsTenant(); | ||||
|             //事务 | ||||
|             var dbResult = await itenant.UseTranAsync(async () => | ||||
|             { | ||||
|  | ||||
|                 if (mqttDBRpc.IsCollectDevicesFullUp) | ||||
|                 { | ||||
|                     await collectDeviceService.AsDeleteable().ExecuteCommandAsync(); | ||||
|                 } | ||||
|                 var collectDevices = new List<CollectDevice>(); | ||||
|  | ||||
|  | ||||
|                 if (mqttDBRpc.CollectDevices != null && mqttDBRpc.CollectDevices.Length > 0) | ||||
|                 { | ||||
|                     MemoryStream stream = new(mqttDBRpc.CollectDevices); | ||||
|                     var previewResult = await collectDeviceService.PreviewAsync(stream); | ||||
|                     if (previewResult.FirstOrDefault().Value.HasError) | ||||
|                     { | ||||
|                         throw new(previewResult.Select(a => a.Value.Results.Where(a => !a.isSuccess).ToList()).ToList().ToJsonString()); | ||||
|                     } | ||||
|                     foreach (var item in previewResult) | ||||
|                     { | ||||
|                         if (item.Key == ExportHelpers.CollectDeviceSheetName) | ||||
|                         { | ||||
|                             var collectDeviceImports = ((ImportPreviewOutput<CollectDevice>)item.Value).Data; | ||||
|                             collectDevices = collectDeviceImports.Values.Adapt<List<CollectDevice>>(); | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                     await collectDeviceService.ImportAsync(previewResult); | ||||
|  | ||||
|                 } | ||||
|  | ||||
|                 if (mqttDBRpc.IsUploadDevicesFullUp) | ||||
|                 { | ||||
|                     await uploadDeviceService.AsDeleteable().ExecuteCommandAsync(); | ||||
|  | ||||
|                 } | ||||
|                 var uploadDevices = new List<UploadDevice>(); | ||||
|  | ||||
|                 if (mqttDBRpc.UploadDevices != null && mqttDBRpc.UploadDevices.Length > 0) | ||||
|                 { | ||||
|                     MemoryStream stream1 = new(mqttDBRpc.UploadDevices); | ||||
|                     var previewResult1 = await uploadDeviceService.PreviewAsync(stream1); | ||||
|                     if (previewResult1.FirstOrDefault().Value.HasError) | ||||
|                     { | ||||
|                         throw new(previewResult1.Select(a => a.Value.Results.Where(a => !a.isSuccess).ToList()).ToList().ToJsonString()); | ||||
|                     } | ||||
|                     foreach (var item in previewResult1) | ||||
|                     { | ||||
|                         if (item.Key == ExportHelpers.UploadDeviceSheetName) | ||||
|                         { | ||||
|                             var uploadDeviceImports = ((ImportPreviewOutput<UploadDevice>)item.Value).Data; | ||||
|                             uploadDevices = uploadDeviceImports.Values.Adapt<List<UploadDevice>>(); | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                     await uploadDeviceService.ImportAsync(previewResult1); | ||||
|  | ||||
|                 } | ||||
|  | ||||
|                 if (mqttDBRpc.IsDeviceVariablesFullUp) | ||||
|                 { | ||||
|                     await variableService.AsDeleteable().ExecuteCommandAsync(); | ||||
|  | ||||
|                 } | ||||
|                 if (mqttDBRpc.DeviceVariables != null && mqttDBRpc.DeviceVariables.Length > 0) | ||||
|                 { | ||||
|                     MemoryStream stream2 = new(mqttDBRpc.DeviceVariables); | ||||
|                     var previewResult2 = await variableService.PreviewAsync(stream2, collectDevices, uploadDevices); | ||||
|                     if (previewResult2.FirstOrDefault().Value.HasError) | ||||
|                     { | ||||
|                         throw new(previewResult2.Select(a => a.Value.Results.Where(a => !a.isSuccess).ToList()).ToList().ToJsonString()); | ||||
|                     } | ||||
|                     await variableService.ImportAsync(previewResult2); | ||||
|                 } | ||||
|             }); | ||||
|             CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_CollectDevice);//cache删除 | ||||
|             CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_UploadDevice);//cache删除 | ||||
|  | ||||
|             if (dbResult.IsSuccess)//如果成功了 | ||||
|             { | ||||
|                 _clientLogger.LogInformation("子网关接收配置,并保存至数据库-执行成功"); | ||||
|                 result = OperResult.CreateSuccessResult(); | ||||
|                 if (mqttDBRpc.IsRestart) | ||||
|                 { | ||||
|                     _clientLogger.LogInformation("子网关接收配置,并重启"); | ||||
|                     await ServiceHelper.GetBackgroundService<CollectDeviceWorker>().RestartDeviceThreadAsync(); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 //写日志 | ||||
|                 result.Message = dbResult.ErrorMessage; | ||||
|             } | ||||
|  | ||||
|             var variableMessage = new MqttApplicationMessageBuilder() | ||||
|     .WithTopic(GetRpcReturnTopic(args.ApplicationMessage.Topic)) | ||||
|     .WithPayload(result.ToJsonString()).Build(); | ||||
|             if (_mqttClient.IsConnected) | ||||
|                 await _mqttClient.PublishAsync(variableMessage); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task DBUploadTopicMethod(MqttApplicationMessageReceivedEventArgs args) | ||||
|     { | ||||
|         MqttDBUploadRpcResult result = new(); | ||||
|         var collectDeviceService = App.GetService<CollectDeviceService>(); | ||||
|         var variableService = App.GetService<VariableService>(); | ||||
|         var uploadDeviceService = App.GetService<UploadDeviceService>(); | ||||
|         result.CollectDevices = collectDeviceService.GetCacheList(false); | ||||
|         result.DeviceVariables = await variableService.GetListAsync(); | ||||
|         result.UploadDevices = uploadDeviceService.GetCacheList(false); | ||||
|  | ||||
|         var variableMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic(GetRpcReturnTopic(args.ApplicationMessage.Topic)) | ||||
| .WithPayload(result.ToJsonString()).Build(); | ||||
|         if (_mqttClient.IsConnected) | ||||
|             await _mqttClient.PublishAsync(variableMessage); | ||||
|     } | ||||
|  | ||||
|     private string GetRpcReturnIdTopic(string gatewayId, string topic, string rpcId) | ||||
|     { | ||||
|         var responseTopic = $"{gatewayId}/{topic}/rpc/Return/rpcId"; | ||||
|         return responseTopic; | ||||
|     } | ||||
|  | ||||
|     private string GetRpcReturnTopic(string gatewayId, string topic) | ||||
|     { | ||||
|         var responseTopic = $"{gatewayId}/{topic}/rpc/Return"; | ||||
|         return responseTopic; | ||||
|     } | ||||
|  | ||||
|     private string GetRpcReturnTopic(string requestTopic) | ||||
|     { | ||||
|         var responseTopic = $"{requestTopic}/Return"; | ||||
|         return responseTopic; | ||||
|     } | ||||
|  | ||||
|     private string GetRpcTopic(string gatewayId, string topic) | ||||
|     { | ||||
|         var requestTopic = $"{gatewayId}/{topic}/rpc"; | ||||
|         return requestTopic; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 初始化 | ||||
|     /// </summary> | ||||
|     private async Task InitAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             ManageGatewayConfig = App.GetConfig<ManageGatewayConfig>("ManageGatewayConfig"); | ||||
|             if (ManageGatewayConfig?.Enable != true) | ||||
|             { | ||||
|                 ManageStatuString = new OperResult($"已退出:不启用管理功能"); | ||||
|                 _manageLogger.LogWarning("已退出:不启用管理功能"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var mqttFactory = new MqttFactory(new MqttNetLogger(_manageLogger)); | ||||
|                 var mqttServerOptions = mqttFactory.CreateServerOptionsBuilder() | ||||
|                     .WithDefaultEndpointBoundIPAddress(string.IsNullOrEmpty(ManageGatewayConfig.MqttBrokerIP) ? null : IPAddress.Parse(ManageGatewayConfig.MqttBrokerIP)) | ||||
|                     .WithDefaultEndpointPort(ManageGatewayConfig.MqttBrokerPort) | ||||
|                     .WithDefaultEndpoint() | ||||
|                     .Build(); | ||||
|                 _mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions); | ||||
|                 if (_mqttServer != null) | ||||
|                 { | ||||
|                     _mqttServer.ValidatingConnectionAsync += MqttServer_ValidatingConnectionAsync;//认证 | ||||
|                     _mqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync;//认证 | ||||
|  | ||||
|                     await _mqttServer.StartAsync(); | ||||
|                 } | ||||
|                 ManageStatuString = OperResult.CreateSuccessResult(); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _manageLogger.LogError(ex, "初始化失败"); | ||||
|             ManageStatuString = new($"初始化失败-{ex.Message}"); | ||||
|         } | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             ClientGatewayConfig = App.GetConfig<ClientGatewayConfig>("ClientGatewayConfig"); | ||||
|             if (ClientGatewayConfig?.Enable != true) | ||||
|             { | ||||
|                 ClientStatuString = new OperResult($"已退出:不启用子网关功能"); | ||||
|                 _clientLogger.LogWarning("已退出:不启用子网关功能"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var mqttFactory = new MqttFactory(new MqttNetLogger(_clientLogger)); | ||||
|                 _mqttClientOptions = mqttFactory.CreateClientOptionsBuilder() | ||||
|                   .WithCredentials(ClientGatewayConfig.UserName, ClientGatewayConfig.Password)//账密 | ||||
|                   .WithTcpServer(ClientGatewayConfig.MqttBrokerIP, ClientGatewayConfig.MqttBrokerPort)//服务器 | ||||
|                   .WithClientId(ClientGatewayConfig.GatewayId) | ||||
|                   .WithCleanSession(true) | ||||
|                   .WithKeepAlivePeriod(TimeSpan.FromSeconds(120.0)) | ||||
|                   .WithoutThrowOnNonSuccessfulConnectResponse() | ||||
|                   .Build(); | ||||
|                 _mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder() | ||||
|                     .WithTopicFilter( | ||||
|                         f => | ||||
|                         { | ||||
|                             f.WithTopic(ClientGatewayConfig.WriteRpcTopic); | ||||
|                             f.WithAtMostOnceQoS(); | ||||
|                         }) | ||||
|                       .WithTopicFilter( | ||||
|                         f => | ||||
|                         { | ||||
|                             f.WithTopic(GetRpcTopic(ClientGatewayConfig.GatewayId, ClientGatewayConfig.DBDownTopic)); | ||||
|                             f.WithAtMostOnceQoS(); | ||||
|                         }) | ||||
|                                         .WithTopicFilter( | ||||
|                         f => | ||||
|                         { | ||||
|                             f.WithTopic(GetRpcTopic(ClientGatewayConfig.GatewayId, ClientGatewayConfig.DBUploadTopic)); | ||||
|                             f.WithAtMostOnceQoS(); | ||||
|                         }) | ||||
|                     .Build(); | ||||
|                 _mqttClient = mqttFactory.CreateMqttClient(); | ||||
|                 _mqttClient.ConnectedAsync += MqttClient_ConnectedAsync; | ||||
|                 _mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync; | ||||
|                 await TryMqttClientAsync(CancellationToken.None); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _clientLogger.LogError(ex, "初始化失败"); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private async Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs args) | ||||
|     { | ||||
|         if (args.ApplicationMessage.Topic == GetRpcTopic(ClientGatewayConfig.GatewayId, ClientGatewayConfig.DBUploadTopic)) | ||||
|         { | ||||
|             _clientLogger.LogInformation("子网关配置上传"); | ||||
|             await DBUploadTopicMethod(args); | ||||
|             return; | ||||
|         } | ||||
|         if (args.ApplicationMessage.Topic == GetRpcTopic(ClientGatewayConfig.GatewayId, ClientGatewayConfig.DBDownTopic)) | ||||
|         { | ||||
|  | ||||
|             _clientLogger.LogInformation("子网关接收配置,并保存至数据库"); | ||||
|             await DBDownTopicMethod(args); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|         if (args.ApplicationMessage.Topic == ClientGatewayConfig.WriteRpcTopic) | ||||
|         { | ||||
|  | ||||
|             await WriteRpcTopicMethod(args); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task MqttClient_ConnectedAsync(MqttClientConnectedEventArgs args) | ||||
|     { | ||||
|         var subResult = await _mqttClient.SubscribeAsync(_mqttSubscribeOptions); | ||||
|         if (subResult.Items.Any(a => a.ResultCode > (MqttClientSubscribeResultCode)10)) | ||||
|         { | ||||
|             _clientLogger?.LogWarning("订阅失败-" + subResult.Items | ||||
|                 .Where(a => a.ResultCode > (MqttClientSubscribeResultCode)10) | ||||
|                 .Select(a => | ||||
|                 new | ||||
|                 { | ||||
|                     Topic = a.TopicFilter.Topic, | ||||
|                     ResultCode = a.ResultCode.ToString() | ||||
|                 } | ||||
|                 ) | ||||
|                 .ToJsonString() | ||||
|                 ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs eventArgs) | ||||
|     { | ||||
|         if (eventArgs.ApplicationMessage.Topic == GetRpcReturnTopic(ManageGatewayConfig.WriteRpcTopic)) | ||||
|         { | ||||
|             if (_writerRpcResultWaitingCalls.Count > 0) | ||||
|             { | ||||
|                 var payloadBuffer = eventArgs.ApplicationMessage.PayloadSegment.ToArray(); | ||||
|                 var manageMqttRpcResult = Encoding.UTF8.GetString(payloadBuffer).FromJsonString<ManageMqttRpcResult>(); | ||||
|                 var key = GetRpcReturnIdTopic(manageMqttRpcResult.GatewayId, ManageGatewayConfig.WriteRpcTopic, manageMqttRpcResult.RpcId); | ||||
|                 if (!_writerRpcResultWaitingCalls.TryRemove(key, out var writeRpcResultAsync)) | ||||
|                 { | ||||
|                     return CompletedTask.Instance; | ||||
|                 } | ||||
|                 writeRpcResultAsync.Set(manageMqttRpcResult); | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|             if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable)) | ||||
|             { | ||||
|                 return CompletedTask.Instance; | ||||
|             } | ||||
|  | ||||
|             var payloadBuffer = eventArgs.ApplicationMessage.PayloadSegment.ToArray(); | ||||
|             awaitable.Set(payloadBuffer); | ||||
|         } | ||||
|  | ||||
|         return CompletedTask.Instance; | ||||
|     } | ||||
|  | ||||
|     private Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs arg) | ||||
|     { | ||||
|         if (ManageGatewayConfig.UserName != arg.UserName) | ||||
|         { | ||||
|             arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; | ||||
|             return CompletedTask.Instance; | ||||
|         } | ||||
|         if (ManageGatewayConfig.Password != arg.Password) | ||||
|         { | ||||
|             arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; | ||||
|             return CompletedTask.Instance; | ||||
|         } | ||||
|         _manageLogger?.LogInformation(ToString() + "-" + arg.ClientId + "-客户端已连接成功"); | ||||
|         return CompletedTask.Instance; | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult> TryMqttClientAsync(CancellationToken token) | ||||
|     { | ||||
|         if (_mqttClient?.IsConnected == true) | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         return await Cilent(); | ||||
|  | ||||
|         async Task<OperResult> Cilent() | ||||
|         { | ||||
|             if (_mqttClient?.IsConnected == true) | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             try | ||||
|             { | ||||
|                 await clientLock.WaitAsync(); | ||||
|                 if (_mqttClient?.IsConnected == true) | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(5000)); | ||||
|                 using CancellationTokenSource StoppingToken = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutToken.Token); | ||||
|                 if (_mqttClient?.IsConnected == true) | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 if (_mqttClient == null) | ||||
|                 { | ||||
|                     return new OperResult("未初始化"); | ||||
|                 } | ||||
|                 var result = await _mqttClient?.ConnectAsync(_mqttClientOptions, StoppingToken.Token); | ||||
|                 if (result.ResultCode == MqttClientConnectResultCode.Success) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result.ReasonString); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 clientLock.Release(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     private async Task WriteRpcTopicMethod(MqttApplicationMessageReceivedEventArgs args) | ||||
|     { | ||||
|         var manageMqttRpcFrom = args.ApplicationMessage.PayloadSegment.Count > 0 ? Encoding.UTF8.GetString(args.ApplicationMessage.PayloadSegment).FromJsonString<ManageMqttRpcFrom>() : null; | ||||
|         if (manageMqttRpcFrom != null && manageMqttRpcFrom.GatewayId == ClientGatewayConfig.GatewayId) | ||||
|         { | ||||
|             ManageMqttRpcResult mqttRpcResult = new() { RpcId = manageMqttRpcFrom.RpcId, GatewayId = manageMqttRpcFrom.GatewayId }; | ||||
|             _rpcCore ??= App.GetService<RpcSingletonService>(); | ||||
|             var result = await _rpcCore.InvokeDeviceMethodAsync("子网关RPC" + "-" + args.ClientId, | ||||
|     manageMqttRpcFrom.WriteInfos.Where( | ||||
|     a => !mqttRpcResult.Message.Any(b => b.Key == a.Key)).ToDictionary(a => a.Key, a => a.Value)); | ||||
|             mqttRpcResult.Message.AddRange(result); | ||||
|             mqttRpcResult.Success = !mqttRpcResult.Message.Any(a => !a.Value.IsSuccess); | ||||
|  | ||||
|             var variableMessage = new MqttApplicationMessageBuilder() | ||||
|     .WithTopic(GetRpcReturnTopic(args.ApplicationMessage.Topic)) | ||||
|     .WithPayload(mqttRpcResult.ToJsonString()).Build(); | ||||
|             if (_mqttClient.IsConnected) | ||||
|                 await _mqttClient.PublishAsync(variableMessage); | ||||
|         } | ||||
|     } | ||||
|     #endregion | ||||
|     private async Task<ManageMqttRpcResult> RpcWriteExecuteAsync(int timeOut, byte[] payload, string requestTopic, string key, CancellationToken token) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             using WaitDataAsync<ManageMqttRpcResult> waitDataAsync = new(); | ||||
|             if (!_writerRpcResultWaitingCalls.TryAdd(key, waitDataAsync)) | ||||
|             { | ||||
|                 throw new InvalidOperationException(); | ||||
|             } | ||||
|             waitDataAsync.SetCancellationToken(token); | ||||
|  | ||||
|             //请求子网关的数据 | ||||
|             var message = new MqttApplicationMessageBuilder().WithTopic(requestTopic).WithPayload(payload).Build(); | ||||
|             await _mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(message), token); | ||||
|  | ||||
|             var result = await waitDataAsync.WaitAsync(timeOut); | ||||
|             switch (result) | ||||
|             { | ||||
|                 case WaitDataStatus.SetRunning: | ||||
|                     return waitDataAsync.WaitResult; | ||||
|                 case WaitDataStatus.Overtime: | ||||
|                     throw new TimeoutException(); | ||||
|                 case WaitDataStatus.Canceled: | ||||
|                     { | ||||
|                         throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。"); | ||||
|                     } | ||||
|                 case WaitDataStatus.Default: | ||||
|                 case WaitDataStatus.Disposed: | ||||
|                 default: | ||||
|                     throw new Exception(ThingsGatewayStatus.UnknownError.GetDescription()); | ||||
|             } | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             _writerRpcResultWaitingCalls.Remove(key); | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using MQTTnet.Diagnostics; | ||||
|  | ||||
|  | ||||
| namespace ThingsGateway.Application; | ||||
|  | ||||
| internal class MqttNetLogger : IMqttNetLogger | ||||
| { | ||||
|     readonly ILogger LogMessage; | ||||
|     public MqttNetLogger(ILogger logger) | ||||
|     { | ||||
|         LogMessage = logger; | ||||
|     } | ||||
|  | ||||
|     public bool IsEnabled => true; | ||||
|     public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception) | ||||
|     { | ||||
|         switch (logLevel) | ||||
|         { | ||||
|             case MqttNetLogLevel.Verbose: | ||||
|                 LogMessage?.Log(LogLevel.Trace, source, message != null ? (parameters != null ? message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty : message) : string.Empty, exception); | ||||
|                 break; | ||||
|  | ||||
|             case MqttNetLogLevel.Info: | ||||
|                 LogMessage?.Log(LogLevel.Information, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception); | ||||
|                 break; | ||||
|  | ||||
|             case MqttNetLogLevel.Warning: | ||||
|                 LogMessage?.Log(LogLevel.Warning, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception); | ||||
|                 break; | ||||
|  | ||||
|             case MqttNetLogLevel.Error: | ||||
|                 LogMessage?.Log(LogLevel.Warning, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -71,9 +71,9 @@ public class DeviceVariableRunTime : DeviceVariable | ||||
|     [DataTable(Order = 3, IsShow = true, Sortable = false, CellClass = " table-text-truncate ")] | ||||
|     public object Value { get => _value; private set => _value = value; } | ||||
|     /// <summary> | ||||
|     /// 最近一次值 | ||||
|     /// 上次值 | ||||
|     /// </summary> | ||||
|     [Description("最近一次值")] | ||||
|     [Description("上次值")] | ||||
|     [DataTable(Order = 3, IsShow = true, Sortable = false, CellClass = " table-text-truncate ")] | ||||
|     public object LastSetValue { get; private set; } | ||||
|  | ||||
| @@ -82,18 +82,15 @@ public class DeviceVariableRunTime : DeviceVariable | ||||
|     /// </summary> | ||||
|     /// <param name="value"></param> | ||||
|     /// <param name="dateTime"></param> | ||||
|     public OperResult SetValue(object value, DateTime dateTime = default) | ||||
|     /// <param name="isOnline"></param> | ||||
|     public OperResult SetValue(object value, DateTime dateTime = default, bool isOnline = true) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             IsOnline = isOnline; | ||||
|  | ||||
|             if (value != null) | ||||
|             if (!IsOnline) | ||||
|             { | ||||
|                 IsOnline = true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 IsOnline = false; | ||||
|                 RawValue = value; | ||||
|                 Set(value); | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
| @@ -143,11 +140,14 @@ public class DeviceVariableRunTime : DeviceVariable | ||||
|                 if ((data?.ToString() != _value?.ToString() && LastSetValue?.ToString() != data?.ToString()) || isOnlineChanged) | ||||
|                 { | ||||
|                     ChangeTime = time; | ||||
|  | ||||
|                     LastSetValue = _value; | ||||
|  | ||||
|                     if (IsOnline) | ||||
|                     { | ||||
|                         _value = data; | ||||
|                     } | ||||
|                     LastSetValue = data; | ||||
|  | ||||
|                     VariableValueChange?.Invoke(this); | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -20,6 +20,8 @@ public class VariableData | ||||
|     public long Id { get; set; } | ||||
|     /// <inheritdoc cref="MemoryVariable.Name"/> | ||||
|     public string Name { get; set; } | ||||
|     /// <inheritdoc cref="DeviceVariable.Unit"/> | ||||
|     public string Unit { get; set; } | ||||
|     /// <inheritdoc cref="MemoryVariable.Description"/> | ||||
|     public string Description { get; set; } | ||||
|     /// <inheritdoc cref="DeviceVariableRunTime.DeviceName"/> | ||||
| @@ -36,7 +38,7 @@ public class VariableData | ||||
|     public bool IsOnline { get; set; } | ||||
|     /// <inheritdoc cref="MemoryVariable.ReadExpressions"/> | ||||
|     public string ReadExpressions { get; set; } | ||||
|     /// <inheritdoc cref="DeviceVariable.WriteExpressions"/> | ||||
|     /// <inheritdoc cref="MemoryVariable.WriteExpressions"/> | ||||
|     public string WriteExpressions { get; set; } | ||||
|     /// <inheritdoc cref="DeviceVariable.IntervalTime"/> | ||||
|     public int IntervalTime { get; set; } | ||||
|   | ||||
| @@ -10,6 +10,8 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using Newtonsoft.Json.Linq; | ||||
| @@ -17,8 +19,6 @@ using Newtonsoft.Json.Linq; | ||||
| using ThingsGateway.Foundation; | ||||
| using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| namespace ThingsGateway.Application; | ||||
| /// <summary> | ||||
| /// <para></para> | ||||
| @@ -38,6 +38,10 @@ public abstract class CollectBase : DriverBase | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public abstract bool IsSupportRequest { get; } | ||||
|     /// <summary> | ||||
|     /// 一般底层驱动,也有可能为null | ||||
|     /// </summary> | ||||
|     protected abstract IReadWriteDevice PLC { get; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 数据转换器 | ||||
| @@ -100,9 +104,9 @@ public abstract class CollectBase : DriverBase | ||||
|                         StopBits = config.StopBits, | ||||
|                     }; | ||||
|                     FoundataionConfig.SetValue(SerialConfigExtension.SerialProperty, data); | ||||
|                     var serialClient = new SerialClient(); | ||||
|                     (serialClient).Setup(FoundataionConfig); | ||||
|                     return OperResult.CreateSuccessResult((object)serialClient); | ||||
|                     var serialSession = new SerialsSession(); | ||||
|                     (serialSession).Setup(FoundataionConfig); | ||||
|                     return OperResult.CreateSuccessResult((object)serialSession); | ||||
|                 case ShareChannelEnum.TcpClientEx: | ||||
|                     FoundataionConfig.SetRemoteIPHost(new IPHost($"{config.IP}:{config.Port}")); | ||||
|                     var tcpClient = new TcpClientEx(); | ||||
| @@ -155,7 +159,7 @@ public abstract class CollectBase : DriverBase | ||||
|             { | ||||
|                 deviceVariableSourceRead.DeviceVariables.ForEach(it => | ||||
|                 { | ||||
|                     var operResult = it.SetValue(null); | ||||
|                     var operResult = it.SetValue(null, isOnline: false); | ||||
|                     if (!operResult.IsSuccess) | ||||
|                     { | ||||
|                         _logger.LogWarning("变量值更新失败:" + operResult.Message); | ||||
| @@ -179,10 +183,22 @@ public abstract class CollectBase : DriverBase | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入变量值 | ||||
|     /// 批量写入变量值,需返回变量名称/结果 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public abstract Task<OperResult> WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token); | ||||
|     public virtual async Task<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<DeviceVariableRunTime, JToken> writeInfoLists, CancellationToken token) | ||||
|     { | ||||
|         if (PLC == null) | ||||
|             throw new("未初始化成功"); | ||||
|         Dictionary<string, OperResult> operResults = new(); | ||||
|         foreach (var writeInfo in writeInfoLists) | ||||
|         { | ||||
|             var result = await PLC.WriteAsync(writeInfo.Key.VariableAddress, writeInfo.Key.DataType, writeInfo.Value.ToString(), token); | ||||
|             await Task.Delay(10, token); //防止密集写入 | ||||
|             operResults.Add(writeInfo.Key.Name, result); | ||||
|         } | ||||
|         return operResults; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 初始化 | ||||
| @@ -206,6 +222,31 @@ public abstract class CollectBase : DriverBase | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal override void NewMessage(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4) | ||||
|     { | ||||
|         if (IsSaveLog) | ||||
|         { | ||||
|             if (arg3.StartsWith(FoundationConst.LogMessageHeader)) | ||||
|             { | ||||
|                 var customLevel = App.GetConfig<Microsoft.Extensions.Logging.LogLevel?>("Logging:LogLevel:BackendLog") ?? Microsoft.Extensions.Logging.LogLevel.Trace; | ||||
|                 if ((byte)arg1 < (byte)customLevel) | ||||
|                 { | ||||
|                     var logRuntime = new BackendLog | ||||
|                     { | ||||
|                         LogLevel = (Microsoft.Extensions.Logging.LogLevel)arg1, | ||||
|                         LogMessage = arg3, | ||||
|                         LogSource = "采集设备:" + CurDevice.Name, | ||||
|                         LogTime = SysDateTimeExtensions.CurrentDateTime, | ||||
|                         Exception = null, | ||||
|                     }; | ||||
|                     _logQueues.Enqueue(logRuntime); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         base.NewMessage(arg1, arg2, arg3, arg4); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 返回全部内容字节数组 | ||||
|     /// <br></br> | ||||
|   | ||||
| @@ -12,7 +12,10 @@ | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| using ThingsGateway.Foundation; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| @@ -40,6 +43,7 @@ public abstract class DriverBase : DisposableObject | ||||
|         LogMessage = new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace }; | ||||
|         LogMessage.AddLogger(new EasyLogger(Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace }); | ||||
|         FoundataionConfig.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|         Task.Factory.StartNew(LogInsertAsync); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
| @@ -67,6 +71,12 @@ public abstract class DriverBase : DisposableObject | ||||
|     /// </summary> | ||||
|     public bool IsLogOut { get; set; } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 是否存储报文 | ||||
|     /// </summary> | ||||
|     public bool IsSaveLog { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 报文信息 | ||||
|     /// </summary> | ||||
| @@ -82,11 +92,14 @@ public abstract class DriverBase : DisposableObject | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public abstract bool IsConnected(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 存储日志队列 | ||||
|     /// </summary> | ||||
|     protected ConcurrentQueue<BackendLog> _logQueues = new(); | ||||
|     /// <summary> | ||||
|     /// 设备报文 | ||||
|     /// </summary> | ||||
|     internal void NewMessage(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4) | ||||
|     internal virtual void NewMessage(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4) | ||||
|     { | ||||
|         if (IsLogOut) | ||||
|         { | ||||
| @@ -102,6 +115,27 @@ public abstract class DriverBase : DisposableObject | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private async Task LogInsertAsync() | ||||
|     { | ||||
|         var db = DbContext.Db.CopyNew(); | ||||
|         while (!DisposedValue) | ||||
|         { | ||||
|             if (_logQueues.Count > 0) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var data = _logQueues.ToListWithDequeue(); | ||||
|                     await db.InsertableWithAttr(data).ExecuteCommandAsync();//入库 | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|             await Task.Delay(5000); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 底层日志输出 | ||||
|   | ||||
| @@ -14,6 +14,8 @@ using Furion; | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using ThingsGateway.Foundation; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| namespace ThingsGateway.Application; | ||||
| @@ -153,6 +155,32 @@ public abstract class UpLoadBase : DriverBase | ||||
|             _logger.Log_Out(arg1, arg2, arg3, arg4); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal override void NewMessage(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4) | ||||
|     { | ||||
|         if (IsSaveLog) | ||||
|         { | ||||
|             if (arg3.StartsWith(FoundationConst.LogMessageHeader)) | ||||
|             { | ||||
|                 var customLevel = App.GetConfig<Microsoft.Extensions.Logging.LogLevel?>("Logging:LogLevel:BackendLog") ?? Microsoft.Extensions.Logging.LogLevel.Trace; | ||||
|                 if ((byte)arg1 < (byte)customLevel) | ||||
|                 { | ||||
|                     var logRuntime = new BackendLog | ||||
|                     { | ||||
|                         LogLevel = (Microsoft.Extensions.Logging.LogLevel)arg1, | ||||
|                         LogMessage = arg3, | ||||
|                         LogSource = "上传设备:" + CurDevice.Name, | ||||
|                         LogTime = SysDateTimeExtensions.CurrentDateTime, | ||||
|                         Exception = null, | ||||
|                     }; | ||||
|                     _logQueues.Enqueue(logRuntime); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         base.NewMessage(arg1, arg2, arg3, arg4); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,7 @@ using Newtonsoft.Json.Linq; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| using ThingsGateway.Admin.Core.JsonExtensions; | ||||
| using ThingsGateway.Foundation; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
|  | ||||
| @@ -48,92 +49,172 @@ public class RpcSingletonService : ISingleton | ||||
|     /// 反向RPC入口方法 | ||||
|     /// </summary> | ||||
|     /// <param name="sourceDes">触发该方法的源说明</param> | ||||
|     /// <param name="item">指定键为变量名称,值为附带方法参数或写入值</param> | ||||
|     /// <param name="items">指定键为变量名称,值为附带方法参数或写入值</param> | ||||
|     /// <param name="isBlazor">如果是true,不检查<see cref="MemoryVariable.RpcWriteEnable"/>字段</param> | ||||
|     /// <param name="token"><see cref="CancellationToken"/> 取消源</param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> InvokeDeviceMethodAsync(string sourceDes, KeyValuePair<string, string> item, bool isBlazor = false, CancellationToken token = default) | ||||
|     public async Task<Dictionary<string, OperResult>> InvokeDeviceMethodAsync(string sourceDes, Dictionary<string, string> items, bool isBlazor = false, CancellationToken token = default) | ||||
|     { | ||||
|         //避免并发过高,这里延时10ms | ||||
|         await Task.Delay(10, token); | ||||
|         OperResult data = new(); | ||||
|         var tag = _globalDeviceData.AllVariables.FirstOrDefault(it => it.Name == item.Key); | ||||
|         if (tag == null) return new OperResult("不存在变量:" + item.Key); | ||||
|         if (tag.ProtectTypeEnum == ProtectTypeEnum.ReadOnly) return new OperResult("只读变量"); | ||||
|         if (!tag.RpcWriteEnable && !isBlazor) return new OperResult("不允许远程写入"); | ||||
|  | ||||
|         if (tag.IsMemoryVariable == true) | ||||
|         Dictionary<CollectDeviceCore, Dictionary<DeviceVariableRunTime, JToken>> WriteVariables = new(); | ||||
|         Dictionary<CollectDeviceCore, Dictionary<DeviceVariableRunTime, string>> WriteMethods = new(); | ||||
|         Dictionary<string, OperResult> results = new(); | ||||
|         foreach (var item in items) | ||||
|         { | ||||
|             return tag.SetValue(item.Value); | ||||
|         } | ||||
|         var dev = _collectDeviceHostService.CollectDeviceCores.FirstOrDefault(it => it.Device.Id == tag.DeviceId); | ||||
|         if (dev == null) return new OperResult("系统错误,不存在对应采集设备,请稍候重试"); | ||||
|         if (dev.Device.DeviceStatus == DeviceStatusEnum.OffLine) return new OperResult("设备已离线"); | ||||
|         if (dev.Device.DeviceStatus == DeviceStatusEnum.Pause) return new OperResult("设备已暂停"); | ||||
|         if (string.IsNullOrEmpty(tag.OtherMethod)) | ||||
|         { | ||||
|             //写入变量 | ||||
|             JToken tagValue; | ||||
|             try | ||||
|             { | ||||
|                 tagValue = JToken.Parse(item.Value); | ||||
|             } | ||||
|             catch (Exception) | ||||
|             { | ||||
|                 tagValue = JToken.Parse("\"" + item.Value + "\""); | ||||
|             } | ||||
|  | ||||
|             data = await dev.InVokeWriteAsync(tag, tagValue, token); | ||||
|             _logQueues.Enqueue( | ||||
|                 new RpcLog() | ||||
|             OperResult data = new(); | ||||
|             var tag = _globalDeviceData.AllVariables.FirstOrDefault(it => it.Name == item.Key); | ||||
|             if (tag == null) | ||||
|                 results.Add(item.Key, new("不存在变量:" + item.Key)); | ||||
|             if (tag.ProtectTypeEnum == ProtectTypeEnum.ReadOnly) | ||||
|                 results.Add(item.Key, new("只读变量:" + item.Key)); | ||||
|             if (!tag.RpcWriteEnable && !isBlazor) | ||||
|                 results.Add(item.Key, new("不允许远程写入:" + item.Key)); | ||||
|  | ||||
|             if (tag.IsMemoryVariable == true) | ||||
|             { | ||||
|                 results.Add(item.Key, tag.SetValue(item.Value)); | ||||
|             } | ||||
|             var dev = _collectDeviceHostService.CollectDeviceCores.FirstOrDefault(it => it.Device.Id == tag.DeviceId); | ||||
|             if (dev == null) | ||||
|                 results.Add(item.Key, new OperResult("系统错误,不存在对应采集设备,请稍候重试")); | ||||
|             if (dev.Device.DeviceStatus == DeviceStatusEnum.OffLine) | ||||
|                 results.Add(item.Key, new OperResult("设备已离线")); | ||||
|             if (dev.Device.DeviceStatus == DeviceStatusEnum.Pause) | ||||
|                 results.Add(item.Key, new OperResult("设备已暂停")); | ||||
|  | ||||
|             if (!results.ContainsKey(item.Key)) | ||||
|             { | ||||
|                 if (string.IsNullOrEmpty(tag.OtherMethod)) | ||||
|                 { | ||||
|                     LogTime = SysDateTimeExtensions.CurrentDateTime, | ||||
|                     OperateMessage = data.Exception, | ||||
|                     IsSuccess = data.IsSuccess, | ||||
|                     OperateMethod = WriteVariable, | ||||
|                     OperateObject = tag.Name, | ||||
|                     OperateSource = sourceDes, | ||||
|                     ParamJson = item.Value, | ||||
|                     ResultJson = data.Message | ||||
|                     //写入变量 | ||||
|                     JToken tagValue; | ||||
|                     try | ||||
|                     { | ||||
|                         tagValue = JToken.Parse(item.Value); | ||||
|                     } | ||||
|                     catch (Exception) | ||||
|                     { | ||||
|                         tagValue = JToken.Parse("\"" + item.Value + "\""); | ||||
|                     } | ||||
|                     if (WriteVariables.ContainsKey(dev)) | ||||
|                     { | ||||
|  | ||||
|                         WriteVariables[dev].Add(tag, tagValue); | ||||
|  | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         WriteVariables.Add(dev, new()); | ||||
|                         WriteVariables[dev].Add(tag, tagValue); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (WriteMethods.ContainsKey(dev)) | ||||
|                     { | ||||
|  | ||||
|                         WriteMethods[dev].Add(tag, item.Value); | ||||
|  | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         WriteVariables.Add(dev, new()); | ||||
|                         WriteVariables[dev].Add(tag, item.Value); | ||||
|                     } | ||||
|                 } | ||||
|                 ); | ||||
|             if (!data.IsSuccess) | ||||
|             { | ||||
|                 _logger.LogWarning($"写入变量[{tag.Name}]失败:{data.Message}"); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|  | ||||
|         foreach (var item in WriteVariables) | ||||
|         { | ||||
|             //执行变量附带的方法 | ||||
|             var method = dev.DeviceVariableMethodSources.FirstOrDefault(it => it.DeviceVariable == tag); | ||||
|             try | ||||
|             { | ||||
|                 data = await dev.InvokeMethodAsync(method, false, item.Value, token); | ||||
|                 var result = await item.Key.InVokeWriteAsync(item.Value, token); | ||||
|                 foreach (var resultItem in result) | ||||
|                 { | ||||
|                     string operObj; | ||||
|                     string parJson; | ||||
|                     if (resultItem.Key.IsNullOrEmpty()) | ||||
|                     { | ||||
|                         operObj = items.Select(x => x.Key).ToJsonString(); | ||||
|                         parJson = items.Select(x => x.Value).ToJsonString(); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         operObj = resultItem.Key; | ||||
|                         parJson = items[resultItem.Key]; | ||||
|  | ||||
|                     } | ||||
|                     _logQueues.Enqueue( | ||||
|           new RpcLog() | ||||
|           { | ||||
|               LogTime = SysDateTimeExtensions.CurrentDateTime, | ||||
|               OperateMessage = resultItem.Value.Exception, | ||||
|               IsSuccess = resultItem.Value.IsSuccess, | ||||
|               OperateMethod = WriteVariable, | ||||
|               OperateObject = operObj, | ||||
|               OperateSource = sourceDes, | ||||
|               ParamJson = parJson, | ||||
|               ResultJson = resultItem.Value.Message | ||||
|           } | ||||
|           ); | ||||
|                     if (!resultItem.Value.IsSuccess) | ||||
|                     { | ||||
|                         _logger.LogWarning($"写入变量[{resultItem.Key}]失败:{resultItem.Value.Message}"); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 results.AddRange(result); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 data = new OperResult<string>(ex); | ||||
|             } | ||||
|             _logQueues.Enqueue( | ||||
| new RpcLog() | ||||
| { | ||||
|     LogTime = SysDateTimeExtensions.CurrentDateTime, | ||||
|     OperateMessage = data.Exception, | ||||
|     IsSuccess = data.IsSuccess, | ||||
|     OperateMethod = tag.OtherMethod, | ||||
|     OperateObject = tag.Name, | ||||
|     OperateSource = sourceDes, | ||||
|     ParamJson = item.Value?.ToString(), | ||||
|     ResultJson = data.Message | ||||
| } | ||||
| ); | ||||
|             if (!data.IsSuccess) | ||||
|             { | ||||
|                 _logger.LogWarning($"执行变量[{tag.Name}]方法[{tag.OtherMethod}]失败:{data.Message}"); | ||||
|                 _logger.LogWarning($"写入变量异常:{ex.Message + Environment.NewLine + ex.StackTrace}"); | ||||
|             } | ||||
|         } | ||||
|         return data; | ||||
|         foreach (var item in WriteMethods) | ||||
|         { | ||||
|             foreach (var writeMethod in item.Value) | ||||
|             { | ||||
|  | ||||
|                 //执行变量附带的方法 | ||||
|                 var method = item.Key.DeviceVariableMethodSources.FirstOrDefault(it => it.DeviceVariable == writeMethod.Key); | ||||
|                 OperResult<string> result; | ||||
|                 try | ||||
|                 { | ||||
|                     result = await item.Key.InvokeMethodAsync(method, false, writeMethod.Value, token); | ||||
|                     results.Add(writeMethod.Key.Name, result); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     result = new OperResult<string>(ex); | ||||
|                     results.Add(writeMethod.Key.Name, result); | ||||
|                 } | ||||
|                 _logQueues.Enqueue( | ||||
|     new RpcLog() | ||||
|     { | ||||
|         LogTime = SysDateTimeExtensions.CurrentDateTime, | ||||
|         OperateMessage = result.Exception, | ||||
|         IsSuccess = result.IsSuccess, | ||||
|         OperateMethod = writeMethod.Key.OtherMethod, | ||||
|         OperateObject = writeMethod.Key.Name, | ||||
|         OperateSource = sourceDes, | ||||
|         ParamJson = writeMethod.Value?.ToString(), | ||||
|         ResultJson = result.Message | ||||
|     } | ||||
|     ); | ||||
|                 if (!result.IsSuccess) | ||||
|                 { | ||||
|                     _logger.LogWarning($"执行变量[{writeMethod.Key.Name}]方法[{writeMethod.Key.OtherMethod}]失败:{result.Message}"); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     private async Task RpcLogInsertAsync() | ||||
|     { | ||||
|   | ||||
| @@ -22,6 +22,9 @@ public class DriverPluginSeedData : ISqlSugarEntitySeedData<DriverPlugin> | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<DriverPlugin> SeedData() | ||||
|     { | ||||
|         return SeedDataUtil.GetSeedData<DriverPlugin>("driver_plugin.json"); | ||||
|         return SeedDataUtil.GetSeedData<DriverPlugin>("driver_plugin.json") | ||||
|             .Concat(SeedDataUtil.GetSeedData<DriverPlugin>("pro_driver_plugin.json")) | ||||
|             .Concat(SeedDataUtil.GetSeedData<DriverPlugin>("custom_driver_plugin.json")) | ||||
|             ; | ||||
|     } | ||||
| } | ||||
| @@ -319,6 +319,39 @@ | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 442505, | ||||
|       "FileName": "ThingsGateway.DLT645", | ||||
|       "AssembleName": "DLT645_2007", | ||||
|       "DriverTypeEnum": "Collect", | ||||
|       "FilePath": "Plugins/ThingsGateway.DLT645/ThingsGateway.DLT645.dll", | ||||
|       "CreateTime": "2023/8/6 18:22:33", | ||||
|       "CreateUser": "superAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": 0, | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 442506, | ||||
|       "FileName": "ThingsGateway.DLT645", | ||||
|       "AssembleName": "DLT645_2007OverTcp", | ||||
|       "DriverTypeEnum": "Collect", | ||||
|       "FilePath": "Plugins/ThingsGateway.DLT645/ThingsGateway.DLT645.dll", | ||||
|       "CreateTime": "2023/8/6 18:22:33", | ||||
|       "CreateUser": "superAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": 0, | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -95,6 +95,23 @@ | ||||
|       "UpdateUser": "superAdmin", | ||||
|       "UpdateUserId": "212725263002001" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "200001904", | ||||
|       "Title": "管理网关", | ||||
|       "Icon": "mdi-database-sync-outline", | ||||
|       "Component": "/gatewayconfig/manage", | ||||
|       "Category": "MENU", | ||||
|       "ParentId": "200001", | ||||
|       "SortCode": "3", | ||||
|       "TargetType": "SELF", | ||||
|       "CreateTime": "2023-02-26 01:02:12.089", | ||||
|       "CreateUser": "superAdmin", | ||||
|       "CreateUserId": "212725263002001", | ||||
|       "IsDelete": false, | ||||
|       "UpdateTime": "2023-03-03 18:01:49.2309339", | ||||
|       "UpdateUser": "superAdmin", | ||||
|       "UpdateUserId": "212725263002001" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "200001004", | ||||
|       "Title": "运行状态", | ||||
|   | ||||
| @@ -22,6 +22,6 @@ public class OpenApiUserSeedData : ISqlSugarEntitySeedData<OpenApiUser> | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<OpenApiUser> SeedData() | ||||
|     { | ||||
|         return SeedDataUtil.GetSeedData<OpenApiUser>("gatewayopenapi_user.json"); | ||||
|         return SeedDataUtil.GetSeedData<OpenApiUser>("gateway_openapi_user.json"); | ||||
|     } | ||||
| } | ||||
| @@ -360,6 +360,8 @@ public class CollectDeviceService : DbRepository<CollectDevice>, ICollectDeviceS | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|  | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
| @@ -367,10 +369,17 @@ public class CollectDeviceService : DbRepository<CollectDevice>, ICollectDeviceS | ||||
|     public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file) | ||||
|     { | ||||
|         _fileService.ImportVerification(file); | ||||
|         using var fs = new MemoryStream(); | ||||
|         using var stream = file.OpenReadStream(512000000); | ||||
|         await stream.CopyToAsync(fs); | ||||
|         var sheetNames = MiniExcel.GetSheetNames(fs); | ||||
|         using var stream = new MemoryStream(); | ||||
|         using var fs = file.OpenReadStream(512000000); | ||||
|         await fs.CopyToAsync(stream); | ||||
|         return await PreviewAsync(stream); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(MemoryStream stream) | ||||
|     { | ||||
|         var sheetNames = MiniExcel.GetSheetNames(stream); | ||||
|         var deviceDicts = GetCacheList(false).ToDictionary(a => a.Name); | ||||
|         var pluginDicts = _driverPluginService.GetCacheList(false).ToDictionary(a => a.AssembleName); | ||||
|  | ||||
| @@ -381,7 +390,7 @@ public class CollectDeviceService : DbRepository<CollectDevice>, ICollectDeviceS | ||||
|         foreach (var sheetName in sheetNames) | ||||
|         { | ||||
|             //单页数据 | ||||
|             var rows = fs.Query(useHeaderRow: true, sheetName: sheetName).Cast<IDictionary<string, object>>(); | ||||
|             var rows = stream.Query(useHeaderRow: true, sheetName: sheetName).Cast<IDictionary<string, object>>(); | ||||
|             #region 采集设备sheet | ||||
|             if (sheetName == ExportHelpers.CollectDeviceSheetName) | ||||
|             { | ||||
| @@ -496,8 +505,7 @@ public class CollectDeviceService : DbRepository<CollectDevice>, ICollectDeviceS | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         return ImportPreviews; | ||||
|         return Task.FromResult(ImportPreviews); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|   | ||||
| @@ -63,6 +63,7 @@ public class BackendLogService : DbRepository<BackendLog>, IBackendLogService | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
| @@ -86,6 +87,8 @@ public class BackendLogService : DbRepository<BackendLog>, IBackendLogService | ||||
|     private ISugarQueryable<BackendLog> GetPage(BackendLogPageInput input) | ||||
|     { | ||||
|         var query = Context.Queryable<BackendLog>() | ||||
|                            .WhereIF(input.StartTime != null, a => a.LogTime >= input.StartTime.Value.ToLocalTime()) | ||||
|                            .WhereIF(input.EndTime != null, a => a.LogTime <= input.EndTime.Value.ToLocalTime()) | ||||
|                            .WhereIF(!string.IsNullOrEmpty(input.Source), it => it.LogSource.Contains(input.Source)) | ||||
|                            .WhereIF(!string.IsNullOrEmpty(input.Level), it => it.LogLevel.ToString().Contains(input.Level)); | ||||
|         for (int i = 0; i < input.SortField.Count; i++) | ||||
|   | ||||
| @@ -19,6 +19,15 @@ namespace ThingsGateway.Application; | ||||
| /// </summary> | ||||
| public class BackendLogPageInput : BasePageInput | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 开始时间 | ||||
|     /// </summary> | ||||
|     public DateTime? StartTime { get; set; } = DateTime.UtcNow.AddDays(-1); | ||||
|     /// <summary> | ||||
|     /// 结束时间 | ||||
|     /// </summary> | ||||
|     public DateTime? EndTime { get; set; } = DateTime.UtcNow.AddDays(1); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 日志源 | ||||
|     /// </summary> | ||||
| @@ -55,6 +64,14 @@ public class BackendLogInput | ||||
| /// </summary> | ||||
| public class RpcLogPageInput : BasePageInput | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 开始时间 | ||||
|     /// </summary> | ||||
|     public DateTime? StartTime { get; set; } = DateTime.UtcNow.AddDays(-1); | ||||
|     /// <summary> | ||||
|     /// 结束时间 | ||||
|     /// </summary> | ||||
|     public DateTime? EndTime { get; set; } = DateTime.UtcNow.AddDays(1); | ||||
|     /// <summary> | ||||
|     /// 操作源 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -44,6 +44,8 @@ public class RpcLogService : DbRepository<RpcLog>, IRpcLogService | ||||
|     private ISugarQueryable<RpcLog> GetPage(RpcLogPageInput input) | ||||
|     { | ||||
|         var query = Context.Queryable<RpcLog>() | ||||
|                            .WhereIF(input.StartTime != null, a => a.LogTime >= input.StartTime.Value.ToLocalTime()) | ||||
|                            .WhereIF(input.EndTime != null, a => a.LogTime <= input.EndTime.Value.ToLocalTime()) | ||||
|                            .WhereIF(!string.IsNullOrEmpty(input.Source), it => it.OperateSource.Contains(input.Source)) | ||||
|                            .WhereIF(!string.IsNullOrEmpty(input.Object), it => it.OperateObject.Contains(input.Object)) | ||||
|                            .WhereIF(!string.IsNullOrEmpty(input.Method), it => it.OperateMethod.Contains(input.Method)); | ||||
| @@ -88,6 +90,7 @@ public class RpcLogService : DbRepository<RpcLog>, IRpcLogService | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -64,10 +64,10 @@ public class UploadDeviceService : DbRepository<UploadDevice>, IUploadDeviceServ | ||||
|     [OperDesc("复制上传设备")] | ||||
|     public async Task CopyDevAsync(IEnumerable<UploadDevice> input) | ||||
|     { | ||||
|         var newId = YitIdHelper.NextId(); | ||||
|         var newDevs = input.Adapt<List<UploadDevice>>(); | ||||
|         newDevs.ForEach(a => | ||||
|         { | ||||
|             var newId = YitIdHelper.NextId(); | ||||
|             a.Id = newId; | ||||
|             a.Name = "Copy-" + a.Name + "-" + newId.ToString(); | ||||
|         }); | ||||
| @@ -284,6 +284,7 @@ public class UploadDeviceService : DbRepository<UploadDevice>, IUploadDeviceServ | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
| @@ -305,15 +306,19 @@ public class UploadDeviceService : DbRepository<UploadDevice>, IUploadDeviceServ | ||||
|         CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_UploadDevice);//cache删除 | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file) | ||||
|     { | ||||
|         _fileService.ImportVerification(file); | ||||
|         using var fs = new MemoryStream(); | ||||
|         using var stream = file.OpenReadStream(512000000); | ||||
|         await stream.CopyToAsync(fs); | ||||
|         var sheetNames = MiniExcel.GetSheetNames(fs); | ||||
|         using var stream = new MemoryStream(); | ||||
|         using var fs = file.OpenReadStream(512000000); | ||||
|         await fs.CopyToAsync(stream); | ||||
|         return await PreviewAsync(stream); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(MemoryStream stream) | ||||
|     { | ||||
|         var sheetNames = MiniExcel.GetSheetNames(stream); | ||||
|         var deviceDicts = GetCacheList(false).ToDictionary(a => a.Name); | ||||
|         var pluginDicts = _driverPluginService.GetCacheList(false).ToDictionary(a => a.AssembleName); | ||||
|  | ||||
| @@ -324,7 +329,7 @@ public class UploadDeviceService : DbRepository<UploadDevice>, IUploadDeviceServ | ||||
|         foreach (var sheetName in sheetNames) | ||||
|         { | ||||
|             //单页数据 | ||||
|             var rows = (fs.Query(useHeaderRow: true, sheetName: sheetName)).Cast<IDictionary<string, object>>(); | ||||
|             var rows = (stream.Query(useHeaderRow: true, sheetName: sheetName)).Cast<IDictionary<string, object>>(); | ||||
|             #region 上传设备sheet | ||||
|             if (sheetName == ExportHelpers.UploadDeviceSheetName) | ||||
|             { | ||||
| @@ -434,7 +439,7 @@ public class UploadDeviceService : DbRepository<UploadDevice>, IUploadDeviceServ | ||||
|  | ||||
|  | ||||
|  | ||||
|         return ImportPreviews; | ||||
|         return Task.FromResult(ImportPreviews); | ||||
|     } | ||||
|     #endregion | ||||
| } | ||||
| @@ -55,7 +55,7 @@ public interface IVariableService : ITransient | ||||
|     /// <summary> | ||||
|     /// 导出 | ||||
|     /// </summary> | ||||
|     Task<MemoryStream> ExportFileAsync(List<DeviceVariable> collectDeviceVariables = null); | ||||
|     Task<MemoryStream> ExportFileAsync(List<DeviceVariable> collectDeviceVariables = null, string deviceName = null); | ||||
|     /// <summary> | ||||
|     /// 导出 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -129,11 +129,11 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|         { | ||||
|             uploadDevid = _uploadDeviceService.GetIdByName(input.UploadDeviceName); | ||||
|         } | ||||
|  | ||||
|         if (!string.IsNullOrEmpty(input.UploadDeviceName)) | ||||
|         { | ||||
|             var pageInfo = await query.ToPagedListAsync(input.Current, input.Size, a => a.VariablePropertys.ContainsKey(uploadDevid ?? 0));//分页 | ||||
|             var pageInfo = await query.ToPagedListAsync(input.Current, input.Size, a => SqlFunc.JsonLike(a.VariablePropertys, uploadDevid.ToString()));//分页 | ||||
|             return pageInfo; | ||||
|  | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -313,12 +313,13 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     [OperDesc("导出采集变量表", IsRecordPar = false)] | ||||
|     public async Task<MemoryStream> ExportFileAsync(List<DeviceVariable> deviceVariables = null) | ||||
|     public async Task<MemoryStream> ExportFileAsync(List<DeviceVariable> deviceVariables = null, string deviceName = null) | ||||
|     { | ||||
|         deviceVariables ??= await GetListAsync(a => !a.IsMemoryVariable); | ||||
|  | ||||
| @@ -343,7 +344,8 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|            ); | ||||
|        Dictionary<string, object> variableExport = new(); | ||||
|        //变量实体没有包含设备名称,手动插入 | ||||
|        variableExport.Add(ExportHelpers.DeviceName, collectDeviceDicts[devData.DeviceId].Name); | ||||
|        var devName = collectDeviceDicts.GetValueOrDefault(devData.DeviceId)?.Name ?? deviceName; | ||||
|        variableExport.Add(ExportHelpers.DeviceName, devName); | ||||
|  | ||||
|        foreach (var item in data) | ||||
|        { | ||||
| @@ -405,6 +407,7 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|  | ||||
|         var memoryStream = new MemoryStream(); | ||||
|         await memoryStream.SaveAsAsync(sheets); | ||||
|         memoryStream.Seek(0, SeekOrigin.Begin); | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
| @@ -445,7 +448,7 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|                    try | ||||
|                    { | ||||
|                        var variable = ((ExpandoObject)item).ConvertToEntity<DeviceVariable>(true); | ||||
|  | ||||
|                        variable.IsMemoryVariable = true; | ||||
|                        //变量ID都需要手动补录 | ||||
|                        variables.Add(variable); | ||||
|                        if (dbVariableDicts.TryGetValue(variable.Name, out var dbvar1)) | ||||
| @@ -570,15 +573,21 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|         return ImportPreviews; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file) | ||||
|     { | ||||
|         _fileService.ImportVerification(file); | ||||
|         using var fs = new MemoryStream(); | ||||
|         using var stream = file.OpenReadStream(512000000); | ||||
|         await stream.CopyToAsync(fs); | ||||
|         var sheetNames = MiniExcel.GetSheetNames(fs); | ||||
|         using var stream = new MemoryStream(); | ||||
|         using var fs = file.OpenReadStream(512000000); | ||||
|         await fs.CopyToAsync(stream); | ||||
|         return await PreviewAsync(stream); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(MemoryStream stream, List<CollectDevice> memCollectDevices = null, List<UploadDevice> memUploadDevices = null) | ||||
|     { | ||||
|  | ||||
|         var sheetNames = MiniExcel.GetSheetNames(stream); | ||||
|  | ||||
|         var dbVariables = await Context.Queryable<DeviceVariable>().Select(it => new { it.Id, it.Name }).ToListAsync(); | ||||
|         //转为字典,提高查找效率 | ||||
| @@ -590,7 +599,7 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|         foreach (var sheetName in sheetNames) | ||||
|         { | ||||
|             //单页数据 | ||||
|             var rows = fs.Query(useHeaderRow: true, sheetName: sheetName, configuration: new OpenXmlConfiguration { EnableSharedStringCache = false }) | ||||
|             var rows = stream.Query(useHeaderRow: true, sheetName: sheetName, configuration: new OpenXmlConfiguration { EnableSharedStringCache = false }) | ||||
|                 .Cast<IDictionary<string, object>>(); | ||||
|  | ||||
|             if (sheetName == ExportHelpers.DeviceVariableSheetName) | ||||
| @@ -599,7 +608,10 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|                 ImportPreviewOutput<DeviceVariable> importPreviewOutput = new(); | ||||
|                 ImportPreviews.Add(sheetName, importPreviewOutput); | ||||
|                 deviceImportPreview = importPreviewOutput; | ||||
|                 var cacheDeviceDicts = _collectDeviceService.GetCacheList(false).ToDictionary(a => a.Name); | ||||
|  | ||||
|                 var cacheDeviceDicts = memCollectDevices == null ? _collectDeviceService.GetCacheList(false).ToDictionary(a => a.Name) : | ||||
|                     _collectDeviceService.GetCacheList(false).Concat(memCollectDevices).ToDictionary(a => a.Name) | ||||
|                     ; | ||||
|                 //线程安全 | ||||
|                 var variables = new ConcurrentList<DeviceVariable>(); | ||||
|                 //并行注意线程安全 | ||||
| @@ -678,7 +690,9 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService | ||||
|                         .Where(a => a.GetCustomAttribute<VariablePropertyAttribute>() != null) | ||||
|                         .ToDictionary(a => a.FindDisplayAttribute(a => a.GetCustomAttribute<VariablePropertyAttribute>()?.Description)); | ||||
|  | ||||
|                     var cacheUpdeviceDicts = _uploadDeviceService.GetCacheList(false).ToDictionary(a => a.Name); | ||||
|                     var cacheUpdeviceDicts = memUploadDevices == null ? _uploadDeviceService.GetCacheList(false).ToDictionary(a => a.Name) : | ||||
|                     _uploadDeviceService.GetCacheList(false).Concat(memUploadDevices).ToDictionary(a => a.Name) | ||||
|                         ; | ||||
|                     rows.ParallelForEach(item => | ||||
|                        { | ||||
|                            try | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user