Compare commits
117 Commits
10.11.27.0
...
10.12.27.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5deef89b96 | ||
|
|
ce5fa0f37d | ||
|
|
694437c7d5 | ||
|
|
0e78cdefe7 | ||
|
|
087dc9aaa3 | ||
|
|
dacf255f1a | ||
|
|
e3960ce115 | ||
|
|
0cf098be85 | ||
|
|
c93c80468c | ||
|
|
a464594885 | ||
|
|
b42f8afa35 | ||
|
|
facf8bd401 | ||
|
|
feeb17eca3 | ||
|
|
b3405cd674 | ||
|
|
c35f9cef93 | ||
|
|
3f382202db | ||
|
|
2a3493cc82 | ||
|
|
aaa459ebe0 | ||
|
|
51a8acbc3e | ||
|
|
dc132a1999 | ||
|
|
0c31cfcbc5 | ||
|
|
0e44bc67cd | ||
|
|
12dfbba42c | ||
|
|
96b4287f3a | ||
|
|
7d406de29f | ||
|
|
81f0ef466a | ||
|
|
3f2d6b133c | ||
|
|
e776dc67eb | ||
|
|
bc5827d140 | ||
|
|
21838bf4af | ||
|
|
6090108597 | ||
|
|
b47b9e6f43 | ||
|
|
18d1cffb2d | ||
|
|
516fd7f235 | ||
|
|
2ee16c3533 | ||
|
|
7d22f5c78e | ||
|
|
3e604ee2fd | ||
|
|
47e442874c | ||
|
|
2a8c0cbab1 | ||
|
|
c26898b49d | ||
|
|
00c24d06a3 | ||
|
|
3461f34240 | ||
|
|
aa1ce08c02 | ||
|
|
9c230c2da9 | ||
|
|
21215d0379 | ||
|
|
7448183791 | ||
|
|
35edd7dc43 | ||
|
|
bd178831e3 | ||
|
|
fe9ec6ad10 | ||
|
|
6f9ec2e24b | ||
|
|
c0337e2b19 | ||
|
|
8a95f48f5a | ||
|
|
14f3c31265 | ||
|
|
1bad65378f | ||
|
|
db3affc67e | ||
|
|
5ee8b50a92 | ||
|
|
301beda2a2 | ||
|
|
628b51a353 | ||
|
|
f03445bc83 | ||
|
|
55a2ff5487 | ||
|
|
0fef7dcf3b | ||
|
|
19d9702606 | ||
|
|
a8a9774932 | ||
|
|
aad0f0e8c3 | ||
|
|
e74eae50a7 | ||
|
|
3b16d7019f | ||
|
|
3e038028c2 | ||
|
|
b1d8041f7e | ||
|
|
53a98b26cd | ||
|
|
42c740fa1b | ||
|
|
556819c90c | ||
|
|
2522333a9c | ||
|
|
bd4ce7c09b | ||
|
|
156ed88bd6 | ||
|
|
2416226eb0 | ||
|
|
976323a716 | ||
|
|
3c9e397403 | ||
|
|
79406ad4a0 | ||
|
|
20c44f10ca | ||
|
|
31d6b2a9e6 | ||
|
|
68e5a9c546 | ||
|
|
b2ea9f99b9 | ||
|
|
6d7d0e468a | ||
|
|
ff1f632de2 | ||
|
|
fc3d7015ee | ||
|
|
40c5acb522 | ||
|
|
f9cc1cbb05 | ||
|
|
cf6e8b58f0 | ||
|
|
615e3bb24c | ||
|
|
4a7534b210 | ||
|
|
58e099cb93 | ||
|
|
a94a9c953c | ||
|
|
35e1ffa3e9 | ||
|
|
4921642151 | ||
|
|
d71ee29da8 | ||
|
|
901aa2d59f | ||
|
|
764957c014 | ||
|
|
0e3898218b | ||
|
|
61f13cef3c | ||
|
|
0b663d9e01 | ||
|
|
6c95c6209f | ||
|
|
4d223d2622 | ||
|
|
e8d7e91b64 | ||
|
|
8175f541ec | ||
|
|
0adbdb926b | ||
|
|
42adee9980 | ||
|
|
427a7404bc | ||
|
|
3658199e0a | ||
|
|
82eedee50a | ||
|
|
6a18fc3e06 | ||
|
|
c37e314ed6 | ||
|
|
a937a85d90 | ||
|
|
35dd4ae9d3 | ||
|
|
0b829ac85c | ||
|
|
aa247422d2 | ||
|
|
2e00e8c135 | ||
|
|
34dd2cf0a7 |
3
.gitignore
vendored
@@ -364,8 +364,5 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
/src/*Pro*/
|
/src/*Pro*/
|
||||||
/src/*Pro*
|
/src/*Pro*
|
||||||
/src/**/*Pro*
|
|
||||||
/src/*pro*
|
|
||||||
/src/*pro*/
|
|
||||||
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
||||||
/src/.idea/
|
/src/.idea/
|
||||||
|
|||||||
55
README.md
@@ -1,35 +1,44 @@
|
|||||||
# ThingsGateway
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="logo.svg" width = "400" height = "200" alt="The name of the image" align=center />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[](https://gitee.com/ThingsGateway/ThingsGateway/stargazers)
|
||||||
|
[](https://github.com/ThingsGateway/ThingsGateway)
|
||||||
|
[](https://deepwiki.com/ThingsGateway/ThingsGateway)
|
||||||
|
[](https://www.nuget.org/packages/ThingsGateway.Foundation/)
|
||||||
|
[](https://www.nuget.org/packages/ThingsGateway.Foundation/)
|
||||||
|
[](https://thingsgateway.cn/docs/1)
|
||||||
|
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569">
|
||||||
|
<img src="https://img.shields.io/badge/QQ群-605534569-red" alt="QQ">
|
||||||
|
</a>
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
|
A cross-platform, high-performance edge data collection gateway based on net8/10.
|
||||||
A cross-platform, high-performance edge data collection gateway based on net9.
|
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|
||||||
[Documentation](https://thingsgateway.cn/).
|
[Documentation](https://thingsgateway.cn/).
|
||||||
|
|
||||||
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
|
||||||
[Demo](https://demo.thingsgateway.cn/)
|
[Demo](https://demo.thingsgateway.cn/)
|
||||||
|
|
||||||
|
|
||||||
Account: **SuperAdmin**
|
Account: **SuperAdmin**
|
||||||
|
|
||||||
|
|
||||||
Password: **111111**
|
Password: **111111**
|
||||||
|
|
||||||
|
|
||||||
**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
|
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
@@ -44,7 +53,7 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
|
|||||||
|
|
||||||
### Plugin List
|
### Plugin List
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Data Collection Plugins
|
#### Data Collection Plugins
|
||||||
|
|
||||||
@@ -74,28 +83,28 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
|
|||||||
| TDengineDB | Time-series database storage |
|
| TDengineDB | Time-series database storage |
|
||||||
| QuestDB | Time-series database storage |
|
| QuestDB | Time-series database storage |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
||||||
[License](https://thingsgateway.cn/docs/1)
|
[License](https://thingsgateway.cn/docs/1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Sponsorship
|
## Sponsorship
|
||||||
|
|
||||||
|
|
||||||
[Sponsorship Approach](https://thingsgateway.cn/docs/1000)
|
[Sponsorship Approach](https://thingsgateway.cn/docs/1000)
|
||||||
|
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
|
|
||||||
QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
|
QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
|
||||||
|
|
||||||
|
|
||||||
## Pro Plugins
|
## Pro Plugins
|
||||||
|
|
||||||
|
|
||||||
[Plugin List](https://thingsgateway.cn/docs/1001)
|
[Plugin List](https://thingsgateway.cn/docs/1001)
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
# ThingsGateway
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="logo.svg" width = "400" height = "200" alt="The name of the image" align=center />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[](https://gitee.com/ThingsGateway/ThingsGateway/stargazers)
|
||||||
|
[](https://github.com/ThingsGateway/ThingsGateway)
|
||||||
|
[](https://deepwiki.com/ThingsGateway/ThingsGateway)
|
||||||
|
[](https://www.nuget.org/packages/ThingsGateway.Foundation/)
|
||||||
|
[](https://www.nuget.org/packages/ThingsGateway.Foundation/)
|
||||||
|
[](https://thingsgateway.cn/docs/1)
|
||||||
|
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569">
|
||||||
|
<img src="https://img.shields.io/badge/QQ群-605534569-red" alt="QQ">
|
||||||
|
</a>
|
||||||
|
|
||||||
## 介绍
|
## 介绍
|
||||||
|
|
||||||
基于net9的跨平台高性能边缘采集网关
|
基于net8/10的跨平台高性能边缘采集网关
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
@@ -19,7 +32,6 @@
|
|||||||
|
|
||||||
密码 : **111111**
|
密码 : **111111**
|
||||||
|
|
||||||
**右上角个人弹出框中,切换到物联网关模块**
|
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
|
|||||||
BIN
icon.ico
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
BIN
icon.png
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 177 KiB |
9
logo.svg
Normal file
|
After Width: | Height: | Size: 236 KiB |
@@ -45,6 +45,7 @@ public class VerificatInfo : PrimaryIdEntity
|
|||||||
/// 登录IP
|
/// 登录IP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)]
|
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)]
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public string LoginIp { get; set; }
|
public string LoginIp { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -78,5 +79,6 @@ public class VerificatInfo : PrimaryIdEntity
|
|||||||
/// 登录设备
|
/// 登录设备
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 100)]
|
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 100)]
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public string Device { get; set; }
|
public string Device { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,10 +251,12 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
|
|||||||
|
|
||||||
if (exception == null)
|
if (exception == null)
|
||||||
{
|
{
|
||||||
|
if (logger.IsEnabled(LogLevel.Information))
|
||||||
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}");
|
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (logger.IsEnabled(LogLevel.Warning))
|
||||||
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}");
|
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using System.ComponentModel;
|
||||||
|
using System.Runtime;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
@@ -20,10 +21,6 @@ public class HardwareInfo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DriveInfo DriveInfo { get; set; }
|
public DriveInfo DriveInfo { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 硬件信息获取
|
|
||||||
/// </summary>
|
|
||||||
public MachineInfo? MachineInfo { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主机环境
|
/// 主机环境
|
||||||
@@ -40,19 +37,118 @@ public class HardwareInfo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string OsArchitecture { get; set; }
|
public string OsArchitecture { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 唯一编码
|
|
||||||
/// </summary>
|
|
||||||
public string UUID { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>系统名称</summary>
|
||||||
/// 进程占用内存
|
public String OSName { get; set; }
|
||||||
/// </summary>
|
|
||||||
[AutoGenerateColumn(Ignore = true)]
|
/// <summary>系统版本</summary>
|
||||||
public int WorkingSet { get; set; }
|
public String OSVersion { get; set; }
|
||||||
|
public String UUID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>内存总量。单位MB</summary>
|
||||||
|
public UInt64 Memory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>可用内存。单位MB</summary>
|
||||||
|
public UInt64 AvailableMemory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>CPU占用率</summary>
|
||||||
|
public Double CpuRate { get; set; }
|
||||||
|
public Double Battery { get; set; }
|
||||||
|
public Double Temperature { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>处理器型号</summary>
|
||||||
|
public String? Processor { get; set; }
|
||||||
|
#region GC与进程内存信息
|
||||||
|
|
||||||
|
/// <summary>GC 认为“内存吃紧”的阈值。单位:MB</summary>
|
||||||
|
[DisplayName("GC高内存阈值")]
|
||||||
|
public UInt64 HighMemoryLoadThreshold { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC 可用内存上限。单位:MB</summary>
|
||||||
|
[DisplayName("GC可用内存上限")]
|
||||||
|
public UInt64 TotalAvailableMemory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>当前托管堆容量。单位:MB</summary>
|
||||||
|
[DisplayName("托管堆容量")]
|
||||||
|
public UInt64 HeapSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>托管堆已用内存。单位:MB</summary>
|
||||||
|
[DisplayName("托管堆已用")]
|
||||||
|
public UInt64 TotalMemory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>托管堆碎片大小。单位:MB</summary>
|
||||||
|
[DisplayName("托管堆碎片")]
|
||||||
|
public UInt64 FragmentedBytes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC识别可用内存。单位:MB</summary>
|
||||||
|
[DisplayName("GC识别可用内存")]
|
||||||
|
public UInt64 GCAvailableMemory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC 已提交的内存。单位:MB</summary>
|
||||||
|
[DisplayName("GC已提交内存")]
|
||||||
|
public UInt64 CommittedBytes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC 累计分配的托管内存。单位:MB</summary>
|
||||||
|
[DisplayName("GC累计分配")]
|
||||||
|
public UInt64 TotalAllocatedBytes { get; set; }
|
||||||
|
/// <summary>GC 暂停累计时间。单位:毫秒</summary>
|
||||||
|
[DisplayName("GC累计暂停时间")]
|
||||||
|
public UInt64 TotalPauseDurationMs { get; set; }
|
||||||
|
/// <summary>GC 代0收集次数</summary>
|
||||||
|
[DisplayName("GC Gen0 次数")]
|
||||||
|
public Int32 GcGen0Count { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC 代1收集次数</summary>
|
||||||
|
[DisplayName("GC Gen1 次数")]
|
||||||
|
public Int32 GcGen1Count { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC 代2收集次数</summary>
|
||||||
|
[DisplayName("GC Gen2 次数")]
|
||||||
|
public Int32 GcGen2Count { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Server GC 是否启用</summary>
|
||||||
|
[DisplayName("是否使用Server GC")]
|
||||||
|
public Boolean IsServerGC { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC 延迟模式</summary>
|
||||||
|
[DisplayName("GC延迟模式")]
|
||||||
|
public GCLatencyMode? GCLatencyMode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>GC 固定对象数</summary>
|
||||||
|
[DisplayName("固定对象数")]
|
||||||
|
public Int64 PinnedObjectsCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>终结队列挂起对象数</summary>
|
||||||
|
[DisplayName("终结挂起数")]
|
||||||
|
public Int64 FinalizationPendingCount { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 进程内存信息
|
||||||
|
|
||||||
|
/// <summary>进程虚拟内存使用量。单位:MB</summary>
|
||||||
|
[DisplayName("虚拟内存")]
|
||||||
|
public UInt64 VirtualMemory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>进程私有内存使用量。单位:MB</summary>
|
||||||
|
[DisplayName("私有内存")]
|
||||||
|
public UInt64 PrivateMemory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>进程峰值工作集。单位:MB</summary>
|
||||||
|
[DisplayName("峰值工作集")]
|
||||||
|
public UInt64 PeakWorkingSet { get; set; }
|
||||||
|
|
||||||
|
/// <summary>进程当前工作集。单位:MB</summary>
|
||||||
|
[DisplayName("当前工作集")]
|
||||||
|
public UInt64 WorkingSet { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新时间
|
/// 更新时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string UpdateTime { get; set; }
|
public DateTime UpdateTime { get; set; }
|
||||||
|
public ulong AppRunTotalMinute { get; set; }
|
||||||
|
public ulong SystemRunTotalMinute { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using ThingsGateway.Extension;
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
using ThingsGateway.NewLife.Caching;
|
using ThingsGateway.NewLife.Caching;
|
||||||
using ThingsGateway.NewLife.Threading;
|
using ThingsGateway.NewLife.Threading;
|
||||||
@@ -43,7 +41,7 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 运行信息获取
|
/// 运行信息获取
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HardwareInfo HardwareInfo { get; } = new();
|
public HardwareInfo HardwareInfo { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public HardwareInfoOptions HardwareInfoOptions { get; private set; }
|
public HardwareInfoOptions HardwareInfoOptions { get; private set; }
|
||||||
@@ -76,9 +74,10 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (HardwareInfo.MachineInfo == null)
|
var machine = MachineInfo.GetCurrent();
|
||||||
|
if (HardwareInfo == null)
|
||||||
{
|
{
|
||||||
HardwareInfo.MachineInfo = MachineInfo.GetCurrent();
|
HardwareInfo = machine.AdaptHardwareInfo();
|
||||||
|
|
||||||
string currentPath = Directory.GetCurrentDirectory();
|
string currentPath = Directory.GetCurrentDirectory();
|
||||||
DriveInfo drive = new(Path.GetPathRoot(currentPath));
|
DriveInfo drive = new(Path.GetPathRoot(currentPath));
|
||||||
@@ -88,10 +87,9 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
HardwareInfo.DriveInfo = drive;
|
HardwareInfo.DriveInfo = drive;
|
||||||
HardwareInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构
|
HardwareInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构
|
||||||
HardwareInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架
|
HardwareInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架
|
||||||
HardwareInfo.Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production";
|
HardwareInfo.Environment = App.HostEnvironment.EnvironmentName;
|
||||||
HardwareInfo.UUID = HardwareInfo.MachineInfo.UUID;
|
|
||||||
|
|
||||||
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
|
HardwareInfo.UpdateTime = TimerX.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -99,9 +97,12 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HardwareInfo.MachineInfo.Refresh();
|
var machine = MachineInfo.GetCurrent();
|
||||||
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
|
machine.Refresh();
|
||||||
HardwareInfo.WorkingSet = (Environment.WorkingSet / 1024.0 / 1024.0).ToInt();
|
machine.AdaptHardwareInfo(HardwareInfo);
|
||||||
|
HardwareInfo.AppRunTotalMinute = (ulong)Runtime.AppTickCount64 / 1000 / 60;
|
||||||
|
HardwareInfo.SystemRunTotalMinute = (ulong)Runtime.TickCount64 / 1000 / 60;
|
||||||
|
HardwareInfo.UpdateTime = TimerX.Now;
|
||||||
error = false;
|
error = false;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -123,10 +124,10 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
{
|
{
|
||||||
Date = TimerX.Now,
|
Date = TimerX.Now,
|
||||||
DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(),
|
DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(),
|
||||||
Battery = (HardwareInfo.MachineInfo.Battery * 100).ToInt(),
|
Battery = (HardwareInfo.Battery * 100).ToInt(),
|
||||||
MemoryUsage = (HardwareInfo.WorkingSet),
|
MemoryUsage = (HardwareInfo.WorkingSet),
|
||||||
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(),
|
CpuUsage = (HardwareInfo.CpuRate * 100).ToInt(),
|
||||||
Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(),
|
Temperature = (HardwareInfo.Temperature).ToInt(),
|
||||||
};
|
};
|
||||||
await _db.InsertableT(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
await _db.InsertableT(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
||||||
MemoryCache.Remove(CacheKey);
|
MemoryCache.Remove(CacheKey);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class HistoryHardwareInfo
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "内存")]
|
[SugarColumn(ColumnDescription = "内存")]
|
||||||
public int MemoryUsage { get; set; }
|
public UInt64 MemoryUsage { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "CPU使用率")]
|
[SugarColumn(ColumnDescription = "CPU使用率")]
|
||||||
|
|||||||
@@ -46,12 +46,44 @@
|
|||||||
"FileTypeError": "Not supported format {0}"
|
"FileTypeError": "Not supported format {0}"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||||
"Environment": "HostEnvironment",
|
"DriveInfo": "Current Disk Info",
|
||||||
"FrameworkDescription": ".NETFramework",
|
"AppRunTotalMinute": "AppRunTotalMinute(min)",
|
||||||
"OsArchitecture": "System Architecture",
|
"SystemRunTotalMinute": "SystemRunTotalMinute(min)",
|
||||||
"UpdateTime": "UpdateTime",
|
"Environment": "Host Environment",
|
||||||
"UUID": "UUID"
|
"FrameworkDescription": ".NET Framework",
|
||||||
|
"OsArchitecture": "OS Architecture",
|
||||||
|
"OSName": "OS Name",
|
||||||
|
"OSVersion": "OS Version",
|
||||||
|
"UUID": "UUID",
|
||||||
|
"Memory": "Total Memory",
|
||||||
|
"AvailableMemory": "Available Memory",
|
||||||
|
"CpuRate": "CPU Usage",
|
||||||
|
"Battery": "Battery Level",
|
||||||
|
"Temperature": "Temperature",
|
||||||
|
"Processor": "Processor Model",
|
||||||
|
"HighMemoryLoadThreshold": "GC High Memory Threshold",
|
||||||
|
"TotalAvailableMemory": "GC Total Available Memory",
|
||||||
|
"HeapSize": "Managed Heap Size",
|
||||||
|
"TotalMemory": "Managed Heap Used",
|
||||||
|
"FragmentedBytes": "Managed Heap Fragmented",
|
||||||
|
"GCAvailableMemory": "GC Available Memory",
|
||||||
|
"CommittedBytes": "GC Committed Bytes",
|
||||||
|
"TotalAllocatedBytes": "GC Total Allocated (MB)",
|
||||||
|
"TotalPauseDurationMs": "GC Total Pause Duration (ms)",
|
||||||
|
"GcGen0Count": "GC Gen0 Count",
|
||||||
|
"GcGen1Count": "GC Gen1 Count",
|
||||||
|
"GcGen2Count": "GC Gen2 Count",
|
||||||
|
"IsServerGC": "Server GC",
|
||||||
|
"GCLatencyMode": "GC Latency Mode",
|
||||||
|
"PinnedObjectsCount": "Pinned Objects Count",
|
||||||
|
"FinalizationPendingCount": "Finalization Pending Count",
|
||||||
|
"VirtualMemory": "Virtual Memory",
|
||||||
|
"PrivateMemory": "Private Memory",
|
||||||
|
"PeakWorkingSet": "Peak Working Set",
|
||||||
|
"WorkingSet": "Current Working Set",
|
||||||
|
"UpdateTime": "Update Time"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.HardwareJob": {
|
"ThingsGateway.Admin.Application.HardwareJob": {
|
||||||
"GetHardwareInfoFail": "Get Hardwareinfo Fail"
|
"GetHardwareInfoFail": "Get Hardwareinfo Fail"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -46,11 +46,42 @@
|
|||||||
"FileTypeError": "不支持 {0} 格式"
|
"FileTypeError": "不支持 {0} 格式"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||||
|
"DriveInfo": "当前磁盘信息",
|
||||||
|
"AppRunTotalMinute": "软件运行时长(min)",
|
||||||
|
"SystemRunTotalMinute": "系统运行时长(min)",
|
||||||
"Environment": "主机环境",
|
"Environment": "主机环境",
|
||||||
"FrameworkDescription": "NET框架",
|
"FrameworkDescription": ".NET 框架",
|
||||||
"OsArchitecture": "系统架构",
|
"OsArchitecture": "系统架构",
|
||||||
"UpdateTime": "更新时间",
|
"OSName": "系统名称",
|
||||||
"UUID": "唯一编码"
|
"OSVersion": "系统版本",
|
||||||
|
"UUID": "UUID",
|
||||||
|
"Memory": "系统总内存",
|
||||||
|
"AvailableMemory": "系统可用内存",
|
||||||
|
"CpuRate": "CPU 占用率",
|
||||||
|
"Battery": "电池电量",
|
||||||
|
"Temperature": "温度",
|
||||||
|
"Processor": "处理器型号",
|
||||||
|
"HighMemoryLoadThreshold": "GC 高内存阈值",
|
||||||
|
"TotalAvailableMemory": "GC 总内存阈值",
|
||||||
|
"HeapSize": "托管堆容量",
|
||||||
|
"TotalMemory": "托管对象占用",
|
||||||
|
"FragmentedBytes": "托管堆碎片",
|
||||||
|
"GCAvailableMemory": "GC 可用内存",
|
||||||
|
"CommittedBytes": "GC 提交内存总量",
|
||||||
|
"TotalAllocatedBytes": "GC 累计分配(MB)",
|
||||||
|
"TotalPauseDurationMs": "GC 累计暂停时间(ms)",
|
||||||
|
"GcGen0Count": "GC Gen0 次数",
|
||||||
|
"GcGen1Count": "GC Gen1 次数",
|
||||||
|
"GcGen2Count": "GC Gen2 次数",
|
||||||
|
"IsServerGC": "Server GC",
|
||||||
|
"GCLatencyMode": "GC 延迟模式",
|
||||||
|
"PinnedObjectsCount": "固定对象数",
|
||||||
|
"FinalizationPendingCount": "终结挂起数",
|
||||||
|
"VirtualMemory": "虚拟内存",
|
||||||
|
"PrivateMemory": "私有内存",
|
||||||
|
"PeakWorkingSet": "峰值工作集",
|
||||||
|
"WorkingSet": "当前工作集",
|
||||||
|
"UpdateTime": "更新时间"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.HardwareJob": {
|
"ThingsGateway.Admin.Application.HardwareJob": {
|
||||||
"GetHardwareInfoFail": "获取硬件信息出错"
|
"GetHardwareInfoFail": "获取硬件信息出错"
|
||||||
|
|||||||
@@ -10,10 +10,16 @@
|
|||||||
|
|
||||||
using Riok.Mapperly.Abstractions;
|
using Riok.Mapperly.Abstractions;
|
||||||
|
|
||||||
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
|
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
|
||||||
public static partial class AdminMapper
|
public static partial class AdminMapper
|
||||||
{
|
{
|
||||||
|
public static partial HardwareInfo AdaptHardwareInfo(this MachineInfo src);
|
||||||
|
public static partial void AdaptHardwareInfo(this MachineInfo src, HardwareInfo dto);
|
||||||
|
|
||||||
public static partial LoginInput AdaptLoginInput(this OpenApiLoginInput src);
|
public static partial LoginInput AdaptLoginInput(this OpenApiLoginInput src);
|
||||||
public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src);
|
public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src);
|
||||||
public static partial SessionOutput AdaptSessionOutput(this SysUser src);
|
public static partial SessionOutput AdaptSessionOutput(this SysUser src);
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ public class AdminOAuthHandler<TOptions>(
|
|||||||
var loginEvent = new LoginEvent
|
var loginEvent = new LoginEvent
|
||||||
{
|
{
|
||||||
Ip = appService.RemoteIpAddress,
|
Ip = appService.RemoteIpAddress,
|
||||||
Device = appService.UserAgent?.Platform,
|
Device = appService.UserAgent?.Platform ?? "Unknown",
|
||||||
Expire = expire,
|
Expire = expire,
|
||||||
SysUser = sysUser,
|
SysUser = sysUser,
|
||||||
VerificatId = CommonUtils.GetSingleId()
|
VerificatId = CommonUtils.GetSingleId()
|
||||||
@@ -156,7 +156,7 @@ public class AdminOAuthHandler<TOptions>(
|
|||||||
//生成verificat信息
|
//生成verificat信息
|
||||||
var verificatInfo = new VerificatInfo
|
var verificatInfo = new VerificatInfo
|
||||||
{
|
{
|
||||||
Device = loginEvent.Device,
|
Device = loginEvent.Device ?? "Unknown",
|
||||||
Expire = loginEvent.Expire,
|
Expire = loginEvent.Expire,
|
||||||
VerificatTimeout = tokenTimeout,
|
VerificatTimeout = tokenTimeout,
|
||||||
Id = loginEvent.VerificatId,
|
Id = loginEvent.VerificatId,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"Module": 2,
|
"Module": 2,
|
||||||
"Title": "权限管理",
|
"Title": "权限管理",
|
||||||
"Code": "System",
|
"Code": "System",
|
||||||
"NavLinkMatch": "All",
|
"NavLinkMatch": "Prefix",
|
||||||
"Category": "MENU",
|
"Category": "MENU",
|
||||||
"Target": "_self",
|
"Target": "_self",
|
||||||
"Href": null,
|
"Href": null,
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"ParentId": 0,
|
"ParentId": 0,
|
||||||
"Module": 2,
|
"Module": 2,
|
||||||
"Title": "系统运维",
|
"Title": "系统运维",
|
||||||
"NavLinkMatch": "All",
|
"NavLinkMatch": "Prefix",
|
||||||
"Code": "System",
|
"Code": "System",
|
||||||
"Category": "MENU",
|
"Category": "MENU",
|
||||||
"Target": "_self",
|
"Target": "_self",
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ public class AuthService : IAuthService
|
|||||||
var logingEvent = new LoginEvent
|
var logingEvent = new LoginEvent
|
||||||
{
|
{
|
||||||
Ip = _appService.RemoteIpAddress,
|
Ip = _appService.RemoteIpAddress,
|
||||||
Device = _appService.UserAgent?.Platform,
|
Device = _appService.UserAgent?.Platform ?? "Unknown",
|
||||||
Expire = expire,
|
Expire = expire,
|
||||||
SysUser = sysUser,
|
SysUser = sysUser,
|
||||||
VerificatId = verificatId
|
VerificatId = verificatId
|
||||||
@@ -344,7 +344,7 @@ public class AuthService : IAuthService
|
|||||||
//生成verificat信息
|
//生成verificat信息
|
||||||
var verificatInfo = new VerificatInfo
|
var verificatInfo = new VerificatInfo
|
||||||
{
|
{
|
||||||
Device = loginEvent.Device,
|
Device = loginEvent.Device ?? "Unknown",
|
||||||
Expire = loginEvent.Expire,
|
Expire = loginEvent.Expire,
|
||||||
VerificatTimeout = tokenTimeout,
|
VerificatTimeout = tokenTimeout,
|
||||||
Id = loginEvent.VerificatId,
|
Id = loginEvent.VerificatId,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// <typeparam name="TEntry"></typeparam>
|
/// <typeparam name="TEntry"></typeparam>
|
||||||
public class EventService<TEntry> : IEventService<TEntry>, IDisposable
|
public class EventService<TEntry> : IEventService<TEntry>, IDisposable
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<string, Func<TEntry, Task>> Cache = new();
|
private NonBlockingDictionary<string, Func<TEntry, Task>> Cache = new();
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
internal sealed class NoticeService : INoticeService
|
internal sealed class NoticeService : INoticeService
|
||||||
{
|
{
|
||||||
private IEventService<AppMessage>? MessageDispatchService { get; set; }
|
private IEventService<AppMessage>? MessageDispatchService { get; set; }
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
if (sysRole != null)
|
if (sysRole != null)
|
||||||
{
|
{
|
||||||
var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
|
var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
|
||||||
var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu).Where(a => menuIds.Contains(a.Id));
|
var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu && menuIds.Contains(a.Id));
|
||||||
|
|
||||||
#region 角色模块处理
|
#region 角色模块处理
|
||||||
|
|
||||||
|
|||||||
@@ -550,7 +550,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
if (sysUser != null)
|
if (sysUser != null)
|
||||||
{
|
{
|
||||||
var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
|
var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
|
||||||
var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu).Where(a => menuIds.Contains(a.Id));
|
var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu && menuIds.Contains(a.Id));
|
||||||
|
|
||||||
#region 用户模块处理
|
#region 用户模块处理
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
|
|||||||
public void Add(VerificatInfo verificatInfo)
|
public void Add(VerificatInfo verificatInfo)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
db.Insertable<VerificatInfo>(verificatInfo).ExecuteCommand();
|
db.InsertableT<VerificatInfo>(verificatInfo).ExecuteCommand();
|
||||||
VerificatInfoService.RemoveCache(verificatInfo.Id);
|
VerificatInfoService.RemoveCache(verificatInfo.Id);
|
||||||
if (verificatInfo != null)
|
if (verificatInfo != null)
|
||||||
VerificatInfoService.SetCahce(verificatInfo);
|
VerificatInfoService.SetCahce(verificatInfo);
|
||||||
@@ -132,7 +132,7 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
|
|||||||
public void Update(VerificatInfo verificatInfo)
|
public void Update(VerificatInfo verificatInfo)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
db.Updateable<VerificatInfo>(verificatInfo).ExecuteCommand();
|
db.UpdateableT<VerificatInfo>(verificatInfo).ExecuteCommand();
|
||||||
VerificatInfoService.RemoveCache(verificatInfo.Id);
|
VerificatInfoService.RemoveCache(verificatInfo.Id);
|
||||||
if (verificatInfo != null)
|
if (verificatInfo != null)
|
||||||
VerificatInfoService.SetCahce(verificatInfo);
|
VerificatInfoService.SetCahce(verificatInfo);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -19,19 +19,21 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
<PackageReference Include="Riok.Mapperly" Version="4.3.0" ExcludeAssets="runtime" PrivateAssets="all">
|
||||||
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
||||||
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
|
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
|
||||||
<PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" />
|
<PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" />
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net10.0' ">
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(NET10Version)" />
|
||||||
<PackageReference Include="System.Formats.Asn1" Version="$(NET9Version)" />
|
<PackageReference Include="System.Formats.Asn1" Version="$(NET10Version)" />
|
||||||
<PackageReference Include="System.Threading.RateLimiting" Version="$(NET9Version)" />
|
<PackageReference Include="System.Threading.RateLimiting" Version="$(NET10Version)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="SeedData\Admin\*.json" />
|
<Content Remove="SeedData\Admin\*.json" />
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
<div class="tg-table h-100">
|
<div class="tg-table h-100">
|
||||||
|
|
||||||
<Table TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
|
<Table Id=@Id TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
|
||||||
DataService="DataService" CreateItemCallback="CreateItemCallback!"
|
DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode OnColumnCreating=OnColumnCreating
|
||||||
IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" BeforeShowEditDialogCallback="BeforeShowEditDialogCallback!"
|
IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!"
|
||||||
IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender
|
IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender
|
||||||
ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton
|
ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton
|
||||||
ShowEmpty="ShowEmpty" EmptyText="@EmptyText" EmptyImage="@($"{WebsiteConst.DefaultResourceUrl}images/empty.svg")" SortString="@SortString" EditDialogSize="EditDialogSize"
|
ShowEmpty="ShowEmpty" EmptyText="@EmptyText" EmptyImage="@($"{WebsiteConst.DefaultResourceUrl}images/empty.svg")" SortString="@SortString" EditDialogSize="EditDialogSize"
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo
|
ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo
|
||||||
SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText
|
SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText
|
||||||
ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode
|
ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode
|
||||||
ShowExportCsvButton=@ShowExportCsvButton SelectedRowsChanged=SelectedRowsChanged ShowCardView=ShowCardView
|
ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView OnColumnVisibleChanged=OnColumnVisibleChanged
|
||||||
FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn
|
FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn
|
||||||
IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval
|
IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval
|
||||||
AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh
|
AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
ShowMultiFilterHeader=ShowMultiFilterHeader
|
ShowMultiFilterHeader=ShowMultiFilterHeader
|
||||||
ShowFilterHeader=ShowFilterHeader
|
ShowFilterHeader=ShowFilterHeader
|
||||||
ShowColumnList=ShowColumnList ExtendButtonColumnWidth="@ExtendButtonColumnWidth"
|
ShowColumnList=ShowColumnList ExtendButtonColumnWidth="@ExtendButtonColumnWidth"
|
||||||
CustomerSearchModel="CustomerSearchModel" SelectedRows="SelectedRows" ModelEqualityComparer="ModelEqualityComparer!"
|
CustomerSearchModel="CustomerSearchModel" ModelEqualityComparer="ModelEqualityComparer!"
|
||||||
ShowExtendEditButtonCallback="ShowExtendEditButtonCallback!" ShowExtendDeleteButtonCallback="ShowExtendDeleteButtonCallback!"
|
ShowExtendEditButtonCallback="ShowExtendEditButtonCallback!" ShowExtendDeleteButtonCallback="ShowExtendDeleteButtonCallback!"
|
||||||
DisableExtendEditButton="DisableExtendEditButton!" DisableExtendDeleteButton="DisableExtendDeleteButton!"
|
DisableExtendEditButton="DisableExtendEditButton!" DisableExtendDeleteButton="DisableExtendDeleteButton!"
|
||||||
DisableExtendEditButtonCallback="DisableExtendEditButtonCallback!" DisableExtendDeleteButtonCallback="DisableExtendDeleteButtonCallback!"
|
DisableExtendEditButtonCallback="DisableExtendEditButtonCallback!" DisableExtendDeleteButtonCallback="DisableExtendDeleteButtonCallback!"
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
DoubleClickToEdit="DoubleClickToEdit"
|
DoubleClickToEdit="DoubleClickToEdit"
|
||||||
OnDoubleClickCellCallback="OnDoubleClickCellCallback"
|
OnDoubleClickCellCallback="OnDoubleClickCellCallback"
|
||||||
OnDoubleClickRowCallback="OnDoubleClickRowCallback"
|
OnDoubleClickRowCallback="OnDoubleClickRowCallback"
|
||||||
|
RowContentTemplate="RowContentTemplate"
|
||||||
OnClickRowCallback="OnClickRowCallback">
|
OnClickRowCallback="OnClickRowCallback">
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,6 +13,41 @@ namespace ThingsGateway.Admin.Razor;
|
|||||||
[CascadingTypeParameter(nameof(TItem))]
|
[CascadingTypeParameter(nameof(TItem))]
|
||||||
public partial class AdminTable<TItem> where TItem : class, new()
|
public partial class AdminTable<TItem> where TItem : class, new()
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc cref="Table{TItem}.OnColumnVisibleChanged"/>
|
||||||
|
[Parameter]
|
||||||
|
public Func<string, bool, Task> OnColumnVisibleChanged { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Table{TItem}.OnColumnCreating"/>
|
||||||
|
[Parameter]
|
||||||
|
public Func<List<ITableColumn>, Task> OnColumnCreating { get; set; }
|
||||||
|
/// <inheritdoc cref="Table{TItem}.RenderMode"/>
|
||||||
|
[Parameter]
|
||||||
|
public TableRenderMode RenderMode { get; set; }
|
||||||
|
|
||||||
|
public List<ITableColumn> Columns => Instance?.Columns;
|
||||||
|
|
||||||
|
public IEnumerable<ITableColumn> GetVisibleColumns => Instance?.GetVisibleColumns();
|
||||||
|
public List<TItem> Rows => Instance?.Rows;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/>
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<List<TItem>> SelectedRowsChanged { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Table{TItem}.SelectedRows"/>
|
||||||
|
[Parameter]
|
||||||
|
public List<TItem> SelectedRows { get; set; } = new();
|
||||||
|
|
||||||
|
private async Task privateSelectedRowsChanged(List<TItem> items)
|
||||||
|
{
|
||||||
|
SelectedRows = items;
|
||||||
|
if (SelectedRowsChanged.HasDelegate)
|
||||||
|
await SelectedRowsChanged.InvokeAsync(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Table{TItem}.DoubleClickToEdit"/>
|
/// <inheritdoc cref="Table{TItem}.DoubleClickToEdit"/>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool DoubleClickToEdit { get; set; } = false;
|
public bool DoubleClickToEdit { get; set; } = false;
|
||||||
@@ -22,6 +57,10 @@ public partial class AdminTable<TItem> where TItem : class, new()
|
|||||||
/// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/>
|
/// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; }
|
public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; }
|
||||||
|
/// <inheritdoc cref="Table{TItem}.RowContentTemplate"/>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TableRowContext<TItem>>? RowContentTemplate { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/>
|
/// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<TItem, Task>? OnClickRowCallback { get; set; }
|
public Func<TItem, Task>? OnClickRowCallback { get; set; }
|
||||||
@@ -128,6 +167,9 @@ public partial class AdminTable<TItem> where TItem : class, new()
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public IDataService<TItem> DataService { get; set; }
|
public IDataService<TItem> DataService { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="Table{TItem}.CreateItemCallback"/>
|
/// <inheritdoc cref="Table{TItem}.CreateItemCallback"/>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<TItem> CreateItemCallback { get; set; }
|
public Func<TItem> CreateItemCallback { get; set; }
|
||||||
@@ -210,14 +252,6 @@ public partial class AdminTable<TItem> where TItem : class, new()
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TItem>? SearchTemplate { get; set; }
|
public RenderFragment<TItem>? SearchTemplate { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="Table{TItem}.SelectedRows"/>
|
|
||||||
[Parameter]
|
|
||||||
public List<TItem>? SelectedRows { get; set; } = new List<TItem>();
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/>
|
|
||||||
[Parameter]
|
|
||||||
public EventCallback<List<TItem>> SelectedRowsChanged { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Table{TItem}.SetRowClassFormatter"/>
|
/// <inheritdoc cref="Table{TItem}.SetRowClassFormatter"/>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<TItem, string?>? SetRowClassFormatter { get; set; }
|
public Func<TItem, string?>? SetRowClassFormatter { get; set; }
|
||||||
@@ -266,6 +300,15 @@ public partial class AdminTable<TItem> where TItem : class, new()
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowExportButton { get; set; } = false;
|
public bool ShowExportButton { get; set; } = false;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Table{TItem}.DisableEditButtonCallback"/>
|
||||||
|
public Func<List<TItem>, bool> DisableEditButtonCallback { get; set; } = (list) =>
|
||||||
|
list.Count != 1;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Table{TItem}.DisableDeleteButtonCallback"/>
|
||||||
|
[Parameter]
|
||||||
|
public Func<List<TItem>, bool> DisableDeleteButtonCallback { get; set; } = (list) =>
|
||||||
|
list.Count <= 0;
|
||||||
|
|
||||||
/// <inheritdoc cref="Table{TItem}.ShowExportCsvButton"/>
|
/// <inheritdoc cref="Table{TItem}.ShowExportCsvButton"/>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowExportCsvButton { get; set; } = false;
|
public bool ShowExportCsvButton { get; set; } = false;
|
||||||
|
|||||||
@@ -125,13 +125,22 @@ public class BlazorAppContext
|
|||||||
var ownMenus = OwnMenus.Where(a => a.Module == CurrentModuleId);
|
var ownMenus = OwnMenus.Where(a => a.Module == CurrentModuleId);
|
||||||
OwnMenuItems = AdminResourceUtil.BuildMenuTrees(ownMenus).ToList();
|
OwnMenuItems = AdminResourceUtil.BuildMenuTrees(ownMenus).ToList();
|
||||||
AllOwnMenuItems = AdminResourceUtil.BuildMenuTrees(OwnMenus).ToList();
|
AllOwnMenuItems = AdminResourceUtil.BuildMenuTrees(OwnMenus).ToList();
|
||||||
OwnSameLevelMenuItems = ownMenus.Where(a => !a.Href.IsNullOrWhiteSpace()).Select(item => new MenuItem()
|
OwnSameLevelMenuItems = ownMenus.Where(a => !a.Href.IsNullOrWhiteSpace()).Select(item =>
|
||||||
{
|
{
|
||||||
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All,
|
var menu = new MenuItem()
|
||||||
|
{
|
||||||
|
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix,
|
||||||
Text = item.Title,
|
Text = item.Title,
|
||||||
Icon = item.Icon,
|
Icon = item.Icon,
|
||||||
Url = item.Href,
|
Url = item.Href,
|
||||||
Target = item.Target.ToString(),
|
Target = item.Target.ToString(),
|
||||||
|
};
|
||||||
|
if (menu.Url.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
menu.Match = Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix;
|
||||||
|
}
|
||||||
|
return menu;
|
||||||
|
|
||||||
}).ToList();
|
}).ToList();
|
||||||
UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id)).ToList();
|
UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id)).ToList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
"SetDefaultModule": "Set as default module"
|
"SetDefaultModule": "Set as default module"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
||||||
|
"GCMemoryInfoConfig": "GCMemoryInfoConfig(MB)",
|
||||||
|
"GCMemoryInfo": "GCMemoryInfo(MB)",
|
||||||
|
"GCAnalyzeInfo": "GCAnalyzeInfo",
|
||||||
"AvailableDisk": "Available Disk",
|
"AvailableDisk": "Available Disk",
|
||||||
"AvailableMemory": "Available Memory",
|
"AvailableMemory": "Available Memory",
|
||||||
"CpuUsage": "CPU Usage",
|
"CpuUsage": "CPU Usage",
|
||||||
|
|||||||
@@ -22,6 +22,10 @@
|
|||||||
"SetDefaultModule": "设置为默认模块"
|
"SetDefaultModule": "设置为默认模块"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
||||||
|
"GCMemoryInfoConfig": "GC配置信息(MB)",
|
||||||
|
"GCMemoryInfo": "GC内存信息(MB)",
|
||||||
|
"GCAnalyzeInfo": "GC统计",
|
||||||
|
|
||||||
"AvailableDisk": "可用磁盘",
|
"AvailableDisk": "可用磁盘",
|
||||||
"AvailableMemory": "可用内存",
|
"AvailableMemory": "可用内存",
|
||||||
"CpuUsage": "CPU使用率",
|
"CpuUsage": "CPU使用率",
|
||||||
|
|||||||
@@ -5,131 +5,189 @@
|
|||||||
@using ThingsGateway.Admin.Application
|
@using ThingsGateway.Admin.Application
|
||||||
@namespace ThingsGateway.Admin.Razor
|
@namespace ThingsGateway.Admin.Razor
|
||||||
<div class="row g-2 mx-1 form-inline">
|
<div class="row g-2 mx-1 form-inline">
|
||||||
|
<div class="col-12 col-md-12">
|
||||||
<div class="col-12 col-md-4">
|
|
||||||
|
|
||||||
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
|
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
|
||||||
<HeaderTemplate>
|
<HeaderTemplate>
|
||||||
@Localizer["SystemInfo"]
|
<div class="d-flex justify-content-between align-items-center w-100">
|
||||||
|
<span>@Localizer["SystemInfo"]</span>
|
||||||
|
<small class="text-muted">
|
||||||
|
@HardwareJob.HardwareInfo.UpdateTime.ToString("yyyy-MM-dd HH:mm:ss")
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
</HeaderTemplate>
|
</HeaderTemplate>
|
||||||
|
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
<EditorFormObject IsDisplay Model="HardwareJob.HardwareInfo" ItemsPerRow="1" RowType=RowType.Inline LabelWidth="160">
|
<EditorForm class="mt-3" AutoGenerateAllItem="false" IsDisplay Model="HardwareJob.HardwareInfo" ItemsPerRow=3 RowType=RowType.Inline LabelWidth=300>
|
||||||
<FieldItems>
|
<FieldItems>
|
||||||
<EditorItem @bind-Field="@context.MachineInfo">
|
|
||||||
|
<EditorItem @bind-Field="@context.OSName" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
<EditorItem @bind-Field="@context.OsArchitecture" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
<EditorItem @bind-Field="@context.OSVersion" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
<EditorItem @bind-Field="@context.Environment" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
<EditorItem @bind-Field="@context.FrameworkDescription" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
<EditorItem @bind-Field="@context.UUID" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
<EditorItem @bind-Field="@context.SystemRunTotalMinute" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
<EditorItem @bind-Field="@context.AppRunTotalMinute" GroupName="@Localizer["SystemInfo"]" />
|
||||||
|
|
||||||
|
|
||||||
|
<EditorItem @bind-Field="@context.Memory" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 />
|
||||||
|
<EditorItem @bind-Field="@context.TotalAvailableMemory" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 />
|
||||||
|
<EditorItem @bind-Field="@context.HighMemoryLoadThreshold" GroupName="@Localizer["GCMemoryConfig"]" GroupOrder=2 />
|
||||||
|
|
||||||
|
<EditorItem TValue="bool" TModel="HardwareInfo" @bind-Field="@context.IsServerGC" GroupName="@Localizer["GCMemoryConfig"]" GroupOrder=2>
|
||||||
<EditTemplate Context="value">
|
<EditTemplate Context="value">
|
||||||
<div class="col-12 col-md-12">
|
<div class="col-12">
|
||||||
<Display @bind-Value="@value.MachineInfo.OSName" DisplayText="@Localizer[nameof(value.MachineInfo.OSName)]" ShowTooltip ShowLabel=true>
|
<h6></h6>
|
||||||
</Display>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-12">
|
|
||||||
<Display @bind-Value="@value.MachineInfo.OSVersion" DisplayText="@Localizer[nameof(value.MachineInfo.OSVersion)]" ShowTooltip ShowLabel=true>
|
|
||||||
</Display>
|
|
||||||
</div>
|
</div>
|
||||||
</EditTemplate>
|
</EditTemplate>
|
||||||
</EditorItem>
|
</EditorItem>
|
||||||
|
|
||||||
<EditorItem @bind-Field="@context.UUID">
|
<EditorItem @bind-Field="@context.IsServerGC" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 />
|
||||||
<EditTemplate Context="value">
|
<EditorItem @bind-Field="@context.GCLatencyMode" GroupName="@Localizer["GCMemoryInfoConfig"]" GroupOrder=2 />
|
||||||
<div class="col-12 col-md-12">
|
|
||||||
<Display @bind-Value="@value.UUID" ShowTooltip ShowLabel=true>
|
|
||||||
</Display>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
<EditorItem @bind-Field="@context.WorkingSet" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem @bind-Field="@context.PrivateMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem @bind-Field="@context.PeakWorkingSet" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem TValue="ulong" TModel="HardwareInfo" @bind-Field="@context.HeapSize" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3>
|
||||||
|
<EditTemplate Context="value">
|
||||||
|
<div class="col-12">
|
||||||
|
<h6></h6>
|
||||||
|
</div>
|
||||||
</EditTemplate>
|
</EditTemplate>
|
||||||
</EditorItem>
|
</EditorItem>
|
||||||
<EditorItem @bind-Field="@context.DriveInfo" Ignore=true>
|
<EditorItem @bind-Field="@context.HeapSize" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem @bind-Field="@context.TotalMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem @bind-Field="@context.FragmentedBytes" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem @bind-Field="@context.CommittedBytes" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
|
||||||
|
<EditorItem TValue="ulong" TModel="HardwareInfo" @bind-Field="@context.AvailableMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3>
|
||||||
|
<EditTemplate Context="value">
|
||||||
|
<div class="col-12">
|
||||||
|
<h6></h6>
|
||||||
|
</div>
|
||||||
|
</EditTemplate>
|
||||||
</EditorItem>
|
</EditorItem>
|
||||||
|
|
||||||
|
<EditorItem @bind-Field="@context.AvailableMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem @bind-Field="@context.GCAvailableMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
<EditorItem @bind-Field="@context.VirtualMemory" GroupName="@Localizer["GCMemoryInfo"]" GroupOrder=3 />
|
||||||
|
|
||||||
|
|
||||||
|
<EditorItem @bind-Field="@context.GcGen0Count" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 />
|
||||||
|
<EditorItem @bind-Field="@context.GcGen1Count" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 />
|
||||||
|
<EditorItem @bind-Field="@context.GcGen2Count" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 />
|
||||||
|
<EditorItem TValue="ulong" TModel="HardwareInfo" @bind-Field="@context.TotalAllocatedBytes" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4>
|
||||||
|
<EditTemplate Context="value">
|
||||||
|
<div class="col-12">
|
||||||
|
<h6></h6>
|
||||||
|
</div>
|
||||||
|
</EditTemplate>
|
||||||
|
</EditorItem>
|
||||||
|
|
||||||
|
<EditorItem @bind-Field="@context.TotalAllocatedBytes" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 />
|
||||||
|
<EditorItem @bind-Field="@context.TotalPauseDurationMs" GroupName="@Localizer["GCAnalyzeInfo"]" GroupOrder=4 />
|
||||||
|
|
||||||
</FieldItems>
|
</FieldItems>
|
||||||
</EditorFormObject>
|
</EditorForm>
|
||||||
|
|
||||||
</BodyTemplate>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-8">
|
|
||||||
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
|
|
||||||
<HeaderTemplate>
|
|
||||||
@Localizer["HardwareInfo"]
|
|
||||||
</HeaderTemplate>
|
|
||||||
|
|
||||||
<BodyTemplate>
|
|
||||||
<div class="d-flex align-items-center justify-content-center w-100 h-100" >
|
|
||||||
|
|
||||||
<div class="row g-2 mx-1 form-inline d-flex justify-content-center w-100">
|
|
||||||
<div class="col-12 col-md-4 justify-content-center h-100" >
|
|
||||||
@{
|
|
||||||
var item = HardwareJob.HardwareInfo.MachineInfo.CpuRate;
|
|
||||||
}
|
|
||||||
<div class="d-flex flex-column align-items-center">
|
|
||||||
<Circle Width="200" class="m-3"
|
|
||||||
Value=@((int)(item*100<=100?item*100:100))
|
|
||||||
Color=@((item*100>70?Color.Warning:Color.Success))
|
|
||||||
StrokeWidth="4" ShowProgress=false>
|
|
||||||
<div class="circle-hardware">
|
|
||||||
<span>
|
|
||||||
@Localizer["CpuUsage"] <i> @((item * 100).ToString("F0") + " %")</i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Circle>
|
|
||||||
<div class="mt-1">
|
|
||||||
<span>@(HardwareJob.HardwareInfo.MachineInfo.Processor)</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-4 justify-content-center h-100">
|
|
||||||
@{
|
|
||||||
var availableMemory = HardwareJob.HardwareInfo.MachineInfo.AvailableMemory;
|
|
||||||
var memory = HardwareJob.HardwareInfo.MachineInfo.Memory;
|
|
||||||
}
|
|
||||||
<div class="d-flex flex-column align-items-center ">
|
|
||||||
<Circle Width="200" class="m-3"
|
|
||||||
Value=@((int)(memory>availableMemory?100 - (availableMemory * 100.00 / memory):100))
|
|
||||||
Color=@((availableMemory * 100.00 / memory<20?Color.Warning:Color.Success))
|
|
||||||
StrokeWidth="4">
|
|
||||||
<div class="circle-hardware">
|
|
||||||
<h6> @((100 - (availableMemory * 100.00 / memory)).ToString("F2") + " %") </h6>
|
|
||||||
|
|
||||||
<span> @Localizer["WorkingSet"] <i> @(HardwareJob.HardwareInfo.WorkingSet + " MB")</i></span>
|
|
||||||
<span> @Localizer["AvailableMemory"] <i> @((availableMemory / 1024.00 / 1024).ToString("F2") + " GB")</i></span>
|
|
||||||
<span> @Localizer["TotalMemory"] <i> @((memory / 1024.00 / 1024).ToString("F2") + " GB")</i></span>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Circle>
|
|
||||||
<div class="mt-1">
|
|
||||||
<span> @Localizer["MemoryUsage"] </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-4 justify-content-center h-100">
|
|
||||||
@{
|
|
||||||
var totalFreeSpace = HardwareJob.HardwareInfo.DriveInfo.TotalFreeSpace;
|
|
||||||
var totalSize = HardwareJob.HardwareInfo.DriveInfo.TotalSize;
|
|
||||||
}
|
|
||||||
<div class="d-flex flex-column align-items-center ">
|
|
||||||
<Circle Width="200" class="m-3"
|
|
||||||
Value=@((int)(totalSize>totalFreeSpace?100 - (totalFreeSpace * 100.00 / totalSize):100))
|
|
||||||
Color=@((totalFreeSpace * 100.00 / totalSize<20?Color.Warning:Color.Success))
|
|
||||||
StrokeWidth="4">
|
|
||||||
<div class="circle-hardware">
|
|
||||||
<h6> @((100 - (totalFreeSpace * 100.00 / totalSize)).ToString("F2") + " %")</h6>
|
|
||||||
<span> @(HardwareJob.HardwareInfo.DriveInfo.VolumeLabel) </span>
|
|
||||||
<span> @Localizer["AvailableDisk"] <i> @((totalFreeSpace / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
|
|
||||||
<span> @Localizer["TotalDisk"] <i> @((totalSize / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
|
|
||||||
</div>
|
|
||||||
</Circle>
|
|
||||||
<div class="mt-1">
|
|
||||||
<span> @Localizer["DiskUsage"] </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</BodyTemplate>
|
</BodyTemplate>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-2 mx-1 form-inline mt-2">
|
||||||
|
|
||||||
|
<div class="col-12 col-md-12">
|
||||||
|
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
|
||||||
|
<HeaderTemplate>
|
||||||
|
@Localizer["HardwareInfo"]
|
||||||
|
</HeaderTemplate>
|
||||||
|
|
||||||
|
<BodyTemplate>
|
||||||
|
<div class="d-flex align-items-center justify-content-center w-100 h-100">
|
||||||
|
<div class="row g-2 mx-1 form-inline d-flex justify-content-center w-100">
|
||||||
|
<!-- ✅ CPU 使用率 -->
|
||||||
|
<div class="col-12 col-md-4 justify-content-center h-100">
|
||||||
|
@{
|
||||||
|
var cpuUsage = HardwareJob.HardwareInfo.CpuRate;
|
||||||
|
}
|
||||||
|
<div class="d-flex flex-column align-items-center">
|
||||||
|
<Circle Width="200" class="m-3"
|
||||||
|
Value=@((int)(cpuUsage*100<=100?cpuUsage*100:100))
|
||||||
|
Color=@((cpuUsage*100>70?Color.Warning:Color.Success))
|
||||||
|
StrokeWidth="4" ShowProgress=false>
|
||||||
|
<div class="circle-hardware">
|
||||||
|
<span>
|
||||||
|
@Localizer["CpuUsage"] <i>@((cpuUsage * 100).ToString("F0") + " %")</i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Circle>
|
||||||
|
<div class="mt-1">
|
||||||
|
<span>@(HardwareJob.HardwareInfo.Processor)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ✅ 内存使用率 -->
|
||||||
|
<div class="col-12 col-md-4 justify-content-center h-100">
|
||||||
|
@{
|
||||||
|
var availableMemory = HardwareJob.HardwareInfo.AvailableMemory;
|
||||||
|
var memory = HardwareJob.HardwareInfo.Memory;
|
||||||
|
}
|
||||||
|
<div class="d-flex flex-column align-items-center">
|
||||||
|
<Circle Width="200" class="m-3"
|
||||||
|
Value=@((int)(memory>availableMemory?100 - (availableMemory * 100.00 / memory):100))
|
||||||
|
Color=@((availableMemory * 100.00 / memory<20?Color.Warning:Color.Success))
|
||||||
|
StrokeWidth="4">
|
||||||
|
<div class="circle-hardware">
|
||||||
|
<h6>@((100 - (availableMemory * 100.00 / memory)).ToString("F2") + " %")</h6>
|
||||||
|
<span>@Localizer["WorkingSet"] <i>@(HardwareJob.HardwareInfo.WorkingSet + " MB")</i></span>
|
||||||
|
<span>@Localizer["AvailableMemory"] <i>@((availableMemory / 1024.00).ToString("F2") + " GB")</i></span>
|
||||||
|
<span>@Localizer["TotalMemory"] <i>@((memory / 1024.00).ToString("F2") + " GB")</i></span>
|
||||||
|
</div>
|
||||||
|
</Circle>
|
||||||
|
<div class="mt-1">
|
||||||
|
<span>@Localizer["MemoryUsage"]</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ✅ 磁盘使用率 -->
|
||||||
|
<div class="col-12 col-md-4 justify-content-center h-100">
|
||||||
|
@{
|
||||||
|
var totalFreeSpace = HardwareJob.HardwareInfo.DriveInfo.TotalFreeSpace;
|
||||||
|
var totalSize = HardwareJob.HardwareInfo.DriveInfo.TotalSize;
|
||||||
|
}
|
||||||
|
<div class="d-flex flex-column align-items-center">
|
||||||
|
<Circle Width="200" class="m-3"
|
||||||
|
Value=@((int)(totalSize>totalFreeSpace?100 - (totalFreeSpace * 100.00 / totalSize):100))
|
||||||
|
Color=@((totalFreeSpace * 100.00 / totalSize<20?Color.Warning:Color.Success))
|
||||||
|
StrokeWidth="4">
|
||||||
|
<div class="circle-hardware">
|
||||||
|
<h6>@((100 - (totalFreeSpace * 100.00 / totalSize)).ToString("F2") + " %")</h6>
|
||||||
|
<span>@(HardwareJob.HardwareInfo.DriveInfo.VolumeLabel)</span>
|
||||||
|
<span>@Localizer["AvailableDisk"] <i>@((totalFreeSpace / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
|
||||||
|
<span>@Localizer["TotalDisk"] <i>@((totalSize / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
|
||||||
|
</div>
|
||||||
|
</Circle>
|
||||||
|
<div class="mt-1">
|
||||||
|
<span>@Localizer["DiskUsage"]</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BodyTemplate>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="row g-2 mx-1 form-inline">
|
<div class="row g-2 mx-1 form-inline">
|
||||||
|
|
||||||
<div class="col-12 col-md-12">
|
<div class="col-12 col-md-12">
|
||||||
@@ -140,7 +198,7 @@
|
|||||||
</HeaderTemplate>
|
</HeaderTemplate>
|
||||||
|
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
<Chart @ref=LineChart OnInitAsync="OnInit" Height="var(--line-chart-height)" Width="100%" OnAfterInitAsync="()=>{chartInit=true;return Task.CompletedTask;}" />
|
<Chart @ref=LineChart OnInitAsync="OnInit" Height="var(--line-chart-height)" Width="100%" OnAfterInitAsync="() => { chartInit = true; return Task.CompletedTask; }" />
|
||||||
</BodyTemplate>
|
</BodyTemplate>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public partial class HardwareInfoPage : IDisposable
|
|||||||
ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)];
|
ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)];
|
||||||
ChartDataSource.Options.X.Title = Localizer["DateTime"];
|
ChartDataSource.Options.X.Title = Localizer["DateTime"];
|
||||||
ChartDataSource.Options.Y.Title = Localizer["Data"];
|
ChartDataSource.Options.Y.Title = Localizer["Data"];
|
||||||
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz"));
|
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm"));
|
||||||
ChartDataSource.Data.Add(new ChartDataset()
|
ChartDataSource.Data.Add(new ChartDataset()
|
||||||
{
|
{
|
||||||
Tension = 0.4f,
|
Tension = 0.4f,
|
||||||
@@ -116,7 +116,7 @@ public partial class HardwareInfoPage : IDisposable
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos();
|
var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos();
|
||||||
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz"));
|
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm"));
|
||||||
ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage);
|
ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage);
|
||||||
ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage);
|
ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage);
|
||||||
ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage);
|
ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-10 h-100">
|
<div class="col-12 col-sm-10 h-100 ps-2">
|
||||||
<AdminTable @ref=table TItem="SysUser"
|
<AdminTable @ref=table TItem="SysUser"
|
||||||
AutoGenerateColumns="true"
|
AutoGenerateColumns="true"
|
||||||
ShowAdvancedSearch=false
|
ShowAdvancedSearch=false
|
||||||
|
|||||||
@@ -20,5 +20,6 @@ public class Startup : AppStartup
|
|||||||
services.AddScoped<IMenuService, MenuService>();
|
services.AddScoped<IMenuService, MenuService>();
|
||||||
services.AddScoped<IAuthRazorService, AuthRazorService>();
|
services.AddScoped<IAuthRazorService, AuthRazorService>();
|
||||||
services.AddBootstrapBlazorTableExportService();
|
services.AddBootstrapBlazorTableExportService();
|
||||||
|
services.AddBootstrapBlazorWinBoxService();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,21 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
||||||
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" />
|
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.4" />
|
||||||
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
|
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
|
||||||
|
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
|
||||||
|
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET8Version)" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET8Version)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
|
<ItemGroup Condition="'$(TargetFramework)'=='net10.0'">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET10Version)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
|
||||||
|
|
||||||
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
|
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -41,14 +41,21 @@ public static class AdminResourceUtil
|
|||||||
return items
|
return items
|
||||||
.Where(it => it.ParentId == parentId)
|
.Where(it => it.ParentId == parentId)
|
||||||
.Select((item, index) =>
|
.Select((item, index) =>
|
||||||
new MenuItem()
|
|
||||||
{
|
{
|
||||||
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All,
|
var menu = new MenuItem()
|
||||||
|
{
|
||||||
|
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix,
|
||||||
Text = item.Title,
|
Text = item.Title,
|
||||||
Icon = item.Icon,
|
Icon = item.Icon,
|
||||||
Url = item.Href,
|
Url = item.Href,
|
||||||
Target = item.Target.ToString(),
|
Target = item.Target.ToString(),
|
||||||
Items = BuildMenuTrees(items, item.Id).ToList()
|
Items = BuildMenuTrees(items, item.Id).ToList()
|
||||||
|
};
|
||||||
|
if (menu.Url.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
menu.Match = Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix;
|
||||||
|
}
|
||||||
|
return menu;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,13 @@
|
|||||||
<link rel="apple-touch-icon" href="favicon.png">
|
<link rel="apple-touch-icon" href="favicon.png">
|
||||||
<base href="/" />
|
<base href="/" />
|
||||||
<title>ThingsGateway</title>
|
<title>ThingsGateway</title>
|
||||||
<link rel="stylesheet" href=@($"_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css?v={this.GetType().Assembly.GetName().Version}") />
|
<link rel="stylesheet" href=@($"_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css") />
|
||||||
<link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css?v={this.GetType().Assembly.GetName().Version}") />
|
<link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css") />
|
||||||
<link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/motronic.min.css?v={this.GetType().Assembly.GetName().Version}") />
|
<link rel="stylesheet" href=@($"_content/BootstrapBlazor/css/motronic.min.css") />
|
||||||
<link rel="stylesheet" href=@($"ThingsGateway.AdminServer.styles.css?v={this.GetType().Assembly.GetName().Version}") />
|
<link rel="stylesheet" href=@($"ThingsGateway.AdminServer.styles.css") />
|
||||||
<link rel="stylesheet" href=@($"{WebsiteConst.DefaultResourceUrl}css/site.css?v={this.GetType().Assembly.GetName().Version}") />
|
<link rel="stylesheet" href=@($"{WebsiteConst.DefaultResourceUrl}css/site.css") />
|
||||||
|
<link rel="stylesheet" href=@($"{WebsiteConst.DefaultResourceUrl}css/devui.css") />
|
||||||
|
|
||||||
@* <script src=@($"{WebsiteConst.DefaultResourceUrl}js/theme.js") type="module"></script><!-- 初始主题 --> *@
|
@* <script src=@($"{WebsiteConst.DefaultResourceUrl}js/theme.js") type="module"></script><!-- 初始主题 --> *@
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="./manifest.json" />
|
<link rel="manifest" href="./manifest.json" />
|
||||||
@@ -38,12 +40,13 @@
|
|||||||
|
|
||||||
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
||||||
|
|
||||||
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js")></script>
|
||||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
|
||||||
<script src="_framework/blazor.web.js"></script>
|
<script src="_framework/blazor.web.js"></script>
|
||||||
<!-- PWA Service Worker -->
|
<!-- PWA Service Worker -->
|
||||||
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
||||||
|
|
||||||
|
<script src="pwa-install.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
|
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
|
||||||
}
|
}
|
||||||
@* 版本号 *@
|
@* 版本号 *@
|
||||||
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div>
|
<div class="px-1 navbar-header-text text-nowrap d-none d-lg-block">@_versionString</div>
|
||||||
|
|
||||||
@* 主题切换 *@
|
@* 主题切换 *@
|
||||||
@* <ThemeToggle /> *@
|
@* <ThemeToggle /> *@
|
||||||
@@ -93,8 +93,15 @@
|
|||||||
ShowFullscreenToolbarButton=false ShowContextMenuFullScreen=false ShowFullScreen=false AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
|
ShowFullscreenToolbarButton=false ShowContextMenuFullScreen=false ShowFullScreen=false AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
|
||||||
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
||||||
{
|
{
|
||||||
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
|
return Task.FromResult(!(a.Url == "/" || a.Url.IsNullOrEmpty()));
|
||||||
})>
|
})
|
||||||
|
>
|
||||||
|
<BeforeContextMenuTemplate>
|
||||||
|
|
||||||
|
<ContextMenuItem Icon="fa fa-window-restore" Text="@Localizer["WindowRestore"]" OnClick="WinboxRender"></ContextMenuItem>
|
||||||
|
<ContextMenuDivider></ContextMenuDivider>
|
||||||
|
|
||||||
|
</BeforeContextMenuTemplate>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Main>
|
</Main>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
|
|||||||
@@ -120,6 +120,38 @@ public partial class MainLayout : IDisposable
|
|||||||
|
|
||||||
#endregion 注销
|
#endregion 注销
|
||||||
|
|
||||||
|
|
||||||
|
private async Task WinboxRender(ContextMenuItem item, object? context)
|
||||||
|
{
|
||||||
|
if (context is TabItem tabItem)
|
||||||
|
{
|
||||||
|
await WinboxRender(tabItem.ChildContent, tabItem.Text);
|
||||||
|
//await _tab.RemoveTab(tabItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Inject]
|
||||||
|
[NotNull]
|
||||||
|
private WinBoxService? WinBoxService { get; set; }
|
||||||
|
|
||||||
|
private async Task WinboxRender(RenderFragment item, string title)
|
||||||
|
{
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
var option = new WinBoxOption()
|
||||||
|
{
|
||||||
|
Title = title,
|
||||||
|
ContentTemplate = item,
|
||||||
|
Max = false,
|
||||||
|
Width = "80%",
|
||||||
|
Height = "80%",
|
||||||
|
Top = "0%",
|
||||||
|
Left = "10%",
|
||||||
|
Background = "var(--bb-primary-color)",
|
||||||
|
Overflow = true
|
||||||
|
};
|
||||||
|
await WinBoxService.Show(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
private string _versionString = string.Empty;
|
private string _versionString = string.Empty;
|
||||||
[Inject]
|
[Inject]
|
||||||
[NotNull]
|
[NotNull]
|
||||||
|
|||||||
@@ -90,9 +90,10 @@ public class Startup : AppStartup
|
|||||||
.AddInteractiveServerComponents(options =>
|
.AddInteractiveServerComponents(options =>
|
||||||
{
|
{
|
||||||
options.RootComponents.MaxJSRootComponents = 500;
|
options.RootComponents.MaxJSRootComponents = 500;
|
||||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
|
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
|
||||||
options.MaxBufferedUnacknowledgedRenderBatches = 20;
|
options.MaxBufferedUnacknowledgedRenderBatches = 5;
|
||||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10);
|
options.DisconnectedCircuitMaxRetained = 1;
|
||||||
|
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||||
})
|
})
|
||||||
.AddHubOptions(options =>
|
.AddHubOptions(options =>
|
||||||
{
|
{
|
||||||
@@ -100,9 +101,9 @@ public class Startup : AppStartup
|
|||||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||||
options.StreamBufferCapacity = 30;
|
options.StreamBufferCapacity = 30;
|
||||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
||||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||||
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
|
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
|
||||||
});
|
});
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -110,18 +111,20 @@ public class Startup : AppStartup
|
|||||||
services.AddServerSideBlazor(options =>
|
services.AddServerSideBlazor(options =>
|
||||||
{
|
{
|
||||||
options.RootComponents.MaxJSRootComponents = 500;
|
options.RootComponents.MaxJSRootComponents = 500;
|
||||||
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
|
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
|
||||||
options.MaxBufferedUnacknowledgedRenderBatches = 20;
|
options.MaxBufferedUnacknowledgedRenderBatches = 5;
|
||||||
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10);
|
options.DisconnectedCircuitMaxRetained = 1;
|
||||||
}).AddHubOptions(options =>
|
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
|
||||||
|
})
|
||||||
|
.AddHubOptions(options =>
|
||||||
{
|
{
|
||||||
//单个传入集线器消息的最大大小。默认 32 KB
|
//单个传入集线器消息的最大大小。默认 32 KB
|
||||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||||
options.StreamBufferCapacity = 30;
|
options.StreamBufferCapacity = 30;
|
||||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
||||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||||
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
|
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
|
||||||
});
|
});
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -132,7 +135,11 @@ public class Startup : AppStartup
|
|||||||
services.Configure<ForwardedHeadersOptions>(options =>
|
services.Configure<ForwardedHeadersOptions>(options =>
|
||||||
{
|
{
|
||||||
options.ForwardedHeaders = ForwardedHeaders.All;
|
options.ForwardedHeaders = ForwardedHeaders.All;
|
||||||
|
#if NET10_0_OR_GREATER
|
||||||
|
options.KnownIPNetworks.Clear();
|
||||||
|
#else
|
||||||
options.KnownNetworks.Clear();
|
options.KnownNetworks.Clear();
|
||||||
|
#endif
|
||||||
options.KnownProxies.Clear();
|
options.KnownProxies.Clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -204,7 +211,16 @@ public class Startup : AppStartup
|
|||||||
public void Use(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
|
public void Use(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
|
||||||
{
|
{
|
||||||
var app = (WebApplication)applicationBuilder;
|
var app = (WebApplication)applicationBuilder;
|
||||||
app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All, KnownNetworks = { }, KnownProxies = { } });
|
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||||
|
{
|
||||||
|
ForwardedHeaders = ForwardedHeaders.All,
|
||||||
|
#if NET10_0_OR_GREATER
|
||||||
|
KnownIPNetworks = { },
|
||||||
|
#else
|
||||||
|
KnownNetworks = { },
|
||||||
|
#endif
|
||||||
|
KnownProxies = { }
|
||||||
|
});
|
||||||
app.UseBootstrapBlazor();
|
app.UseBootstrapBlazor();
|
||||||
|
|
||||||
// 启用本地化
|
// 启用本地化
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -15,14 +15,10 @@
|
|||||||
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
||||||
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
|
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
|
||||||
|
|
||||||
|
<CETCompat>false</CETCompat>
|
||||||
|
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||||
<!--动态适用GC-->
|
<!--动态适用GC-->
|
||||||
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
|
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
|
||||||
<CETCompat>false</CETCompat>
|
|
||||||
<!--使用自托管线程池-->
|
|
||||||
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
|
|
||||||
|
|
||||||
<!--使用工作站GC-->
|
|
||||||
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
|
|
||||||
|
|
||||||
<!--<PlatformTarget>x86</PlatformTarget>-->
|
<!--<PlatformTarget>x86</PlatformTarget>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -53,9 +49,9 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net10.0' ">
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET10Version)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET10Version)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 177 KiB |
34
src/Admin/ThingsGateway.AdminServer/wwwroot/pwa-install.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
let installPromptTriggered = false;
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
||||||
|
return match ? match[2] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasShownInstallPrompt() {
|
||||||
|
return getCookie("tgPWAInstallPromptShown") === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
function markInstallPromptShown() {
|
||||||
|
document.cookie = "tgPWAInstallPromptShown=true; max-age=31536000; path=/";
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('beforeinstallprompt', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!hasShownInstallPrompt() && !installPromptTriggered) {
|
||||||
|
installPromptTriggered = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
e.prompt()
|
||||||
|
.then(() => e.userChoice)
|
||||||
|
.then(choiceResult => {
|
||||||
|
markInstallPromptShown();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
// 可选错误处理
|
||||||
|
});
|
||||||
|
}, 2000); // 延迟 2 秒提示
|
||||||
|
} else {
|
||||||
|
// console.log("已提示过安装,不再弹出");
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -477,7 +477,7 @@ public class ConcurrentList<T> : IList<T>, IReadOnlyList<T>
|
|||||||
{
|
{
|
||||||
lock (((ICollection)m_list).SyncRoot)
|
lock (((ICollection)m_list).SyncRoot)
|
||||||
{
|
{
|
||||||
return m_list.IndexOf(item);
|
return m_list.LastIndexOf(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
|
public static class EnumerableQueryPageOptionsExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<T> GetData<T>(this IEnumerable<T> datas, QueryPageOptions option, out int totalCount, FilterKeyValueAction where = null)
|
||||||
|
{
|
||||||
|
totalCount = 0;
|
||||||
|
if (datas == null)
|
||||||
|
return new List<T>();
|
||||||
|
where ??= option.ToFilter();
|
||||||
|
if (where.HasFilters())
|
||||||
|
{
|
||||||
|
datas = datas.Where(where.GetFilterFunc<T>());//name asc模式
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.SortList.Count > 0)
|
||||||
|
{
|
||||||
|
datas = datas.Sort(option.SortList);//name asc模式
|
||||||
|
}
|
||||||
|
if (option.AdvancedSortList.Count > 0)
|
||||||
|
{
|
||||||
|
datas = datas.Sort(option.AdvancedSortList);//name asc模式
|
||||||
|
}
|
||||||
|
if (option.SortOrder != SortOrder.Unset && !option.SortName.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
datas = datas.Sort(option.SortName, option.SortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount = datas.Count();
|
||||||
|
|
||||||
|
if (option.IsPage)
|
||||||
|
{
|
||||||
|
datas = datas.Skip((option.PageIndex - 1) * option.PageItems).Take(option.PageItems);
|
||||||
|
}
|
||||||
|
else if (option.IsVirtualScroll)
|
||||||
|
{
|
||||||
|
datas = datas.Skip((option.StartIndex) * option.PageItems).Take(option.PageItems);
|
||||||
|
}
|
||||||
|
return datas;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
||||||
|
{
|
||||||
|
if (queryFunc != null)
|
||||||
|
query = queryFunc(query);
|
||||||
|
where ??= option.ToFilter();
|
||||||
|
|
||||||
|
if (where.HasFilters())
|
||||||
|
{
|
||||||
|
query = query.Where(where.GetFilterFunc<T>());//name asc模式
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(option.SortName))
|
||||||
|
{
|
||||||
|
var invoker = Utility.GetSortFunc<T>();
|
||||||
|
query = invoker(query, option.SortName, option.SortOrder);
|
||||||
|
}
|
||||||
|
else if (option.SortList.Count > 0)
|
||||||
|
{
|
||||||
|
var invoker = Utility.GetSortListFunc<T>();
|
||||||
|
query = invoker(query, option.SortList);
|
||||||
|
}
|
||||||
|
else if (option.AdvancedSortList.Count > 0)
|
||||||
|
{
|
||||||
|
var invoker = Utility.GetSortListFunc<T>();
|
||||||
|
query = invoker(query, option.AdvancedSortList);
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据查询条件返回QueryData
|
||||||
|
/// </summary>
|
||||||
|
public static QueryData<T> GetQueryData<T>(this IEnumerable<T> datas, QueryPageOptions option, FilterKeyValueAction where = null)
|
||||||
|
{
|
||||||
|
var ret = new QueryData<T>()
|
||||||
|
{
|
||||||
|
IsSorted = option.SortOrder != SortOrder.Unset,
|
||||||
|
IsFiltered = option.Filters.Count > 0,
|
||||||
|
IsAdvanceSearch = option.AdvanceSearches.Count > 0 || option.CustomerSearches.Count > 0,
|
||||||
|
IsSearch = option.Searches.Count > 0
|
||||||
|
};
|
||||||
|
var items = datas.GetData(option, out var totalCount, where);
|
||||||
|
ret.TotalCount = totalCount;
|
||||||
|
|
||||||
|
if (totalCount > 0)
|
||||||
|
{
|
||||||
|
if (!items.Any() && option.PageIndex != 1)
|
||||||
|
{
|
||||||
|
option.PageIndex = 1;
|
||||||
|
items = datas.GetData(option, out totalCount, where);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.Items = items.ToList();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据查询条件返回QueryData
|
||||||
|
/// </summary>
|
||||||
|
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
|
||||||
|
{
|
||||||
|
var ret = new QueryData<SelectedItem>()
|
||||||
|
{
|
||||||
|
IsSorted = false,
|
||||||
|
IsFiltered = false,
|
||||||
|
IsAdvanceSearch = false,
|
||||||
|
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||||
|
};
|
||||||
|
|
||||||
|
var items = datas.Skip((option.StartIndex)).Take(option.Count);
|
||||||
|
ret.TotalCount = datas.Count();
|
||||||
|
|
||||||
|
ret.Items = func(items).ToList();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
using ThingsGateway.Common.Extension;
|
using ThingsGateway.Common.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.DB;
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 导出excel扩展
|
/// 导出excel扩展
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
using Microsoft.AspNetCore.Components.Forms;
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace ThingsGateway.DB;
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
@@ -31,8 +31,8 @@ public static class GenericExtensions
|
|||||||
|
|
||||||
// 比较oldModel和model的属性,找出差异
|
// 比较oldModel和model的属性,找出差异
|
||||||
var differences = properties
|
var differences = properties
|
||||||
.Where(prop => prop.CanRead && prop.CanWrite) // 确保属性可读可写
|
.Where(prop => prop.CanRead && prop.CanWrite && !Equals(prop.GetValue(oldModel), prop.GetValue(model))) // 确保属性可读可写
|
||||||
.Where(prop => !Equals(prop.GetValue(oldModel), prop.GetValue(model))) // 找出值不同的属性
|
// 找出值不同的属性
|
||||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(model)); // 将属性名和新值存储到字典中
|
.ToDictionary(prop => prop.Name, prop => prop.GetValue(model)); // 将属性名和新值存储到字典中
|
||||||
|
|
||||||
// 应用差异到channels列表中的每个Channel对象
|
// 应用差异到channels列表中的每个Channel对象
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Common.Extension;
|
namespace ThingsGateway.Common.Extension;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对象拓展类
|
/// 对象拓展类
|
||||||
@@ -48,113 +50,7 @@ public static class ObjectExtensions
|
|||||||
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
|
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTimeOffset 转换成本地 DateTime
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTime ConvertToDateTime(this DateTimeOffset dateTime)
|
|
||||||
{
|
|
||||||
if (dateTime.Offset.Equals(TimeSpan.Zero))
|
|
||||||
return dateTime.UtcDateTime;
|
|
||||||
if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime)))
|
|
||||||
return dateTime.ToLocalTime().DateTime;
|
|
||||||
else
|
|
||||||
return dateTime.DateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTimeOffset? 转换成本地 DateTime?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTime? ConvertToDateTime(this DateTimeOffset? dateTime)
|
|
||||||
{
|
|
||||||
return dateTime.HasValue ? dateTime.Value.ConvertToDateTime() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTime 转换成 DateTimeOffset
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime)
|
|
||||||
{
|
|
||||||
return DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTime? 转换成 DateTimeOffset?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTimeOffset? ConvertToDateTimeOffset(this DateTime? dateTime)
|
|
||||||
{
|
|
||||||
return dateTime.HasValue ? dateTime.Value.ConvertToDateTimeOffset() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将流保存到本地磁盘
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static void CopyToSave(this Stream stream, string path)
|
|
||||||
{
|
|
||||||
// 空检查
|
|
||||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path));
|
|
||||||
|
|
||||||
using var fileStream = File.Create(path);
|
|
||||||
stream.CopyTo(fileStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将字节数组保存到本地磁盘
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes"></param>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static void CopyToSave(this byte[] bytes, string path)
|
|
||||||
{
|
|
||||||
using var stream = new MemoryStream(bytes);
|
|
||||||
stream.CopyToSave(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将流保存到本地磁盘
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
/// <param name="path">需包含文件名完整路径</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task CopyToSaveAsync(this Stream stream, string path)
|
|
||||||
{
|
|
||||||
// 空检查
|
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文件名判断
|
|
||||||
if (string.IsNullOrWhiteSpace(Path.GetFileName(path)))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("The parameter of <path> parameter must include the complete file name.");
|
|
||||||
}
|
|
||||||
|
|
||||||
using var fileStream = File.Create(path);
|
|
||||||
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将字节数组保存到本地磁盘
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes"></param>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task CopyToSaveAsync(this byte[] bytes, string path)
|
|
||||||
{
|
|
||||||
using var stream = new MemoryStream(bytes);
|
|
||||||
await stream.CopyToSaveAsync(path).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 合并两个字典
|
/// 合并两个字典
|
||||||
@@ -186,7 +82,7 @@ public static class ObjectExtensions
|
|||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="dic">字典</param>
|
/// <param name="dic">字典</param>
|
||||||
/// <param name="newDic">新字典</param>
|
/// <param name="newDic">新字典</param>
|
||||||
internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic)
|
internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic)
|
||||||
{
|
{
|
||||||
foreach (var (key, value) in newDic)
|
foreach (var (key, value) in newDic)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public static class ParallelExtensions
|
|||||||
/// <typeparam name="T">集合元素类型</typeparam>
|
/// <typeparam name="T">集合元素类型</typeparam>
|
||||||
/// <param name="source">要操作的集合</param>
|
/// <param name="source">要操作的集合</param>
|
||||||
/// <param name="body">要执行的操作</param>
|
/// <param name="body">要执行的操作</param>
|
||||||
public static void ParallelForEach<T>(this IList<T> source, Action<T> body)
|
public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T> body)
|
||||||
{
|
{
|
||||||
ParallelOptions options = new();
|
ParallelOptions options = new();
|
||||||
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
|
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
|
||||||
@@ -38,7 +38,7 @@ public static class ParallelExtensions
|
|||||||
/// <typeparam name="T">集合元素类型</typeparam>
|
/// <typeparam name="T">集合元素类型</typeparam>
|
||||||
/// <param name="source">要操作的集合</param>
|
/// <param name="source">要操作的集合</param>
|
||||||
/// <param name="body">要执行的操作</param>
|
/// <param name="body">要执行的操作</param>
|
||||||
public static void ParallelForEach<T>(this IList<T> source, Action<T, ParallelLoopState, long> body)
|
public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T, ParallelLoopState, long> body)
|
||||||
{
|
{
|
||||||
ParallelOptions options = new();
|
ParallelOptions options = new();
|
||||||
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
|
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
|
||||||
@@ -53,7 +53,7 @@ public static class ParallelExtensions
|
|||||||
/// <param name="source">要操作的集合</param>
|
/// <param name="source">要操作的集合</param>
|
||||||
/// <param name="body">要执行的操作</param>
|
/// <param name="body">要执行的操作</param>
|
||||||
/// <param name="parallelCount">最大并行度</param>
|
/// <param name="parallelCount">最大并行度</param>
|
||||||
public static void ParallelForEach<T>(this IList<T> source, Action<T> body, int parallelCount)
|
public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T> body, int parallelCount)
|
||||||
{
|
{
|
||||||
// 创建并行操作的选项对象,设置最大并行度为指定的值
|
// 创建并行操作的选项对象,设置最大并行度为指定的值
|
||||||
var options = new ParallelOptions();
|
var options = new ParallelOptions();
|
||||||
@@ -109,7 +109,7 @@ public static class ParallelExtensions
|
|||||||
/// <param name="parallelCount">最大并行度</param>
|
/// <param name="parallelCount">最大并行度</param>
|
||||||
/// <param name="cancellationToken">取消操作的标志</param>
|
/// <param name="cancellationToken">取消操作的标志</param>
|
||||||
/// <returns>表示异步操作的任务</returns>
|
/// <returns>表示异步操作的任务</returns>
|
||||||
public static Task ParallelForEachAsync<T>(this IList<T> source, Func<T, CancellationToken, ValueTask> body, int parallelCount, CancellationToken cancellationToken = default)
|
public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, CancellationToken, ValueTask> body, int parallelCount, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
// 创建并行操作的选项对象,设置最大并行度和取消标志
|
// 创建并行操作的选项对象,设置最大并行度和取消标志
|
||||||
var options = new ParallelOptions();
|
var options = new ParallelOptions();
|
||||||
@@ -126,7 +126,7 @@ public static class ParallelExtensions
|
|||||||
/// <param name="body">异步执行的操作</param>
|
/// <param name="body">异步执行的操作</param>
|
||||||
/// <param name="cancellationToken">取消操作的标志</param>
|
/// <param name="cancellationToken">取消操作的标志</param>
|
||||||
/// <returns>表示异步操作的任务</returns>
|
/// <returns>表示异步操作的任务</returns>
|
||||||
public static Task ParallelForEachAsync<T>(this IList<T> source, Func<T, CancellationToken, ValueTask> body, CancellationToken cancellationToken = default)
|
public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, CancellationToken, ValueTask> body, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return ParallelForEachAsync(source, body, Environment.ProcessorCount, cancellationToken);
|
return ParallelForEachAsync(source, body, Environment.ProcessorCount, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
|
|
||||||
using ThingsGateway.Common.Extension;
|
using ThingsGateway.Common.Extension;
|
||||||
|
|
||||||
|
|
||||||
#if NET8_0_OR_GREATER
|
#if NET8_0_OR_GREATER
|
||||||
using System.Collections.Frozen;
|
using System.Collections.Frozen;
|
||||||
#endif
|
#endif
|
||||||
@@ -27,7 +28,7 @@ internal class CacheManager
|
|||||||
{
|
{
|
||||||
private IMemoryCache Cache { get; set; }
|
private IMemoryCache Cache { get; set; }
|
||||||
|
|
||||||
private IServiceProvider Provider { get; set; }
|
private static IServiceProvider Provider => App.RootServices;
|
||||||
|
|
||||||
[NotNull]
|
[NotNull]
|
||||||
private static CacheManager? Instance { get; set; }
|
private static CacheManager? Instance { get; set; }
|
||||||
@@ -40,8 +41,7 @@ internal class CacheManager
|
|||||||
static CacheManager()
|
static CacheManager()
|
||||||
{
|
{
|
||||||
Instance = new();
|
Instance = new();
|
||||||
Instance.Provider = App.RootServices;
|
Instance.Cache = Provider.GetRequiredService<IMemoryCache>();
|
||||||
Instance.Cache = Instance.Provider.GetRequiredService<IMemoryCache>();
|
|
||||||
Options = App.RootServices.GetRequiredService<IOptions<BootstrapBlazorOptions>>().Value;
|
Options = App.RootServices.GetRequiredService<IOptions<BootstrapBlazorOptions>>().Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,37 +105,6 @@ internal class CacheManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置 App 开始时间
|
|
||||||
/// </summary>
|
|
||||||
public void SetStartTime() => SetStartTime(DateTimeOffset.Now);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置 App 开始时间
|
|
||||||
/// </summary>
|
|
||||||
private void SetStartTime(DateTimeOffset startDateTimeOffset)
|
|
||||||
{
|
|
||||||
GetOrCreate("BootstrapBlazor_StartTime", entry =>
|
|
||||||
{
|
|
||||||
entry.Priority = CacheItemPriority.NeverRemove;
|
|
||||||
return startDateTimeOffset;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取 App 开始时间
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public DateTimeOffset GetStartTime()
|
|
||||||
{
|
|
||||||
var ret = DateTimeOffset.MinValue;
|
|
||||||
if (Cache.TryGetValue("BootstrapBlazor_StartTime", out var v) && v is DateTimeOffset d)
|
|
||||||
{
|
|
||||||
ret = d;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获得 缓存数量
|
/// 获得 缓存数量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -236,7 +205,7 @@ internal class CacheManager
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IStringLocalizer? CreateLocalizerByType(Type resourceSource) => resourceSource.Assembly.IsDynamic
|
public static IStringLocalizer? CreateLocalizerByType(Type resourceSource) => resourceSource.Assembly.IsDynamic
|
||||||
? null
|
? null
|
||||||
: Instance.Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource);
|
: Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获得 <see cref="JsonLocalizationOptions"/> 值
|
/// 获得 <see cref="JsonLocalizationOptions"/> 值
|
||||||
@@ -244,7 +213,7 @@ internal class CacheManager
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static JsonLocalizationOptions GetJsonLocalizationOption()
|
private static JsonLocalizationOptions GetJsonLocalizationOption()
|
||||||
{
|
{
|
||||||
var localizationOptions = Instance.Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
|
var localizationOptions = Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
|
||||||
return localizationOptions.Value;
|
return localizationOptions.Value;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -253,7 +222,7 @@ internal class CacheManager
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static BootstrapBlazorOptions GetBootstrapBlazorOption()
|
private static BootstrapBlazorOptions GetBootstrapBlazorOption()
|
||||||
{
|
{
|
||||||
var localizationOptions = Instance.Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
|
var localizationOptions = Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
|
||||||
return localizationOptions.Value;
|
return localizationOptions.Value;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -269,7 +238,7 @@ internal class CacheManager
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
IStringLocalizer? ret = null;
|
IStringLocalizer? ret = null;
|
||||||
var factories = Instance.Provider.GetServices<IStringLocalizerFactory>();
|
var factories = Provider.GetServices<IStringLocalizerFactory>();
|
||||||
var factory = factories.LastOrDefault(a => a is not JsonStringLocalizerFactory);
|
var factory = factories.LastOrDefault(a => a is not JsonStringLocalizerFactory);
|
||||||
if (factory != null)
|
if (factory != null)
|
||||||
{
|
{
|
||||||
@@ -287,8 +256,15 @@ internal class CacheManager
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="assembly">Assembly 程序集实例</param>
|
/// <param name="assembly">Assembly 程序集实例</param>
|
||||||
/// <param name="typeName">类型名称</param>
|
/// <param name="typeName">类型名称</param>
|
||||||
public static IEnumerable<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName)
|
public static FrozenSet<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName)
|
||||||
=> GetJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);
|
=> GetJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定文化本地化资源集合
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly 程序集实例</param>
|
||||||
|
/// <param name="typeName">类型名称</param>
|
||||||
|
public static FrozenDictionary<string, string>? GetAllHasValueStringsByTypeName(Assembly assembly, string typeName)
|
||||||
|
=> GetHasValueJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通过指定程序集获取所有本地化信息键值集合
|
/// 通过指定程序集获取所有本地化信息键值集合
|
||||||
@@ -299,7 +275,7 @@ internal class CacheManager
|
|||||||
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
|
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
|
||||||
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
|
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IEnumerable<LocalizedString>? GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
|
public static FrozenSet<LocalizedString>? GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
|
||||||
{
|
{
|
||||||
if (assembly.IsDynamic)
|
if (assembly.IsDynamic)
|
||||||
{
|
{
|
||||||
@@ -309,13 +285,15 @@ internal class CacheManager
|
|||||||
cultureName ??= CultureInfo.CurrentUICulture.Name;
|
cultureName ??= CultureInfo.CurrentUICulture.Name;
|
||||||
if (string.IsNullOrEmpty(cultureName))
|
if (string.IsNullOrEmpty(cultureName))
|
||||||
{
|
{
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
|
var key = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
|
||||||
|
var typeKey = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{typeName}-{cultureName}";
|
||||||
if (forceLoad)
|
if (forceLoad)
|
||||||
{
|
{
|
||||||
Instance.Cache.Remove(key);
|
Instance.Cache.Remove(key);
|
||||||
|
Instance.Cache.Remove(typeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
var localizedItems = Instance.GetOrCreate(key, entry =>
|
var localizedItems = Instance.GetOrCreate(key, entry =>
|
||||||
@@ -336,16 +314,77 @@ internal class CacheManager
|
|||||||
return items.ToHashSet();
|
return items.ToHashSet();
|
||||||
#endif
|
#endif
|
||||||
});
|
});
|
||||||
return localizedItems.Where(item => item.SearchedLocation!.Equals(typeName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
var typeLocalizedItems = Instance.GetOrCreate(typeKey, entry =>
|
||||||
|
{
|
||||||
|
return localizedItems.Where(item => item.SearchedLocation!.Equals(typeName, StringComparison.OrdinalIgnoreCase)).ToFrozenSet();
|
||||||
|
});
|
||||||
|
return typeLocalizedItems;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 通过指定程序集获取所有本地化信息键值集合
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="option">JsonLocalizationOptions 实例</param>
|
||||||
|
/// <param name="assembly">Assembly 程序集实例</param>
|
||||||
|
/// <param name="typeName">类型名称</param>
|
||||||
|
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
|
||||||
|
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static FrozenDictionary<string, string>? GetHasValueJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
|
||||||
|
{
|
||||||
|
if (assembly.IsDynamic)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
cultureName ??= CultureInfo.CurrentUICulture.Name;
|
||||||
/// 通过 ILocalizationResolve 接口实现类获得本地化键值集合
|
if (string.IsNullOrEmpty(cultureName))
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="typeName"></param>
|
return null;
|
||||||
/// <param name="includeParentCultures"></param>
|
}
|
||||||
/// <returns></returns>
|
|
||||||
public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
|
var key = $"{CacheKeyPrefix}-{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
|
||||||
|
|
||||||
|
|
||||||
|
var typeKey = $"{CacheKeyPrefix}-{nameof(GetHasValueJsonStringByTypeName)}-{assembly.GetUniqueName()}-{typeName}-{cultureName}";
|
||||||
|
if (forceLoad)
|
||||||
|
{
|
||||||
|
Instance.Cache.Remove(key);
|
||||||
|
Instance.Cache.Remove(typeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
var localizedItems = Instance.GetOrCreate(key, entry =>
|
||||||
|
{
|
||||||
|
var sections = option.GetJsonStringFromAssembly(assembly, cultureName);
|
||||||
|
var items = sections.SelectMany(section => section.GetChildren().Select(kv =>
|
||||||
|
{
|
||||||
|
var value = kv.Value;
|
||||||
|
if (value == null && option.UseKeyWhenValueIsNull == true)
|
||||||
|
{
|
||||||
|
value = kv.Key;
|
||||||
|
}
|
||||||
|
return new LocalizedString(kv.Key, value ?? "", false, section.Key);
|
||||||
|
}));
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
return items.ToFrozenSet();
|
||||||
|
#else
|
||||||
|
return items.ToHashSet();
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
var typeLocalizedItems = Instance.GetOrCreate(typeKey, entry =>
|
||||||
|
{
|
||||||
|
return localizedItems.Where(item => item.SearchedLocation!.Equals(typeName, StringComparison.OrdinalIgnoreCase) && !item.ResourceNotFound).ToFrozenDictionary(a => a.Name, a => a.Value);
|
||||||
|
});
|
||||||
|
return typeLocalizedItems;
|
||||||
|
}
|
||||||
|
///// <summary>
|
||||||
|
///// 通过 ILocalizationResolve 接口实现类获得本地化键值集合
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="typeName"></param>
|
||||||
|
///// <param name="includeParentCultures"></param>
|
||||||
|
///// <returns></returns>
|
||||||
|
//public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region DisplayName
|
#region DisplayName
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (Logger?.IsEnabled(LogLevel.Error) == true)
|
||||||
Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
|
Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -80,50 +81,16 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private string? GetStringSafely(string name) => GetStringFromJson(name);
|
private string? GetStringSafely(string name) => GetStringFromJson(name);
|
||||||
|
|
||||||
private string? GetStringFromService(string name)
|
|
||||||
{
|
|
||||||
// get string from inject service
|
|
||||||
string? ret = null;
|
|
||||||
if (jsonLocalizationOptions.DisableGetLocalizerFromService == false)
|
|
||||||
{
|
|
||||||
var localizer = Utility.GetStringLocalizerFromService(Assembly, typeName);
|
|
||||||
if (localizer != null && localizer is not JsonStringLocalizer)
|
|
||||||
{
|
|
||||||
var l = localizer[name];
|
|
||||||
if (!l.ResourceNotFound)
|
|
||||||
{
|
|
||||||
ret = l.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? GetStringFromResourceManager(string name)
|
|
||||||
{
|
|
||||||
string? ret = null;
|
|
||||||
if (jsonLocalizationOptions.DisableGetLocalizerFromResourceManager == false)
|
|
||||||
{
|
|
||||||
ret = GetStringSafely(name, CultureInfo.CurrentUICulture);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ConcurrentHashSet<string> _missingManifestCache = [];
|
private readonly ConcurrentHashSet<string> _missingManifestCache = [];
|
||||||
private string? GetStringFromJson(string name)
|
private string? GetStringFromJson(string name)
|
||||||
{
|
{
|
||||||
// get string from json localization file
|
// get string from json localization file
|
||||||
var localizerStrings = MegerResolveLocalizers(CacheManager.GetAllStringsByTypeName(Assembly, typeName));
|
|
||||||
var cacheKey = $"name={name}&culture={CultureInfo.CurrentUICulture.Name}";
|
var cacheKey = $"name={name}&culture={CultureInfo.CurrentUICulture.Name}";
|
||||||
string? ret = null;
|
string? ret = null;
|
||||||
if (!_missingManifestCache.Contain(cacheKey))
|
if (!_missingManifestCache.Contain(cacheKey))
|
||||||
{
|
{
|
||||||
var l = localizerStrings.Find(i => i.Name == name);
|
var localizerStrings = CacheManager.GetAllHasValueStringsByTypeName(Assembly, typeName);
|
||||||
if (l is { ResourceNotFound: false })
|
if (localizerStrings?.TryGetValue(name, out ret) != true)
|
||||||
{
|
|
||||||
ret = l.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// 如果没有找到资源信息则尝试从父类中查找
|
// 如果没有找到资源信息则尝试从父类中查找
|
||||||
ret ??= GetStringFromBaseType(name);
|
ret ??= GetStringFromBaseType(name);
|
||||||
@@ -149,39 +116,25 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
|||||||
if (baseType != type)
|
if (baseType != type)
|
||||||
{
|
{
|
||||||
var baseAssembly = baseType.Assembly;
|
var baseAssembly = baseType.Assembly;
|
||||||
var localizerStrings = MegerResolveLocalizers(CacheManager.GetAllStringsByTypeName(baseAssembly, baseType.FullName!));
|
var localizerStrings = CacheManager.GetAllHasValueStringsByTypeName(baseAssembly, baseType.FullName!);
|
||||||
var l = localizerStrings.Find(i => i.Name == name);
|
_ = localizerStrings?.TryGetValue(name, out ret);
|
||||||
if (l is { ResourceNotFound: false })
|
|
||||||
{
|
|
||||||
ret = l.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LocalizedString> MegerResolveLocalizers(IEnumerable<LocalizedString>? localizerStrings)
|
|
||||||
{
|
|
||||||
var localizers = new List<LocalizedString>(CacheManager.GetTypeStringsFromResolve(typeName));
|
|
||||||
|
|
||||||
if (localizerStrings != null)
|
|
||||||
{
|
|
||||||
localizers.AddRange(localizerStrings);
|
|
||||||
}
|
|
||||||
return localizers;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleMissingResourceItem(string name)
|
private void HandleMissingResourceItem(string name)
|
||||||
{
|
{
|
||||||
localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name);
|
localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name);
|
||||||
if (jsonLocalizationOptions.IgnoreLocalizerMissing == false)
|
if (jsonLocalizationOptions.IgnoreLocalizerMissing == false)
|
||||||
{
|
{
|
||||||
|
if (Logger?.IsEnabled(LogLevel.Information) == true)
|
||||||
Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
|
Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
|
||||||
}
|
}
|
||||||
_missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}");
|
_missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LocalizedString>? _allLocalizerdStrings;
|
private LocalizedString[]? _allLocalizerdStrings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前语言的所有资源信息
|
/// 获取当前语言的所有资源信息
|
||||||
@@ -196,7 +149,7 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
|
|||||||
?? GetAllStringsFromBase()
|
?? GetAllStringsFromBase()
|
||||||
?? GetAllStringsFromJson();
|
?? GetAllStringsFromJson();
|
||||||
|
|
||||||
_allLocalizerdStrings = MegerResolveLocalizers(items);
|
_allLocalizerdStrings = items.ToArray();
|
||||||
}
|
}
|
||||||
return _allLocalizerdStrings;
|
return _allLocalizerdStrings;
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
<PackageReference Include="BootstrapBlazor" Version="9.10.0" />
|
<PackageReference Include="BootstrapBlazor" Version="9.12.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
using Yitter.IdGenerator;
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
namespace ThingsGateway.DB;
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 公共功能
|
/// 公共功能
|
||||||
@@ -27,10 +27,15 @@ public abstract class PrimaryIdEntity : IPrimaryIdEntity
|
|||||||
public virtual long Id { get; set; }
|
public virtual long Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IPrimaryKeyEntity
|
||||||
|
{
|
||||||
|
string ExtJson { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主键实体基类
|
/// 主键实体基类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class PrimaryKeyEntity : PrimaryIdEntity
|
public abstract class PrimaryKeyEntity : PrimaryIdEntity, IPrimaryKeyEntity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 拓展信息
|
/// 拓展信息
|
||||||
|
|||||||
@@ -16,74 +16,6 @@ namespace ThingsGateway.DB;
|
|||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public static class QueryPageOptionsExtensions
|
public static class QueryPageOptionsExtensions
|
||||||
{
|
{
|
||||||
public static IEnumerable<T> GetData<T>(this IEnumerable<T> datas, QueryPageOptions option, out int totalCount, FilterKeyValueAction where = null)
|
|
||||||
{
|
|
||||||
totalCount = 0;
|
|
||||||
if (datas == null)
|
|
||||||
return new List<T>();
|
|
||||||
where ??= option.ToFilter();
|
|
||||||
if (where.HasFilters())
|
|
||||||
{
|
|
||||||
datas = datas.Where(where.GetFilterFunc<T>());//name asc模式
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.SortList.Count > 0)
|
|
||||||
{
|
|
||||||
datas = datas.Sort(option.SortList);//name asc模式
|
|
||||||
}
|
|
||||||
if (option.AdvancedSortList.Count > 0)
|
|
||||||
{
|
|
||||||
datas = datas.Sort(option.AdvancedSortList);//name asc模式
|
|
||||||
}
|
|
||||||
if (option.SortOrder != SortOrder.Unset && !option.SortName.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
datas = datas.Sort(option.SortName, option.SortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
totalCount = datas.Count();
|
|
||||||
|
|
||||||
if (option.IsPage)
|
|
||||||
{
|
|
||||||
datas = datas.Skip((option.PageIndex - 1) * option.PageItems).Take(option.PageItems);
|
|
||||||
}
|
|
||||||
else if (option.IsVirtualScroll)
|
|
||||||
{
|
|
||||||
datas = datas.Skip((option.StartIndex) * option.PageItems).Take(option.PageItems);
|
|
||||||
}
|
|
||||||
return datas;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
|
||||||
{
|
|
||||||
if (queryFunc != null)
|
|
||||||
query = queryFunc(query);
|
|
||||||
where ??= option.ToFilter();
|
|
||||||
|
|
||||||
if (where.HasFilters())
|
|
||||||
{
|
|
||||||
query = query.Where(where.GetFilterFunc<T>());//name asc模式
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(option.SortName))
|
|
||||||
{
|
|
||||||
var invoker = Utility.GetSortFunc<T>();
|
|
||||||
query = invoker(query, option.SortName, option.SortOrder);
|
|
||||||
}
|
|
||||||
else if (option.SortList.Count > 0)
|
|
||||||
{
|
|
||||||
var invoker = Utility.GetSortListFunc<T>();
|
|
||||||
query = invoker(query, option.SortList);
|
|
||||||
}
|
|
||||||
else if (option.AdvancedSortList.Count > 0)
|
|
||||||
{
|
|
||||||
var invoker = Utility.GetSortListFunc<T>();
|
|
||||||
query = invoker(query, option.AdvancedSortList);
|
|
||||||
}
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据查询条件返回sqlsugar ISugarQueryable
|
/// 根据查询条件返回sqlsugar ISugarQueryable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -111,50 +43,4 @@ public static class QueryPageOptionsExtensions
|
|||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据查询条件返回QueryData
|
|
||||||
/// </summary>
|
|
||||||
public static QueryData<T> GetQueryData<T>(this IEnumerable<T> datas, QueryPageOptions option, FilterKeyValueAction where = null)
|
|
||||||
{
|
|
||||||
var ret = new QueryData<T>()
|
|
||||||
{
|
|
||||||
IsSorted = option.SortOrder != SortOrder.Unset,
|
|
||||||
IsFiltered = option.Filters.Count > 0,
|
|
||||||
IsAdvanceSearch = option.AdvanceSearches.Count > 0 || option.CustomerSearches.Count > 0,
|
|
||||||
IsSearch = option.Searches.Count > 0
|
|
||||||
};
|
|
||||||
var items = datas.GetData(option, out var totalCount, where);
|
|
||||||
ret.TotalCount = totalCount;
|
|
||||||
|
|
||||||
if (totalCount > 0)
|
|
||||||
{
|
|
||||||
if (!items.Any() && option.PageIndex != 1)
|
|
||||||
{
|
|
||||||
option.PageIndex = 1;
|
|
||||||
items = datas.GetData(option, out totalCount, where);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.Items = items.ToList();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据查询条件返回QueryData
|
|
||||||
/// </summary>
|
|
||||||
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
|
|
||||||
{
|
|
||||||
var ret = new QueryData<SelectedItem>()
|
|
||||||
{
|
|
||||||
IsSorted = false,
|
|
||||||
IsFiltered = false,
|
|
||||||
IsAdvanceSearch = false,
|
|
||||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
|
||||||
};
|
|
||||||
|
|
||||||
var items = datas.Skip((option.StartIndex)).Take(option.Count);
|
|
||||||
ret.TotalCount = datas.Count();
|
|
||||||
|
|
||||||
ret.Items = func(items).ToList();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
using ThingsGateway;
|
using ThingsGateway;
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.Reflection;
|
using ThingsGateway.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Hosting;
|
namespace Microsoft.Extensions.Hosting;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
namespace ThingsGateway.Extensions;
|
namespace ThingsGateway.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对象拓展类
|
/// 对象拓展类
|
||||||
@@ -28,70 +28,10 @@ namespace ThingsGateway.Extensions;
|
|||||||
[SuppressSniffer]
|
[SuppressSniffer]
|
||||||
public static class ObjectExtensions
|
public static class ObjectExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTimeOffset 转换成本地 DateTime
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTime ConvertToDateTime(this DateTimeOffset dateTime)
|
|
||||||
{
|
|
||||||
if (dateTime.Offset.Equals(TimeSpan.Zero))
|
|
||||||
return dateTime.UtcDateTime;
|
|
||||||
if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime)))
|
|
||||||
return dateTime.ToLocalTime().DateTime;
|
|
||||||
else
|
|
||||||
return dateTime.DateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTimeOffset? 转换成本地 DateTime?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTime? ConvertToDateTime(this DateTimeOffset? dateTime)
|
|
||||||
{
|
|
||||||
return dateTime.HasValue ? dateTime.Value.ConvertToDateTime() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTime 转换成 DateTimeOffset
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime)
|
|
||||||
{
|
|
||||||
return DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 DateTime? 转换成 DateTimeOffset?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateTime"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DateTimeOffset? ConvertToDateTimeOffset(this DateTime? dateTime)
|
|
||||||
{
|
|
||||||
return dateTime.HasValue ? dateTime.Value.ConvertToDateTimeOffset() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将时间戳转换为 DateTime
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="timestamp"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
internal static DateTime ConvertToDateTime(this long timestamp)
|
|
||||||
{
|
|
||||||
var timeStampDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
||||||
var digitCount = (int)Math.Floor(Math.Log10(timestamp) + 1);
|
|
||||||
|
|
||||||
if (digitCount != 13 && digitCount != 10)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Data is not a valid timestamp format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (digitCount == 13
|
|
||||||
? timeStampDateTime.AddMilliseconds(timestamp) // 13 位时间戳
|
|
||||||
: timeStampDateTime.AddSeconds(timestamp)).ToLocalTime(); // 10 位时间戳
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将 IFormFile 转换成 byte[]
|
/// 将 IFormFile 转换成 byte[]
|
||||||
@@ -265,7 +205,7 @@ public static class ObjectExtensions
|
|||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="dic">字典</param>
|
/// <param name="dic">字典</param>
|
||||||
/// <param name="newDic">新字典</param>
|
/// <param name="newDic">新字典</param>
|
||||||
internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic)
|
internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic)
|
||||||
{
|
{
|
||||||
foreach (var (key, value) in newDic)
|
foreach (var (key, value) in newDic)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
|
|||||||
/// <param name="mvcBuilder"></param>
|
/// <param name="mvcBuilder"></param>
|
||||||
/// <param name="configure"></param>
|
/// <param name="configure"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<ConcurrentDictionary<Type, Type>> configure = default)
|
public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<NonBlockingDictionary<Type, Type>> configure = default)
|
||||||
{
|
{
|
||||||
mvcBuilder.Services.AddFromConvertBinding(configure);
|
mvcBuilder.Services.AddFromConvertBinding(configure);
|
||||||
|
|
||||||
@@ -107,13 +107,13 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
|
|||||||
/// <param name="services"></param>
|
/// <param name="services"></param>
|
||||||
/// <param name="configure"></param>
|
/// <param name="configure"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<ConcurrentDictionary<Type, Type>> configure = default)
|
public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<NonBlockingDictionary<Type, Type>> configure = default)
|
||||||
{
|
{
|
||||||
// 非 Web 环境跳过注册
|
// 非 Web 环境跳过注册
|
||||||
if (App.WebHostEnvironment == default) return services;
|
if (App.WebHostEnvironment == default) return services;
|
||||||
|
|
||||||
// 定义模型绑定转换器集合
|
// 定义模型绑定转换器集合
|
||||||
var modelBinderConverts = new ConcurrentDictionary<Type, Type>();
|
var modelBinderConverts = new NonBlockingDictionary<Type, Type>();
|
||||||
modelBinderConverts.TryAdd(typeof(DateTime), typeof(DateTimeModelConvertBinder));
|
modelBinderConverts.TryAdd(typeof(DateTime), typeof(DateTimeModelConvertBinder));
|
||||||
modelBinderConverts.TryAdd(typeof(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder));
|
modelBinderConverts.TryAdd(typeof(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder));
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.AspNetCore;
|
namespace ThingsGateway.AspNetCore;
|
||||||
|
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ public class FromConvertBinder : IModelBinder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 定义模型绑定转换器集合
|
/// 定义模型绑定转换器集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts;
|
private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
|
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
|
||||||
public FromConvertBinder(ConcurrentDictionary<Type, Type> modelBinderConverts)
|
public FromConvertBinder(NonBlockingDictionary<Type, Type> modelBinderConverts)
|
||||||
{
|
{
|
||||||
_modelBinderConverts = modelBinderConverts;
|
_modelBinderConverts = modelBinderConverts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ public class FromConvertBinderProvider : IModelBinderProvider
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 定义模型绑定转换器集合
|
/// 定义模型绑定转换器集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts;
|
private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
|
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
|
||||||
public FromConvertBinderProvider(ConcurrentDictionary<Type, Type> modelBinderConverts)
|
public FromConvertBinderProvider(NonBlockingDictionary<Type, Type> modelBinderConverts)
|
||||||
{
|
{
|
||||||
_modelBinderConverts = modelBinderConverts;
|
_modelBinderConverts = modelBinderConverts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.AspNetCore;
|
namespace ThingsGateway.AspNetCore;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.AspNetCore;
|
namespace ThingsGateway.AspNetCore;
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
using ThingsGateway;
|
using ThingsGateway;
|
||||||
using ThingsGateway.ConfigurableOptions;
|
using ThingsGateway.ConfigurableOptions;
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,12 @@ public static class PBKDF2Encryption
|
|||||||
using var rng = RandomNumberGenerator.Create();
|
using var rng = RandomNumberGenerator.Create();
|
||||||
var salt = new byte[saltSize];
|
var salt = new byte[saltSize];
|
||||||
rng.GetBytes(salt);
|
rng.GetBytes(salt);
|
||||||
|
#if NET10_0_OR_GREATER
|
||||||
|
var hash = Rfc2898DeriveBytes.Pbkdf2(System.Text.Encoding.UTF8.GetBytes(text), salt, iterationCount, HashAlgorithmName.SHA256, derivedKeyLength);
|
||||||
|
#else
|
||||||
using var pbkdf2 = new Rfc2898DeriveBytes(text, salt, iterationCount, HashAlgorithmName.SHA256);
|
using var pbkdf2 = new Rfc2898DeriveBytes(text, salt, iterationCount, HashAlgorithmName.SHA256);
|
||||||
var hash = pbkdf2.GetBytes(derivedKeyLength);
|
var hash = pbkdf2.GetBytes(derivedKeyLength);
|
||||||
|
#endif
|
||||||
|
|
||||||
// 分别编码盐和哈希,并用分隔符拼接
|
// 分别编码盐和哈希,并用分隔符拼接
|
||||||
return Convert.ToBase64String(salt) + SaltHashSeparator + Convert.ToBase64String(hash);
|
return Convert.ToBase64String(salt) + SaltHashSeparator + Convert.ToBase64String(hash);
|
||||||
@@ -65,8 +68,12 @@ public static class PBKDF2Encryption
|
|||||||
if (saltBytes.Length != saltSize || storedHashBytes.Length != derivedKeyLength)
|
if (saltBytes.Length != saltSize || storedHashBytes.Length != derivedKeyLength)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#if NET10_0_OR_GREATER
|
||||||
|
var computedHash = Rfc2898DeriveBytes.Pbkdf2(System.Text.Encoding.UTF8.GetBytes(text), saltBytes, iterationCount, HashAlgorithmName.SHA256, derivedKeyLength);
|
||||||
|
#else
|
||||||
using var pbkdf2 = new Rfc2898DeriveBytes(text, saltBytes, iterationCount, HashAlgorithmName.SHA256);
|
using var pbkdf2 = new Rfc2898DeriveBytes(text, saltBytes, iterationCount, HashAlgorithmName.SHA256);
|
||||||
var computedHash = pbkdf2.GetBytes(derivedKeyLength);
|
var computedHash = pbkdf2.GetBytes(derivedKeyLength);
|
||||||
|
#endif
|
||||||
|
|
||||||
return computedHash.SequenceEqual(storedHashBytes);
|
return computedHash.SequenceEqual(storedHashBytes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.Templates.Extensions;
|
using ThingsGateway.Templates.Extensions;
|
||||||
|
|
||||||
namespace ThingsGateway.DataValidation;
|
namespace ThingsGateway.DataValidation;
|
||||||
@@ -40,7 +40,7 @@ public static class DataValidator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证类型正则表达式
|
/// 验证类型正则表达式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas;
|
private static readonly NonBlockingDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
@@ -57,7 +57,7 @@ public static class DataValidator
|
|||||||
ValidationItemMetadatas = GetValidationValidationItemMetadatas();
|
ValidationItemMetadatas = GetValidationValidationItemMetadatas();
|
||||||
|
|
||||||
// 缓存所有正则表达式
|
// 缓存所有正则表达式
|
||||||
GetValidationTypeValidationItemMetadataCached = new ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)>();
|
GetValidationTypeValidationItemMetadataCached = new NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -203,7 +203,7 @@ public static class DataValidator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取验证类型验证Item集合
|
/// 获取验证类型验证Item集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached;
|
private static readonly NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取验证类型正则表达式(需要缓存)
|
/// 获取验证类型正则表达式(需要缓存)
|
||||||
@@ -267,9 +267,9 @@ public static class DataValidator
|
|||||||
/// 获取验证类型所有有效的正则表达式
|
/// 获取验证类型所有有效的正则表达式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static ConcurrentDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas()
|
private static NonBlockingDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas()
|
||||||
{
|
{
|
||||||
var vaidationItems = new ConcurrentDictionary<string, ValidationItemMetadataAttribute>();
|
var vaidationItems = new NonBlockingDictionary<string, ValidationItemMetadataAttribute>();
|
||||||
|
|
||||||
// 查找所有 [ValidationMessageType] 类型中的 [ValidationMessage] 消息定义
|
// 查找所有 [ValidationMessageType] 类型中的 [ValidationMessage] 消息定义
|
||||||
var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields()
|
var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields()
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ public static class DependencyInjectionServiceCollectionExtensions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 类型名称集合
|
/// 类型名称集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<string, Type> TypeNamedCollection;
|
private static readonly NonBlockingDictionary<string, Type> TypeNamedCollection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建代理方法
|
/// 创建代理方法
|
||||||
@@ -374,7 +374,7 @@ public static class DependencyInjectionServiceCollectionExtensions
|
|||||||
GlobalServiceProxyType = App.EffectiveTypes
|
GlobalServiceProxyType = App.EffectiveTypes
|
||||||
.FirstOrDefault(u => typeof(AspectDispatchProxy).IsAssignableFrom(u) && typeof(IGlobalDispatchProxy).IsAssignableFrom(u) && u.IsClass && !u.IsInterface && !u.IsAbstract);
|
.FirstOrDefault(u => typeof(AspectDispatchProxy).IsAssignableFrom(u) && typeof(IGlobalDispatchProxy).IsAssignableFrom(u) && u.IsClass && !u.IsInterface && !u.IsAbstract);
|
||||||
|
|
||||||
TypeNamedCollection = new ConcurrentDictionary<string, Type>();
|
TypeNamedCollection = new NonBlockingDictionary<string, Type>();
|
||||||
DispatchCreateMethod = typeof(AspectDispatchProxy).GetMethod(nameof(AspectDispatchProxy.Create));
|
DispatchCreateMethod = typeof(AspectDispatchProxy).GetMethod(nameof(AspectDispatchProxy.Create));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ using System.Collections.Concurrent;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.UnifyResult;
|
using ThingsGateway.UnifyResult;
|
||||||
|
|
||||||
namespace ThingsGateway.DynamicApiController;
|
namespace ThingsGateway.DynamicApiController;
|
||||||
|
|||||||
@@ -28,21 +28,21 @@ internal static class Penetrates
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求动词映射字典
|
/// 请求动词映射字典
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static ConcurrentDictionary<string, string> VerbToHttpMethods { get; private set; }
|
internal static NonBlockingDictionary<string, string> VerbToHttpMethods { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 控制器排序集合
|
/// 控制器排序集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static ConcurrentDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
|
internal static NonBlockingDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static Penetrates()
|
static Penetrates()
|
||||||
{
|
{
|
||||||
ControllerOrderCollection = new ConcurrentDictionary<string, (string, int, Type)>();
|
ControllerOrderCollection = new NonBlockingDictionary<string, (string, int, Type)>();
|
||||||
|
|
||||||
VerbToHttpMethods = new ConcurrentDictionary<string, string>
|
VerbToHttpMethods = new NonBlockingDictionary<string, string>
|
||||||
{
|
{
|
||||||
["post"] = "POST",
|
["post"] = "POST",
|
||||||
["add"] = "POST",
|
["add"] = "POST",
|
||||||
@@ -67,13 +67,13 @@ internal static class Penetrates
|
|||||||
["patch"] = "PATCH"
|
["patch"] = "PATCH"
|
||||||
};
|
};
|
||||||
|
|
||||||
//IsApiControllerCached = new ConcurrentDictionary<Type, bool>();
|
//IsApiControllerCached = new NonBlockingDictionary<Type, bool>();
|
||||||
}
|
}
|
||||||
|
|
||||||
///// <summary>
|
///// <summary>
|
||||||
///// <see cref="IsApiController(Type)"/> 缓存集合
|
///// <see cref="IsApiController(Type)"/> 缓存集合
|
||||||
///// </summary>
|
///// </summary>
|
||||||
//private static readonly ConcurrentDictionary<Type, bool> IsApiControllerCached;
|
//private static readonly NonBlockingDictionary<Type, bool> IsApiControllerCached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否是Api控制器
|
/// 是否是Api控制器
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ internal sealed class EventBusHostedService : BackgroundService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 事件处理程序集合
|
/// 事件处理程序集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new();
|
private readonly NonBlockingDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
@@ -295,6 +295,7 @@ internal sealed class EventBusHostedService : BackgroundService
|
|||||||
, retryAction: (total, times) =>
|
, retryAction: (total, times) =>
|
||||||
{
|
{
|
||||||
// 输出重试日志
|
// 输出重试日志
|
||||||
|
if (_logger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Warning) == true)
|
||||||
_logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId);
|
_logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId);
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.Templates.Extensions;
|
using ThingsGateway.Templates.Extensions;
|
||||||
|
|
||||||
namespace ThingsGateway.FriendlyException;
|
namespace ThingsGateway.FriendlyException;
|
||||||
@@ -31,7 +31,7 @@ public static class Oops
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 方法错误异常特性
|
/// 方法错误异常特性
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<MethodBase, MethodIfException> _errorMethods;
|
private static readonly NonBlockingDictionary<MethodBase, MethodIfException> _errorMethods;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 错误代码类型
|
/// 错误代码类型
|
||||||
@@ -41,7 +41,7 @@ public static class Oops
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 错误消息字典
|
/// 错误消息字典
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<string, string> _errorCodeMessages;
|
private static readonly NonBlockingDictionary<string, string> _errorCodeMessages;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 友好异常设置
|
/// 友好异常设置
|
||||||
@@ -53,7 +53,7 @@ public static class Oops
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
static Oops()
|
static Oops()
|
||||||
{
|
{
|
||||||
_errorMethods = new ConcurrentDictionary<MethodBase, MethodIfException>();
|
_errorMethods = new NonBlockingDictionary<MethodBase, MethodIfException>();
|
||||||
_friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true);
|
_friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true);
|
||||||
_errorCodeTypes = GetErrorCodeTypes();
|
_errorCodeTypes = GetErrorCodeTypes();
|
||||||
_errorCodeMessages = GetErrorCodeMessages();
|
_errorCodeMessages = GetErrorCodeMessages();
|
||||||
@@ -258,9 +258,9 @@ public static class Oops
|
|||||||
/// 获取所有错误消息
|
/// 获取所有错误消息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static ConcurrentDictionary<string, string> GetErrorCodeMessages()
|
private static NonBlockingDictionary<string, string> GetErrorCodeMessages()
|
||||||
{
|
{
|
||||||
var defaultErrorCodeMessages = new ConcurrentDictionary<string, string>();
|
var defaultErrorCodeMessages = new NonBlockingDictionary<string, string>();
|
||||||
|
|
||||||
// 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义
|
// 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义
|
||||||
var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute))))
|
var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute))))
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using Microsoft.AspNetCore.SignalR;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway;
|
using ThingsGateway;
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.InstantMessaging;
|
using ThingsGateway.InstantMessaging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder;
|
namespace Microsoft.AspNetCore.Builder;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public static class ILoggerExtensions
|
|||||||
/// 设置日志上下文
|
/// 设置日志上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger"></param>
|
/// <param name="logger"></param>
|
||||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties)
|
public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Logging;
|
namespace ThingsGateway.Logging;
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public static class StringLoggingExtensions
|
|||||||
/// 配置日志上下文
|
/// 配置日志上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message"></param>
|
/// <param name="message"></param>
|
||||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties)
|
public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public sealed class FileLoggerProvider : ILoggerProvider, ISupportExternalScope
|
|||||||
/// 记录日志所有滚动文件名
|
/// 记录日志所有滚动文件名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks>
|
/// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks>
|
||||||
internal readonly ConcurrentDictionary<string, FileInfo> _rollingFileNames = new();
|
internal readonly NonBlockingDictionary<string, FileInfo> _rollingFileNames = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件日志写入器
|
/// 文件日志写入器
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ using System.Text.Json;
|
|||||||
|
|
||||||
using ThingsGateway;
|
using ThingsGateway;
|
||||||
using ThingsGateway.DataValidation;
|
using ThingsGateway.DataValidation;
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.FriendlyException;
|
using ThingsGateway.FriendlyException;
|
||||||
using ThingsGateway.JsonSerialization;
|
using ThingsGateway.JsonSerialization;
|
||||||
using ThingsGateway.Logging;
|
using ThingsGateway.Logging;
|
||||||
@@ -151,7 +151,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await MonitorAsync(actionMethod, context.ActionArguments, context, next).ConfigureAwait(false);
|
await MonitorAsync(actionMethod, context.ActionArguments, context, () => next()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -183,7 +183,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await MonitorAsync(actionMethod, context.HandlerArguments, context, next).ConfigureAwait(false);
|
await MonitorAsync(actionMethod, context.HandlerArguments, context, () => next()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -789,12 +789,12 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
return typeName;
|
return typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task MonitorAsync(MethodInfo actionMethod, IDictionary<string, object> parameterValues, FilterContext context, dynamic next)
|
private async Task MonitorAsync<T>(MethodInfo actionMethod, IDictionary<string, object> parameterValues, FilterContext context, Func<Task<T>> next)
|
||||||
{
|
{
|
||||||
// 排除 WebSocket 请求处理
|
// 排除 WebSocket 请求处理
|
||||||
if (context.HttpContext.IsWebSocketRequest())
|
if (context.HttpContext.IsWebSocketRequest())
|
||||||
{
|
{
|
||||||
_ = await next();
|
_ = await next().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,14 +805,14 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
if (actionMethod.IsDefined(typeof(SuppressMonitorAttribute), true)
|
if (actionMethod.IsDefined(typeof(SuppressMonitorAttribute), true)
|
||||||
|| actionMethod.DeclaringType.IsDefined(typeof(SuppressMonitorAttribute), true))
|
|| actionMethod.DeclaringType.IsDefined(typeof(SuppressMonitorAttribute), true))
|
||||||
{
|
{
|
||||||
_ = await next();
|
_ = await next().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否自定义了日志筛选器,如果是则检查是否符合条件
|
// 判断是否自定义了日志筛选器,如果是则检查是否符合条件
|
||||||
if (LoggingMonitorSettings.InternalWriteFilter?.Invoke(context) == false)
|
if (LoggingMonitorSettings.InternalWriteFilter?.Invoke(context) == false)
|
||||||
{
|
{
|
||||||
_ = await next();
|
_ = await next().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,7 +825,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
// 解决局部和全局触发器同时配置触发两次问题
|
// 解决局部和全局触发器同时配置触发两次问题
|
||||||
if (isDefinedScopedAttribute && Settings.FromGlobalFilter == true)
|
if (isDefinedScopedAttribute && Settings.FromGlobalFilter == true)
|
||||||
{
|
{
|
||||||
_ = await next();
|
_ = await next().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,7 +839,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
&& !Settings.IncludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase))
|
&& !Settings.IncludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// 查找是否包含匹配,忽略大小写
|
// 查找是否包含匹配,忽略大小写
|
||||||
_ = await next();
|
_ = await next().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,7 +847,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
if (Settings.GlobalEnabled
|
if (Settings.GlobalEnabled
|
||||||
&& Settings.ExcludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase))
|
&& Settings.ExcludeOfMethods.Contains(methodFullName, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_ = await next();
|
_ = await next().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -958,7 +958,8 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
|
|
||||||
// 计算接口执行时间
|
// 计算接口执行时间
|
||||||
var timeOperation = Stopwatch.StartNew();
|
var timeOperation = Stopwatch.StartNew();
|
||||||
var resultContext = await next();
|
|
||||||
|
var resultContext = await next().ConfigureAwait(false);
|
||||||
timeOperation.Stop();
|
timeOperation.Stop();
|
||||||
writer.WriteNumber("timeOperationElapsedMilliseconds", timeOperation.ElapsedMilliseconds);
|
writer.WriteNumber("timeOperationElapsedMilliseconds", timeOperation.ElapsedMilliseconds);
|
||||||
|
|
||||||
@@ -1014,8 +1015,13 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
var environment = httpContext.RequestServices.GetRequiredService<IWebHostEnvironment>().EnvironmentName;
|
var environment = httpContext.RequestServices.GetRequiredService<IWebHostEnvironment>().EnvironmentName;
|
||||||
writer.WriteString(nameof(environment), environment);
|
writer.WriteString(nameof(environment), environment);
|
||||||
|
|
||||||
|
Exception exception = null;
|
||||||
// 获取异常对象情况
|
// 获取异常对象情况
|
||||||
Exception exception = resultContext.Exception;
|
if (resultContext is PageHandlerExecutedContext pageHandlerExecutedContext)
|
||||||
|
exception = pageHandlerExecutedContext.Exception;
|
||||||
|
else if (resultContext is ActionExecutedContext actionExecutedContext)
|
||||||
|
exception = actionExecutedContext.Exception;
|
||||||
|
|
||||||
if (exception == null)
|
if (exception == null)
|
||||||
{
|
{
|
||||||
// 解析存储的验证信息
|
// 解析存储的验证信息
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public sealed partial class StringLoggingPart
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 配置日志上下文
|
/// 配置日志上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public StringLoggingPart ScopeContext(IDictionary<string, object> properties)
|
public StringLoggingPart ScopeContext(IDictionary<string, object> properties)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public static class Log
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 配置日志上下文
|
/// 配置日志上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties)
|
public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.Options;
|
using ThingsGateway.Options;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Options;
|
namespace Microsoft.Extensions.Options;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ internal sealed class JobCancellationToken : IJobCancellationToken
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 取消作业执行 Token 集合
|
/// 取消作业执行 Token 集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancellationTokenSources;
|
private readonly NonBlockingDictionary<string, CancellationTokenSource> _cancellationTokenSources;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 作业调度器日志服务
|
/// 作业调度器日志服务
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 177 KiB |
@@ -167,7 +167,7 @@ public partial class JobDetail
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 带命名规则的数据库列名
|
/// 带命名规则的数据库列名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new();
|
private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取数据库列名
|
/// 获取数据库列名
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 作业计划集合
|
/// 作业计划集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, Scheduler> _schedulers = new();
|
private readonly NonBlockingDictionary<string, Scheduler> _schedulers = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 作业计划构建器集合
|
/// 作业计划构建器集合
|
||||||
@@ -204,7 +204,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Schedule hosted service preload completed, and a total of <{Count}> schedulers are appended.", _schedulers.Count);
|
_logger.LogInformation("Schedule hosted service preload completed, and a total of <{Count}> schedulers are appended.", _schedulers.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -369,10 +369,12 @@ internal sealed class ScheduleHostedService : BackgroundService
|
|||||||
// 写入作业执行详细日志
|
// 写入作业执行详细日志
|
||||||
if (executionException == null)
|
if (executionException == null)
|
||||||
{
|
{
|
||||||
|
if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information) == true)
|
||||||
jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext);
|
jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error) == true)
|
||||||
jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext);
|
jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ public partial class Trigger
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 带命名规则的数据库列名
|
/// 带命名规则的数据库列名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new();
|
private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取数据库列名
|
/// 获取数据库列名
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ using System.Xml.Linq;
|
|||||||
using System.Xml.XPath;
|
using System.Xml.XPath;
|
||||||
|
|
||||||
using ThingsGateway.DynamicApiController;
|
using ThingsGateway.DynamicApiController;
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.Reflection;
|
using ThingsGateway.Reflection;
|
||||||
|
|
||||||
namespace ThingsGateway.SpecificationDocument;
|
namespace ThingsGateway.SpecificationDocument;
|
||||||
@@ -83,11 +83,11 @@ public static class SpecificationDocumentBuilder
|
|||||||
|
|
||||||
// 初始化常量
|
// 初始化常量
|
||||||
_groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)");
|
_groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)");
|
||||||
GetActionGroupsCached = new ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>();
|
GetActionGroupsCached = new NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>();
|
||||||
GetControllerGroupsCached = new ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>>();
|
GetControllerGroupsCached = new NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>>();
|
||||||
GetGroupOpenApiInfoCached = new ConcurrentDictionary<string, SpecificationOpenApiInfo>();
|
GetGroupOpenApiInfoCached = new NonBlockingDictionary<string, SpecificationOpenApiInfo>();
|
||||||
GetControllerTagCached = new ConcurrentDictionary<ControllerActionDescriptor, string>();
|
GetControllerTagCached = new NonBlockingDictionary<ControllerActionDescriptor, string>();
|
||||||
GetActionTagCached = new ConcurrentDictionary<ApiDescription, string>();
|
GetActionTagCached = new NonBlockingDictionary<ApiDescription, string>();
|
||||||
|
|
||||||
// 默认分组,支持多个逗号分割
|
// 默认分组,支持多个逗号分割
|
||||||
DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) };
|
DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) };
|
||||||
@@ -143,7 +143,7 @@ public static class SpecificationDocumentBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取分组信息缓存集合
|
/// 获取分组信息缓存集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached;
|
private static readonly NonBlockingDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取分组配置信息
|
/// 获取分组配置信息
|
||||||
@@ -738,7 +738,7 @@ public static class SpecificationDocumentBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取控制器组缓存集合
|
/// 获取控制器组缓存集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached;
|
private static readonly NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取控制器分组列表
|
/// 获取控制器分组列表
|
||||||
@@ -773,7 +773,7 @@ public static class SpecificationDocumentBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合
|
/// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached;
|
private static readonly NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取动作方法分组列表
|
/// 获取动作方法分组列表
|
||||||
@@ -808,7 +808,7 @@ public static class SpecificationDocumentBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
|
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<ControllerActionDescriptor, string> GetControllerTagCached;
|
private static readonly NonBlockingDictionary<ControllerActionDescriptor, string> GetControllerTagCached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取控制器标签
|
/// 获取控制器标签
|
||||||
@@ -835,7 +835,7 @@ public static class SpecificationDocumentBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
|
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConcurrentDictionary<ApiDescription, string> GetActionTagCached;
|
private static readonly NonBlockingDictionary<ApiDescription, string> GetActionTagCached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取动作方法标签
|
/// 获取动作方法标签
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.SpecificationDocument;
|
namespace ThingsGateway.SpecificationDocument;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Templates.Extensions;
|
namespace ThingsGateway.Templates.Extensions;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<Import Project="..\..\PackNuget.props" />
|
<Import Project="..\..\PackNuget.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.4" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="$(NET9Version)" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="$(NET10Version)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||||
@@ -39,10 +39,10 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net10.0' ">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(NET10Version)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(NET10Version)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(NET10Version)" />
|
||||||
|
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Http;
|
|||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.UnifyResult;
|
using ThingsGateway.UnifyResult;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc;
|
namespace Microsoft.AspNetCore.Mvc;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ using Microsoft.Extensions.Options;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.FriendlyException;
|
using ThingsGateway.FriendlyException;
|
||||||
|
|
||||||
namespace ThingsGateway.UnifyResult;
|
namespace ThingsGateway.UnifyResult;
|
||||||
@@ -51,12 +51,12 @@ public static class UnifyContext
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 规范化结果提供器
|
/// 规范化结果提供器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static ConcurrentDictionary<string, UnifyMetadata> UnifyProviders = new();
|
internal static NonBlockingDictionary<string, UnifyMetadata> UnifyProviders = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 规范化序列化配置
|
/// 规范化序列化配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static ConcurrentDictionary<string, object> UnifySerializerSettings = new();
|
internal static NonBlockingDictionary<string, object> UnifySerializerSettings = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取异常元数据
|
/// 获取异常元数据
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Converters.Json;
|
namespace ThingsGateway.Converters.Json;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace ThingsGateway.Extensions;
|
namespace ThingsGateway.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="Assembly" /> 拓展类
|
/// <see cref="Assembly" /> 拓展类
|
||||||
|
|||||||
@@ -11,10 +11,10 @@
|
|||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace ThingsGateway.Extensions;
|
namespace ThingsGateway.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="ConcurrentDictionary{TKey, TValue}" /> 拓展类
|
/// <see cref="NonBlockingDictionary{TKey, TValue}" /> 拓展类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class ConcurrentDictionaryExtensions
|
internal static class ConcurrentDictionaryExtensions
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,7 @@ internal static class ConcurrentDictionaryExtensions
|
|||||||
/// <typeparam name="TKey">字典键类型</typeparam>
|
/// <typeparam name="TKey">字典键类型</typeparam>
|
||||||
/// <typeparam name="TValue">字典值类型</typeparam>
|
/// <typeparam name="TValue">字典值类型</typeparam>
|
||||||
/// <param name="dictionary">
|
/// <param name="dictionary">
|
||||||
/// <see cref="ConcurrentDictionary{TKey, TValue}" />
|
/// <see cref="NonBlockingDictionary{TKey, TValue}" />
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="key">
|
/// <param name="key">
|
||||||
/// <typeparamref name="TKey" />
|
/// <typeparamref name="TKey" />
|
||||||
@@ -36,7 +36,7 @@ internal static class ConcurrentDictionaryExtensions
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <see cref="bool" />
|
/// <see cref="bool" />
|
||||||
/// </returns>
|
/// </returns>
|
||||||
internal static bool TryUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary
|
internal static bool TryUpdate<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dictionary
|
||||||
, TKey key
|
, TKey key
|
||||||
, Func<TValue, TValue> updateFactory
|
, Func<TValue, TValue> updateFactory
|
||||||
, out TValue? value)
|
, out TValue? value)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ using Microsoft.Extensions.Hosting;
|
|||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace ThingsGateway.Extensions;
|
namespace ThingsGateway.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 核心模块 <see cref="IServiceCollection" /> 拓展类
|
/// 核心模块 <see cref="IServiceCollection" /> 拓展类
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace ThingsGateway.Extensions;
|
namespace ThingsGateway.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="DataTable" /> 和 <see cref="DataSet" /> 拓展类
|
/// <see cref="DataTable" /> 和 <see cref="DataSet" /> 拓展类
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Extensions;
|
namespace ThingsGateway.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 委托拓展类
|
/// 委托拓展类
|
||||||
|
|||||||