Compare commits
216 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fef6259d09 | ||
|
|
e24f19e88b | ||
|
|
cc6de1e71c | ||
|
|
b0240a1c95 | ||
|
|
8acdec333b | ||
|
|
eafcbb81f9 | ||
|
|
4b083463c8 | ||
|
|
78e834e04f | ||
|
|
2ed1893620 | ||
|
|
35f188a7dd | ||
|
|
07f577a9dd | ||
|
|
25c72b50c0 | ||
|
|
e07346ebea | ||
|
|
699b466291 | ||
|
|
f0f24ff96f | ||
|
|
1113997c71 | ||
|
|
487fb6e5c9 | ||
|
|
62908a323c | ||
|
|
021131271a | ||
|
|
c20fa4dbd2 | ||
|
|
b1826678da | ||
|
|
1630338f4e | ||
|
|
78af3d979c | ||
|
|
8346dfb1f5 | ||
|
|
5ecb4e4fe4 | ||
|
|
ec9ff23b23 | ||
|
|
149e9931e7 | ||
|
|
58b62094bd | ||
|
|
d357074ad4 | ||
|
|
d9bacc129b | ||
|
|
28d869b099 | ||
|
|
d04e87718a | ||
|
|
8d81bf60c9 | ||
|
|
4fe9094ff7 | ||
|
|
4cfde45755 | ||
|
|
c28844dbeb | ||
|
|
837ea7c8d6 | ||
|
|
6c85ac8827 | ||
|
|
c8eb8bf35d | ||
|
|
0f6cbddd57 | ||
|
|
530b4755bf | ||
|
|
679395b3fe | ||
|
|
9293442836 | ||
|
|
4841b412f3 | ||
|
|
f03575ad89 | ||
|
|
e0e5428ebd | ||
|
|
015f7edae2 | ||
|
|
d5414f5d91 | ||
|
|
4bc3512d42 | ||
|
|
4c305994ca | ||
|
|
788e2cba04 | ||
|
|
b79b79970e | ||
|
|
8b0f4de516 | ||
|
|
a17000ad6d | ||
|
|
c9f63c58df | ||
|
|
c311b1c706 | ||
|
|
072451afae | ||
|
|
7349793463 | ||
|
|
c0144eab86 | ||
|
|
ba79b25944 | ||
|
|
3fb34b12a0 | ||
|
|
b6c17e76c2 | ||
|
|
1201f20d79 | ||
|
|
98ae8e692e | ||
|
|
8342dbff4e | ||
|
|
a22aa0f9a7 | ||
|
|
b49d221ec1 | ||
|
|
f1be315147 | ||
|
|
8aaac2a3d4 | ||
|
|
bd07d30e47 | ||
|
|
54e103f00f | ||
|
|
5404d8f7ab | ||
|
|
1364e02c67 | ||
|
|
b4818e2f9a | ||
|
|
734cf57d4a | ||
|
|
9236610ec1 | ||
|
|
c33828a5d9 | ||
|
|
faadb03e46 | ||
|
|
3a0c4c51d2 | ||
|
|
6c28c2b91e | ||
|
|
5226df6194 | ||
|
|
1ad23a3cbb | ||
|
|
c4149ca304 | ||
|
|
68b02fe950 | ||
|
|
d9408523a5 | ||
|
|
40ad0f063a | ||
|
|
40d85e8926 | ||
|
|
e655f719e6 | ||
|
|
ede0ea02c5 | ||
|
|
e579575b21 | ||
|
|
06ffcc9fdf | ||
|
|
774d03e510 | ||
|
|
adcac9dfe6 | ||
|
|
399e14c70c | ||
|
|
ec8b51033b | ||
|
|
ca5d167b6a | ||
|
|
fcd1b001d1 | ||
|
|
1cabbefe04 | ||
|
|
ecefe22c95 | ||
|
|
64db77b9a2 | ||
|
|
19d7360ad5 | ||
|
|
2f014cd827 | ||
|
|
7fb84205d9 | ||
|
|
47e1127c5f | ||
|
|
e036b59306 | ||
|
|
f4904c3b53 | ||
|
|
3002a63ba5 | ||
|
|
b69717e6c3 | ||
|
|
d07962953f | ||
|
|
857cf0d21e | ||
|
|
5f08c2615d | ||
|
|
e87428ef33 | ||
|
|
03fd54fe70 | ||
|
|
086c2c8253 | ||
|
|
05c19a32ea | ||
|
|
99d174906a | ||
|
|
82e30a326a | ||
|
|
2963a9cdca | ||
|
|
288da75b2b | ||
|
|
0943a496dd | ||
|
|
e5dd7cc2fa | ||
|
|
358836ef9f | ||
|
|
aab4fac6c5 | ||
|
|
525540b603 | ||
|
|
b30eeb4694 | ||
|
|
3faf0aa2fc | ||
|
|
fd728dec5d | ||
|
|
08b14b72d4 | ||
|
|
de2e005abf | ||
|
|
0fc75239a6 | ||
|
|
390fe30a0d | ||
|
|
633f49fcd2 | ||
|
|
30c0ba93b9 | ||
|
|
e935fb9621 | ||
|
|
5ce8bb1d08 | ||
|
|
1acd12980a | ||
|
|
683235dd8a | ||
|
|
65fe183ad4 | ||
|
|
f39d5d355c | ||
|
|
eccc8e0ff0 | ||
|
|
dd4d8e775c | ||
|
|
7a7f857b2f | ||
|
|
10882b7d93 | ||
|
|
e669b81005 | ||
|
|
28a81d9539 | ||
|
|
6bf5e4a6b8 | ||
|
|
a51eee93f4 | ||
|
|
bece2555c2 | ||
|
|
d98d405009 | ||
|
|
8d0881632c | ||
|
|
e8b81da897 | ||
|
|
acf4fbf750 | ||
|
|
b4f1921d33 | ||
|
|
94b4816f53 | ||
|
|
b6ddafde3e | ||
|
|
b01036818f | ||
|
|
ad7da1a0c3 | ||
|
|
1e0818d9d9 | ||
|
|
a4686f01c3 | ||
|
|
84d76f9aab | ||
|
|
b90bf5eb86 | ||
|
|
fe258f3fe5 | ||
|
|
d339494594 | ||
|
|
adfaf13055 | ||
|
|
8abfeb5923 | ||
|
|
c1a027a771 | ||
|
|
5f3094d79b | ||
|
|
925b81aca8 | ||
|
|
c0c8437966 | ||
|
|
1d27b2fc4a | ||
|
|
5da43e7808 | ||
|
|
5edbb558ae | ||
|
|
7347cc1df2 | ||
|
|
20cac11b2a | ||
|
|
75c35c4ff8 | ||
|
|
9d573512d0 | ||
|
|
7921365853 | ||
|
|
43263fd3b9 | ||
|
|
59042a5ead | ||
|
|
91b14de807 | ||
|
|
81fab2be08 | ||
|
|
165b742782 | ||
|
|
76fef9c807 | ||
|
|
e69ea0b9dc | ||
|
|
98d3183f2b | ||
|
|
a29390a951 | ||
|
|
6291ce8617 | ||
|
|
c76b1b50a0 | ||
|
|
cc45e2aec0 | ||
|
|
17efebb8e8 | ||
|
|
5c94c733ee | ||
|
|
156b89dd9c | ||
|
|
34ba9f67e7 | ||
|
|
5ddaa6b872 | ||
|
|
9043fa7f56 | ||
|
|
4c8e487dc9 | ||
|
|
d3b87179aa | ||
|
|
2166de8331 | ||
|
|
f0bc3f001f | ||
|
|
50448e7085 | ||
|
|
cd1d42353e | ||
|
|
5e588bf737 | ||
|
|
7ff777d178 | ||
|
|
861621189a | ||
|
|
dcc00e08fd | ||
|
|
a1b8a47d4b | ||
|
|
1fd6b5e239 | ||
|
|
f91e45bf44 | ||
|
|
0675a45592 | ||
|
|
0c7c6ae451 | ||
|
|
cf089e8c4c | ||
|
|
90928ac679 | ||
|
|
3bbcf71784 | ||
|
|
222734775d | ||
|
|
f23ee7a6e0 | ||
|
|
5b075aa6d5 |
4
.gitignore
vendored
@@ -366,6 +366,6 @@ FodyWeavers.xsd
|
|||||||
/src/*Pro*
|
/src/*Pro*
|
||||||
/src/*pro*
|
/src/*pro*
|
||||||
/src/*pro*/
|
/src/*pro*/
|
||||||
/src/ThingsGateway.Web.Entry/.config/
|
|
||||||
/doc/.*
|
/doc/.*
|
||||||
|
/src/ThingsGateway.Server/.config/
|
||||||
|
/src/nuget.exe
|
||||||
|
|||||||
2
LICENSE
@@ -87,7 +87,7 @@
|
|||||||
granted to You under this License for that Work shall terminate
|
granted to You under this License for that Work shall terminate
|
||||||
as of the date such litigation is filed.
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
4. Cachetribution. You may reproduce and distribute copies of the
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
modifications, and in Source or Object form, provided that You
|
modifications, and in Source or Object form, provided that You
|
||||||
meet the following conditions:
|
meet the following conditions:
|
||||||
|
|||||||
120
README.md
@@ -1,67 +1,65 @@
|
|||||||
|
|
||||||
# ThingsGateway
|
# ThingsGateway
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
A cross-platform, high-performance edge data collection gateway based on net8, capable of handling millions of data points per.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[ThingsGateway Documentation](https://diego2098.gitee.io/thingsgateway-docs/).
|
||||||
|
|
||||||
|
[**NuGet Address**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
||||||
|
|
||||||
|
### Plugin List
|
||||||
|
|
||||||
|
#### Data Collection Plugins
|
||||||
|
|
||||||
## 介绍
|
| Plugin Name | Remarks |
|
||||||
|
|
||||||
**NetCore** 跨平台边缘采集网关(工业设备采集)
|
|
||||||
|
|
||||||
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
|
||||||
|
|
||||||
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
|
|
||||||
|
|
||||||
|
|
||||||
## 文档
|
|
||||||
|
|
||||||
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
|
|
||||||
|
|
||||||
### 插件列表
|
|
||||||
|
|
||||||
#### 采集插件
|
|
||||||
| 插件名称 | 备注 |
|
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| ModbusMaster | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路 |
|
| Modbus | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links |
|
||||||
| S7 | 西门子PLC S7系列 |
|
| SiemensS7 | Siemens PLC S7 series |
|
||||||
| Dlt6452007 | Master,支持串口/Tcp/Udp链路 |
|
| Dlt6452007 | Supports Serial/Tcp/Udp links |
|
||||||
| OpcDaClient | 64位编译 |
|
| OpcDaMaster | Compiled for 64-bit |
|
||||||
| OpcUaClient | 支持证书登录,扩展对象Json读写 |
|
| OpcUaMaster | Supports certificate login, object extension, Json read/write |
|
||||||
|
|
||||||
#### 业务插件
|
#### Business Plugins
|
||||||
| 插件名称 | 备注 |
|
|
||||||
|
| Plugin Name | Remarks |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| ModbusSlave | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路,支持Rpc反写 |
|
| ModbusSlave | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links, supports Rpc reverse writing |
|
||||||
| OpcUaServer | OpcUa服务端,支持Rpc反写 |
|
| OpcUaServer | OpcUa server, supports Rpc reverse writing |
|
||||||
| Mqtt Client | Mqtt客户端,支持Rpc反写,脚本自定义上传内容 |
|
| MqttClient | Mqtt client, supports Rpc reverse writing, script-customizable upload content |
|
||||||
| Mqtt Server | Mqtt服务端,支持WebSocket,支持Rpc反写,脚本自定义上传内容 |
|
| MqttServer | Mqtt server, supports WebSocket, supports Rpc reverse writing, script-customizable upload content |
|
||||||
| Kafka Client | 数据生产,脚本自定义上传内容 |
|
| KafkaProducer | Script-customizable upload content |
|
||||||
| RabbitMQ Client | 数据生产,脚本自定义上传内容 |
|
| RabbitMQProducer | Script-customizable upload content |
|
||||||
| SqlDB | 关系数据库存储,支持历史存储和实时数据更新 |
|
| SqlDB | Relational database storage, supports historical storage and real-time data updates |
|
||||||
| SqlHisAlarm | 报警历史数据关系数据库存储 |
|
| SqlHisAlarm | Alarm historical data relational database storage |
|
||||||
| TDengineDB | 时序数据库存储 |
|
| TDengineDB | Time-series database storage |
|
||||||
| QuestDB | 时序数据库存储 |
|
| QuestDB | Time-series database storage |
|
||||||
|
|
||||||
## 协议
|
|
||||||
|
|
||||||
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE) 开源协议。
|
|
||||||
|
|
||||||
## 演示
|
|
||||||
|
|
||||||
[ThingsGateway演示地址](http://120.24.62.140:5000/)
|
|
||||||
|
|
||||||
账户 : **superAdmin**
|
|
||||||
|
|
||||||
密码 : **111111**
|
|
||||||
|
|
||||||
## 赞助
|
|
||||||
|
|
||||||
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/1000)
|
|
||||||
|
|
||||||
## 社区
|
|
||||||
|
|
||||||
QQ群:605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
|
|
||||||
|
|
||||||
## Pro插件
|
|
||||||
|
|
||||||
[插件列表](https://diego2098.gitee.io/thingsgateway-docs/docs/1001)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE)
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
[ThingsGateway Demo Address](http://47.119.161.158:5000/)
|
||||||
|
|
||||||
|
Account: **SuperAdmin**
|
||||||
|
|
||||||
|
Password: **111111**
|
||||||
|
|
||||||
|
**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
|
||||||
|
|
||||||
|
## Sponsorship
|
||||||
|
|
||||||
|
[Sponsorship Approach](https://diego2098.gitee.io/thingsgateway-docs/docs/1000)
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
|
## Pro Plugins
|
||||||
|
|
||||||
|
[Plugin List](https://diego2098.gitee.io/thingsgateway-docs/docs/1001)
|
||||||
66
README.zh-CN.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
# ThingsGateway
|
||||||
|
|
||||||
|
## 介绍
|
||||||
|
|
||||||
|
基于net8的跨平台高性能边缘采集网关,单机采集数据点位可达百万
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
|
||||||
|
|
||||||
|
[**底层驱动 NuGet地址**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
||||||
|
|
||||||
|
### 插件列表
|
||||||
|
|
||||||
|
#### 采集插件
|
||||||
|
| 插件名称 | 备注 |
|
||||||
|
|-------|-------|
|
||||||
|
| Modbus | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路 |
|
||||||
|
| SiemensS7 | 西门子PLC S7系列 |
|
||||||
|
| Dlt6452007 | 支持串口/Tcp/Udp链路 |
|
||||||
|
| OpcDaMaster | 64位编译 |
|
||||||
|
| OpcUaMaster | 支持证书登录,扩展对象,Json读写 |
|
||||||
|
|
||||||
|
#### 业务插件
|
||||||
|
| 插件名称 | 备注 |
|
||||||
|
|-------|-------|
|
||||||
|
| ModbusSlave | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路,支持Rpc反写 |
|
||||||
|
| OpcUaServer | OpcUa服务端,支持Rpc反写 |
|
||||||
|
| MqttClient | Mqtt客户端,支持Rpc反写,脚本自定义上传内容 |
|
||||||
|
| MqttServer | Mqtt服务端,支持WebSocket,支持Rpc反写,脚本自定义上传内容 |
|
||||||
|
| KafkaProducer | 脚本自定义上传内容 |
|
||||||
|
| RabbitMQProducer | 脚本自定义上传内容 |
|
||||||
|
| SqlDB | 关系数据库存储,支持历史存储和实时数据更新 |
|
||||||
|
| SqlHisAlarm | 报警历史数据关系数据库存储 |
|
||||||
|
| TDengineDB | 时序数据库存储 |
|
||||||
|
| QuestDB | 时序数据库存储 |
|
||||||
|
|
||||||
|
## 协议
|
||||||
|
|
||||||
|
[Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE)
|
||||||
|
|
||||||
|
## 演示
|
||||||
|
|
||||||
|
[ThingsGateway演示地址](http://47.119.161.158:5000/)
|
||||||
|
|
||||||
|
账户 : **SuperAdmin**
|
||||||
|
|
||||||
|
密码 : **111111**
|
||||||
|
|
||||||
|
**右上角个人弹出框中,切换到物联网关模块**
|
||||||
|
|
||||||
|
## 赞助
|
||||||
|
|
||||||
|
[赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/1000)
|
||||||
|
|
||||||
|
## 社区
|
||||||
|
|
||||||
|
QQ群:605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
|
||||||
|
|
||||||
|
## Pro插件
|
||||||
|
|
||||||
|
[插件列表](https://diego2098.gitee.io/thingsgateway-docs/docs/1001)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ import Highlight from '@site/src/components/Highlight.js';
|
|||||||
|
|
||||||
## 一、说明
|
## 一、说明
|
||||||
|
|
||||||
**ThingsGateway** 基于NET6、7、8,默认开发IDE为VS2022(**17.8版本以上**),安装VS时需勾选ASP.NET类别.
|
**ThingsGateway** 基于NET6、8,默认开发IDE为VS2022(**17.8版本以上**),安装VS时需勾选ASP.NET类别.
|
||||||
|
|
||||||
<img src={require("@site/static/img/docs/vs2022install.png").default} />
|
<img src={require("@site/static/img/docs/vs2022install.png").default} />
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ import Highlight from '@site/src/components/Highlight.js';
|
|||||||
|
|
||||||
<img src={require("@site/static/img/docs/生成解决方案.png").default} />
|
<img src={require("@site/static/img/docs/生成解决方案.png").default} />
|
||||||
|
|
||||||
2、 设置ThingsGateway.Web.Entry为启动项目,开始调试或开始执行
|
2、 设置ThingsGateway.Server为启动项目,开始调试或开始执行
|
||||||
<img src={require("@site/static/img/docs/设置启动项目.png").default} />
|
<img src={require("@site/static/img/docs/设置启动项目.png").default} />
|
||||||
<img src={require("@site/static/img/docs/开始执行.png").default} />
|
<img src={require("@site/static/img/docs/开始执行.png").default} />
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ import Highlight from '@site/src/components/Highlight.js';
|
|||||||
|
|
||||||
:::tip 提示
|
:::tip 提示
|
||||||
|
|
||||||
测试环境下,账密会自动填充为超级管理员账号,默认账户:**superAdmin**,密码:**111111**
|
测试环境下,账密会自动填充为超级管理员账号,默认账户:**SuperAdmin**,密码:**111111**
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ function Banner() {
|
|||||||
<div className="ThingsGateway-get-start-btn">
|
<div className="ThingsGateway-get-start-btn">
|
||||||
<Link className="ThingsGateway-get-start" to={useBaseUrl("docs/")}>
|
<Link className="ThingsGateway-get-start" to={useBaseUrl("docs/")}>
|
||||||
入门指南
|
入门指南
|
||||||
<span className="ThingsGateway-version">v5.0</span>
|
<span className="ThingsGateway-version">v6.0</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,7 +114,7 @@ function Gitee() {
|
|||||||
className={"ThingsGateway-log-jiao" + (isDarkTheme ? " dark" : "")}
|
className={"ThingsGateway-log-jiao" + (isDarkTheme ? " dark" : "")}
|
||||||
></div>
|
></div>
|
||||||
<div className="ThingsGateway-log-number">
|
<div className="ThingsGateway-log-number">
|
||||||
<div style={{ color: "#723cff" }}>600 +</div>
|
<div style={{ color: "#723cff" }}>700 +</div>
|
||||||
<span className={isDarkTheme ? " dark" : ""}>Stars</span>
|
<span className={isDarkTheme ? " dark" : ""}>Stars</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
BIN
doc/static/img/docs/login.png
vendored
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 152 KiB |
BIN
doc/static/img/docs/开始执行.png
vendored
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 19 KiB |
BIN
doc/static/img/docs/生成解决方案.png
vendored
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 70 KiB |
BIN
doc/static/img/docs/设置启动项目.png
vendored
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 22 KiB |
@@ -1,25 +1,247 @@
|
|||||||
[*.cs]
|
root = true
|
||||||
|
|
||||||
# IDE0290: 使用主构造函数
|
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
|
||||||
dotnet_diagnostic.IDE0290.severity = none
|
###############################
|
||||||
|
# Core EditorConfig Options #
|
||||||
|
###############################
|
||||||
|
|
||||||
# IDE0028: 简化集合初始化
|
# All files
|
||||||
dotnet_diagnostic.IDE0028.severity = none
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
spelling_exclusion_path = .\exclusion.dic
|
||||||
|
|
||||||
# CA1822: 将成员标记为 static
|
# Microsoft .NET properties
|
||||||
dotnet_diagnostic.CA1822.severity = none
|
csharp_new_line_before_members_in_object_initializers = false
|
||||||
|
csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
|
||||||
|
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||||
|
csharp_style_var_elsewhere = true:suggestion
|
||||||
|
csharp_style_var_for_built_in_types = true:suggestion
|
||||||
|
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||||
|
dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True
|
||||||
|
dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field
|
||||||
|
dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef
|
||||||
|
dotnet_naming_rule.unity_serialized_field_rule.severity = warning
|
||||||
|
dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style
|
||||||
|
dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
|
||||||
|
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
|
||||||
|
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
|
||||||
|
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
|
||||||
|
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
|
||||||
|
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||||
|
dotnet_style_qualification_for_event = false:suggestion
|
||||||
|
dotnet_style_qualification_for_field = false:suggestion
|
||||||
|
dotnet_style_qualification_for_method = false:suggestion
|
||||||
|
dotnet_style_qualification_for_property = false:suggestion
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||||
|
|
||||||
# IDE0052: 删除未读的私有成员
|
# ReSharper properties
|
||||||
dotnet_diagnostic.IDE0052.severity = none
|
resharper_autodetect_indent_settings = true
|
||||||
|
resharper_formatter_off_tag = @formatter:off
|
||||||
|
resharper_formatter_on_tag = @formatter:on
|
||||||
|
resharper_formatter_tags_enabled = true
|
||||||
|
resharper_new_line_before_while = true
|
||||||
|
resharper_place_attribute_on_same_line = false
|
||||||
|
resharper_show_autodetect_configure_formatting_tip = false
|
||||||
|
resharper_use_indent_from_vs = false
|
||||||
|
|
||||||
# CA2254: 模板应为静态表达式
|
# ReSharper inspection severities
|
||||||
dotnet_diagnostic.CA2254.severity = none
|
resharper_arrange_redundant_parentheses_highlighting = hint
|
||||||
|
resharper_arrange_this_qualifier_highlighting = hint
|
||||||
|
resharper_arrange_type_member_modifiers_highlighting = hint
|
||||||
|
resharper_arrange_type_modifiers_highlighting = hint
|
||||||
|
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
|
||||||
|
resharper_built_in_type_reference_style_highlighting = hint
|
||||||
|
resharper_redundant_base_qualifier_highlighting = warning
|
||||||
|
resharper_suggest_var_or_type_built_in_types_highlighting = hint
|
||||||
|
resharper_suggest_var_or_type_elsewhere_highlighting = hint
|
||||||
|
resharper_suggest_var_or_type_simple_types_highlighting = hint
|
||||||
|
resharper_web_config_module_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_type_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_wrong_module_highlighting = warning
|
||||||
|
|
||||||
# CA1854: 首选 “IDictionary.TryGetValue(TKey, out TValue)” 方法
|
# Code files
|
||||||
dotnet_diagnostic.CA1854.severity = none
|
[*.{cs,csx,vb,vbx}]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
# IDE0090: 使用 "new(...)"
|
[*.{cs,css,js,json,*html,razor,txt,log}]
|
||||||
dotnet_diagnostic.IDE0090.severity = none
|
charset = utf-8-bom
|
||||||
|
|
||||||
# IDE0305: 简化集合初始化
|
[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
|
||||||
dotnet_diagnostic.IDE0305.severity = none
|
indent_size = 2
|
||||||
|
|
||||||
|
# Xml config files
|
||||||
|
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{ps1,psm1}]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.sh]
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# .NET Coding Conventions #
|
||||||
|
###############################
|
||||||
|
[*.{cs,vb}]
|
||||||
|
# Organize usings
|
||||||
|
dotnet_sort_system_directives_first = false
|
||||||
|
# this. preferences
|
||||||
|
dotnet_style_qualification_for_field = false:silent
|
||||||
|
dotnet_style_qualification_for_property = false:silent
|
||||||
|
dotnet_style_qualification_for_method = false:silent
|
||||||
|
dotnet_style_qualification_for_event = false:silent
|
||||||
|
# Language keywords vs BCL types preferences
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:silent
|
||||||
|
# Parentheses preferences
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||||
|
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||||
|
# Modifier preferences
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||||
|
dotnet_style_readonly_field = true:suggestion
|
||||||
|
# Expression-level preferences
|
||||||
|
dotnet_style_object_initializer = true:suggestion
|
||||||
|
dotnet_style_collection_initializer = true:suggestion
|
||||||
|
dotnet_style_explicit_tuple_names = true:suggestion
|
||||||
|
dotnet_style_null_propagation = true:suggestion
|
||||||
|
dotnet_style_coalesce_expression = true:suggestion
|
||||||
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||||
|
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||||
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||||
|
dotnet_style_prefer_auto_properties = true:silent
|
||||||
|
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||||
|
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||||
|
###############################
|
||||||
|
# Naming Conventions #
|
||||||
|
###############################
|
||||||
|
# Style Definitions
|
||||||
|
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||||
|
# Use PascalCase for constant fields
|
||||||
|
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||||
|
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||||
|
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
|
||||||
|
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
|
tab_width = 4
|
||||||
|
end_of_line = crlf
|
||||||
|
dotnet_style_prefer_collection_expression = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||||
|
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||||
|
dotnet_style_namespace_match_folder = true:suggestion
|
||||||
|
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
|
||||||
|
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
|
||||||
|
dotnet_code_quality_unused_parameters = all:suggestion
|
||||||
|
###############################
|
||||||
|
# C# Coding Conventions #
|
||||||
|
###############################
|
||||||
|
[*.cs]
|
||||||
|
# var preferences
|
||||||
|
csharp_style_var_for_built_in_types = true:silent
|
||||||
|
csharp_style_var_when_type_is_apparent = true:silent
|
||||||
|
csharp_style_var_elsewhere = true:silent
|
||||||
|
csharp_prefer_static_local_function = true:silent
|
||||||
|
# Expression-bodied members
|
||||||
|
csharp_style_expression_bodied_methods = false:silent
|
||||||
|
csharp_style_expression_bodied_constructors = false:silent
|
||||||
|
csharp_style_expression_bodied_operators = false:silent
|
||||||
|
csharp_style_expression_bodied_properties = true:silent
|
||||||
|
csharp_style_expression_bodied_indexers = true:silent
|
||||||
|
csharp_style_expression_bodied_accessors = true:silent
|
||||||
|
# Pattern matching preferences
|
||||||
|
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||||
|
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||||
|
# Null-checking preferences
|
||||||
|
csharp_style_throw_expression = true:suggestion
|
||||||
|
csharp_style_conditional_delegate_call = true:suggestion
|
||||||
|
# Modifier preferences
|
||||||
|
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
|
||||||
|
# Expression-level preferences
|
||||||
|
csharp_prefer_braces = true:silent
|
||||||
|
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||||
|
csharp_prefer_simple_default_expression = true:suggestion
|
||||||
|
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||||
|
csharp_style_inlined_variable_declaration = true:suggestion
|
||||||
|
###############################
|
||||||
|
# C# Formatting Rules #
|
||||||
|
###############################
|
||||||
|
# New line preferences
|
||||||
|
csharp_new_line_before_open_brace = all
|
||||||
|
csharp_new_line_before_else = true
|
||||||
|
csharp_new_line_before_catch = true
|
||||||
|
csharp_new_line_before_finally = true
|
||||||
|
csharp_new_line_before_members_in_object_initializers = true
|
||||||
|
csharp_new_line_before_members_in_anonymous_types = true
|
||||||
|
csharp_new_line_between_query_expression_clauses = true
|
||||||
|
# Indentation preferences
|
||||||
|
csharp_indent_case_contents = true
|
||||||
|
csharp_indent_switch_labels = true
|
||||||
|
csharp_indent_labels = flush_left
|
||||||
|
# Space preferences
|
||||||
|
csharp_space_after_cast = false
|
||||||
|
csharp_space_after_keywords_in_control_flow_statements = true
|
||||||
|
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_parentheses = false
|
||||||
|
csharp_space_before_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_after_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_around_binary_operators = before_and_after
|
||||||
|
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||||
|
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||||
|
# Wrapping preferences
|
||||||
|
csharp_preserve_single_line_statements = true
|
||||||
|
csharp_preserve_single_line_blocks = true
|
||||||
|
###############################
|
||||||
|
# VB Coding Conventions #
|
||||||
|
###############################
|
||||||
|
[*.vb]
|
||||||
|
# Modifier preferences
|
||||||
|
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
||||||
|
[*.cs]
|
||||||
|
# Add file header
|
||||||
|
file_header_template = Copyright (c) Argo Zhang (argo@163.com). All rights reserved.\nLicensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\nWebsite: https://www.blazor.zone or https://argozhang.github.io/
|
||||||
|
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||||
|
csharp_style_expression_bodied_local_functions = true:silent
|
||||||
|
csharp_using_directive_placement = outside_namespace:silent
|
||||||
|
csharp_prefer_simple_using_statement = true:suggestion
|
||||||
|
csharp_style_prefer_method_group_conversion = true:silent
|
||||||
|
csharp_style_prefer_top_level_statements = true:silent
|
||||||
|
csharp_style_prefer_primary_constructors = true:suggestion
|
||||||
|
csharp_style_expression_bodied_lambdas = true:silent
|
||||||
|
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||||
|
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||||
|
csharp_style_prefer_index_operator = true:suggestion
|
||||||
|
csharp_style_prefer_range_operator = true:suggestion
|
||||||
|
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||||
|
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||||
|
csharp_style_prefer_tuple_swap = true:suggestion
|
||||||
|
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||||
|
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||||
|
csharp_style_prefer_readonly_struct_member = true:suggestion
|
||||||
|
csharp_style_prefer_readonly_struct = true:suggestion
|
||||||
|
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
|
||||||
|
csharp_style_prefer_switch_expression = true:suggestion
|
||||||
|
csharp_style_prefer_pattern_matching = true:silent
|
||||||
|
csharp_style_prefer_not_pattern = true:suggestion
|
||||||
|
csharp_style_prefer_extended_property_pattern = true:suggestion
|
||||||
|
|||||||
13
src/Delete .vs .bat
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
set "folder=%~dp0"
|
||||||
|
attrib -s -h "%folder%\.vs" >nul 2>&1
|
||||||
|
if exist "%folder%\.vs" (
|
||||||
|
rd /s /q "%folder%\.vs"
|
||||||
|
echo 删除了.vs文件夹:%folder%\.vs
|
||||||
|
)
|
||||||
|
|
||||||
|
echo 删除完成!
|
||||||
|
pause
|
||||||
19
src/Delete bin Or obj.bat
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
set "folder=%~dp0"
|
||||||
|
for /r "%folder%" /d %%i in (*) do (
|
||||||
|
set "dirname=%%~nxi"
|
||||||
|
if /I "!dirname!"=="bin" (
|
||||||
|
rd /s /q "%%i"
|
||||||
|
echo 删除了名称为"bin"的文件夹:%%i
|
||||||
|
)
|
||||||
|
if /I "!dirname!"=="obj" (
|
||||||
|
rd /s /q "%%i"
|
||||||
|
echo 删除了名称为"obj"的文件夹:%%i
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo 删除完成!
|
||||||
|
pause
|
||||||
10
src/Delete nupkgs.bat
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
set "folder=%~dp0/nupkgs"
|
||||||
|
rd /s /q "%folder%"
|
||||||
|
echo 删除了名称为"nupkgs"的文件夹
|
||||||
|
|
||||||
|
echo 删除完成!
|
||||||
|
pause
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<NoWarn>CS8618;CS8625;CS8600;CS8601;CS8604;CS8714;CS8602;CS8603;CS8619;CS8621</NoWarn>
|
<NoWarn>CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;</NoWarn>
|
||||||
<TargetFrameworks>net6.0;</TargetFrameworks>
|
<TargetFrameworks>net6.0;</TargetFrameworks>
|
||||||
<!--<TargetFrameworks>net6.0;</TargetFrameworks>-->
|
<LangVersion>12.0</LangVersion>
|
||||||
<LangVersion>11.0</LangVersion>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<Nullable>enable</Nullable>
|
||||||
<Nullable>enable</Nullable>
|
<Version>6.0.0.9</Version>
|
||||||
<Version>5.0.1.0</Version>
|
<Authors>Diego</Authors>
|
||||||
<Authors>Diego</Authors>
|
<Company>Diego</Company>
|
||||||
<Company>Diego</Company>
|
<Product>Diego</Product>
|
||||||
<Product>Diego</Product>
|
<Copyright>版权所有 © 2023-present Diego</Copyright>
|
||||||
<Copyright>版权所有 © 2023-present Diego</Copyright>
|
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||||
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
<RepositoryType>Gitee</RepositoryType>
|
||||||
<RepositoryType>Gitee</RepositoryType>
|
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
|
||||||
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
|
</PropertyGroup>
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Admin'">
|
|
||||||
<DefineConstants>Admin</DefineConstants>
|
|
||||||
</PropertyGroup>
|
<ItemGroup>
|
||||||
<ItemGroup>
|
<None Include="$(SolutionDir)..\README.md" Pack="true" PackagePath="\" />
|
||||||
<None Include="$(SolutionDir)..\README.md" Pack="true" PackagePath="\" />
|
<None Include="$(SolutionDir)..\LICENSE" Pack="true" PackagePath="\" />
|
||||||
<None Include="$(SolutionDir)..\LICENSE" Pack="true" PackagePath="\" />
|
<None Include="$(SolutionDir)..\icon.png" Pack="true" PackagePath="\" />
|
||||||
<None Include="$(SolutionDir)..\icon.png" Pack="true" PackagePath="\" />
|
<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" />
|
||||||
<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
</Project>
|
||||||
</Project>
|
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net45;netstandard2.0;net6.0;</TargetFrameworks>
|
<TargetFrameworks>net462;netstandard2.0;net6.0;</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
</PropertyGroup>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<ItemGroup>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<Content Remove="Locales\*.json" />
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<EmbeddedResource Include="Locales\*.json">
|
||||||
<PackageOutputPath>$(SolutionDir)nupkgs</PackageOutputPath>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
<PackageVersion>$(Version)</PackageVersion>
|
</EmbeddedResource>
|
||||||
<PackageTags>ThingsGateway;Diego;Blazor;设备采集;边缘网关;物联网</PackageTags>
|
</ItemGroup>
|
||||||
<PackageProjectUrl>https://gitee.com/diego2098/ThingsGateway</PackageProjectUrl>
|
<ItemGroup>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<None Include="$(SolutionDir)Foundation.props" Pack="true" PackagePath="\" />
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
</ItemGroup>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
</Project>
|
||||||
<OutputPath></OutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="$(SolutionDir)Foundation.props" Pack="true" PackagePath="\" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
|
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<!--<IncludeSymbols>true</IncludeSymbols>
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>-->
|
||||||
<PackageOutputPath>$(SolutionDir)nupkgs</PackageOutputPath>
|
<PackageOutputPath>$(SolutionDir)nupkgs</PackageOutputPath>
|
||||||
<PackageVersion>$(Version)</PackageVersion>
|
<PackageVersion>$(Version)</PackageVersion>
|
||||||
<PackageTags>ThingsGateway;Diego;Blazor;设备采集;边缘网关;物联网</PackageTags>
|
<PackageTags>ThingsGateway;Diego;Blazor;设备采集;边缘网关;物联网</PackageTags>
|
||||||
@@ -11,9 +13,11 @@
|
|||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
<OutputPath></OutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
|
<DebugSymbols>True</DebugSymbols>
|
||||||
|
<DebugType>Embedded</DebugType>
|
||||||
|
<EmbedAllSources>True</EmbedAllSources>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="$(SolutionDir)PackNuget.props" Pack="true" PackagePath="\" />
|
<None Include="$(SolutionDir)PackNuget.props" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -6,12 +6,17 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="$(SolutionDir)Plugin.props" Pack="true" PackagePath="\" />
|
<None Include="$(SolutionDir)Plugin.props" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Remove="Locales\*.json" />
|
||||||
|
<EmbeddedResource Include="Locales\*.json">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
||||||
<ProjectReference Include="$(SolutionDir)ThingsGateway.Gateway.Blazor\ThingsGateway.Gateway.Blazor.csproj">
|
<ProjectReference Include="$(SolutionDir)ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj">
|
||||||
<Private>false</Private>
|
<Private>false</Private>
|
||||||
<ExcludeAssets>runtime</ExcludeAssets>
|
<ExcludeAssets>runtime</ExcludeAssets>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,35 +1,31 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.8.34322.80
|
VisualStudioVersion = 17.9.34622.214
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3CBF3D90-92F4-4932-8E63-19BD344B19D6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Server", "ThingsGateway.Server\ThingsGateway.Server.csproj", "{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Razor", "ThingsGateway.Razor\ThingsGateway.Razor.csproj", "{D3E8D660-AD12-4C46-871F-144D9C83ED2B}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "ThingsGateway.Core\ThingsGateway.Core.csproj", "{2904D604-40E9-459C-81E4-2B55FFB27706}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "others", "others", "{2C6B6E2F-76B9-47E1-B3BB-A76595275F2A}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
|
Delete .vs .bat = Delete .vs .bat
|
||||||
|
Delete Bin And Obj.bat = Delete Bin And Obj.bat
|
||||||
Directory.Build.props = Directory.Build.props
|
Directory.Build.props = Directory.Build.props
|
||||||
Plugin.props = Plugin.props
|
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Cache", "ThingsGateway.Cache\ThingsGateway.Cache.csproj", "{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Razor", "ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj", "{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "ThingsGateway.Components\ThingsGateway.Components.csproj", "{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{5EE2DAEF-05DF-4590-B1FA-42917D859F73}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "ThingsGateway.Core\ThingsGateway.Core.csproj", "{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.NewLife.X", "ThingsGateway.NewLife.X\ThingsGateway.NewLife.X.csproj", "{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{E43C28BD-E208-424E-8A67-75BE712DC7B4}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{1A13C5CC-69C9-42D9-8046-96DDECA0467A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}"
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{E2F43E54-D2B6-4789-86BA-53395CA78A44}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{6AE58F3E-E996-448F-994A-C054EA01EC96}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{2733F3EB-8854-43B7-9DF4-859EA34B35CB}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{BF1ED6B2-7779-4FE6-8332-85C6713F42BB}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -37,57 +33,42 @@ Global
|
|||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Release|Any CPU.Build.0 = Release|Any CPU
|
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{2904D604-40E9-459C-81E4-2B55FFB27706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2904D604-40E9-459C-81E4-2B55FFB27706}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{2904D604-40E9-459C-81E4-2B55FFB27706}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
{2904D604-40E9-459C-81E4-2B55FFB27706}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Release|Any CPU.Build.0 = Release|Any CPU
|
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{E2F43E54-D2B6-4789-86BA-53395CA78A44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E2F43E54-D2B6-4789-86BA-53395CA78A44}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E2F43E54-D2B6-4789-86BA-53395CA78A44}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{E2F43E54-D2B6-4789-86BA-53395CA78A44}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6AE58F3E-E996-448F-994A-C054EA01EC96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{6AE58F3E-E996-448F-994A-C054EA01EC96}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{6AE58F3E-E996-448F-994A-C054EA01EC96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{6AE58F3E-E996-448F-994A-C054EA01EC96}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{2733F3EB-8854-43B7-9DF4-859EA34B35CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{2733F3EB-8854-43B7-9DF4-859EA34B35CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{2733F3EB-8854-43B7-9DF4-859EA34B35CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{2733F3EB-8854-43B7-9DF4-859EA34B35CB}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D} = {BF1ED6B2-7779-4FE6-8332-85C6713F42BB}
|
{D3E8D660-AD12-4C46-871F-144D9C83ED2B} = {3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}
|
||||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8} = {BF1ED6B2-7779-4FE6-8332-85C6713F42BB}
|
{2904D604-40E9-459C-81E4-2B55FFB27706} = {3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}
|
||||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E} = {BF1ED6B2-7779-4FE6-8332-85C6713F42BB}
|
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832} = {1A13C5CC-69C9-42D9-8046-96DDECA0467A}
|
||||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
{5EE2DAEF-05DF-4590-B1FA-42917D859F73} = {1A13C5CC-69C9-42D9-8046-96DDECA0467A}
|
||||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9} = {3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}
|
||||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
|
||||||
{E2F43E54-D2B6-4789-86BA-53395CA78A44} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
RESX_NeutralResourcesLanguage = zh-Hans
|
SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282}
|
||||||
SolutionGuid = {789BA852-9F20-4421-A555-E665A12587D1}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
using ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.ApiController;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 后台登录控制器
|
|
||||||
/// </summary>
|
|
||||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
|
|
||||||
[Route("auth")]
|
|
||||||
[LoggingMonitor]
|
|
||||||
public class AuthController : IDynamicApiController
|
|
||||||
{
|
|
||||||
private readonly IAuthService _authService;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc cref="AuthController"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="authService"></param>
|
|
||||||
public AuthController(IAuthService authService)
|
|
||||||
{
|
|
||||||
_authService = authService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 后台登录
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[AllowAnonymous]
|
|
||||||
[HttpPost("login")]
|
|
||||||
[Description(EventSubscriberConst.Login)]
|
|
||||||
public async Task<LoginOutput> LoginAsync(LoginInput input)
|
|
||||||
{
|
|
||||||
return await _authService.LoginAsync(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 后台登出
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost("logout")]
|
|
||||||
[Description(EventSubscriberConst.LoginOut)]
|
|
||||||
[Authorize]
|
|
||||||
[IgnoreRolePermission]
|
|
||||||
public async Task LogoutAsync(LoginOutIput input)
|
|
||||||
{
|
|
||||||
await _authService.LoginOutAsync(input.VerificatId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.ApiController;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 导出文件
|
|
||||||
/// </summary>
|
|
||||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
|
|
||||||
[Route("export")]
|
|
||||||
[LoggingMonitor]
|
|
||||||
public class ExportController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IOperateLogService _operateLogService;
|
|
||||||
private readonly IVisitLogService _visitLogService;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc cref="ExportController"/>
|
|
||||||
/// </summary>
|
|
||||||
public ExportController(
|
|
||||||
IOperateLogService operateLogService,
|
|
||||||
IVisitLogService visitLogService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_operateLogService = operateLogService;
|
|
||||||
_visitLogService = visitLogService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下载操作日志
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("operateLog")]
|
|
||||||
public async Task<IActionResult> DownloadOperateLogAsync([FromQuery] OperateLogInput input)
|
|
||||||
{
|
|
||||||
if (input.All)
|
|
||||||
{
|
|
||||||
var fileStreamResult = await _operateLogService.ExportFileAsync();
|
|
||||||
return fileStreamResult;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var fileStreamResult = await _operateLogService.ExportFileAsync(input);
|
|
||||||
return fileStreamResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下载访问日志
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("visitLog")]
|
|
||||||
public async Task<IActionResult> DownloadVisitLogAsync([FromQuery] VisitLogInput input)
|
|
||||||
{
|
|
||||||
if (input.All)
|
|
||||||
{
|
|
||||||
var fileStreamResult = await _visitLogService.ExportFileAsync();
|
|
||||||
return fileStreamResult;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var fileStreamResult = await _visitLogService.ExportFileAsync(input);
|
|
||||||
return fileStreamResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
global using Furion.DynamicApiController;
|
|
||||||
|
|
||||||
global using System;
|
|
||||||
global using System.Threading.Tasks;
|
|
||||||
|
|
||||||
global using ThingsGateway.Admin.Application;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
|
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<doc>
|
|
||||||
<assembly>
|
|
||||||
<name>ThingsGateway.Admin.ApiController</name>
|
|
||||||
</assembly>
|
|
||||||
<members>
|
|
||||||
<member name="T:ThingsGateway.Admin.ApiController.AuthController">
|
|
||||||
<summary>
|
|
||||||
后台登录控制器
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Admin.ApiController.AuthController.#ctor(ThingsGateway.Admin.Application.IAuthService)">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc cref="T:ThingsGateway.Admin.ApiController.AuthController"/>
|
|
||||||
</summary>
|
|
||||||
<param name="authService"></param>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Admin.ApiController.AuthController.LoginAsync(ThingsGateway.Admin.Application.LoginInput)">
|
|
||||||
<summary>
|
|
||||||
后台登录
|
|
||||||
</summary>
|
|
||||||
<param name="input"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Admin.ApiController.AuthController.LogoutAsync(ThingsGateway.Admin.Application.LoginOutIput)">
|
|
||||||
<summary>
|
|
||||||
后台登出
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Admin.ApiController.ExportController">
|
|
||||||
<summary>
|
|
||||||
导出文件
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Admin.ApiController.ExportController.#ctor(ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc cref="T:ThingsGateway.Admin.ApiController.ExportController"/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Admin.ApiController.ExportController.DownloadOperateLogAsync(ThingsGateway.Admin.Application.OperateLogInput)">
|
|
||||||
<summary>
|
|
||||||
下载操作日志
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Admin.ApiController.ExportController.DownloadVisitLogAsync(ThingsGateway.Admin.Application.VisitLogInput)">
|
|
||||||
<summary>
|
|
||||||
下载访问日志
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
</members>
|
|
||||||
</doc>
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,37 +9,160 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
using Rougamo;
|
||||||
|
using Rougamo.Context;
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
using ThingsGateway.Core.Extension;
|
||||||
|
using ThingsGateway.Core.Json.Extension;
|
||||||
|
|
||||||
|
using UAParser;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 操作事件说明特性
|
/// Aop拦截器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
public class OperDescAttribute : MoAttribute
|
||||||
public class OperDescAttribute : Attribute
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 操作记录标识
|
/// 日志消息队列(线程安全)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="description"></param>
|
private static readonly ConcurrentQueue<SysOperateLog> _logMessageQueue = new();
|
||||||
/// <param name="catcategory"></param>
|
|
||||||
public OperDescAttribute(string description, string catcategory = CateGoryConst.Log_OPERATE)
|
static OperDescAttribute()
|
||||||
{
|
{
|
||||||
Description = description;
|
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
||||||
Catcategory = catcategory;
|
Task.Factory.StartNew(ProcessQueue, TaskCreationOptions.LongRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public OperDescAttribute(string description, bool isRecordPar = true, object localizerType = null)
|
||||||
/// 分类
|
{
|
||||||
/// </summary>
|
Description = description;
|
||||||
public string Catcategory { get; }
|
IsRecordPar = isRecordPar;
|
||||||
|
LocalizerType = (Type)localizerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override AccessFlags Flags => AccessFlags.Public | AccessFlags.Method;
|
||||||
|
public override Feature Features => Feature.OnException | Feature.OnSuccess;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 说明
|
/// 说明,需配置本地化json文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
|
|
||||||
|
public Type? LocalizerType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 记录参数,默认true
|
/// 是否记录进出参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRecordPar { get; set; } = true;
|
public bool IsRecordPar { get; }
|
||||||
}
|
|
||||||
|
public override void OnSuccess(MethodContext context)
|
||||||
|
{
|
||||||
|
//插入操作日志
|
||||||
|
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||||
|
WriteToQueue(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnException(MethodContext context)
|
||||||
|
{
|
||||||
|
//插入异常日志
|
||||||
|
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||||
|
|
||||||
|
log.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
||||||
|
log.ExeStatus = false;//操作状态为失败
|
||||||
|
if (context.Exception is UserFriendlyException exception)
|
||||||
|
log.ExeMessage = exception?.Message;
|
||||||
|
else
|
||||||
|
log.ExeMessage = context.Exception?.ToString();
|
||||||
|
|
||||||
|
WriteToQueue(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将日志消息写入数据库中
|
||||||
|
/// </summary>
|
||||||
|
private static async Task ProcessQueue()
|
||||||
|
{
|
||||||
|
var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
|
||||||
|
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
|
||||||
|
while (!(appLifetime.ApplicationStopping.IsCancellationRequested || appLifetime.ApplicationStopped.IsCancellationRequested))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_logMessageQueue.Count > 0)
|
||||||
|
{
|
||||||
|
await db.InsertableWithAttr(_logMessageQueue.ToListWithDequeue()).ExecuteCommandAsync();//入库
|
||||||
|
}
|
||||||
|
await Task.Delay(3000, appLifetime.ApplicationStopping).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SysOperateLog GetOperLog(Type? localizerType, MethodContext context)
|
||||||
|
{
|
||||||
|
var str = App.HttpContext?.Request?.Headers?.UserAgent;
|
||||||
|
var methodBase = context.Method;
|
||||||
|
ClientInfo? clientInfo = null;
|
||||||
|
if (str.HasValue)
|
||||||
|
{
|
||||||
|
clientInfo = Parser.GetDefault().Parse(str);
|
||||||
|
}
|
||||||
|
string? paramJson = null;
|
||||||
|
if (IsRecordPar)
|
||||||
|
{
|
||||||
|
var args = context.Arguments;
|
||||||
|
var parametersInfo = methodBase.GetParameters();
|
||||||
|
var parametersDict = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
for (int i = 0; i < parametersInfo.Length; i++)
|
||||||
|
{
|
||||||
|
parametersDict[parametersInfo[i].Name!] = args[i];
|
||||||
|
}
|
||||||
|
paramJson = parametersDict.ToSystemTextJsonString();
|
||||||
|
}
|
||||||
|
var result = context.ReturnValue;
|
||||||
|
var resultJson = IsRecordPar ? result?.ToSystemTextJsonString() : null;
|
||||||
|
//操作日志表实体
|
||||||
|
var log = new SysOperateLog
|
||||||
|
{
|
||||||
|
Name = (localizerType == null ? App.CreateLocalizerByType(typeof(OperDescAttribute)) : App.CreateLocalizerByType(localizerType))![Description],
|
||||||
|
Category = LogCateGoryEnum.Operate,
|
||||||
|
ExeStatus = true,
|
||||||
|
OpIp = App.HttpContext?.Connection?.RemoteIpAddress?.MapToIPv4()?.ToString(),
|
||||||
|
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
||||||
|
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
||||||
|
OpTime = DateTime.Now,
|
||||||
|
OpAccount = UserManager.UserAccount,
|
||||||
|
ReqUrl = null,
|
||||||
|
ReqMethod = "browser",
|
||||||
|
ResultJson = resultJson,
|
||||||
|
ClassName = methodBase.ReflectedType!.Name,
|
||||||
|
MethodName = methodBase.Name,
|
||||||
|
ParamJson = paramJson,
|
||||||
|
VerificatId = UserManager.VerificatId,
|
||||||
|
};
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将日志消息写入队列中等待后台任务出队写入数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logMsg">结构化日志消息</param>
|
||||||
|
private void WriteToQueue(SysOperateLog logMsg)
|
||||||
|
{
|
||||||
|
_logMessageQueue.Enqueue(logMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,258 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using Furion.Reflection;
|
|
||||||
using Furion.Reflection.Extensions;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using ThingsGateway.Core.Extension.ConcurrentQueue;
|
|
||||||
using ThingsGateway.Core.Extension.Json;
|
|
||||||
|
|
||||||
using UAParser;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// AOP处理操作日志
|
|
||||||
/// </summary>
|
|
||||||
public class OperDispatchProxy : AspectDispatchProxy, IDispatchProxy
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 服务提供器,可以用来解析服务,如:Services.GetService()
|
|
||||||
/// </summary>
|
|
||||||
public IServiceProvider Services { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前服务实例
|
|
||||||
/// </summary>
|
|
||||||
public object Target { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override object Invoke(MethodInfo method, object[] args)
|
|
||||||
{
|
|
||||||
var desc = method.GetActualCustomAttribute<OperDescAttribute>(Target);
|
|
||||||
if (desc == null)
|
|
||||||
{
|
|
||||||
return Invoke(method, args);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exception exception = default;
|
|
||||||
object result = default;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = Invoke(method, args);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
exception = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteOperLog(method, args, desc, result, exception);
|
|
||||||
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
return result;//返回结果
|
|
||||||
}
|
|
||||||
|
|
||||||
object Invoke(MethodInfo method, object[] args)
|
|
||||||
{
|
|
||||||
//如果不带返回值
|
|
||||||
if (method.ReturnType == typeof(void))
|
|
||||||
{
|
|
||||||
return method.Invoke(Target, args);//直接返回
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var result = method.Invoke(Target, args);
|
|
||||||
return result;//返回结果
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异步无返回值
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
|
||||||
public override async Task InvokeAsync(MethodInfo method, object[] args)
|
|
||||||
{
|
|
||||||
var desc = method.GetActualCustomAttribute<OperDescAttribute>(Target, true);
|
|
||||||
if (desc == null)
|
|
||||||
{
|
|
||||||
var task = method.Invoke(Target, args) as Task;
|
|
||||||
await task;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exception exception = default;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var task = method.Invoke(Target, args) as Task;
|
|
||||||
await task;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
exception = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteOperLog(method, args, desc, null, exception);
|
|
||||||
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异步带返回值
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
|
||||||
public override async Task<T> InvokeAsyncT<T>(MethodInfo method, object[] args)
|
|
||||||
{
|
|
||||||
var desc = method.GetActualCustomAttribute<OperDescAttribute>(Target, true);
|
|
||||||
if (desc == null)
|
|
||||||
{
|
|
||||||
var taskT = method.Invoke(Target, args) as Task<T>;
|
|
||||||
var result = await taskT;
|
|
||||||
return result;//返回结果
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
T result = default;
|
|
||||||
//写入操作日志
|
|
||||||
Exception exception = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var taskT = method.Invoke(Target, args) as Task<T>;
|
|
||||||
result = await taskT;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
exception = ex;
|
|
||||||
}
|
|
||||||
WriteOperLog(method, args, desc, result, exception);
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return result;//返回结果
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteOperLog(MethodInfo method, object[] args, OperDescAttribute desc, object result, Exception exception)
|
|
||||||
{
|
|
||||||
//写入操作日志
|
|
||||||
var str = App.HttpContext?.Request?.Headers?.UserAgent;
|
|
||||||
ClientInfo clientInfo = null;
|
|
||||||
if (str.HasValue)
|
|
||||||
{
|
|
||||||
clientInfo = Parser.GetDefault().Parse(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder stringBuilder = new();
|
|
||||||
if (desc.IsRecordPar)
|
|
||||||
{
|
|
||||||
var parameters = method.GetParameters();
|
|
||||||
var jsonParameters = parameters.Select((p, i) => $"\"{p.Name}\": {args[i].ToJsonString()}");
|
|
||||||
stringBuilder.Append('{');
|
|
||||||
stringBuilder.Append(string.Join(", ", jsonParameters));
|
|
||||||
stringBuilder.Append('}');
|
|
||||||
}
|
|
||||||
var paramJson = stringBuilder.ToString();
|
|
||||||
var resultJson = desc.IsRecordPar ? result?.ToJsonString() : null;
|
|
||||||
//操作日志表实体
|
|
||||||
var log = new SysOperateLog
|
|
||||||
{
|
|
||||||
Name = desc.Description,
|
|
||||||
Category = desc.Catcategory,
|
|
||||||
ExeStatus = LogConst.SUCCESS,
|
|
||||||
OpIp = App.HttpContext?.Connection?.RemoteIpAddress?.MapToIPv4().ToString(),
|
|
||||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
|
||||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
|
||||||
OpTime = DateTime.Now,
|
|
||||||
OpAccount = UserManager.UserAccount,
|
|
||||||
ReqUrl = "",
|
|
||||||
ReqMethod = LogConst.LOG_REQMETHOD,
|
|
||||||
ResultJson = resultJson,
|
|
||||||
ClassName = method.ReflectedType.Name,
|
|
||||||
MethodName = method.Name,
|
|
||||||
ParamJson = paramJson,
|
|
||||||
VerificatId = UserManager.VerificatId,
|
|
||||||
};
|
|
||||||
//如果异常不为空
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
log.Category = CateGoryConst.Log_EXCEPTION;//操作类型为异常
|
|
||||||
log.ExeStatus = LogConst.FAIL;//操作状态为失败
|
|
||||||
log.ExeMessage = exception.Source + ":" + exception.Message + Environment.NewLine + exception.StackTrace;
|
|
||||||
}
|
|
||||||
WriteToQueue(log);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 日志消息队列(线程安全)
|
|
||||||
/// </summary>
|
|
||||||
private static readonly ConcurrentQueue<SysOperateLog> _logMessageQueue = new();
|
|
||||||
|
|
||||||
static OperDispatchProxy()
|
|
||||||
{
|
|
||||||
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
|
||||||
Task.Factory.StartNew(ProcessQueue, TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将日志消息写入队列中等待后台任务出队写入数据库
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logMsg">结构化日志消息</param>
|
|
||||||
private void WriteToQueue(SysOperateLog logMsg)
|
|
||||||
{
|
|
||||||
_logMessageQueue.Enqueue(logMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将日志消息写入数据库中
|
|
||||||
/// </summary>
|
|
||||||
private static async Task ProcessQueue()
|
|
||||||
{
|
|
||||||
var db = DbContext.Db.CopyNew();
|
|
||||||
var appLifetime = App.GetService<IHostApplicationLifetime>();
|
|
||||||
while (!(appLifetime.ApplicationStopping.IsCancellationRequested || appLifetime.ApplicationStopped.IsCancellationRequested))
|
|
||||||
{
|
|
||||||
if (_logMessageQueue.Count > 0)
|
|
||||||
{
|
|
||||||
await db.InsertableWithAttr(_logMessageQueue.ToListWithDequeue()).ExecuteCommandAsync();//入库
|
|
||||||
}
|
|
||||||
await Task.Delay(3000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 忽略Excel导入导出
|
/// 忽略Excel导入导出
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 需要角色授权权限
|
/// 需要角色授权权限
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,11 +9,15 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 种子数据忽略新增
|
/// 种子数据忽略新增
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class IgnoreSeedDataAddAttribute : Attribute
|
public class IgnoreSeedDataAddAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -20,6 +25,7 @@ public class IgnoreSeedDataAddAttribute : Attribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 种子数据忽略修改
|
/// 种子数据忽略修改
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class IgnoreSeedDataUpdateAttribute : Attribute
|
public class IgnoreSeedDataUpdateAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -27,6 +33,7 @@ public class IgnoreSeedDataUpdateAttribute : Attribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 忽略初始化表
|
/// 忽略初始化表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class IgnoreInitTableAttribute : Attribute
|
public class IgnoreInitTableAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 管理员才能访问
|
/// 管理员才能访问
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,9 +9,12 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最小值校验
|
/// 最小值校验
|
||||||
@@ -21,18 +25,12 @@ public class MinValueAttribute : ValidationAttribute
|
|||||||
/// 最小值
|
/// 最小值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public MinValueAttribute(double value)
|
public MinValueAttribute(UInt64 value)
|
||||||
{
|
{
|
||||||
MinValue = value;
|
MinValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double MinValue { get; set; }
|
private UInt64 MinValue { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override string FormatErrorMessage(string name)
|
|
||||||
{
|
|
||||||
return base.FormatErrorMessage(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最小值校验
|
/// 最小值校验
|
||||||
@@ -43,10 +41,10 @@ public class MinValueAttribute : ValidationAttribute
|
|||||||
{
|
{
|
||||||
if (value is null)
|
if (value is null)
|
||||||
{
|
{
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var input = Convert.ToDouble(value);
|
var input = Convert.ToUInt64(value);
|
||||||
return input > MinValue;
|
return input >= MinValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
676
src/ThingsGateway.Admin.Application/Common/ConcurrentList.cs
Normal file
@@ -0,0 +1,676 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application.ConcurrentList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 线程安全的List,其基本操作和List一致。
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public class ConcurrentList<T> : IList<T>, IReadOnlyList<T>
|
||||||
|
{
|
||||||
|
private readonly List<T> m_list;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="collection"></param>
|
||||||
|
public ConcurrentList(IEnumerable<T> collection)
|
||||||
|
{
|
||||||
|
this.m_list = new List<T>(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public ConcurrentList()
|
||||||
|
{
|
||||||
|
this.m_list = new List<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="capacity"></param>
|
||||||
|
public ConcurrentList(int capacity)
|
||||||
|
{
|
||||||
|
this.m_list = new List<T>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素数量
|
||||||
|
/// </summary>
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为只读
|
||||||
|
/// </summary>
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取索引元素
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list[index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加元素
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空所有元素
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否包含某个元素
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool Contains(T item)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.Contains(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 复制到
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array"></param>
|
||||||
|
/// <param name="arrayIndex"></param>
|
||||||
|
public void CopyTo(T[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回迭代器
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.ToList().GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回迭代器组合
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 索引
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int IndexOf(T item)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.IndexOf(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 插入
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
public void Insert(int index, T item)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Insert(index, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除元素
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool Remove(T item)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.Remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按索引移除
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
if (index < this.m_list.Count)
|
||||||
|
{
|
||||||
|
this.m_list.RemoveAt(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置容量
|
||||||
|
/// </summary>
|
||||||
|
public int Capacity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.Capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Capacity = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.AddRange(IEnumerable{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="collection"></param>
|
||||||
|
public void AddRange(IEnumerable<T> collection)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.AddRange(collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.BinarySearch(T)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int BinarySearch(T item)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.BinarySearch(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.BinarySearch(T, IComparer{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="comparer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int BinarySearch(T item, IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.BinarySearch(item, comparer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.BinarySearch(int, int, T, IComparer{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="comparer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int BinarySearch(int index, int count, T item, IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.BinarySearch(index, count, item, comparer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.ConvertAll{TOutput}(Converter{T, TOutput})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TOutput"></typeparam>
|
||||||
|
/// <param name="converter"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.ConvertAll(converter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.Find(Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T Find(Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.Find(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindAll(Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<T> FindAll(Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindAll(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindIndex(int, int, Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int FindIndex(int startIndex, int count, Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindIndex(startIndex, count, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindIndex(int, Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex"></param>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int FindIndex(int startIndex, Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindIndex(startIndex, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindIndex(Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int FindIndex(Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindIndex(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindLast(Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T FindLast(Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindLast(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindLastIndex(int, int, Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int FindLastIndex(int startIndex, int count, Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindLastIndex(startIndex, count, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindLastIndex(int, Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex"></param>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int FindLastIndex(int startIndex, Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindLastIndex(startIndex, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.FindLastIndex(Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int FindLastIndex(Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.FindLastIndex(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.ForEach(Action{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
public void ForEach(Action<T> action)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.ForEach(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.GetRange(int, int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<T> GetRange(int index, int count)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.GetRange(index, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.IndexOf(T, int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int IndexOf(T item, int index)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.IndexOf(item, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.IndexOf(T, int, int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int IndexOf(T item, int index, int count)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.IndexOf(item, index, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.InsertRange(int, IEnumerable{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="collection"></param>
|
||||||
|
public void InsertRange(int index, IEnumerable<T> collection)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.InsertRange(index, collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.LastIndexOf(T)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int LastIndexOf(T item)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.IndexOf(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.LastIndexOf(T, int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int LastIndexOf(T item, int index)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.LastIndexOf(item, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.LastIndexOf(T, int, int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int LastIndexOf(T item, int index, int count)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.LastIndexOf(item, index, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.RemoveAll(Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
public void RemoveAll(Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.RemoveAll(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.RemoveRange(int, int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
public void RemoveRange(int index, int count)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.RemoveRange(index, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.Reverse()"/>
|
||||||
|
/// </summary>
|
||||||
|
public void Reverse()
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.Reverse(int, int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
public void Reverse(int index, int count)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Reverse(index, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.Sort()"/>
|
||||||
|
/// </summary>
|
||||||
|
public void Sort()
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.Sort(Comparison{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="comparison"></param>
|
||||||
|
public void Sort(Comparison<T> comparison)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Sort(comparison);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.Sort(IComparer{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="comparer"></param>
|
||||||
|
public void Sort(IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Sort(comparer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.Sort(int, int, IComparer{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <param name="comparer"></param>
|
||||||
|
public void Sort(int index, int count, IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.Sort(index, count, comparer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.ToArray"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T[] ToArray()
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.TrimExcess"/>
|
||||||
|
/// </summary>
|
||||||
|
public void TrimExcess()
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
this.m_list.TrimExcess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="List{T}.TrueForAll(Predicate{T})"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool TrueForAll(Predicate<T> match)
|
||||||
|
{
|
||||||
|
lock (((ICollection)this.m_list).SyncRoot)
|
||||||
|
{
|
||||||
|
return this.m_list.TrueForAll(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"Demo": {
|
|
||||||
"IsDemo": false,
|
|
||||||
"UpPWEnable": false //是否允许修改密码
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"Demo": {
|
|
||||||
"IsDemo": false,
|
|
||||||
"UpPWEnable": false //是否允许修改密码
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
//日志配置
|
|
||||||
"LogJob": {
|
|
||||||
"DaysAgo": 10 //清理日志
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
//日志配置
|
|
||||||
"LogJob": {
|
|
||||||
"DaysAgo": 10 //清理日志
|
|
||||||
}
|
|
||||||
}
|
|
||||||
70
src/ThingsGateway.Admin.Application/Const/CacheConst.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application
|
||||||
|
{
|
||||||
|
public class CacheConst
|
||||||
|
{
|
||||||
|
public const string Cache_Prefix_Admin = "ThingsGatewayAdmin:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 系统字典表缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_SysDict = $"{CacheConst.Cache_Prefix_Admin}SysDict:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户表缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_SysUser = $"{CacheConst.Cache_Prefix_Admin}SysUser:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户账号关系缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_SysUserAccount = $"{CacheConst.Cache_Prefix_Admin}SysUserAccount:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源表缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_SysResource = $"{CacheConst.Cache_Prefix_Admin}SysResource:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关系表缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_SysRelation = $"{CacheConst.Cache_Prefix_Admin}SysRelation:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色表缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_SysRole = $"{CacheConst.Cache_Prefix_Admin}SysRole:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token表缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_Token = $"{CacheConst.Cache_Prefix_Admin}Token:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token表缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_HardwareInfo = $"{CacheConst.Cache_Prefix_Admin}Cache_HardwareInfo:";
|
||||||
|
|
||||||
|
#region 登录错误次数
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录错误次数缓存Key
|
||||||
|
/// </summary>
|
||||||
|
public const string Cache_LoginErrorCount = $"{CacheConst.Cache_Prefix_Admin}LoginErrorCount:";
|
||||||
|
|
||||||
|
#endregion 登录错误次数
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 分类常量
|
|
||||||
/// </summary>
|
|
||||||
public class CateGoryConst
|
|
||||||
{
|
|
||||||
#region 系统配置
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统基础
|
|
||||||
/// </summary>
|
|
||||||
public const string Config_SYS_BASE = "SYS_BASE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 业务定义
|
|
||||||
/// </summary>
|
|
||||||
public const string Config_BIZ_DEFINE = "BIZ_DEFINE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录策略
|
|
||||||
/// </summary>
|
|
||||||
public const string LOGIN_POLICY = "LOGIN_POLICY";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 密码策略
|
|
||||||
/// </summary>
|
|
||||||
public const string Config_PWD_POLICY = "PWD_POLICY";
|
|
||||||
|
|
||||||
#endregion 系统配置
|
|
||||||
|
|
||||||
#region 资源表
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 菜单
|
|
||||||
/// </summary>
|
|
||||||
public const string Resource_MENU = "MENU";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 单页
|
|
||||||
/// </summary>
|
|
||||||
public const string Resource_SPA = "SPA";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 按钮
|
|
||||||
/// </summary>
|
|
||||||
public const string Resource_BUTTON = "BUTTON";
|
|
||||||
|
|
||||||
#endregion 资源表
|
|
||||||
|
|
||||||
#region 关系表
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户有哪些角色
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_USER_HAS_ROLE = "SYS_USER_HAS_ROLE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 角色有哪些资源
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_ROLE_HAS_RESOURCE = "SYS_ROLE_HAS_RESOURCE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///用户有哪些资源
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_USER_HAS_RESOURCE = "SYS_USER_HAS_RESOURCE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 角色有哪些权限
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_ROLE_HAS_PERMISSION = "SYS_ROLE_HAS_PERMISSION";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 角色有哪些OPENAPI权限
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_ROLE_HAS_OPENAPIPERMISSION = "SYS_ROLE_HAS_OPENAPIPERMISSION";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户有哪些权限
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_USER_HAS_PERMISSION = "SYS_USER_HAS_PERMISSION";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户有哪些OPENAPI权限
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_USER_HAS_OPENAPIPERMISSION = "Relation_SYS_USER_HAS_OPENAPIPERMISSION";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户工作台数据
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_USER_WORKBENCH_DATA = "SYS_USER_WORKBENCH_DATA";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户主页数据
|
|
||||||
/// </summary>
|
|
||||||
public const string Relation_SYS_USER_DEFAULT_RAZOR = "Relation_SYS_USER_DEFAULT_RAZOR";
|
|
||||||
|
|
||||||
#endregion 关系表
|
|
||||||
|
|
||||||
#region 日志表
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录
|
|
||||||
/// </summary>
|
|
||||||
public const string Log_LOGIN = "LOGIN";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登出
|
|
||||||
/// </summary>
|
|
||||||
public const string Log_LOGOUT = "LOGOUT";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作
|
|
||||||
/// </summary>
|
|
||||||
public const string Log_OPERATE = "OPERATE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异常
|
|
||||||
/// </summary>
|
|
||||||
public const string Log_EXCEPTION = "EXCEPTION";
|
|
||||||
|
|
||||||
#endregion 日志表
|
|
||||||
|
|
||||||
#region 角色表
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 全局
|
|
||||||
/// </summary>
|
|
||||||
public const string Role_GLOBAL = "GLOBAL";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Api
|
|
||||||
/// </summary>
|
|
||||||
public const string Role_API = "API";
|
|
||||||
|
|
||||||
#endregion 角色表
|
|
||||||
|
|
||||||
#region Api分组
|
|
||||||
|
|
||||||
public const string ThingsGatewayAdmin = "ThingsGateway.Admin";
|
|
||||||
public const string ThingsGatewayApi = "ThingsGateway.OpenApi";
|
|
||||||
|
|
||||||
#endregion Api分组
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 授权用户常量
|
/// 授权用户常量
|
||||||
@@ -21,7 +25,7 @@ public class ClaimConst
|
|||||||
public const string Account = "Account";
|
public const string Account = "Account";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 账号类型
|
/// SuperAdmin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SuperAdmin = "SuperAdmin";
|
public const string SuperAdmin = "SuperAdmin";
|
||||||
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统配置常量
|
|
||||||
/// </summary>
|
|
||||||
public class ConfigConst
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 系统默认工作台
|
|
||||||
/// </summary>
|
|
||||||
public const string SYS_DEFAULT_WORKBENCH_DATA = "SYS_DEFAULT_WORKBENCH_DATA";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统默认主页
|
|
||||||
/// </summary>
|
|
||||||
public const string SYS_DEFAULT_DEFAULT_RAZOR = "SYS_DEFAULT_DEFAULT_RAZOR";
|
|
||||||
|
|
||||||
#region 登录策略
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录验证码开关
|
|
||||||
/// </summary>
|
|
||||||
public const string LOGIN_CAPTCHA_OPEN = "LOGIN_CAPTCHA_OPEN";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 单用户登录开关
|
|
||||||
/// </summary>
|
|
||||||
public const string LOGIN_SINGLE_OPEN = "LOGIN_SINGLE_OPEN";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录错误锁定时长
|
|
||||||
/// </summary>
|
|
||||||
public const string LOGIN_ERROR_LOCK = "LOGIN_ERROR_LOCK";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录错误锁定时长
|
|
||||||
/// </summary>
|
|
||||||
public const string LOGIN_ERROR_RESET_TIME = "LOGIN_ERROR_RESET_TIME";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录错误次数
|
|
||||||
/// </summary>
|
|
||||||
public const string LOGIN_ERROR_COUNT = "LOGIN_ERROR_COUNT";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verificat过期时间(分)
|
|
||||||
/// </summary>
|
|
||||||
public const string LOGIN_VERIFICAT_EXPIRES = "LOGIN_VERIFICAT_EXPIRES";
|
|
||||||
|
|
||||||
#endregion 登录策略
|
|
||||||
|
|
||||||
#region 密码策略
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 默认用户密码
|
|
||||||
/// </summary>
|
|
||||||
public const string PWD_DEFAULT_PASSWORD = "PWD_DEFAULT_PASSWORD";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 密码最小长度
|
|
||||||
/// </summary>
|
|
||||||
public const string PWD_MIN_LENGTH = "PWD_MIN_LENGTH";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 包含数字
|
|
||||||
/// </summary>
|
|
||||||
public const string PWD_CONTAIN_NUM = "PWD_CONTAIN_NUM";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 包含小写字母
|
|
||||||
/// </summary>
|
|
||||||
public const string PWD_CONTAIN_LOWER = "PWD_CONTAIN_LOWER";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 包含大写字母
|
|
||||||
/// </summary>
|
|
||||||
public const string PWD_CONTAIN_UPPER = "PWD_CONTAIN_UPPER";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 包含特殊字符
|
|
||||||
/// </summary>
|
|
||||||
public const string PWD_CONTAIN_CHARACTER = "PWD_CONTAIN_CHARACTER";
|
|
||||||
|
|
||||||
#endregion 密码策略
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,6 +9,9 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -16,7 +20,22 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
public class ResourceConst
|
public class ResourceConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 系统内置单页面编码
|
/// 系统内置编码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string System = "system";
|
public const string System = "System";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 系统管理内置ID 1
|
||||||
|
/// </summary>
|
||||||
|
public const long SystemId = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SPA内置ID 2
|
||||||
|
/// </summary>
|
||||||
|
public const long SpaId = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SPA内置
|
||||||
|
/// </summary>
|
||||||
|
public const string SpaTitle = "SPA";
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,6 +9,9 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -15,13 +19,23 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class RoleConst
|
public class RoleConst
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 超级管理员Id
|
||||||
|
/// </summary>
|
||||||
|
public const long SuperAdminId = 212725263002001;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 超级管理员
|
/// 超级管理员
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SuperAdmin = "superAdmin";
|
public const string SuperAdmin = "SuperAdmin";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 业务管理员
|
/// 业务管理员
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string BizAdmin = "bizAdmin";
|
public const string BizAdmin = "BizAdmin";
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// api角色
|
||||||
|
/// </summary>
|
||||||
|
public const string ApiRole = "ApiRole";
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SqlSugar系统常量
|
/// SqlSugar系统常量
|
||||||
@@ -25,6 +29,16 @@ public class SqlSugarConst
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string DB_Log = "DB_Log";
|
public const string DB_Log = "DB_Log";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DB_TokenCache
|
||||||
|
/// </summary>
|
||||||
|
public const string DB_TokenCache = "DB_TokenCache";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DB_HardwareInfo
|
||||||
|
/// </summary>
|
||||||
|
public const string DB_HardwareInfo = "DB_HardwareInfo";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DB_Custom
|
/// DB_Custom
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统层常量
|
|
||||||
/// </summary>
|
|
||||||
public class SystemConst
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 系统配置表缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_DevConfig = CacheConst.Cache_Prefix_Admin + "SysConfig:";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录验证码缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_Captcha = CacheConst.Cache_Prefix_Admin + "Captcha:";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户表缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_SysUser = CacheConst.Cache_Prefix_Admin + "SysUser";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户手机号关系缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_SysUserPhone = CacheConst.Cache_Prefix_Admin + "SysUserPhone";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户手机号关系缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_SysUserAccount = CacheConst.Cache_Prefix_Admin + "SysUserAccount";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 资源表缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_SysResource = CacheConst.Cache_Prefix_Admin + "SysResource:";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 字典表缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_DevDict = CacheConst.Cache_Prefix_Admin + "DevDict";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关系表缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_SysRelation = CacheConst.Cache_Prefix_Admin + "SysRelation:";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 角色表缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_SysRole = CacheConst.Cache_Prefix_Admin + "SysRole";
|
|
||||||
|
|
||||||
#region 登录错误次数
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录错误次数缓存Key
|
|
||||||
/// </summary>
|
|
||||||
public const string Cache_LoginErrorCount = CacheConst.Cache_Prefix_Admin + "LoginErrorCount:";
|
|
||||||
|
|
||||||
#endregion 登录错误次数
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
[Route("api/auth")]
|
||||||
|
[LoggingMonitor]
|
||||||
|
public class AuthController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IAuthService _authService;
|
||||||
|
|
||||||
|
public AuthController(IAuthService authService)
|
||||||
|
{
|
||||||
|
_authService = authService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("login")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public Task<LoginOutput> LoginAsync([FromBody] LoginInput input)
|
||||||
|
{
|
||||||
|
return _authService.LoginAsync(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("logout")]
|
||||||
|
[Authorize]
|
||||||
|
[IgnoreRolePermission]
|
||||||
|
public async Task<IActionResult> LogoutAsync([FromQuery] string returnUrl)
|
||||||
|
{
|
||||||
|
await _authService.LoginOutAsync();
|
||||||
|
return Redirect(QueryHelpers.AddQueryString(Request.PathBase + CookieAuthenticationDefaults.LoginPath, new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["ReturnUrl"] = returnUrl
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Localization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文化 Controller
|
||||||
|
/// </summary>
|
||||||
|
[Route("[controller]/[action]")]
|
||||||
|
public class CultureController : Controller
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设置文化方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="culture"></param>
|
||||||
|
/// <param name="redirectUri"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult SetCulture(string culture, string redirectUri)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(culture))
|
||||||
|
{
|
||||||
|
HttpContext.Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HttpContext.Response.Cookies.Append(
|
||||||
|
CookieRequestCultureProvider.DefaultCookieName,
|
||||||
|
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture, culture)), new CookieOptions()
|
||||||
|
{
|
||||||
|
Expires = DateTimeOffset.Now.AddYears(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
//更改全局文化,采集后台也会变化
|
||||||
|
//var cultureInfo = new CultureInfo(culture);
|
||||||
|
//CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
|
||||||
|
//CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
|
||||||
|
|
||||||
|
//CultureInfo.CurrentCulture = cultureInfo;
|
||||||
|
//CultureInfo.CurrentUICulture = cultureInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocalRedirect(redirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重置文化方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="redirectUri"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult ResetCulture(string redirectUri)
|
||||||
|
{
|
||||||
|
HttpContext.Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName);
|
||||||
|
|
||||||
|
return LocalRedirect(redirectUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,22 +9,18 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace ThingsGateway.Demo.Web
|
namespace ThingsGateway.Admin.Application
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件下载
|
/// 文件下载
|
||||||
/// </summary>
|
/// </summary>
|
||||||
#if DEMO
|
[Route("api/file")]
|
||||||
#else
|
|
||||||
|
|
||||||
[ApiDescriptionSettings(IgnoreApi = true)]
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("[controller]")]
|
|
||||||
public class FileController : ControllerBase
|
public class FileController : ControllerBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,7 +28,7 @@ namespace ThingsGateway.Demo.Web
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">相对路径</param>
|
/// <param name="fileName">相对路径</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet("download")]
|
||||||
public IActionResult Download(string fileName)
|
public IActionResult Download(string fileName)
|
||||||
{
|
{
|
||||||
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", fileName);
|
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", fileName);
|
||||||
@@ -0,0 +1,350 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using ThingsGateway.Core.Json.Extension;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规范化RESTful风格返回值
|
||||||
|
/// </summary>
|
||||||
|
public class ResultFilter : IAsyncActionFilter
|
||||||
|
{
|
||||||
|
public const string ValidationFailedKey = $"{nameof(ResultFilter)}Validate";
|
||||||
|
|
||||||
|
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||||
|
{
|
||||||
|
// 排除 WebSocket 请求处理
|
||||||
|
if (context.HttpContext.IsWebSocketRequest())
|
||||||
|
{
|
||||||
|
await next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var httpContext = context.HttpContext;
|
||||||
|
var unifyResult = httpContext.RequestServices.GetRequiredService<IUnifyResultProvider>();
|
||||||
|
|
||||||
|
#region 验证
|
||||||
|
|
||||||
|
// 解析验证消息
|
||||||
|
{
|
||||||
|
if (!context.ModelState.IsValid)
|
||||||
|
{
|
||||||
|
var allValidationResults = new List<ValidationResult>();
|
||||||
|
int errorCount = 0;
|
||||||
|
//重新获取错误信息
|
||||||
|
foreach (var item in context.ActionArguments)
|
||||||
|
{
|
||||||
|
if (errorCount == context.ModelState.ErrorCount) break;
|
||||||
|
foreach (var parameter in context.ModelState)
|
||||||
|
{
|
||||||
|
if (errorCount == context.ModelState.ErrorCount) break;
|
||||||
|
var validationResults = new List<ValidationResult>();
|
||||||
|
var validationContext = new ValidationContext(item.Value!);
|
||||||
|
ValidateProperty(validationContext, validationResults, parameter.Key);
|
||||||
|
allValidationResults.AddRange(validationResults);
|
||||||
|
errorCount += validationResults.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//var validationMetadata = GetValidationMetadata(context.ModelState!);
|
||||||
|
if (allValidationResults.Count > 0)
|
||||||
|
{
|
||||||
|
string? errorMessage;
|
||||||
|
if (allValidationResults.Count == 1)
|
||||||
|
{
|
||||||
|
errorMessage = allValidationResults.FirstOrDefault()!.ErrorMessage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = allValidationResults!.ToDictionary(a => a.MemberNames.FirstOrDefault()!, a => a.ErrorMessage).ToSystemTextJsonString();
|
||||||
|
}
|
||||||
|
var result = unifyResult.OnValidateFailed(context, errorMessage);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
context.Result = result;
|
||||||
|
|
||||||
|
// 存储验证执行结果
|
||||||
|
context.HttpContext.Items[ValidationFailedKey] = errorMessage;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 验证
|
||||||
|
|
||||||
|
// 执行 Action 并获取结果
|
||||||
|
ActionExecutedContext? actionExecutedContext = await next();
|
||||||
|
|
||||||
|
#region 异常
|
||||||
|
|
||||||
|
// 如果出现异常
|
||||||
|
if (actionExecutedContext.Exception != null)
|
||||||
|
{
|
||||||
|
// 判断是否支持 MVC 规范化处理
|
||||||
|
if (UnifyContext.CheckHttpContextNonUnify(httpContext)) return;
|
||||||
|
// 执行规范化异常处理
|
||||||
|
actionExecutedContext.Result = unifyResult.OnException(actionExecutedContext);
|
||||||
|
actionExecutedContext.ExceptionHandled = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 异常
|
||||||
|
|
||||||
|
#region 状态码
|
||||||
|
|
||||||
|
// 处理已经含有状态码结果的 Result
|
||||||
|
if (actionExecutedContext.Result is IStatusCodeActionResult statusCodeResult && statusCodeResult.StatusCode != null)
|
||||||
|
{
|
||||||
|
// 小于 200 或者 大于 299 都不是成功值,直接跳过
|
||||||
|
if (statusCodeResult.StatusCode.Value < 200 || statusCodeResult.StatusCode.Value > 299)
|
||||||
|
{
|
||||||
|
// 处理规范化结果
|
||||||
|
if (!UnifyContext.CheckStatusCodeNonUnify(httpContext))
|
||||||
|
{
|
||||||
|
var statusCode = statusCodeResult.StatusCode.Value;
|
||||||
|
|
||||||
|
// 解决刷新 Token 时间和 Token 时间相近问题
|
||||||
|
if (statusCodeResult.StatusCode.Value == StatusCodes.Status401Unauthorized
|
||||||
|
&& httpContext.Response.Headers.ContainsKey("access-token")
|
||||||
|
&& httpContext.Response.Headers.ContainsKey("x-access-token"))
|
||||||
|
{
|
||||||
|
httpContext.Response.StatusCode = statusCode = StatusCodes.Status403Forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 Response 已经完成输出,则禁止写入
|
||||||
|
if (httpContext.Response.HasStarted) return;
|
||||||
|
|
||||||
|
await unifyResult.OnResponseStatusCodes(httpContext, statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 状态码
|
||||||
|
|
||||||
|
#region 成功
|
||||||
|
|
||||||
|
// 获取控制器信息
|
||||||
|
var actionDescriptor = actionExecutedContext.ActionDescriptor as ControllerActionDescriptor;
|
||||||
|
|
||||||
|
// 判断是否支持 MVC 规范化处理或特定检查
|
||||||
|
if (UnifyContext.CheckHttpContextNonUnify(httpContext)) return;
|
||||||
|
|
||||||
|
// 判断是否跳过规范化处理
|
||||||
|
if (UnifyContext.CheckSucceededNonUnify(actionDescriptor!.MethodInfo)) return;
|
||||||
|
|
||||||
|
// 处理 BadRequestObjectResult 类型规范化处理
|
||||||
|
if (actionExecutedContext.Result is BadRequestObjectResult badRequestObjectResult)
|
||||||
|
{
|
||||||
|
// 解析验证消息
|
||||||
|
var validationMetadata = GetValidationMetadata(badRequestObjectResult.Value!);
|
||||||
|
|
||||||
|
var result = unifyResult.OnValidateFailed(context, validationMetadata);
|
||||||
|
if (result != null) actionExecutedContext.Result = result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IActionResult? result = default;
|
||||||
|
|
||||||
|
// 检查是否是有效的结果(可进行规范化的结果)
|
||||||
|
if (UnifyContext.CheckVaildResult(actionExecutedContext.Result!, out var data))
|
||||||
|
{
|
||||||
|
result = unifyResult.OnSucceeded(actionExecutedContext, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是不能规范化的结果类型,则跳过
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
actionExecutedContext.Result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 成功
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取验证错误信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errors"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static string GetValidationMetadata(object errors)
|
||||||
|
{
|
||||||
|
object? validationResults = null;
|
||||||
|
string? message = default;
|
||||||
|
|
||||||
|
// 判断是否是集合类型
|
||||||
|
if (errors is IEnumerable && errors is not string)
|
||||||
|
{
|
||||||
|
// 如果是模型验证字典类型
|
||||||
|
if (errors is ModelStateDictionary modelState)
|
||||||
|
{
|
||||||
|
// 将验证错误信息转换成字典并序列化成 Json
|
||||||
|
validationResults = modelState.Where(u => modelState[u.Key]!.ValidationState == ModelValidationState.Invalid)
|
||||||
|
.ToDictionary(u => u.Key, u => modelState[u.Key]!.Errors.Select(c => c.ErrorMessage).ToArray());
|
||||||
|
}
|
||||||
|
// 如果是 ValidationProblemDetails 特殊类型
|
||||||
|
else if (errors is ValidationProblemDetails validation)
|
||||||
|
{
|
||||||
|
validationResults = validation.Errors
|
||||||
|
.ToDictionary(u => u.Key, u => u.Value.ToArray());
|
||||||
|
}
|
||||||
|
// 如果是字典类型
|
||||||
|
else if (errors is IDictionary<string, string[]> dicResults)
|
||||||
|
{
|
||||||
|
validationResults = dicResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = JsonSerializer.Serialize(validationResults, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||||
|
WriteIndented = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取异常元数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetExceptionMetadata(ActionContext context)
|
||||||
|
{
|
||||||
|
// 判断是否是 ExceptionContext 或者 ActionExecutedContext
|
||||||
|
var exception = context is ExceptionContext exContext
|
||||||
|
? exContext.Exception
|
||||||
|
: (
|
||||||
|
context is ActionExecutedContext edContext
|
||||||
|
? edContext.Exception
|
||||||
|
: default
|
||||||
|
);
|
||||||
|
|
||||||
|
string? errors = exception?.InnerException?.Message ?? exception?.Message;
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证整个模型时验证属性方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
/// <param name="pName"></param>
|
||||||
|
private void ValidateProperty(ValidationContext context, List<ValidationResult> results, string pName)
|
||||||
|
{
|
||||||
|
// 获得所有可写属性
|
||||||
|
var pi = context.ObjectType.GetPropertyByName(pName);
|
||||||
|
if (pi != null)
|
||||||
|
{
|
||||||
|
// 设置其关联属性字段
|
||||||
|
var propertyValue = pi.GetValue(context.ObjectInstance);
|
||||||
|
var fieldIdentifier = new FieldIdentifier(context.ObjectInstance, pi.Name);
|
||||||
|
context.DisplayName = fieldIdentifier.GetDisplayName();
|
||||||
|
context.MemberName = fieldIdentifier.FieldName;
|
||||||
|
|
||||||
|
// 组件进行验证
|
||||||
|
ValidateDataAnnotations(propertyValue, context, results, pi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过属性设置的 DataAnnotation 进行数据验证
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
/// <param name="propertyInfo"></param>
|
||||||
|
/// <param name="memberName"></param>
|
||||||
|
private void ValidateDataAnnotations(object? value, ValidationContext context, List<ValidationResult> results, PropertyInfo propertyInfo, string? memberName = null)
|
||||||
|
{
|
||||||
|
var rules = propertyInfo.GetCustomAttributes(true).OfType<ValidationAttribute>().ToList();
|
||||||
|
var metadataType = context.ObjectType.GetCustomAttribute<MetadataTypeAttribute>(false);
|
||||||
|
if (metadataType != null)
|
||||||
|
{
|
||||||
|
var p = metadataType.MetadataClassType.GetPropertyByName(propertyInfo.Name);
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
rules.AddRange(p.GetCustomAttributes(true).OfType<ValidationAttribute>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var displayName = context.DisplayName;
|
||||||
|
memberName ??= propertyInfo.Name;
|
||||||
|
var attributeSpan = nameof(Attribute).AsSpan();
|
||||||
|
foreach (var rule in rules)
|
||||||
|
{
|
||||||
|
var result = rule.GetValidationResult(value, context);
|
||||||
|
if (result != null && result != ValidationResult.Success)
|
||||||
|
{
|
||||||
|
// 查找 resx 资源文件中的 ErrorMessage
|
||||||
|
var ruleNameSpan = rule.GetType().Name.AsSpan();
|
||||||
|
var index = ruleNameSpan.IndexOf(attributeSpan, StringComparison.OrdinalIgnoreCase);
|
||||||
|
var ruleName = ruleNameSpan[..index];
|
||||||
|
var find = false;
|
||||||
|
|
||||||
|
// 通过设置 ErrorMessage 检索
|
||||||
|
if (!context.ObjectType.Assembly.IsDynamic && !find
|
||||||
|
&& !string.IsNullOrEmpty(rule.ErrorMessage)
|
||||||
|
&& App.CreateLocalizerByType(context.ObjectType)!.TryGetLocalizerString(rule.ErrorMessage, out var msg))
|
||||||
|
{
|
||||||
|
rule.ErrorMessage = msg;
|
||||||
|
find = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过 Attribute 检索
|
||||||
|
if (!rule.GetType().Assembly.IsDynamic && !find
|
||||||
|
&& App.CreateLocalizerByType(rule.GetType())!.TryGetLocalizerString(nameof(rule.ErrorMessage), out msg))
|
||||||
|
{
|
||||||
|
rule.ErrorMessage = msg;
|
||||||
|
find = true;
|
||||||
|
}
|
||||||
|
// 通过 字段.规则名称 检索
|
||||||
|
if (!context.ObjectType.Assembly.IsDynamic && !find
|
||||||
|
&& App.CreateLocalizerByType(context.ObjectType)!.TryGetLocalizerString($"{memberName}.{ruleName.ToString()}", out msg))
|
||||||
|
{
|
||||||
|
rule.ErrorMessage = msg;
|
||||||
|
find = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!find)
|
||||||
|
{
|
||||||
|
rule.ErrorMessage = result.ErrorMessage;
|
||||||
|
}
|
||||||
|
var errorMessage = !string.IsNullOrEmpty(rule.ErrorMessage) && rule.ErrorMessage.Contains("{0}")
|
||||||
|
? rule.FormatErrorMessage(displayName)
|
||||||
|
: rule.ErrorMessage;
|
||||||
|
results.Add(new ValidationResult(errorMessage, new string[] { memberName }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规范化结果提供器
|
||||||
|
/// </summary>
|
||||||
|
public interface IUnifyResultProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 异常返回值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
IActionResult OnException(ActionExecutedContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成功返回值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
IActionResult OnSucceeded(ActionExecutedContext context, object? data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证失败返回值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="errors"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
IActionResult OnValidateFailed(ActionExecutingContext context, string? errors);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 拦截返回状态码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="statusCode"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task OnResponseStatusCodes(HttpContext context, int statusCode);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,9 +9,17 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
global using Furion.DynamicApiController;
|
|
||||||
|
|
||||||
global using System;
|
|
||||||
global using System.Threading.Tasks;
|
|
||||||
|
|
||||||
global using ThingsGateway.Admin.Application;
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 禁止规范化处理
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
|
||||||
|
public sealed class NonUnifyAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规范化结果上下文
|
||||||
|
/// </summary>
|
||||||
|
public static class UnifyContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启用规范化结果
|
||||||
|
/// </summary>
|
||||||
|
internal static bool EnabledUnifyHandler = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查请求成功是否进行规范化处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
|
||||||
|
internal static bool CheckSucceededNonUnify(MethodInfo method)
|
||||||
|
{
|
||||||
|
// 判断是否跳过规范化处理
|
||||||
|
var isSkip = !EnabledUnifyHandler
|
||||||
|
|| method.CustomAttributes.Any(x => typeof(NonUnifyAttribute).IsAssignableFrom(x.AttributeType) || typeof(ProducesResponseTypeAttribute).IsAssignableFrom(x.AttributeType) || typeof(IApiResponseMetadataProvider).IsAssignableFrom(x.AttributeType))
|
||||||
|
|| method.ReflectedType!.IsDefined(typeof(NonUnifyAttribute), true)
|
||||||
|
|| method.DeclaringType!.Assembly.GetName().Name!.StartsWith("Microsoft.AspNetCore.OData");
|
||||||
|
|
||||||
|
return isSkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查短路状态码(>=400)是否进行规范化处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
|
||||||
|
internal static bool CheckStatusCodeNonUnify(HttpContext context)
|
||||||
|
{
|
||||||
|
// 获取终点路由特性
|
||||||
|
var endpointFeature = context.Features.Get<IEndpointFeature>();
|
||||||
|
if (endpointFeature == null) return false;
|
||||||
|
|
||||||
|
// 判断是否跳过规范化处理
|
||||||
|
var isSkip = !EnabledUnifyHandler
|
||||||
|
|| context.Request.Headers["accept"].ToString().Contains("odata.metadata=", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| context.Request.Headers["accept"].ToString().Contains("odata.streaming=", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| ResponseContentTypesOfNonUnify.Any(u => context.Response.Headers["content-type"].ToString().Contains(u, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| context.GetMetadata<NonUnifyAttribute>() != null
|
||||||
|
|| endpointFeature?.Endpoint?.Metadata?.GetMetadata<NonUnifyAttribute>() != null
|
||||||
|
);
|
||||||
|
|
||||||
|
return isSkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 跳过规范化处理的 Response Content-Type
|
||||||
|
/// </summary>
|
||||||
|
internal static string[] ResponseContentTypesOfNonUnify = new[]
|
||||||
|
{
|
||||||
|
"text/event-stream",
|
||||||
|
"application/pdf",
|
||||||
|
"application/octet-stream",
|
||||||
|
"image/"
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查 HttpContext 是否进行规范化处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
/// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
|
||||||
|
internal static bool CheckHttpContextNonUnify(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
var contentType = httpContext.Response.Headers["content-type"].ToString();
|
||||||
|
if (ResponseContentTypesOfNonUnify.Any(u => contentType.Contains(u, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查是否是有效的结果(可进行规范化的结果)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static bool CheckVaildResult(IActionResult result, out object? data)
|
||||||
|
{
|
||||||
|
data = default;
|
||||||
|
|
||||||
|
// 排除以下结果,跳过规范化处理
|
||||||
|
var isDataResult = result switch
|
||||||
|
{
|
||||||
|
ViewResult => false,
|
||||||
|
PartialViewResult => false,
|
||||||
|
FileResult => false,
|
||||||
|
ChallengeResult => false,
|
||||||
|
SignInResult => false,
|
||||||
|
SignOutResult => false,
|
||||||
|
RedirectToPageResult => false,
|
||||||
|
RedirectToRouteResult => false,
|
||||||
|
RedirectResult => false,
|
||||||
|
RedirectToActionResult => false,
|
||||||
|
LocalRedirectResult => false,
|
||||||
|
ForbidResult => false,
|
||||||
|
ViewComponentResult => false,
|
||||||
|
PageResult => false,
|
||||||
|
NotFoundResult => false,
|
||||||
|
NotFoundObjectResult => false,
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 目前支持返回值 ActionResult
|
||||||
|
if (isDataResult) data = result switch
|
||||||
|
{
|
||||||
|
// 处理内容结果
|
||||||
|
ContentResult content => content.Content,
|
||||||
|
// 处理对象结果
|
||||||
|
ObjectResult obj => obj.Value,
|
||||||
|
// 处理 JSON 对象
|
||||||
|
JsonResult json => json.Value,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
return isDataResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全局返回结果
|
/// 全局返回结果
|
||||||
@@ -24,17 +28,12 @@ public class UnifyResult<T>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据
|
/// 数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T Data { get; set; }
|
public T? Data { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 附加数据
|
|
||||||
/// </summary>
|
|
||||||
public object Extras { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 错误信息
|
/// 错误信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object Msg { get; set; }
|
public object? Msg { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 时间
|
/// 时间
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,31 +9,32 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Furion.DataValidation;
|
|
||||||
using Furion.FriendlyException;
|
|
||||||
using Furion.UnifyResult;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 规范化RESTful风格返回值
|
/// 规范化RESTful风格返回值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressSniffer, UnifyModel(typeof(UnifyResult<>))]
|
|
||||||
public class UnifyResultProvider : IUnifyResultProvider
|
public class UnifyResultProvider : IUnifyResultProvider
|
||||||
{
|
{
|
||||||
|
private static IStringLocalizer Localizer = App.CreateLocalizerByType(typeof(UnifyResultProvider))!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异常返回
|
/// 异常返回
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <param name="metadata"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
|
public IActionResult OnException(ActionExecutedContext context)
|
||||||
{
|
{
|
||||||
return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
|
return new JsonResult(RESTfulResult(context.Result is IStatusCodeActionResult statusCodeResult ? statusCodeResult.StatusCode ?? 500 : 500, false, null, context.Exception?.GetTrue()?.Message));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -40,24 +42,18 @@ public class UnifyResultProvider : IUnifyResultProvider
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <param name="statusCode"></param>
|
/// <param name="statusCode"></param>
|
||||||
/// <param name="unifyResultSettings"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = null)
|
public async Task OnResponseStatusCodes(HttpContext context, int statusCode)
|
||||||
{
|
{
|
||||||
// 设置响应状态码
|
|
||||||
UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings);
|
|
||||||
|
|
||||||
switch (statusCode)
|
switch (statusCode)
|
||||||
{
|
{
|
||||||
// 处理 401 状态码
|
// 处理 401 状态码
|
||||||
case StatusCodes.Status401Unauthorized:
|
case StatusCodes.Status401Unauthorized:
|
||||||
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "登录已过期,请重新登录"),
|
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, Localizer["TokenOver"]));
|
||||||
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
|
|
||||||
break;
|
break;
|
||||||
// 处理 403 状态码
|
// 处理 403 状态码
|
||||||
case StatusCodes.Status403Forbidden:
|
case StatusCodes.Status403Forbidden:
|
||||||
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "禁止访问,没有权限"),
|
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, default, "NoPermission"));
|
||||||
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
@@ -70,7 +66,7 @@ public class UnifyResultProvider : IUnifyResultProvider
|
|||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <param name="data"></param>
|
/// <param name="data"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
|
public IActionResult OnSucceeded(ActionExecutedContext context, object? data)
|
||||||
{
|
{
|
||||||
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
|
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
|
||||||
}
|
}
|
||||||
@@ -79,11 +75,11 @@ public class UnifyResultProvider : IUnifyResultProvider
|
|||||||
/// 验证失败返回
|
/// 验证失败返回
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <param name="metadata"></param>
|
/// <param name="errors"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
|
public IActionResult OnValidateFailed(ActionExecutingContext context, string? errors)
|
||||||
{
|
{
|
||||||
return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.FirstErrorMessage ?? metadata.Message));
|
return new JsonResult(RESTfulResult(StatusCodes.Status400BadRequest, false, null, errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -94,14 +90,13 @@ public class UnifyResultProvider : IUnifyResultProvider
|
|||||||
/// <param name="data">数据</param>
|
/// <param name="data">数据</param>
|
||||||
/// <param name="errors">错误信息</param>
|
/// <param name="errors">错误信息</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static UnifyResult<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
|
private static UnifyResult<object> RESTfulResult(int statusCode, bool succeeded = default, object? data = default, object? errors = default)
|
||||||
{
|
{
|
||||||
return new UnifyResult<object>
|
return new UnifyResult<object>
|
||||||
{
|
{
|
||||||
Code = statusCode,
|
Code = statusCode,
|
||||||
Msg = statusCode == StatusCodes.Status200OK ? "请求成功" : errors,
|
Msg = statusCode == StatusCodes.Status200OK ? "Success" : errors,
|
||||||
Data = data,
|
Data = data,
|
||||||
Extras = UnifyContext.Take(),
|
|
||||||
Time = DateTime.Now,
|
Time = DateTime.Now,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
using ThingsGateway.Core;
|
||||||
|
|
||||||
|
using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gitee WebHook
|
||||||
|
/// </summary>
|
||||||
|
[Route("api/[controller]/[action]")]
|
||||||
|
[ApiController]
|
||||||
|
public class GiteeController : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gitee Webhook
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult Webhook([FromQuery] string? id, [FromServices] IConfiguration config, [FromServices] IDispatchService<GiteePostBody> dispatch, [FromBody] GiteePostBody payload)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (Check())
|
||||||
|
{
|
||||||
|
// 全局推送
|
||||||
|
if (payload.HeadCommit != null || payload.Commits?.Count > 0)
|
||||||
|
{
|
||||||
|
dispatch.Dispatch(new DispatchEntry<GiteePostBody>()
|
||||||
|
{
|
||||||
|
Name = "Gitee",
|
||||||
|
Entry = payload
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret ? Ok() : Unauthorized();
|
||||||
|
|
||||||
|
bool Check()
|
||||||
|
{
|
||||||
|
var configId = config.GetValue<string>("WebHooks:Gitee:Id");
|
||||||
|
var configToken = config.GetValue<string>("WebHooks:Gitee:Token");
|
||||||
|
var token = "";
|
||||||
|
if (Request.Headers.TryGetValue("X-Gitee-Token", out var val))
|
||||||
|
{
|
||||||
|
token = val.FirstOrDefault() ?? string.Empty;
|
||||||
|
}
|
||||||
|
return id == configId && token == configToken
|
||||||
|
&& payload.Id == configId && payload.Password == configToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Webhook 测试接口
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Webhook()
|
||||||
|
{
|
||||||
|
return Ok(new { Message = "Ok" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 跨域握手协议
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpOptions]
|
||||||
|
public string Options() => string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
[Route("openapi/auth")]
|
||||||
|
[LoggingMonitor]
|
||||||
|
public class OpenApiController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IAuthService _authService;
|
||||||
|
|
||||||
|
public OpenApiController(IAuthService authService)
|
||||||
|
{
|
||||||
|
_authService = authService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("login")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public Task<LoginOutput> LoginAsync([FromBody] LoginInput input)
|
||||||
|
{
|
||||||
|
return _authService.LoginAsync(input, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("logout")]
|
||||||
|
[Authorize]
|
||||||
|
[IgnoreRolePermission]
|
||||||
|
public async Task<IActionResult> LogoutAsync([FromQuery] string returnUrl)
|
||||||
|
{
|
||||||
|
await _authService.LoginOutAsync();
|
||||||
|
return Redirect(QueryHelpers.AddQueryString(Request.PathBase + CookieAuthenticationDefaults.LoginPath, new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["ReturnUrl"] = returnUrl
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,19 +9,24 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
[Route("api/[controller]/[action]")]
|
||||||
/// SignalR组件
|
[RolePermission]
|
||||||
/// </summary>
|
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||||
public sealed class SignalRComponent : IServiceComponent
|
public class TestController : ControllerBase
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
[HttpGet]
|
||||||
public void Load(IServiceCollection services, ComponentContext componentContext)
|
public async Task Test(QueryPageOptions queryPageOptions)
|
||||||
{
|
{
|
||||||
services.AddSingleton<IUserIdProvider, UserIdProvider>();//用户ID提供器
|
await Task.CompletedTask;
|
||||||
services.AddSignalR();//注册SignalR
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 会话信息
|
|
||||||
/// </summary>
|
|
||||||
public class VerificatInfo : PrimaryIdEntity
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 客户端ID列表
|
|
||||||
/// </summary>
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
|
||||||
public List<string> ClientIds { get; set; } = new List<string>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设备
|
|
||||||
/// </summary>
|
|
||||||
[Description("设备")]
|
|
||||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
|
||||||
public string Device { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 过期时间
|
|
||||||
/// </summary>
|
|
||||||
[Description("过期时间")]
|
|
||||||
public int Expire { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 验证Id
|
|
||||||
/// </summary>
|
|
||||||
[Description("验证Id")]
|
|
||||||
[DataTable(Order = 3, IsShow = true, Sortable = true)]
|
|
||||||
public override long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 在线状态
|
|
||||||
/// </summary>
|
|
||||||
[Description("在线状态")]
|
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
|
||||||
public bool IsOnline => ClientIds.Count > 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接数量
|
|
||||||
/// </summary>
|
|
||||||
[Description("连接数量")]
|
|
||||||
[DataTable(Order = 4, IsShow = true, Sortable = true)]
|
|
||||||
public int OnlineNum => ClientIds.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// verificat剩余有效期
|
|
||||||
/// </summary>
|
|
||||||
[Description("有效期")]
|
|
||||||
[DataTable(Order = 5, IsShow = true, Sortable = true)]
|
|
||||||
public string VerificatRemain { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 超时时间
|
|
||||||
/// </summary>
|
|
||||||
[Description("超时时间")]
|
|
||||||
public DateTime VerificatTimeout { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,18 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
using ThingsGateway.Core;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主键id基类
|
/// 主键id基类
|
||||||
@@ -20,6 +32,7 @@ public abstract class PrimaryIdEntity : IPrimaryIdEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
||||||
public virtual long Id { get; set; }
|
public virtual long Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +46,7 @@ public abstract class PrimaryKeyEntity : PrimaryIdEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "扩展信息", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "扩展信息", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public virtual string? ExtJson { get; set; }
|
public virtual string? ExtJson { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +60,7 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "创建时间", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "创建时间", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public virtual DateTime? CreateTime { get; set; }
|
public virtual DateTime? CreateTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -53,13 +68,16 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "创建人", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "创建人", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
public virtual string CreateUser { get; set; }
|
[NotNull]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public virtual string? CreateUser { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建者Id
|
/// 创建者Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "创建者Id", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "创建者Id", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public virtual long CreateUserId { get; set; }
|
public virtual long CreateUserId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -67,6 +85,7 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "软删除", IsNullable = true)]
|
[SugarColumn(ColumnDescription = "软删除", IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public virtual bool IsDelete { get; set; } = false;
|
public virtual bool IsDelete { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -74,6 +93,7 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "更新时间", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "更新时间", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public virtual DateTime? UpdateTime { get; set; }
|
public virtual DateTime? UpdateTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -81,19 +101,21 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "更新人", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "更新人", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
public virtual string UpdateUser { get; set; }
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public virtual string? UpdateUser { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 修改者Id
|
/// 修改者Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "修改者Id", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "修改者Id", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public virtual long? UpdateUserId { get; set; }
|
public virtual long? UpdateUserId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 排序码
|
/// 排序码
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = false)]
|
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||||
[IgnoreExcel]
|
[AutoGenerateColumn(Visible = false, DefaultSort = true, Sortable = true, DefaultSortOrder = SortOrder.Asc)]
|
||||||
public virtual int SortCode { get; set; }
|
public int? SortCode { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 配置
|
|
||||||
///</summary>
|
|
||||||
[SugarTable("sys_config", TableDescription = "配置表")]
|
|
||||||
[Tenant(SqlSugarConst.DB_Admin)]
|
|
||||||
public class SysConfig : BaseEntity
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 分类
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "分类", Length = 200)]
|
|
||||||
public virtual string Category { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 配置键
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "配置键", Length = 200)]
|
|
||||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
|
||||||
public virtual string ConfigKey { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 配置值
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "配置值", ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")]
|
|
||||||
public virtual string ConfigValue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 备注
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "备注", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 4, IsShow = true)]
|
|
||||||
public string Remark { get; set; }
|
|
||||||
}
|
|
||||||
63
src/ThingsGateway.Admin.Application/Entity/SysDict.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
[SugarTable("sys_dict", TableDescription = "字典表")]
|
||||||
|
[Tenant(SqlSugarConst.DB_Admin)]
|
||||||
|
public class SysDict : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 类型
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "类型", Length = 200)]
|
||||||
|
[AutoGenerateColumn(Ignore = true, Filterable = true, Sortable = true)]
|
||||||
|
public virtual DictTypeEnum DictType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分类
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "分类", Length = 200)]
|
||||||
|
[Required]
|
||||||
|
[AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)]
|
||||||
|
public string Category { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "名称", Length = 200)]
|
||||||
|
[Required]
|
||||||
|
[AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)]
|
||||||
|
public virtual string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 代码
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "代码", ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
||||||
|
[Required]
|
||||||
|
[AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)]
|
||||||
|
public virtual string Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 描述
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "描述", Length = 200, IsNullable = true)]
|
||||||
|
public string Remark { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,6 +9,13 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -15,54 +23,118 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
///</summary>
|
///</summary>
|
||||||
[SugarTable("sys_operatelog", TableDescription = "操作日志表")]
|
[SugarTable("sys_operatelog", TableDescription = "操作日志表")]
|
||||||
[Tenant(SqlSugarConst.DB_Log)]
|
[Tenant(SqlSugarConst.DB_Log)]
|
||||||
public class SysOperateLog : SysVisitLog
|
public class SysOperateLog
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志分类
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "日志分类", Length = 200)]
|
||||||
|
[AutoGenerateColumn(Order = 1, Filterable = true, Sortable = true)]
|
||||||
|
public LogCateGoryEnum Category { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日志名称
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "日志名称", Length = 200)]
|
||||||
|
[AutoGenerateColumn(Order = 2, Filterable = true, Sortable = true)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 类名称
|
/// 类名称
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "类名称", Length = 200)]
|
[SugarColumn(ColumnDescription = "类名称", Length = 200)]
|
||||||
[DataTable(Order = 21, IsShow = true, Sortable = true, DefaultFilter = true, CellClass = " table-text-truncate ")]
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
public string ClassName { get; set; }
|
public string ClassName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 具体消息
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "具体消息", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
|
||||||
[DataTable(Order = 22, IsShow = true, Sortable = true, DefaultFilter = false, CellClass = " table-text-truncate ")]
|
|
||||||
public string ExeMessage { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 方法名称
|
/// 方法名称
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "方法名称", Length = 200)]
|
[SugarColumn(ColumnDescription = "方法名称", Length = 200)]
|
||||||
[DataTable(Order = 23, IsShow = true, Sortable = true, DefaultFilter = true, CellClass = " table-text-truncate ")]
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
public string MethodName { get; set; }
|
public string MethodName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求参数
|
/// 请求参数
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "请求参数", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "请求参数", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
[DataTable(Order = 24, IsShow = true, Sortable = true, DefaultFilter = false, CellClass = " table-text-truncate ")]
|
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
|
||||||
public string ParamJson { get; set; }
|
public string? ParamJson { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求方式
|
/// 请求方式
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "请求方式", Length = 200, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "请求方式", Length = 200, IsNullable = true)]
|
||||||
[DataTable(Order = 25, IsShow = true, Sortable = true, DefaultFilter = true, CellClass = " table-text-truncate ")]
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
public string ReqMethod { get; set; }
|
public string? ReqMethod { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求地址
|
/// 请求地址
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "请求地址", ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
[SugarColumn(ColumnDescription = "请求地址", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
[DataTable(Order = 26, IsShow = true, Sortable = true, DefaultFilter = true, CellClass = " table-text-truncate ")]
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
public string ReqUrl { get; set; }
|
public string? ReqUrl { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回结果
|
/// 返回结果
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "返回结果", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "返回结果", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
[DataTable(Order = 27, IsShow = true, Sortable = true, DefaultFilter = false, CellClass = " table-text-truncate ")]
|
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
|
||||||
public string ResultJson { get; set; }
|
public string? ResultJson { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 具体消息
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "具体消息", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
|
||||||
|
public string? ExeMessage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行状态
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "执行状态", Length = 200)]
|
||||||
|
[AutoGenerateColumn(Filterable = true, Sortable = true)]
|
||||||
|
public bool ExeStatus { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作账号
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "操作账号", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Filterable = true, Sortable = true)]
|
||||||
|
public string? OpAccount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作浏览器
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "操作浏览器", Length = 200)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string OpBrowser { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作ip
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "操作ip", Length = 200)]
|
||||||
|
[AutoGenerateColumn(Filterable = true, Sortable = true)]
|
||||||
|
public string? OpIp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作系统
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "操作系统", Length = 200)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string OpOs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作时间
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "操作时间")]
|
||||||
|
[AutoGenerateColumn(Visible = true, DefaultSort = true, Sortable = true, DefaultSortOrder = SortOrder.Desc)]
|
||||||
|
public DateTime OpTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证Id
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "验证Id")]
|
||||||
|
[IgnoreExcel]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public long VerificatId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,6 +9,11 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -21,7 +27,7 @@ public class SysRelation : PrimaryKeyEntity
|
|||||||
/// 分类
|
/// 分类
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "分类", Length = 200)]
|
[SugarColumn(ColumnDescription = "分类", Length = 200)]
|
||||||
public string Category { get; set; }
|
public RelationCategoryEnum Category { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对象ID
|
/// 对象ID
|
||||||
@@ -33,5 +39,5 @@ public class SysRelation : PrimaryKeyEntity
|
|||||||
/// 目标ID
|
/// 目标ID
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "目标ID", IsNullable = true)]
|
[SugarColumn(ColumnDescription = "目标ID", IsNullable = true)]
|
||||||
public string TargetId { get; set; }
|
public string? TargetId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,7 +9,18 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components.Routing;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
@@ -22,91 +34,72 @@ public class SysResource : BaseEntity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 父id
|
/// 父id
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "父id", IsNullable = true)]
|
[SugarColumn(ColumnDescription = "父id")]
|
||||||
public virtual long? ParentId { get; set; }
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public virtual long ParentId { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模块
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "模块")]
|
||||||
|
[AutoGenerateColumn(Visible = false, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false, Searchable = true)]
|
||||||
|
public virtual long Module { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标题
|
/// 标题
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "标题", Length = 200)]
|
[SugarColumn(ColumnDescription = "标题", Length = 200)]
|
||||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
[Required]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, Searchable = true)]
|
||||||
public virtual string Title { get; set; }
|
public virtual string Title { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 编码
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "编码", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 5, IsShow = true, Sortable = true, DefaultFilter = true)]
|
|
||||||
public virtual string Code { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 分类
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "分类", Length = 200)]
|
|
||||||
[DataTable(Order = 4, IsShow = true, Sortable = true, DefaultFilter = true)]
|
|
||||||
public string Category { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 菜单类型
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "菜单类型", ColumnDataType = "varchar(50)")]
|
|
||||||
[DataTable(Order = 4, IsShow = true, Sortable = true, DefaultFilter = true)]
|
|
||||||
public virtual MenuTypeEnum MenuType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 路径
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "路径", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 3, IsShow = true, Sortable = true)]
|
|
||||||
public virtual string Href { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 图标
|
/// 图标
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "图标", Length = 200, IsNullable = true)]
|
[SugarColumn(ColumnDescription = "图标", Length = 200, IsNullable = true)]
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
[AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)]
|
||||||
public virtual string Icon { get; set; }
|
public virtual string? Icon { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否隐藏
|
/// 编码
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "编码", Length = 200)]
|
||||||
|
[Required]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public virtual string Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分类
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "分类")]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
|
public ResourceCategoryEnum Category { get; set; } = ResourceCategoryEnum.Menu;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 目标类型
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "目标类型", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
|
public virtual TargetEnum? Target { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 菜单匹配类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "隐藏", Length = 200)]
|
[SugarColumn(ColumnDescription = "菜单匹配类型", IsNullable = true)]
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
public bool Hidden { get; set; } = false;
|
public virtual NavLinkMatch? NavLinkMatch { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 路径
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "路径", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, Searchable = true)]
|
||||||
|
public virtual string Href { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 子节点
|
/// 子节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(IsIgnore = true)]
|
[SugarColumn(IsIgnore = true)]
|
||||||
public List<SysResource> Children { get; set; }
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
}
|
public List<SysResource>? Children { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 链接跳转类型
|
|
||||||
/// </summary>
|
|
||||||
public enum MenuTypeEnum
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 目录
|
|
||||||
/// </summary>
|
|
||||||
[Description("目录")]
|
|
||||||
CATALOG = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 组件
|
|
||||||
/// </summary>
|
|
||||||
[Description("组件")]
|
|
||||||
MENU,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 内链
|
|
||||||
/// </summary>
|
|
||||||
[Description("内链")]
|
|
||||||
IFRAME,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 外链
|
|
||||||
/// </summary>
|
|
||||||
[Description("外链")]
|
|
||||||
LINK,
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,6 +9,15 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -21,35 +31,26 @@ public class SysRole : BaseEntity
|
|||||||
/// 编码
|
/// 编码
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "编码", Length = 200)]
|
[SugarColumn(ColumnDescription = "编码", Length = 200)]
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
public string Code { get; set; }
|
public string Code { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 名称
|
/// 名称
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "名称", Length = 200)]
|
[SugarColumn(ColumnDescription = "名称", Length = 200)]
|
||||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
[Required]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
public virtual string Name { get; set; }
|
public virtual string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分类
|
/// 分类
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "分类", Length = 200, IsNullable = false)]
|
[SugarColumn(ColumnDescription = "分类", Length = 200, IsNullable = false)]
|
||||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
public virtual string Category { get; set; }
|
public virtual RoleCategoryEnum Category { get; set; }
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return Id.GetHashCode();
|
return Id.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
if (obj == null || !(obj is UserSelectorOutput))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Id == ((UserSelectorOutput)obj).Id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,6 +9,17 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using Mapster;
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -17,111 +29,159 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
[Tenant(SqlSugarConst.DB_Admin)]
|
[Tenant(SqlSugarConst.DB_Admin)]
|
||||||
public class SysUser : BaseEntity
|
public class SysUser : BaseEntity
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 头像
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "头像", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)]
|
||||||
|
[AdaptIgnore]
|
||||||
|
public virtual string? Avatar { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 账号
|
/// 账号
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "账号", Length = 200)]
|
[SugarColumn(ColumnDescription = "账号", Length = 200)]
|
||||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
[Required]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
public virtual string Account { get; set; }
|
public virtual string Account { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 按钮码集合
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsIgnore = true)]
|
|
||||||
public List<string> ButtonCodeList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 邮箱
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "邮箱", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
|
||||||
public string Email { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上次登录设备
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "上次登录设备", IsNullable = true)]
|
|
||||||
[DataTable(Order = 3, IsShow = true, Sortable = true, DefaultFilter = true)]
|
|
||||||
public string LastLoginDevice { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上次登录ip
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "上次登录ip", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 4, IsShow = true, Sortable = true, DefaultFilter = true)]
|
|
||||||
public string LastLoginIp { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上次登录时间
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "上次登录时间", IsNullable = true)]
|
|
||||||
[DataTable(Order = 5, IsShow = true, Sortable = true, DefaultFilter = true)]
|
|
||||||
public DateTime? LastLoginTime { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最新登录设备
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "最新登录设备", IsNullable = true)]
|
|
||||||
[DataTable(Order = 6, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string LatestLoginDevice { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最新登录ip
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "最新登录ip", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 7, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string LatestLoginIp { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最新登录时间
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "最新登录时间", IsNullable = true)]
|
|
||||||
[DataTable(Order = 8, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public DateTime? LatestLoginTime { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密码
|
/// 密码
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "密码", ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
[SugarColumn(ColumnDescription = "密码", ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 权限码集合
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsIgnore = true)]
|
|
||||||
public List<string> PermissionCodeList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 手机
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "手机", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string Phone { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 数据范围集合
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsIgnore = true)]
|
|
||||||
public List<DataScope> DataScopeList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 角色码集合
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsIgnore = true)]
|
|
||||||
public List<string> RoleCodeList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 角色ID集合
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(IsIgnore = true)]
|
|
||||||
public List<long> RoleIdList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否启用
|
/// 是否启用
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnDescription = "是否启用")]
|
[SugarColumn(ColumnDescription = "是否启用")]
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true, DefaultFilter = false)]
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
public bool UserStatus { get; set; }
|
public bool Status { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 手机
|
||||||
|
/// 这里使用了SM4自动加密解密
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "手机", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
|
public string? Phone { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮箱
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "邮箱", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||||
|
public string? Email { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上次登录ip
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "上次登录ip", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public string? LastLoginIp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上次登录设备
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "上次登录设备", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public string? LastLoginDevice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上次登录时间
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "上次登录时间", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public DateTime? LastLoginTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上次登录地点
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "上次登录地点", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public string LastLoginAddress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最新登录ip
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "最新登录ip", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public string? LatestLoginIp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最新登录时间
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "最新登录时间", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public DateTime? LatestLoginTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最新登录设备
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "最新登录设备", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public string? LatestLoginDevice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最新登录地点
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "最新登录地点", Length = 200, IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
|
||||||
|
public string LatestLoginAddress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认模块
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnDescription = "默认模块")]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public long DefaultModule { get; set; }
|
||||||
|
|
||||||
|
#region other
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按钮码集合
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public Dictionary<string, List<string>> ButtonCodeList { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 权限码集合
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public IEnumerable<string> PermissionCodeList { get; set; } = Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色码集合
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public IEnumerable<string> RoleCodeList { get; set; } = Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色ID集合
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public IEnumerable<long> RoleIdList { get; set; } = Enumerable.Empty<long>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据范围集合
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public IEnumerable<DataScope> DataScopeList { get; set; } = Enumerable.Empty<DataScope>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模块集合
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public IEnumerable<SysResource> ModuleList { get; set; } = Enumerable.Empty<SysResource>();
|
||||||
|
|
||||||
|
#endregion other
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 访问日志表
|
|
||||||
///</summary>
|
|
||||||
[SugarTable("sys_visitlog", TableDescription = "访问日志表")]
|
|
||||||
[Tenant(SqlSugarConst.DB_Log)]
|
|
||||||
public class SysVisitLog : PrimaryIdEntity
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 日志分类
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "日志分类", Length = 200)]
|
|
||||||
[DataTable(Order = 1, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string Category { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 执行状态
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "执行状态", Length = 200)]
|
|
||||||
[DataTable(Order = 3, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string ExeStatus { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 日志名称
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "日志名称", Length = 200)]
|
|
||||||
[DataTable(Order = 2, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作人账号
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "操作人账号", Length = 200, IsNullable = true)]
|
|
||||||
[DataTable(Order = 4, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string OpAccount { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作浏览器
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "操作浏览器", Length = 200)]
|
|
||||||
[DataTable(Order = 5, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string OpBrowser { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作ip
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "操作ip", Length = 200)]
|
|
||||||
[DataTable(Order = 6, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string OpIp { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作系统
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "操作系统", Length = 200)]
|
|
||||||
[DataTable(Order = 7, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public string OpOs { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作时间
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "操作时间")]
|
|
||||||
[DataTable(Order = 8, IsShow = true, Sortable = true, DefaultFilter = false)]
|
|
||||||
public DateTime OpTime { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 验证Id
|
|
||||||
///</summary>
|
|
||||||
[SugarColumn(ColumnDescription = "验证Id")]
|
|
||||||
[IgnoreExcel]
|
|
||||||
[DataTable(Order = 9, IsShow = true, Sortable = true, DefaultFilter = true)]
|
|
||||||
public long VerificatId { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,9 +9,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 登录设备类型枚举
|
/// 登录设备类型枚举
|
||||||
@@ -20,18 +22,10 @@ public enum AuthDeviceTypeEnum
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// PC端
|
/// PC端
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("PC")]
|
|
||||||
PC,
|
PC,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 移动端
|
/// 移动端
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("APP")]
|
|
||||||
APP,
|
APP,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Api
|
|
||||||
/// </summary>
|
|
||||||
[Description("Api")]
|
|
||||||
Api,
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,20 +9,23 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Components;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 键值表示
|
/// 字典表类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StringFilters
|
public enum DictTypeEnum
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 键
|
/// 系统使用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Key { get; set; }
|
System,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 值
|
/// 用户自定义
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Value { get; set; }
|
Define,
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,9 +9,15 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
public interface ISugarService
|
public enum LogCateGoryEnum
|
||||||
{
|
{
|
||||||
public SqlSugarClient NewContent { get; set; }
|
Login,
|
||||||
|
Logout,
|
||||||
|
Operate,
|
||||||
|
Exception
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public enum RelationCategoryEnum
|
||||||
|
{
|
||||||
|
UserHasRole,
|
||||||
|
UserHasResource,
|
||||||
|
UserHasPermission,
|
||||||
|
UserHasOpenApiPermission,
|
||||||
|
UserHasModule,
|
||||||
|
UserDefaultRazor,
|
||||||
|
UserWorkbenchData,
|
||||||
|
RoleHasResource,
|
||||||
|
RoleHasPermission,
|
||||||
|
RoleHasOpenApiPermission,
|
||||||
|
RoleHasModule,
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,6 +9,14 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
global using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
global using NewLife.Caching;
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public enum ResourceCategoryEnum
|
||||||
|
{
|
||||||
|
Module,
|
||||||
|
Menu,
|
||||||
|
Button,
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,8 +9,13 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application.Services.System.Relation.Dto;
|
|
||||||
|
|
||||||
public class RelationOutput
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public enum RoleCategoryEnum
|
||||||
{
|
{
|
||||||
|
Global,
|
||||||
|
Api,
|
||||||
}
|
}
|
||||||
23
src/ThingsGateway.Admin.Application/Enum/TargetEnum.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public enum TargetEnum
|
||||||
|
{
|
||||||
|
_self,
|
||||||
|
_blank,
|
||||||
|
_parent,
|
||||||
|
_top
|
||||||
|
}
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 认证模块事件总线
|
|
||||||
/// </summary>
|
|
||||||
public class AuthEventSubscriber : IEventSubscriber, ISingleton
|
|
||||||
{
|
|
||||||
private readonly ISimpleCacheService _simpleCacheService;
|
|
||||||
public IServiceProvider _services { get; }
|
|
||||||
private readonly SqlSugarScope _db;
|
|
||||||
|
|
||||||
public AuthEventSubscriber(ISimpleCacheService simpleCacheService, IServiceProvider services)
|
|
||||||
{
|
|
||||||
_db = DbContext.Db;
|
|
||||||
_simpleCacheService = simpleCacheService;
|
|
||||||
_services = services;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录事件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[EventSubscribe(EventSubscriberConst.Login)]
|
|
||||||
public async Task Login(EventHandlerExecutingContext context)
|
|
||||||
{
|
|
||||||
var loginEvent = (LoginEvent)context.Source.Payload;//获取参数
|
|
||||||
var sysUser = loginEvent.SysUser;
|
|
||||||
|
|
||||||
#region 登录/密码策略
|
|
||||||
|
|
||||||
var key = SystemConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值
|
|
||||||
_simpleCacheService.Remove(key);//移除登录错误次数
|
|
||||||
|
|
||||||
// 创建新的作用域
|
|
||||||
using var scope = _services.CreateScope();
|
|
||||||
// 解析服务
|
|
||||||
var configService = scope.ServiceProvider.GetRequiredService<IConfigService>();
|
|
||||||
var loginPolicy = await configService.GetListByCategoryAsync(CateGoryConst.Config_PWD_POLICY);//获取密码策略
|
|
||||||
//获取用户verificat列表
|
|
||||||
var tokenInfos = UserTokenCacheUtil.HashGetOne(sysUser.Id);
|
|
||||||
var userToken = tokenInfos.Where(it => it.Id == loginEvent.VerificatId).FirstOrDefault();
|
|
||||||
|
|
||||||
#endregion 登录/密码策略
|
|
||||||
|
|
||||||
#region 重新赋值属性,设置本次登录信息为最新的信息
|
|
||||||
|
|
||||||
sysUser.LastLoginDevice = sysUser.LatestLoginDevice;
|
|
||||||
sysUser.LastLoginIp = sysUser.LatestLoginIp;
|
|
||||||
sysUser.LastLoginTime = sysUser.LatestLoginTime;
|
|
||||||
sysUser.LatestLoginDevice = loginEvent.Device.ToString();
|
|
||||||
sysUser.LatestLoginIp = loginEvent.Ip;
|
|
||||||
sysUser.LatestLoginTime = loginEvent.DateTime;
|
|
||||||
|
|
||||||
#endregion 重新赋值属性,设置本次登录信息为最新的信息
|
|
||||||
|
|
||||||
//更新用户登录信息
|
|
||||||
if (await _db.UpdateableWithAttr(sysUser).UpdateColumns(it => new
|
|
||||||
{
|
|
||||||
it.LastLoginDevice,
|
|
||||||
it.LastLoginIp,
|
|
||||||
it.LastLoginTime,
|
|
||||||
it.LatestLoginDevice,
|
|
||||||
it.LatestLoginIp,
|
|
||||||
it.LatestLoginTime,
|
|
||||||
}).ExecuteCommandAsync() > 0)
|
|
||||||
_simpleCacheService.HashAdd(SystemConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Redis信息
|
|
||||||
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登出事件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[EventSubscribe(EventSubscriberConst.LoginOut)]
|
|
||||||
public async Task LoginOut(EventHandlerExecutingContext context)
|
|
||||||
{
|
|
||||||
_ = (LoginEvent)context.Source.Payload;//获取参数
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取通知服务
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private INoticeService GetNoticeService()
|
|
||||||
{
|
|
||||||
var noticeService = _services.GetService<INoticeService>();//获取服务
|
|
||||||
return noticeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 延迟执行
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="millisecondsDelay">毫秒</param>
|
|
||||||
/// <param name="actionToExecute">方法</param>
|
|
||||||
private async Task DelayedExecutionAsync(int millisecondsDelay, Action actionToExecute)
|
|
||||||
{
|
|
||||||
// 延迟指定的时间
|
|
||||||
await Task.Delay(millisecondsDelay);
|
|
||||||
|
|
||||||
// 执行目标方法
|
|
||||||
actionToExecute.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 事件总线常量
|
|
||||||
/// </summary>
|
|
||||||
public class EventSubscriberConst
|
|
||||||
{
|
|
||||||
#region AuthEventSubscriber
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录
|
|
||||||
/// </summary>
|
|
||||||
public const string Login = "登录";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登出
|
|
||||||
/// </summary>
|
|
||||||
public const string LoginOut = "登出";
|
|
||||||
|
|
||||||
#endregion AuthEventSubscriber
|
|
||||||
|
|
||||||
#region UserEventSubscriber
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 清除用户缓存
|
|
||||||
/// </summary>
|
|
||||||
public const string ClearUserCache = "清除用户缓存";
|
|
||||||
|
|
||||||
#endregion UserEventSubscriber
|
|
||||||
|
|
||||||
#region NoticeEventSubscriber
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 通知用户下线
|
|
||||||
/// </summary>
|
|
||||||
public const string UserLoginOut = "通知用户下线";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 新消息
|
|
||||||
/// </summary>
|
|
||||||
public const string NewMessage = "新消息";
|
|
||||||
|
|
||||||
#endregion NoticeEventSubscriber
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 通知事件总线
|
|
||||||
/// </summary>
|
|
||||||
public class NoticeEventSubsciber : IEventSubscriber, ISingleton
|
|
||||||
{
|
|
||||||
private readonly ISimpleCacheService _simpleCacheService;
|
|
||||||
|
|
||||||
public IServiceProvider _service { get; }
|
|
||||||
private readonly SqlSugarScope _db;
|
|
||||||
|
|
||||||
public NoticeEventSubsciber(ISimpleCacheService simpleCacheService, IServiceProvider service)
|
|
||||||
{
|
|
||||||
_db = DbContext.Db;
|
|
||||||
_simpleCacheService = simpleCacheService;
|
|
||||||
_service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 通知用户下线事件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[EventSubscribe(EventSubscriberConst.UserLoginOut)]
|
|
||||||
public async Task UserLoginOut(EventHandlerExecutingContext context)
|
|
||||||
{
|
|
||||||
var loginEvent = (UserLoginOutEvent)context.Source.Payload;//获取参数
|
|
||||||
//客户端ID列表
|
|
||||||
var clientIds = new List<string>();
|
|
||||||
//遍历verificat列表获取客户端ID列表
|
|
||||||
loginEvent?.VerificatInfos?.ForEach(it =>
|
|
||||||
{
|
|
||||||
clientIds.AddRange(it.ClientIds);
|
|
||||||
});
|
|
||||||
await GetNoticeService().UserLoginOut(loginEvent.UserId, clientIds, loginEvent.Message);//发送消息
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 有新的消息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[EventSubscribe(EventSubscriberConst.NewMessage)]
|
|
||||||
public async Task NewMessage(EventHandlerExecutingContext context)
|
|
||||||
{
|
|
||||||
var newMessageEvent = (NewMessageEvent)context.Source.Payload;//获取参数
|
|
||||||
|
|
||||||
var clientIds = new List<string>();
|
|
||||||
//获取用户verificat列表
|
|
||||||
var verificatInfos = UserTokenCacheUtil.HashGet(newMessageEvent.UserIds.ToArray());
|
|
||||||
verificatInfos.ForEach(it =>
|
|
||||||
{
|
|
||||||
if (it != null)
|
|
||||||
{
|
|
||||||
it = it.Where(it => it.VerificatTimeout > DateTime.Now).ToList();//去掉登录超时的
|
|
||||||
//遍历verificat
|
|
||||||
it.ForEach(it =>
|
|
||||||
{
|
|
||||||
clientIds.AddRange(it.ClientIds);//添加到客户端ID列表
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await GetNoticeService().NewMesage(newMessageEvent.UserIds, clientIds, newMessageEvent.Message);//发送消息
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取通知服务
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private INoticeService GetNoticeService()
|
|
||||||
{
|
|
||||||
var noticeService = _service.CreateScope().ServiceProvider.GetService<INoticeService>();//获取服务
|
|
||||||
return noticeService;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户模块事件总线
|
|
||||||
/// </summary>
|
|
||||||
public class UserEventSubscriber : IEventSubscriber, ISingleton
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider _services;
|
|
||||||
|
|
||||||
public UserEventSubscriber(IServiceProvider services)
|
|
||||||
{
|
|
||||||
this._services = services;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据角色ID列表清除用户缓存
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[EventSubscribe(EventSubscriberConst.ClearUserCache)]
|
|
||||||
public async Task DeleteUserCacheByRoleIds(EventHandlerExecutingContext context)
|
|
||||||
{
|
|
||||||
var roleIds = (List<long>)context.Source.Payload;//获取角色ID
|
|
||||||
|
|
||||||
// 创建新的作用域
|
|
||||||
using var scope = _services.CreateScope();
|
|
||||||
// 解析角色服务
|
|
||||||
var relationService = scope.ServiceProvider.GetRequiredService<IRelationService>();
|
|
||||||
//获取用户和角色关系
|
|
||||||
var relations = await relationService.GetRelationListByTargetIdListAndCategoryAsync(roleIds.Select(it => it.ToString()).ToList(), CateGoryConst.Relation_SYS_USER_HAS_ROLE);
|
|
||||||
if (relations.Count > 0)
|
|
||||||
{
|
|
||||||
var userIds = relations.Select(it => it.ObjectId).ToList();//用户ID列表
|
|
||||||
// 解析用户服务
|
|
||||||
var userService = scope.ServiceProvider.GetRequiredService<ISysUserService>();
|
|
||||||
//从redis中删除
|
|
||||||
userService.DeleteUserFromRedis(userIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,23 +9,25 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using TouchSocket.Core;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Demo;
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public class MenuConfigs : AppConfigBase
|
public class UserFriendlyException : Exception
|
||||||
{
|
{
|
||||||
public static MenuConfigs Default;
|
public UserFriendlyException()
|
||||||
|
|
||||||
static MenuConfigs()
|
|
||||||
{
|
|
||||||
Default = AppConfigBase.GetNewDefault<MenuConfigs>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<NavItem> NavItems { get; set; } = new();
|
|
||||||
|
|
||||||
public MenuConfigs() : base(AppContext.BaseDirectory.CombinePath("MenuConfig.json"))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserFriendlyException(string? message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserFriendlyException(string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool? IsValidationException { get; set; }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ASP.NET Core 服务拓展类
|
||||||
|
/// </summary>
|
||||||
|
public static class AspNetCoreBuilderServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 注册 Mvc 过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFilter"></typeparam>
|
||||||
|
/// <param name="mvcBuilder"></param>
|
||||||
|
/// <param name="configure"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IMvcBuilder AddMvcFilter<TFilter>(this IMvcBuilder mvcBuilder, Action<MvcOptions> configure = default)
|
||||||
|
where TFilter : IFilterMetadata
|
||||||
|
{
|
||||||
|
mvcBuilder.Services.AddMvcFilter<TFilter>(configure);
|
||||||
|
|
||||||
|
return mvcBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册 Mvc 过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFilter"></typeparam>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="configure"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddMvcFilter<TFilter>(this IServiceCollection services, Action<MvcOptions> configure = default)
|
||||||
|
where TFilter : IFilterMetadata
|
||||||
|
{
|
||||||
|
services.Configure<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add<TFilter>();
|
||||||
|
|
||||||
|
// 其他额外配置
|
||||||
|
configure?.Invoke(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册 Mvc 过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="filter"></param>
|
||||||
|
/// <param name="configure"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddMvcFilter(this IServiceCollection services, IFilterMetadata filter, Action<MvcOptions> configure = default)
|
||||||
|
{
|
||||||
|
services.Configure<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add(filter);
|
||||||
|
|
||||||
|
// 其他额外配置
|
||||||
|
configure?.Invoke(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权处理上下文拓展类
|
||||||
|
/// </summary>
|
||||||
|
public static class AuthorizationHandlerContextExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前 HttpContext 上下文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static DefaultHttpContext GetCurrentHttpContext(this AuthorizationHandlerContext context)
|
||||||
|
{
|
||||||
|
DefaultHttpContext? httpContext;
|
||||||
|
|
||||||
|
// 获取 httpContext 对象
|
||||||
|
if (context.Resource is AuthorizationFilterContext filterContext) httpContext = (DefaultHttpContext)filterContext.HttpContext;
|
||||||
|
else if (context.Resource is DefaultHttpContext defaultHttpContext) httpContext = defaultHttpContext;
|
||||||
|
else httpContext = null;
|
||||||
|
|
||||||
|
return httpContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Http 拓展类
|
||||||
|
/// </summary>
|
||||||
|
public static class HttpContextExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 Action 特性
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TAttribute"></typeparam>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static TAttribute GetMetadata<TAttribute>(this HttpContext httpContext)
|
||||||
|
where TAttribute : class
|
||||||
|
{
|
||||||
|
return httpContext.GetEndpoint()?.Metadata?.GetMetadata<TAttribute>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 控制器/Action 描述器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ControllerActionDescriptor GetControllerActionDescriptor(this HttpContext httpContext)
|
||||||
|
{
|
||||||
|
return httpContext.GetEndpoint()?.Metadata?.FirstOrDefault(u => u is ControllerActionDescriptor) as ControllerActionDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置规范化文档自动登录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
/// <param name="accessToken"></param>
|
||||||
|
public static void SigninToSwagger(this HttpContext httpContext, string accessToken)
|
||||||
|
{
|
||||||
|
// 设置 Swagger 刷新自动授权
|
||||||
|
httpContext.Response.Headers["access-token"] = accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置规范化文档退出登录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
public static void SignoutToSwagger(this HttpContext httpContext)
|
||||||
|
{
|
||||||
|
httpContext.Response.Headers["access-token"] = "invalid_token";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置响应头 Tokens
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
/// <param name="accessToken"></param>
|
||||||
|
/// <param name="refreshToken"></param>
|
||||||
|
public static void SetTokensOfResponseHeaders(this HttpContext httpContext, string accessToken, string? refreshToken = null)
|
||||||
|
{
|
||||||
|
httpContext.Response.Headers["access-token"] = accessToken;
|
||||||
|
if (!string.IsNullOrWhiteSpace(refreshToken))
|
||||||
|
{
|
||||||
|
httpContext.Response.Headers["x-access-token"] = refreshToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取本机 IPv4地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetLocalIpAddressToIPv4(this HttpContext context)
|
||||||
|
{
|
||||||
|
return context.Connection.LocalIpAddress?.MapToIPv4()?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取本机 IPv6地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetLocalIpAddressToIPv6(this HttpContext context)
|
||||||
|
{
|
||||||
|
return context.Connection.LocalIpAddress?.MapToIPv6()?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取远程 IPv4地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetRemoteIpAddressToIPv4(this HttpContext context)
|
||||||
|
{
|
||||||
|
return context.Connection.RemoteIpAddress?.MapToIPv4()?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取远程 IPv6地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetRemoteIpAddressToIPv6(this HttpContext context)
|
||||||
|
{
|
||||||
|
return context.Connection.RemoteIpAddress?.MapToIPv6()?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取完整请求地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetRequestUrlAddress(this HttpRequest request)
|
||||||
|
{
|
||||||
|
return new StringBuilder()
|
||||||
|
.Append(request.Scheme)
|
||||||
|
.Append("://")
|
||||||
|
.Append(request.Host)
|
||||||
|
.Append(request.PathBase)
|
||||||
|
.Append(request.Path)
|
||||||
|
.Append(request.QueryString)
|
||||||
|
.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取来源地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="refererHeaderKey"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetRefererUrlAddress(this HttpRequest request, string refererHeaderKey = "Referer")
|
||||||
|
{
|
||||||
|
return request.Headers[refererHeaderKey].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取 Body 内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
/// <remarks>需先在 Startup 的 Configure 中注册 app.EnableBuffering()</remarks>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<string> ReadBodyContentAsync(this HttpContext httpContext)
|
||||||
|
{
|
||||||
|
if (httpContext == null) return default;
|
||||||
|
return await httpContext.Request.ReadBodyContentAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取 Body 内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <remarks>需先在 Startup 的 Configure 中注册 app.EnableBuffering()</remarks>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<string> ReadBodyContentAsync(this HttpRequest request)
|
||||||
|
{
|
||||||
|
request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
using var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true);
|
||||||
|
var body = await reader.ReadToEndAsync();
|
||||||
|
|
||||||
|
request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否是 WebSocket 请求
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsWebSocketRequest(this HttpContext context)
|
||||||
|
{
|
||||||
|
return context.WebSockets.IsWebSocketRequest || context.Request.Path == "/ws";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public static class LocalizerExtensions
|
||||||
|
{
|
||||||
|
public static PropertyInfo? GetPropertyByName(this Type type, string propertyName) => type.GetRuntimeProperties().FirstOrDefault(p => p.Name == propertyName);
|
||||||
|
|
||||||
|
public static MethodInfo? GetMethodByName(this Type type, string methodName) => type.GetRuntimeMethods().FirstOrDefault(p => p.Name == methodName);
|
||||||
|
|
||||||
|
public static FieldInfo? GetFieldByName(this Type type, string fieldName) => type.GetRuntimeFields().FirstOrDefault(p => p.Name == fieldName);
|
||||||
|
|
||||||
|
private static bool IsPublic(PropertyInfo p) => p.GetMethod != null && p.SetMethod != null && p.GetMethod.IsPublic && p.SetMethod.IsPublic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证整个模型时验证属性方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
public static void ValidateProperty(this ValidationContext context, List<ValidationResult> results)
|
||||||
|
{
|
||||||
|
// 获得所有可写属性
|
||||||
|
var properties = context.ObjectType.GetRuntimeProperties().Where(p => IsPublic(p) && p.CanWrite && p.GetIndexParameters().Length == 0);
|
||||||
|
foreach (var pi in properties)
|
||||||
|
{
|
||||||
|
var fieldIdentifier = new FieldIdentifier(context.ObjectInstance, pi.Name);
|
||||||
|
context.DisplayName = fieldIdentifier.GetDisplayName();
|
||||||
|
context.MemberName = fieldIdentifier.FieldName;
|
||||||
|
|
||||||
|
var propertyValue = BootstrapBlazor.Components.Utility.GetPropertyValue(context.ObjectInstance, context.MemberName);
|
||||||
|
|
||||||
|
// 验证 DataAnnotations
|
||||||
|
var messages = new List<ValidationResult>();
|
||||||
|
// 组件进行验证
|
||||||
|
ValidateDataAnnotations(propertyValue, context, messages, pi);
|
||||||
|
if (messages.Count > 0)
|
||||||
|
results.AddRange(messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过属性设置的 DataAnnotation 进行数据验证
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
/// <param name="propertyInfo"></param>
|
||||||
|
/// <param name="memberName"></param>
|
||||||
|
private static void ValidateDataAnnotations(object? value, ValidationContext context, List<ValidationResult> results, PropertyInfo propertyInfo, string? memberName = null)
|
||||||
|
{
|
||||||
|
var rules = propertyInfo.GetCustomAttributes(true).OfType<ValidationAttribute>();
|
||||||
|
var metadataType = context.ObjectType.GetCustomAttribute<MetadataTypeAttribute>(false);
|
||||||
|
if (metadataType != null)
|
||||||
|
{
|
||||||
|
var p = metadataType.MetadataClassType.GetPropertyByName(propertyInfo.Name);
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
rules = rules.Concat(p.GetCustomAttributes(true).OfType<ValidationAttribute>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var displayName = context.DisplayName;
|
||||||
|
memberName ??= propertyInfo.Name;
|
||||||
|
var attributeSpan = nameof(Attribute).AsSpan();
|
||||||
|
foreach (var rule in rules)
|
||||||
|
{
|
||||||
|
var result = rule.GetValidationResult(value, context);
|
||||||
|
if (result != null && result != ValidationResult.Success)
|
||||||
|
{
|
||||||
|
var find = false;
|
||||||
|
var ruleNameSpan = rule.GetType().Name.AsSpan();
|
||||||
|
var index = ruleNameSpan.IndexOf(attributeSpan, StringComparison.OrdinalIgnoreCase);
|
||||||
|
var ruleName = ruleNameSpan[..index];
|
||||||
|
//// 通过设置 ErrorMessage 检索
|
||||||
|
//if (!context.ObjectType.Assembly.IsDynamic && !find
|
||||||
|
// && !string.IsNullOrEmpty(rule.ErrorMessage)
|
||||||
|
// && App.CreateLocalizerByType(context.ObjectType).TryGetLocalizerString(rule.ErrorMessage, out var msg))
|
||||||
|
//{
|
||||||
|
// rule.ErrorMessage = msg;
|
||||||
|
// find = true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// 通过 Attribute 检索
|
||||||
|
//if (!rule.GetType().Assembly.IsDynamic && !find
|
||||||
|
// && App.CreateLocalizerByType(rule.GetType()).TryGetLocalizerString(nameof(rule.ErrorMessage), out msg))
|
||||||
|
//{
|
||||||
|
// rule.ErrorMessage = msg;
|
||||||
|
// find = true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 通过 字段.规则名称 检索
|
||||||
|
if (!context.ObjectType.Assembly.IsDynamic && !find
|
||||||
|
&& App.CreateLocalizerByType(context.ObjectType).TryGetLocalizerString($"{memberName}.{ruleName.ToString()}", out var msg))
|
||||||
|
{
|
||||||
|
rule.ErrorMessage = msg;
|
||||||
|
find = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!find)
|
||||||
|
{
|
||||||
|
rule.ErrorMessage = result.ErrorMessage;
|
||||||
|
}
|
||||||
|
var errorMessage = !string.IsNullOrEmpty(rule.ErrorMessage) && rule.ErrorMessage.Contains("{0}")
|
||||||
|
? rule.FormatErrorMessage(displayName)
|
||||||
|
: rule.ErrorMessage;
|
||||||
|
results.Add(new ValidationResult(errorMessage, new string[] { memberName }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定 Type 的资源文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizer"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryGetLocalizerString(this IStringLocalizer localizer, string key, [MaybeNullWhen(false)] out string? text)
|
||||||
|
{
|
||||||
|
var ret = false;
|
||||||
|
text = null;
|
||||||
|
var l = localizer[key];
|
||||||
|
if (l != null)
|
||||||
|
{
|
||||||
|
ret = !l.ResourceNotFound;
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
text = l.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得类型自身的描述信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modelType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetTypeDisplayName(this Type modelType)
|
||||||
|
{
|
||||||
|
string fieldName = modelType.Name;
|
||||||
|
var cacheKey = $"{nameof(GetTypeDisplayName)}-{CultureInfo.CurrentUICulture.Name}-{modelType.FullName}-{modelType.TypeHandle.Value}";
|
||||||
|
var displayName = App.CacheService.GetOrCreate(cacheKey, entry =>
|
||||||
|
{
|
||||||
|
string? dn = null;
|
||||||
|
// 显示名称为空时通过资源文件查找 FieldName 项
|
||||||
|
var localizer = modelType.Assembly.IsDynamic ? null : App.CreateLocalizerByType(modelType);
|
||||||
|
var stringLocalizer = localizer?[fieldName];
|
||||||
|
if (stringLocalizer is { ResourceNotFound: false })
|
||||||
|
{
|
||||||
|
dn = stringLocalizer.Value;
|
||||||
|
}
|
||||||
|
else if (modelType.IsEnum)
|
||||||
|
{
|
||||||
|
var info = modelType.GetFieldByName(fieldName);
|
||||||
|
if (info != null)
|
||||||
|
{
|
||||||
|
dn = FindDisplayAttribute(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (TryGetProperty(modelType, fieldName, out var propertyInfo))
|
||||||
|
{
|
||||||
|
dn = FindDisplayAttribute(propertyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dn;
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
return displayName ?? fieldName;
|
||||||
|
|
||||||
|
string? FindDisplayAttribute(MemberInfo memberInfo)
|
||||||
|
{
|
||||||
|
// 回退查找 Display 标签
|
||||||
|
var dn = memberInfo.GetCustomAttribute<DisplayAttribute>(true)?.Name
|
||||||
|
?? memberInfo.GetCustomAttribute<DisplayNameAttribute>(true)?.DisplayName
|
||||||
|
?? memberInfo.GetCustomAttribute<DescriptionAttribute>(true)?.Description;
|
||||||
|
|
||||||
|
return dn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 DisplayName属性名称
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
public static string Description<T>(this T item, Expression<Func<T, object>> accessor)
|
||||||
|
{
|
||||||
|
if (accessor.Body == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(accessor));
|
||||||
|
}
|
||||||
|
|
||||||
|
var expression = accessor.Body;
|
||||||
|
if (expression is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.Convert && unaryExpression.Type == typeof(object))
|
||||||
|
{
|
||||||
|
expression = unaryExpression.Operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression is not MemberExpression memberExpression)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Can only access properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeof(T).GetPropertyDisplayName(memberExpression.Member.Name) ?? memberExpression.Member.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得类型属性的描述信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modelType"></param>
|
||||||
|
/// <param name="fieldName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetPropertyDisplayName(this Type modelType, string fieldName)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{nameof(GetPropertyDisplayName)}-{CultureInfo.CurrentUICulture.Name}-{modelType.FullName}-{modelType.TypeHandle.Value}-{fieldName}";
|
||||||
|
var displayName = App.CacheService.GetOrCreate(cacheKey, entry =>
|
||||||
|
{
|
||||||
|
string? dn = null;
|
||||||
|
// 显示名称为空时通过资源文件查找 FieldName 项
|
||||||
|
var localizer = modelType.Assembly.IsDynamic ? null : App.CreateLocalizerByType(modelType);
|
||||||
|
var stringLocalizer = localizer?[fieldName];
|
||||||
|
if (stringLocalizer is { ResourceNotFound: false })
|
||||||
|
{
|
||||||
|
dn = stringLocalizer.Value;
|
||||||
|
}
|
||||||
|
else if (modelType.IsEnum)
|
||||||
|
{
|
||||||
|
var info = modelType.GetFieldByName(fieldName);
|
||||||
|
if (info != null)
|
||||||
|
{
|
||||||
|
dn = FindDisplayAttribute(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (TryGetProperty(modelType, fieldName, out var propertyInfo))
|
||||||
|
{
|
||||||
|
dn = FindDisplayAttribute(propertyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dn;
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
return displayName ?? fieldName;
|
||||||
|
|
||||||
|
string? FindDisplayAttribute(MemberInfo memberInfo)
|
||||||
|
{
|
||||||
|
// 回退查找 Display 标签
|
||||||
|
var dn = memberInfo.GetCustomAttribute<DisplayAttribute>(true)?.Name
|
||||||
|
?? memberInfo.GetCustomAttribute<DisplayNameAttribute>(true)?.DisplayName
|
||||||
|
?? memberInfo.GetCustomAttribute<DescriptionAttribute>(true)?.Description;
|
||||||
|
|
||||||
|
return dn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得方法的描述信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modelType"></param>
|
||||||
|
/// <param name="methodName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetMethodDisplayName(this Type modelType, string methodName)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{nameof(GetMethodDisplayName)}-{CultureInfo.CurrentUICulture.Name}-{modelType.FullName}-{modelType.TypeHandle.Value}-{methodName}";
|
||||||
|
var displayName = App.CacheService.GetOrCreate(cacheKey, entry =>
|
||||||
|
{
|
||||||
|
string? dn = null;
|
||||||
|
// 显示名称为空时通过资源文件查找 methodName 项
|
||||||
|
var localizer = modelType.Assembly.IsDynamic ? null : App.CreateLocalizerByType(modelType);
|
||||||
|
var stringLocalizer = localizer?[methodName];
|
||||||
|
if (stringLocalizer is { ResourceNotFound: false })
|
||||||
|
{
|
||||||
|
dn = stringLocalizer.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var info = modelType.GetMethodByName(methodName);
|
||||||
|
if (info != null)
|
||||||
|
{
|
||||||
|
dn = FindDisplayAttribute(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dn;
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
return displayName ?? methodName;
|
||||||
|
|
||||||
|
string? FindDisplayAttribute(MemberInfo memberInfo)
|
||||||
|
{
|
||||||
|
// 回退查找 Display 标签
|
||||||
|
var dn = memberInfo.GetCustomAttribute<DisplayAttribute>(true)?.Name
|
||||||
|
?? memberInfo.GetCustomAttribute<DisplayNameAttribute>(true)?.DisplayName
|
||||||
|
?? memberInfo.GetCustomAttribute<DescriptionAttribute>(true)?.Description;
|
||||||
|
|
||||||
|
return dn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetProperty(Type modelType, string fieldName, [NotNullWhen(true)] out PropertyInfo? propertyInfo)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{nameof(TryGetProperty)}-{modelType.FullName}-{modelType.TypeHandle.Value}-{fieldName}";
|
||||||
|
propertyInfo = App.CacheService.GetOrCreate(cacheKey, entry =>
|
||||||
|
{
|
||||||
|
IEnumerable<PropertyInfo>? props;
|
||||||
|
|
||||||
|
// 支持 MetadataType
|
||||||
|
var metadataType = modelType.GetCustomAttribute<MetadataTypeAttribute>(false);
|
||||||
|
if (metadataType != null)
|
||||||
|
{
|
||||||
|
props = modelType.GetRuntimeProperties().AsEnumerable().Concat(metadataType.MetadataClassType.GetRuntimeProperties());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
props = modelType.GetRuntimeProperties().AsEnumerable();
|
||||||
|
}
|
||||||
|
|
||||||
|
var pi = props.FirstOrDefault(p => p.Name == fieldName);
|
||||||
|
|
||||||
|
return pi;
|
||||||
|
}, 300);
|
||||||
|
return propertyInfo != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
@@ -8,7 +9,12 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Furion.Extensions;
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@@ -17,14 +23,157 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对象拓展类
|
/// 对象拓展类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressSniffer]
|
public static class ObjectExtensions
|
||||||
public static class ObjectExtension
|
|
||||||
{
|
{
|
||||||
|
/// <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>
|
||||||
|
/// 将 IFormFile 转换成 byte[]
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="formFile"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static byte[] ToByteArray(this IFormFile formFile)
|
||||||
|
{
|
||||||
|
var fileLength = formFile.Length;
|
||||||
|
using var stream = formFile.OpenReadStream();
|
||||||
|
var bytes = new byte[fileLength];
|
||||||
|
|
||||||
|
stream.Read(bytes, 0, (int)fileLength);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 判断是否是富基元类型
|
/// 判断是否是富基元类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -105,32 +254,6 @@ public static class ObjectExtension
|
|||||||
|| method.ReturnType.ToString().StartsWith(typeof(Task).FullName);
|
|| method.ReturnType.ToString().StartsWith(typeof(Task).FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判断类型是否实现某个泛型
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">类型</param>
|
|
||||||
/// <param name="generic">泛型类型</param>
|
|
||||||
/// <returns>bool</returns>
|
|
||||||
public static bool HasImplementedRawGeneric(this Type type, Type generic)
|
|
||||||
{
|
|
||||||
// 检查接口类型
|
|
||||||
var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
|
|
||||||
if (isTheRawGenericType) return true;
|
|
||||||
|
|
||||||
// 检查类型
|
|
||||||
while (type != null && type != typeof(object))
|
|
||||||
{
|
|
||||||
isTheRawGenericType = IsTheRawGenericType(type);
|
|
||||||
if (isTheRawGenericType) return true;
|
|
||||||
type = type.BaseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 判断逻辑
|
|
||||||
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 判断是否是匿名类型
|
/// 判断是否是匿名类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -403,6 +526,12 @@ public static class ObjectExtension
|
|||||||
var _tempStr = !string.IsNullOrWhiteSpace(tempStr) ? tempStr : str;
|
var _tempStr = !string.IsNullOrWhiteSpace(tempStr) ? tempStr : str;
|
||||||
tempStr = _tempStr[..^affix.Length];
|
tempStr = _tempStr[..^affix.Length];
|
||||||
endCleared = true;
|
endCleared = true;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(tempStr))
|
||||||
|
{
|
||||||
|
tempStr = null;
|
||||||
|
endCleared = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (startCleared && endCleared) break;
|
if (startCleared && endCleared) break;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using NewLife.Extension;
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public static class QueryPageOptionsExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 根据查询条件返回sqlsugar ISugarQueryable
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
/// <param name="option"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ISugarQueryable<T> GetQuery<T>(this SqlSugarClient db, QueryPageOptions option)
|
||||||
|
{
|
||||||
|
ISugarQueryable<T>? query = db.Queryable<T>();
|
||||||
|
|
||||||
|
var where = option.ToFilter();
|
||||||
|
if (where.HasFilters())
|
||||||
|
{
|
||||||
|
query = query.Where(where.GetFilterLambda<T>());//name asc模式
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in option.SortList)
|
||||||
|
{
|
||||||
|
query = query.OrderByIF(!string.IsNullOrEmpty(item), $"{item}");//name asc模式
|
||||||
|
}
|
||||||
|
foreach (var item in option.AdvancedSortList)
|
||||||
|
{
|
||||||
|
query = query.OrderByIF(!string.IsNullOrEmpty(item), $"{item}");//name asc模式
|
||||||
|
}
|
||||||
|
query = query.OrderByIF(option.SortOrder != SortOrder.Unset, $"{option.SortName} {option.SortOrder}");
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据查询条件返回IEnumerable
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="datas"></param>
|
||||||
|
/// <param name="option"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IEnumerable<T> GetData<T>(this IEnumerable<T> datas, QueryPageOptions option)
|
||||||
|
{
|
||||||
|
var where = option.ToFilter();
|
||||||
|
if (where.HasFilters())
|
||||||
|
{
|
||||||
|
datas = datas.Where(where.GetFilterFunc<T>());//name asc模式
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.SortList.Any())
|
||||||
|
{
|
||||||
|
datas = datas.Sort(option.SortList);//name asc模式
|
||||||
|
}
|
||||||
|
if (option.AdvancedSortList.Any())
|
||||||
|
{
|
||||||
|
datas = datas.Sort(option.AdvancedSortList);//name asc模式
|
||||||
|
}
|
||||||
|
if (option.SortOrder != SortOrder.Unset && !option.SortName.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
datas = datas.Sort(option.SortName, option.SortOrder);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据查询条件返回QueryData
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="datas"></param>
|
||||||
|
/// <param name="option"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static QueryData<T> GetQueryData<T>(this IEnumerable<T> datas, QueryPageOptions option)
|
||||||
|
{
|
||||||
|
var ret = new QueryData<T>()
|
||||||
|
{
|
||||||
|
IsSorted = option.SortOrder != SortOrder.Unset,
|
||||||
|
IsFiltered = option.Filters.Any(),
|
||||||
|
IsAdvanceSearch = option.AdvanceSearches.Any(),
|
||||||
|
IsSearch = option.Searches.Any() || option.CustomerSearches.Any()
|
||||||
|
};
|
||||||
|
var items = datas.GetData(option);
|
||||||
|
ret.TotalCount = datas.Count();
|
||||||
|
ret.Items = items;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,16 +9,31 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Core;
|
using ThingsGateway.Core;
|
||||||
|
|
||||||
/// <summary>
|
namespace ThingsGateway.Admin.Application;
|
||||||
/// Sqlsugar分页拓展类
|
|
||||||
/// </summary>
|
public static class SqlSugarExtensions
|
||||||
public static class SqlSugarPageExtension
|
|
||||||
{
|
{
|
||||||
|
public static async Task<bool> UpdateRangeAsync<T>(this SqlSugarClient db, List<T> updateObjs) where T : class, new()
|
||||||
|
{
|
||||||
|
return await db.Updateable(updateObjs).ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> UpdateSetColumnsTrueAsync<T>(this SqlSugarClient db, Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression) where T : class, new()
|
||||||
|
{
|
||||||
|
return await db.Updateable<T>().SetColumns(columns, appendColumnsByDataFilter: true).Where(whereExpression)
|
||||||
|
.ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public static ISugarQueryable<TEntity> ExportIgnoreColumns<TEntity>(this ISugarQueryable<TEntity> queryable)
|
public static ISugarQueryable<TEntity> ExportIgnoreColumns<TEntity>(this ISugarQueryable<TEntity> queryable)
|
||||||
{
|
{
|
||||||
return queryable.IgnoreColumns(
|
return queryable.IgnoreColumns(
|
||||||
@@ -27,7 +43,6 @@ public static class SqlSugarPageExtension
|
|||||||
nameof(BaseEntity.CreateTime),
|
nameof(BaseEntity.CreateTime),
|
||||||
nameof(BaseEntity.CreateUser),
|
nameof(BaseEntity.CreateUser),
|
||||||
nameof(BaseEntity.CreateUserId),
|
nameof(BaseEntity.CreateUserId),
|
||||||
nameof(BaseEntity.SortCode),
|
|
||||||
nameof(BaseEntity.ExtJson),
|
nameof(BaseEntity.ExtJson),
|
||||||
nameof(BaseEntity.IsDelete),
|
nameof(BaseEntity.IsDelete),
|
||||||
nameof(BaseEntity.UpdateTime),
|
nameof(BaseEntity.UpdateTime),
|
||||||
@@ -37,14 +52,6 @@ public static class SqlSugarPageExtension
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SqlSugar分页扩展
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
/// <param name="queryable"></param>
|
|
||||||
/// <param name="current"></param>
|
|
||||||
/// <param name="size"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static SqlSugarPagedList<TEntity> ToPagedList<TEntity>(this ISugarQueryable<TEntity> queryable, int current,
|
public static SqlSugarPagedList<TEntity> ToPagedList<TEntity>(this ISugarQueryable<TEntity> queryable, int current,
|
||||||
int size)
|
int size)
|
||||||
{
|
{
|
||||||
@@ -63,14 +70,6 @@ public static class SqlSugarPageExtension
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SqlSugar分页扩展
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
/// <param name="queryable"></param>
|
|
||||||
/// <param name="current"></param>
|
|
||||||
/// <param name="size"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<SqlSugarPagedList<TEntity>> ToPagedListAsync<TEntity>(this ISugarQueryable<TEntity> queryable,
|
public static async Task<SqlSugarPagedList<TEntity>> ToPagedListAsync<TEntity>(this ISugarQueryable<TEntity> queryable,
|
||||||
int current, int size)
|
int current, int size)
|
||||||
{
|
{
|
||||||
@@ -90,15 +89,50 @@ public static class SqlSugarPageExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SqlSugar分页扩展
|
/// SqlSugar分页扩展,查询出结果后再转换实体类
|
||||||
|
/// </summary>
|
||||||
|
public static SqlSugarPagedList<TResult> ToPagedList<TEntity, TResult>(this ISugarQueryable<TEntity> queryable,
|
||||||
|
int current, int size)
|
||||||
|
{
|
||||||
|
var totalCount = 0;
|
||||||
|
var records = queryable.ToPageList(current, size, ref totalCount);
|
||||||
|
var totalPages = (int)Math.Ceiling(totalCount / (double)size);
|
||||||
|
return new SqlSugarPagedList<TResult>
|
||||||
|
{
|
||||||
|
Current = current,
|
||||||
|
Size = size,
|
||||||
|
Records = records.Cast<TResult>(),
|
||||||
|
Total = (int)totalCount,
|
||||||
|
Pages = totalPages,
|
||||||
|
HasNextPages = current < totalPages,
|
||||||
|
HasPrevPages = current - 1 > 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SqlSugar分页扩展,查询出结果后再转换实体类
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<SqlSugarPagedList<TResult>> ToPagedListAsync<TEntity, TResult>(this ISugarQueryable<TEntity> queryable,
|
||||||
|
int current, int size)
|
||||||
|
{
|
||||||
|
RefAsync<int> totalCount = 0;
|
||||||
|
var records = await queryable.ToPageListAsync(current, size, totalCount);
|
||||||
|
var totalPages = (int)Math.Ceiling(totalCount / (double)size);
|
||||||
|
return new SqlSugarPagedList<TResult>
|
||||||
|
{
|
||||||
|
Current = current,
|
||||||
|
Size = size,
|
||||||
|
Records = records.Cast<TResult>(),
|
||||||
|
Total = (int)totalCount,
|
||||||
|
Pages = totalPages,
|
||||||
|
HasNextPages = current < totalPages,
|
||||||
|
HasPrevPages = current - 1 > 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SqlSugar分页扩展,查询前扩展转换实体类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
/// <typeparam name="TResult"></typeparam>
|
|
||||||
/// <param name="queryable"></param>
|
|
||||||
/// <param name="pageIndex"></param>
|
|
||||||
/// <param name="pageSize"></param>
|
|
||||||
/// <param name="expression"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static SqlSugarPagedList<TResult> ToPagedList<TEntity, TResult>(this ISugarQueryable<TEntity> queryable, int pageIndex,
|
public static SqlSugarPagedList<TResult> ToPagedList<TEntity, TResult>(this ISugarQueryable<TEntity> queryable, int pageIndex,
|
||||||
int pageSize, Expression<Func<TEntity, TResult>> expression)
|
int pageSize, Expression<Func<TEntity, TResult>> expression)
|
||||||
{
|
{
|
||||||
@@ -118,15 +152,9 @@ public static class SqlSugarPageExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SqlSugar分页扩展
|
/// SqlSugar分页扩展,查询前扩展转换实体类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
/// <typeparam name="TResult"></typeparam>
|
|
||||||
/// <param name="queryable"></param>
|
|
||||||
/// <param name="pageIndex"></param>
|
|
||||||
/// <param name="pageSize"></param>
|
|
||||||
/// <param name="expression"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<SqlSugarPagedList<TResult>> ToPagedListAsync<TEntity, TResult>(
|
public static async Task<SqlSugarPagedList<TResult>> ToPagedListAsync<TEntity, TResult>(
|
||||||
this ISugarQueryable<TEntity> queryable, int pageIndex, int pageSize, Expression<Func<TEntity, TResult>> expression)
|
this ISugarQueryable<TEntity> queryable, int pageIndex, int pageSize, Expression<Func<TEntity, TResult>> expression)
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using ThingsGateway.Core;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public static class StartupExtensions
|
||||||
|
{
|
||||||
|
private static ConcurrentBag<AppStartup> AppStartups = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反射获取所有AppStartup的继承类,执行名称为第一个参数是<see cref="IServiceCollection"/>的方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="service"></param>
|
||||||
|
public static void ConfigureServices(this WebApplicationBuilder service)
|
||||||
|
{
|
||||||
|
AddStartups(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ConfigureServices获取的全部实例中,执行名称为第一个参数是<see cref="IApplicationBuilder"/>的方法
|
||||||
|
/// </summary>
|
||||||
|
public static void UseServices(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
UseStartups(AppStartups, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 Startup 自动扫描
|
||||||
|
/// </summary>
|
||||||
|
internal static void AddStartups(this WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
App.Configuration = builder.Configuration;
|
||||||
|
if (builder.Environment is IWebHostEnvironment webHostEnvironment)
|
||||||
|
App.WebRootPath = webHostEnvironment.WebRootPath;
|
||||||
|
App.ContentRootPath = builder.Environment.ContentRootPath;
|
||||||
|
App.IsDevelopment = builder.Environment.IsDevelopment();
|
||||||
|
|
||||||
|
// 扫描所有继承 AppStartup 的类
|
||||||
|
var startups = App.EffectiveTypes
|
||||||
|
.Where(u => typeof(AppStartup).IsAssignableFrom(u) && u.IsClass && !u.IsAbstract && !u.IsGenericType)
|
||||||
|
.OrderByDescending(u => GetStartupOrder(u));
|
||||||
|
|
||||||
|
// 注册自定义 startup
|
||||||
|
foreach (var type in startups)
|
||||||
|
{
|
||||||
|
var startup = Activator.CreateInstance(type) as AppStartup;
|
||||||
|
AppStartups.Add(startup!);
|
||||||
|
|
||||||
|
// 获取所有符合依赖注入格式的方法,如返回值void,且第一个参数是 IServiceCollection 类型
|
||||||
|
var serviceMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Where(u => u.ReturnType == typeof(void)
|
||||||
|
&& u.GetParameters().Length > 0
|
||||||
|
&& u.GetParameters().First().ParameterType == typeof(IServiceCollection));
|
||||||
|
|
||||||
|
if (!serviceMethods.Any()) continue;
|
||||||
|
|
||||||
|
// 自动安装属性调用
|
||||||
|
foreach (var method in serviceMethods)
|
||||||
|
{
|
||||||
|
method.Invoke(startup, new[] { builder.Services });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量将自定义 AppStartup 添加到 Startup.cs 的 Configure 中
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startups"></param>
|
||||||
|
/// <param name="app"></param>
|
||||||
|
private static void UseStartups(IEnumerable<AppStartup> startups, IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
App.RootServices = app.ApplicationServices;
|
||||||
|
App.CacheService = app.ApplicationServices.GetRequiredService<ICacheService>();
|
||||||
|
|
||||||
|
// 遍历所有
|
||||||
|
foreach (var startup in startups)
|
||||||
|
{
|
||||||
|
var type = startup.GetType();
|
||||||
|
|
||||||
|
// 获取所有符合依赖注入格式的方法,如返回值 void,且第一个参数是 IApplicationBuilder 类型
|
||||||
|
var configureMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Where(u => u.ReturnType == typeof(void)
|
||||||
|
&& u.GetParameters().Length > 0
|
||||||
|
&& u.GetParameters().First().ParameterType == typeof(IApplicationBuilder));
|
||||||
|
|
||||||
|
if (!configureMethods.Any()) continue;
|
||||||
|
|
||||||
|
// 自动安装属性调用
|
||||||
|
foreach (var method in configureMethods)
|
||||||
|
{
|
||||||
|
method.Invoke(startup, ResolveMethodParameterInstances(app, method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppStartups.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 Startup 排序
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">排序类型</param>
|
||||||
|
/// <returns>int</returns>
|
||||||
|
private static int GetStartupOrder(Type type)
|
||||||
|
{
|
||||||
|
return !type.IsDefined(typeof(AppStartupAttribute), true) ? 0 : type.GetCustomAttribute<AppStartupAttribute>(true)!.Order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析方法参数实例
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app"></param>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static object[] ResolveMethodParameterInstances(IApplicationBuilder app, MethodInfo method)
|
||||||
|
{
|
||||||
|
// 获取方法所有参数
|
||||||
|
var parameters = method.GetParameters();
|
||||||
|
var parameterInstances = new object[parameters.Length];
|
||||||
|
parameterInstances[0] = app;
|
||||||
|
|
||||||
|
// 解析服务
|
||||||
|
for (var i = 1; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
var parameter = parameters[i];
|
||||||
|
parameterInstances[i] = app.ApplicationServices.GetRequiredService(parameter.ParameterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameterInstances;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class AppStartup
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册服务启动配置
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class AppStartupAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="order"></param>
|
||||||
|
public AppStartupAttribute(int order)
|
||||||
|
{
|
||||||
|
Order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序
|
||||||
|
/// </summary>
|
||||||
|
public int Order { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
@@ -8,12 +9,15 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Core.Extension;
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TextWriter扩展
|
/// TextWriter扩展
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class TextWriterExtension
|
public static class TextWriterExtensions
|
||||||
{
|
{
|
||||||
private const string DefaultBackgroundColor = "\x1B[49m";
|
private const string DefaultBackgroundColor = "\x1B[49m";
|
||||||
private const string DefaultForegroundColor = "\x1B[39m\x1B[22m";
|
private const string DefaultForegroundColor = "\x1B[39m\x1B[22m";
|
||||||
3
src/ThingsGateway.Admin.Application/FodyWeavers.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||||
|
<Rougamo />
|
||||||
|
</Weavers>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
#if !NET5_0
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateOnly 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonDateOnlyJsonConverter : JsonConverter<DateOnly>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonDateOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftJsonDateOnlyJsonConverter(string format = "yyyy-MM-dd")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日期格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateOnly ReadJson(JsonReader reader, Type objectType, DateOnly existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var value = JValue.ReadFrom(reader).Value<string>();
|
||||||
|
return DateOnly.Parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, DateOnly value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
serializer.Serialize(writer, value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateOnly? 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonNullableDateOnlyJsonConverter : JsonConverter<DateOnly?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonNullableDateOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftJsonNullableDateOnlyJsonConverter(string format = "yyyy-MM-dd")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日期格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateOnly? ReadJson(JsonReader reader, Type objectType, DateOnly? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var value = JValue.ReadFrom(reader).Value<string>();
|
||||||
|
return !string.IsNullOrWhiteSpace(value) ? DateOnly.Parse(value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, DateOnly? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNull();
|
||||||
|
else serializer.Serialize(writer, value.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTime 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonDateTimeJsonConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 默认构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonDateTimeJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftJsonDateTimeJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return Penetrates.ConvertToDateTime(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
serializer.Serialize(writer, value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTime 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftNullableJsonDateTimeJsonConverter : JsonConverter<DateTime?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 默认构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftNullableJsonDateTimeJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftNullableJsonDateTimeJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return Penetrates.ConvertToDateTime(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNull();
|
||||||
|
else serializer.Serialize(writer, value.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
using ThingsGateway.Core.Extension;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTimeOffset 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonDateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonDateTimeOffsetJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftJsonDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
/// <param name="outputToLocalDateTime"></param>
|
||||||
|
public NewtonsoftJsonDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss", bool outputToLocalDateTime = false)
|
||||||
|
: this(format)
|
||||||
|
{
|
||||||
|
Localized = outputToLocalDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否输出为为当地时间
|
||||||
|
/// </summary>
|
||||||
|
public bool Localized { get; private set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override DateTimeOffset ReadJson(JsonReader reader, Type objectType, DateTimeOffset existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return DateTime.SpecifyKind(Penetrates.ConvertToDateTime(ref reader), Localized ? DateTimeKind.Local : DateTimeKind.Utc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override void WriteJson(JsonWriter writer, DateTimeOffset value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
// 判断是否序列化成当地时间
|
||||||
|
var formatDateTime = Localized ? value.ConvertToDateTime() : value;
|
||||||
|
serializer.Serialize(writer, formatDateTime.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTimeOffset 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonNullableDateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonNullableDateTimeOffsetJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftJsonNullableDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
/// <param name="outputToLocalDateTime"></param>
|
||||||
|
public NewtonsoftJsonNullableDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss", bool outputToLocalDateTime = false)
|
||||||
|
: this(format)
|
||||||
|
{
|
||||||
|
Localized = outputToLocalDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否输出为为当地时间
|
||||||
|
/// </summary>
|
||||||
|
public bool Localized { get; private set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override DateTimeOffset? ReadJson(JsonReader reader, Type objectType, DateTimeOffset? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return DateTime.SpecifyKind(Penetrates.ConvertToDateTime(ref reader), Localized ? DateTimeKind.Local : DateTimeKind.Utc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public override void WriteJson(JsonWriter writer, DateTimeOffset? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNull();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 判断是否序列化成当地时间
|
||||||
|
var formatDateTime = Localized ? value.ConvertToDateTime() : value;
|
||||||
|
serializer.Serialize(writer, formatDateTime!.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解决 long 精度问题
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonLongToStringJsonConverter : JsonConverter<long>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonLongToStringJsonConverter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="overMaxLengthOf17"></param>
|
||||||
|
public NewtonsoftJsonLongToStringJsonConverter(bool overMaxLengthOf17 = false)
|
||||||
|
{
|
||||||
|
OverMaxLengthOf17 = overMaxLengthOf17;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否超过最大长度 17 再处理
|
||||||
|
/// </summary>
|
||||||
|
public bool OverMaxLengthOf17 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override long ReadJson(JsonReader reader, Type objectType, long existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var jt = JValue.ReadFrom(reader);
|
||||||
|
|
||||||
|
return jt.Type == JTokenType.Null // 处理 public long? Property { get; set;} = 0; 情况,也就是类型是 long? 但是也给了默认值
|
||||||
|
? existingValue
|
||||||
|
: jt.Value<long>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, long value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (OverMaxLengthOf17)
|
||||||
|
{
|
||||||
|
if (value.ToString().Length <= 17) writer.WriteValue(value);
|
||||||
|
else writer.WriteValue(value.ToString());
|
||||||
|
}
|
||||||
|
else writer.WriteValue(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解决 long? 精度问题
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonNullableLongToStringJsonConverter : JsonConverter<long?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonNullableLongToStringJsonConverter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="overMaxLengthOf17"></param>
|
||||||
|
public NewtonsoftJsonNullableLongToStringJsonConverter(bool overMaxLengthOf17 = false)
|
||||||
|
{
|
||||||
|
OverMaxLengthOf17 = overMaxLengthOf17;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否超过最大长度 17 再处理
|
||||||
|
/// </summary>
|
||||||
|
public bool OverMaxLengthOf17 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override long? ReadJson(JsonReader reader, Type objectType, long? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var jt = JValue.ReadFrom(reader);
|
||||||
|
return jt.Value<long?>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, long? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNull();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var newValue = value.Value;
|
||||||
|
if (OverMaxLengthOf17)
|
||||||
|
{
|
||||||
|
if (newValue.ToString().Length <= 17) writer.WriteValue(newValue);
|
||||||
|
else writer.WriteValue(newValue.ToString());
|
||||||
|
}
|
||||||
|
else writer.WriteValue(newValue.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
#if !NET5_0
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TimeOnly 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonTimeOnlyJsonConverter : JsonConverter<TimeOnly>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonTimeOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftJsonTimeOnlyJsonConverter(string format = "HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override TimeOnly ReadJson(JsonReader reader, Type objectType, TimeOnly existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var value = JValue.ReadFrom(reader).Value<string>();
|
||||||
|
return TimeOnly.Parse(value!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, TimeOnly value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
serializer.Serialize(writer, value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TimeOnly? 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class NewtonsoftJsonNullableTimeOnlyJsonConverter : JsonConverter<TimeOnly?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public NewtonsoftJsonNullableTimeOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public NewtonsoftJsonNullableTimeOnlyJsonConverter(string format = "HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override TimeOnly? ReadJson(JsonReader reader, Type objectType, TimeOnly? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var value = JValue.ReadFrom(reader).Value<string>();
|
||||||
|
return !string.IsNullOrWhiteSpace(value) ? TimeOnly.Parse(value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, TimeOnly? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNull();
|
||||||
|
else serializer.Serialize(writer, value.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateOnly 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonDateOnlyJsonConverter : JsonConverter<DateOnly>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonDateOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonDateOnlyJsonConverter(string format = "yyyy-MM-dd")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日期格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return DateOnly.Parse(reader.GetString()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateOnly? 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonNullableDateOnlyJsonConverter : JsonConverter<DateOnly?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonNullableDateOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonNullableDateOnlyJsonConverter(string format = "yyyy-MM-dd")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日期格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return DateOnly.TryParse(reader.GetString(), out var date) ? date : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateOnly? value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNullValue();
|
||||||
|
else writer.WriteStringValue(value.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTime 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonDateTimeJsonConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 默认构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonDateTimeJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonDateTimeJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return Penetrates.ConvertToDateTime(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTime? 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonNullableDateTimeJsonConverter : JsonConverter<DateTime?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 默认构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonNullableDateTimeJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonNullableDateTimeJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return Penetrates.ConvertToDateTime(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNullValue();
|
||||||
|
else writer.WriteStringValue(value.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
using ThingsGateway.Core.Extension;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTimeOffset 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonDateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonDateTimeOffsetJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
/// <param name="outputToLocalDateTime"></param>
|
||||||
|
public SystemTextJsonDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss", bool outputToLocalDateTime = false)
|
||||||
|
: this(format)
|
||||||
|
{
|
||||||
|
Localized = outputToLocalDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否输出为为当地时间
|
||||||
|
/// </summary>
|
||||||
|
public bool Localized { get; private set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return DateTime.SpecifyKind(Penetrates.ConvertToDateTime(ref reader), Localized ? DateTimeKind.Local : DateTimeKind.Utc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
// 判断是否序列化成当地时间
|
||||||
|
var formatDateTime = Localized ? value.ConvertToDateTime() : value;
|
||||||
|
writer.WriteStringValue(formatDateTime.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTimeOffset? 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonNullableDateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonNullableDateTimeOffsetJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonNullableDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
/// <param name="outputToLocalDateTime"></param>
|
||||||
|
public SystemTextJsonNullableDateTimeOffsetJsonConverter(string format = "yyyy-MM-dd HH:mm:ss", bool outputToLocalDateTime = false)
|
||||||
|
: this(format)
|
||||||
|
{
|
||||||
|
Localized = outputToLocalDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否输出为为当地时间
|
||||||
|
/// </summary>
|
||||||
|
public bool Localized { get; private set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DateTimeOffset? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return DateTime.SpecifyKind(Penetrates.ConvertToDateTime(ref reader), Localized ? DateTimeKind.Local : DateTimeKind.Utc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTimeOffset? value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNullValue();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 判断是否序列化成当地时间
|
||||||
|
var formatDateTime = Localized ? value.ConvertToDateTime() : value;
|
||||||
|
writer.WriteStringValue(formatDateTime!.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解决 long 精度问题
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonLongToStringJsonConverter : JsonConverter<long>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonLongToStringJsonConverter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="overMaxLengthOf17"></param>
|
||||||
|
public SystemTextJsonLongToStringJsonConverter(bool overMaxLengthOf17 = false)
|
||||||
|
{
|
||||||
|
OverMaxLengthOf17 = overMaxLengthOf17;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否超过最大长度 17 再处理
|
||||||
|
/// </summary>
|
||||||
|
public bool OverMaxLengthOf17 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override long Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return reader.TokenType == JsonTokenType.String
|
||||||
|
? long.Parse(reader.GetString()!)
|
||||||
|
: reader.GetInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (OverMaxLengthOf17)
|
||||||
|
{
|
||||||
|
if (value.ToString().Length <= 17) writer.WriteNumberValue(value);
|
||||||
|
else writer.WriteStringValue(value.ToString());
|
||||||
|
}
|
||||||
|
else writer.WriteStringValue(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解决 long? 精度问题
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonNullableLongToStringJsonConverter : JsonConverter<long?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonNullableLongToStringJsonConverter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="overMaxLengthOf17"></param>
|
||||||
|
public SystemTextJsonNullableLongToStringJsonConverter(bool overMaxLengthOf17 = false)
|
||||||
|
{
|
||||||
|
OverMaxLengthOf17 = overMaxLengthOf17;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否超过最大长度 17 再处理
|
||||||
|
/// </summary>
|
||||||
|
public bool OverMaxLengthOf17 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return reader.TokenType == JsonTokenType.String
|
||||||
|
? long.Parse(reader.GetString()!)
|
||||||
|
: reader.GetInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNullValue();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var newValue = value.Value;
|
||||||
|
if (OverMaxLengthOf17)
|
||||||
|
{
|
||||||
|
if (newValue.ToString().Length <= 17) writer.WriteNumberValue(newValue);
|
||||||
|
else writer.WriteStringValue(newValue.ToString());
|
||||||
|
}
|
||||||
|
else writer.WriteStringValue(newValue.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
#if !NET5_0
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TimeOnly 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonTimeOnlyJsonConverter : JsonConverter<TimeOnly>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonTimeOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonTimeOnlyJsonConverter(string format = "HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return TimeOnly.Parse(reader.GetString()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TimeOnly? 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
public class SystemTextJsonNullableTimeOnlyJsonConverter : JsonConverter<TimeOnly?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 默认构造函数
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonNullableTimeOnlyJsonConverter()
|
||||||
|
: this(default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
public SystemTextJsonNullableTimeOnlyJsonConverter(string format = "HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间格式化格式
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="typeToConvert"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override TimeOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return TimeOnly.TryParse(reader.GetString(), out var time) ? time : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public override void Write(Utf8JsonWriter writer, TimeOnly? value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value == null) writer.WriteNullValue();
|
||||||
|
else writer.WriteStringValue(value.Value.ToString(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
using ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Json 序列化服务拓展类
|
||||||
|
/// </summary>
|
||||||
|
public static class JsonSerializationServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 Json 序列化提供器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TJsonSerializerProvider"></typeparam>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddJsonSerialization<TJsonSerializerProvider>(this IServiceCollection services)
|
||||||
|
where TJsonSerializerProvider : class, IJsonSerializerProvider
|
||||||
|
{
|
||||||
|
services.AddSingleton<IJsonSerializerProvider, TJsonSerializerProvider>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 JsonOptions 序列化选项
|
||||||
|
/// <para>主要给非 Web 环境使用</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="configure"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddJsonOptions(this IServiceCollection services, Action<JsonOptions> configure)
|
||||||
|
{
|
||||||
|
// 手动添加配置
|
||||||
|
services.Configure<JsonOptions>(options =>
|
||||||
|
{
|
||||||
|
configure?.Invoke(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
|
||||||
|
using ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
namespace Newtonsoft.Json;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Newtonsoft.Json 拓展
|
||||||
|
/// </summary>
|
||||||
|
public static class NewtonsoftJsonExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 DateTime/DateTime?/DateTimeOffset/DateTimeOffset? 类型序列化处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="converters"></param>
|
||||||
|
/// <param name="outputFormat"></param>
|
||||||
|
/// <param name="localized">自动转换 DateTimeOffset 为当地时间</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IList<JsonConverter> AddDateTimeTypeConverters(this IList<JsonConverter> converters, string outputFormat = "yyyy-MM-dd HH:mm:ss", bool localized = false)
|
||||||
|
{
|
||||||
|
converters.Add(new NewtonsoftJsonDateTimeJsonConverter(outputFormat));
|
||||||
|
converters.Add(new NewtonsoftNullableJsonDateTimeJsonConverter(outputFormat));
|
||||||
|
|
||||||
|
converters.Add(new NewtonsoftJsonDateTimeOffsetJsonConverter(outputFormat, localized));
|
||||||
|
converters.Add(new NewtonsoftJsonNullableDateTimeOffsetJsonConverter(outputFormat, localized));
|
||||||
|
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 long/long? 类型序列化处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="converters"></param>
|
||||||
|
/// <param name="overMaxLengthOf17">是否超过最大长度 17 再处理</param>
|
||||||
|
/// <remarks></remarks>
|
||||||
|
public static IList<JsonConverter> AddLongTypeConverters(this IList<JsonConverter> converters, bool overMaxLengthOf17 = false)
|
||||||
|
{
|
||||||
|
converters.Add(new NewtonsoftJsonLongToStringJsonConverter(overMaxLengthOf17));
|
||||||
|
converters.Add(new NewtonsoftJsonNullableLongToStringJsonConverter(overMaxLengthOf17));
|
||||||
|
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 DateOnly/DateOnly? 类型序列化处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="converters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IList<JsonConverter> AddDateOnlyConverters(this IList<JsonConverter> converters)
|
||||||
|
{
|
||||||
|
#if !NET5_0
|
||||||
|
converters.Add(new NewtonsoftJsonDateOnlyJsonConverter());
|
||||||
|
converters.Add(new NewtonsoftJsonNullableDateOnlyJsonConverter());
|
||||||
|
#endif
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 TimeOnly/TimeOnly? 类型序列化处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="converters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IList<JsonConverter> AddTimeOnlyConverters(this IList<JsonConverter> converters)
|
||||||
|
{
|
||||||
|
#if !NET5_0
|
||||||
|
converters.Add(new NewtonsoftJsonTimeOnlyJsonConverter());
|
||||||
|
converters.Add(new NewtonsoftJsonNullableTimeOnlyJsonConverter());
|
||||||
|
#endif
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
}
|
||||||