Compare commits
278 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 | ||
![]() |
80cd6b693e | ||
![]() |
04721a12b1 | ||
![]() |
64e22c0e46 | ||
![]() |
d5a70c5b08 | ||
![]() |
eaac7b6bcf | ||
![]() |
b062a491cd | ||
![]() |
1e868517bb | ||
![]() |
7b2a93a2d7 | ||
![]() |
f57f0447c6 | ||
![]() |
7126ff881e | ||
![]() |
e28da4b165 | ||
![]() |
92d9b91f7c | ||
![]() |
149c4a30c0 | ||
![]() |
84e62062ec | ||
![]() |
dc1fb74850 | ||
![]() |
00c6010789 | ||
![]() |
5d35c058e0 | ||
![]() |
1522a521f6 | ||
![]() |
6e11b885f9 | ||
![]() |
442ae6e0e8 | ||
![]() |
6b49e83464 | ||
![]() |
f1ecf13fe1 | ||
![]() |
83d1c8582b | ||
![]() |
179f6cd454 | ||
![]() |
91b1474ff0 | ||
![]() |
15aabc88a1 | ||
![]() |
afc0d3017d | ||
![]() |
9e0b1dc8aa | ||
![]() |
55b482fd26 | ||
![]() |
4f9c9a6566 | ||
![]() |
4725120ee9 | ||
![]() |
a288f50fbb | ||
![]() |
09cf2560a4 | ||
![]() |
16353de7b1 | ||
![]() |
eb5834cb5c | ||
![]() |
3ef5736aed | ||
![]() |
d2e7b77d16 | ||
![]() |
8f794bce75 | ||
![]() |
c33196a50a | ||
![]() |
6e8b5b431f | ||
![]() |
8ca3ecf17f | ||
![]() |
91f7db59ea | ||
![]() |
a0b4501352 | ||
![]() |
345047820a | ||
![]() |
ac3525a953 | ||
![]() |
c8c3f5b134 | ||
![]() |
51319c0718 | ||
![]() |
43399b8b47 | ||
![]() |
a6596042b7 | ||
![]() |
23ae85fc9c | ||
![]() |
2da54862f1 | ||
![]() |
f272fb0559 | ||
![]() |
bd04e33586 | ||
![]() |
b09b9752ca | ||
![]() |
a810a48158 | ||
![]() |
b4f5792aa8 | ||
![]() |
fdf0330b4f | ||
![]() |
ca73743082 | ||
![]() |
df0cde2cfd | ||
![]() |
5a8421e807 | ||
![]() |
025ac95d81 | ||
![]() |
71b5824fdc |
4
.gitignore
vendored
@@ -366,6 +366,6 @@ FodyWeavers.xsd
|
||||
/src/*Pro*
|
||||
/src/*pro*
|
||||
/src/*pro*/
|
||||
/src/ThingsGateway.Web.Entry/.config/
|
||||
/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
|
||||
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
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
93
README.md
@@ -1,38 +1,65 @@
|
||||
|
||||
# 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
|
||||
|
||||
## 介绍
|
||||
|
||||
**NetCore** 跨平台边缘采集网关(工业设备采集)
|
||||
|
||||
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
||||
|
||||
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
|
||||
|
||||
|
||||
## 文档
|
||||
|
||||
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
|
||||
|
||||
## 协议
|
||||
|
||||
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
|
||||
|
||||
## 演示
|
||||
|
||||
[ThingsGateway演示地址](http://120.24.62.140:5000/)
|
||||
|
||||
账户 : **superAdmin**
|
||||
|
||||
密码 : **111111**
|
||||
|
||||
## 赞助
|
||||
|
||||
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
|
||||
|
||||
## 社区
|
||||
|
||||
QQ群:605534569
|
||||
| Plugin Name | Remarks |
|
||||
|-------|-------|
|
||||
| Modbus | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links |
|
||||
| SiemensS7 | Siemens PLC S7 series |
|
||||
| Dlt6452007 | Supports Serial/Tcp/Udp links |
|
||||
| OpcDaMaster | Compiled for 64-bit |
|
||||
| OpcUaMaster | Supports certificate login, object extension, Json read/write |
|
||||
|
||||
#### Business Plugins
|
||||
|
||||
| Plugin Name | Remarks |
|
||||
|-------|-------|
|
||||
| ModbusSlave | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links, supports Rpc reverse writing |
|
||||
| OpcUaServer | OpcUa server, supports Rpc reverse writing |
|
||||
| MqttClient | Mqtt client, supports Rpc reverse writing, script-customizable upload content |
|
||||
| MqttServer | Mqtt server, supports WebSocket, supports Rpc reverse writing, script-customizable upload content |
|
||||
| KafkaProducer | Script-customizable upload content |
|
||||
| RabbitMQProducer | Script-customizable upload content |
|
||||
| SqlDB | Relational database storage, supports historical storage and real-time data updates |
|
||||
| SqlHisAlarm | Alarm historical data relational database storage |
|
||||
| TDengineDB | Time-series database storage |
|
||||
| QuestDB | Time-series database storage |
|
||||
|
||||
|
||||
## 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)
|
||||
|
||||
|
||||
|
@@ -61,7 +61,7 @@ import Highlight from '@site/src/components/Highlight.js';
|
||||
|
||||
```
|
||||
|
||||
`400001`是PLC Modbus的地址表示方式,代表保持寄存器地址0### 1、从站地址
|
||||
`400001`是PLC Modbus的地址表示方式,代表保持寄存器地址0
|
||||
|
||||
- 基本地址
|
||||
|
||||
|
@@ -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} />
|
||||
|
||||
@@ -42,7 +42,7 @@ import Highlight from '@site/src/components/Highlight.js';
|
||||
|
||||
<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} />
|
||||
|
||||
@@ -54,7 +54,7 @@ import Highlight from '@site/src/components/Highlight.js';
|
||||
|
||||
:::tip 提示
|
||||
|
||||
测试环境下,账密会自动填充为超级管理员账号,默认账户:**superAdmin**,密码:**111111**
|
||||
测试环境下,账密会自动填充为超级管理员账号,默认账户:**SuperAdmin**,密码:**111111**
|
||||
|
||||
:::
|
||||
|
||||
|
31
doc/docs/30001.mdx
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
id: 30001
|
||||
title: 串口
|
||||
---
|
||||
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import Tag from "@site/src/components/Tag.js";
|
||||
import Highlight from '@site/src/components/Highlight.js';
|
||||
|
||||
### 前言
|
||||
|
||||
串口使用一个称为串行通信协议的协议来管理数据传输。串行通信协议在数据传输期间控制数据流,包括起始位、数据位、波特率、校验位和停止位等。
|
||||
下面每个名称做一个简单的解释。
|
||||
|
||||
``波特率``是指串口通信中每秒传输的二进制位数
|
||||
``起始位``是1位时间、值为0的位。
|
||||
``数据位``通常可能为1位、1.5位、2位时间。
|
||||
``校验位``是1位时间,根据校验方式确定验证值,比如奇校验时,计算数据位中的值为1的个数,如果是奇数,则为1,否则应为0
|
||||
``停止位``通常可能为1位、1.5位、2位时间。停止位是值为1的位。停止位代表着数据传输结束。
|
||||
|
||||
线路路空闲或者数据传输结束时,值总是1。
|
||||
对于正逻辑的TTL电平,值为1是高电平,对于负逻辑(如RS-232电平)则相反。
|
||||
|
||||
单看文字很难理解具体含义,所以我们看图说话,下面这张串口示波器图清晰的表现出二进制的传输原理
|
||||
|
||||
<img src={require("@site/static/img/docs/串口1.png").default} />
|
||||
|
||||
|
||||
如上图,可以看出2进制是 起始``1``、数据(右到左)``01010101``、停止``0`` ,那么解析出来的数据就是``0x55``
|
||||
|
||||
看到这里,相信大家都对串口知识有了一定的理解,这些基础知识对于c#工程师看似没有必要,但可以让你的实际调试开发工作更加得心应手
|
@@ -274,6 +274,20 @@ module.exports = {
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
type: "category",
|
||||
label: "协议开发教程",
|
||||
items: [
|
||||
{
|
||||
type: "doc",
|
||||
label: "基础知识",
|
||||
id: "30001",
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
type: "category",
|
||||
label: "技术支持/合作",
|
||||
|
@@ -83,7 +83,7 @@ function Banner() {
|
||||
<div className="ThingsGateway-get-start-btn">
|
||||
<Link className="ThingsGateway-get-start" to={useBaseUrl("docs/")}>
|
||||
入门指南
|
||||
<span className="ThingsGateway-version">v5.0</span>
|
||||
<span className="ThingsGateway-version">v6.0</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,7 +114,7 @@ function Gitee() {
|
||||
className={"ThingsGateway-log-jiao" + (isDarkTheme ? " dark" : "")}
|
||||
></div>
|
||||
<div className="ThingsGateway-log-number">
|
||||
<div style={{ color: "#723cff" }}>600 +</div>
|
||||
<div style={{ color: "#723cff" }}>700 +</div>
|
||||
<span className={isDarkTheme ? " dark" : ""}>Stars</span>
|
||||
</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/串口1.png
vendored
Normal file
After Width: | Height: | Size: 430 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: 使用主构造函数
|
||||
dotnet_diagnostic.IDE0290.severity = none
|
||||
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
|
||||
###############################
|
||||
# Core EditorConfig Options #
|
||||
###############################
|
||||
|
||||
# IDE0028: 简化集合初始化
|
||||
dotnet_diagnostic.IDE0028.severity = none
|
||||
# All files
|
||||
[*]
|
||||
indent_style = space
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
spelling_exclusion_path = .\exclusion.dic
|
||||
|
||||
# CA1822: 将成员标记为 static
|
||||
dotnet_diagnostic.CA1822.severity = none
|
||||
# Microsoft .NET properties
|
||||
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: 删除未读的私有成员
|
||||
dotnet_diagnostic.IDE0052.severity = none
|
||||
# ReSharper properties
|
||||
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: 模板应为静态表达式
|
||||
dotnet_diagnostic.CA2254.severity = none
|
||||
# ReSharper inspection severities
|
||||
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)” 方法
|
||||
dotnet_diagnostic.CA1854.severity = none
|
||||
# Code files
|
||||
[*.{cs,csx,vb,vbx}]
|
||||
indent_size = 4
|
||||
|
||||
# IDE0090: 使用 "new(...)"
|
||||
dotnet_diagnostic.IDE0090.severity = none
|
||||
[*.{cs,css,js,json,*html,razor,txt,log}]
|
||||
charset = utf-8-bom
|
||||
|
||||
# IDE0305: 简化集合初始化
|
||||
dotnet_diagnostic.IDE0305.severity = none
|
||||
[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
|
||||
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>
|
||||
<PropertyGroup>
|
||||
<NoWarn>CS8618;CS8625;CS8600;CS8601;CS8604;CS8714;CS8602;CS8603;CS8619;CS8621</NoWarn>
|
||||
<TargetFrameworks>net6.0;</TargetFrameworks>
|
||||
<!--<TargetFrameworks>net6.0;</TargetFrameworks>-->
|
||||
<LangVersion>11.0</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>5.0.0.8</Version>
|
||||
<Authors>Diego</Authors>
|
||||
<Company>Diego</Company>
|
||||
<Product>Diego</Product>
|
||||
<Copyright>版权所有 © 2023-present Diego</Copyright>
|
||||
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||
<RepositoryType>Gitee</RepositoryType>
|
||||
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Admin'">
|
||||
<DefineConstants>Admin</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)..\README.md" Pack="true" PackagePath="\" />
|
||||
<None Include="$(SolutionDir)..\LICENSE" Pack="true" PackagePath="\" />
|
||||
<None Include="$(SolutionDir)..\icon.png" Pack="true" PackagePath="\" />
|
||||
<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<PropertyGroup>
|
||||
<NoWarn>CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;</NoWarn>
|
||||
<TargetFrameworks>net6.0;</TargetFrameworks>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>6.0.0.9</Version>
|
||||
<Authors>Diego</Authors>
|
||||
<Company>Diego</Company>
|
||||
<Product>Diego</Product>
|
||||
<Copyright>版权所有 © 2023-present Diego</Copyright>
|
||||
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||
<RepositoryType>Gitee</RepositoryType>
|
||||
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)..\README.md" Pack="true" PackagePath="\" />
|
||||
<None Include="$(SolutionDir)..\LICENSE" Pack="true" PackagePath="\" />
|
||||
<None Include="$(SolutionDir)..\icon.png" Pack="true" PackagePath="\" />
|
||||
<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@@ -1,21 +1,14 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net45;netstandard2.0;net6.0;</TargetFrameworks>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PackageOutputPath>$(SolutionDir)nupkgs</PackageOutputPath>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<PackageTags>ThingsGateway;Diego;Blazor;设备采集;边缘网关;物联网</PackageTags>
|
||||
<PackageProjectUrl>https://gitee.com/diego2098/ThingsGateway</PackageProjectUrl>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<OutputPath></OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)Foundation.props" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net462;netstandard2.0;net6.0;</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove="Locales\*.json" />
|
||||
<EmbeddedResource Include="Locales\*.json">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)Foundation.props" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@@ -1,9 +1,11 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<!--<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>-->
|
||||
<PackageOutputPath>$(SolutionDir)nupkgs</PackageOutputPath>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<PackageTags>ThingsGateway;Diego;Blazor;设备采集;边缘网关;物联网</PackageTags>
|
||||
@@ -11,9 +13,11 @@
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<OutputPath></OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<EmbedAllSources>True</EmbedAllSources>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)PackNuget.props" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
@@ -1,17 +1,22 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0;</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)Plugin.props" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="Locales\*.json" />
|
||||
<EmbeddedResource Include="Locales\*.json">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</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>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@@ -1,35 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.8.34322.80
|
||||
VisualStudioVersion = 17.9.34622.214
|
||||
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
|
||||
.editorconfig = .editorconfig
|
||||
Delete .vs .bat = Delete .vs .bat
|
||||
Delete Bin And Obj.bat = Delete Bin And Obj.bat
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Plugin.props = Plugin.props
|
||||
EndProjectSection
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}"
|
||||
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}"
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -37,57 +33,42 @@ Global
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF}.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
|
||||
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BDC88C7C-D37B-44FB-9E84-2619F8B8B007}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D3E8D660-AD12-4C46-871F-144D9C83ED2B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2904D604-40E9-459C-81E4-2B55FFB27706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2904D604-40E9-459C-81E4-2B55FFB27706}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2904D604-40E9-459C-81E4-2B55FFB27706}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2904D604-40E9-459C-81E4-2B55FFB27706}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5EE2DAEF-05DF-4590-B1FA-42917D859F73}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{2D61A1A2-60EB-4B77-BE5B-DA4D6AE0D68D} = {BF1ED6B2-7779-4FE6-8332-85C6713F42BB}
|
||||
{D86209BA-BB60-40B1-AD55-1E8E08CCADD8} = {BF1ED6B2-7779-4FE6-8332-85C6713F42BB}
|
||||
{62AF3C40-180D-4C4F-AF35-C1FD2346DA0E} = {BF1ED6B2-7779-4FE6-8332-85C6713F42BB}
|
||||
{E43C28BD-E208-424E-8A67-75BE712DC7B4} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
||||
{C7B0B47B-4AF5-452A-8CB7-B709DC694FAA} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
||||
{D8A1033A-FBE8-4F51-A7B6-282891D69AAF} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
||||
{E2F43E54-D2B6-4789-86BA-53395CA78A44} = {F315F04B-CC19-4ACC-8544-ACFE27D1EF2B}
|
||||
{D3E8D660-AD12-4C46-871F-144D9C83ED2B} = {3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}
|
||||
{2904D604-40E9-459C-81E4-2B55FFB27706} = {3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}
|
||||
{81C3B3AF-9DA6-404B-A20A-31F2FC1CE832} = {1A13C5CC-69C9-42D9-8046-96DDECA0467A}
|
||||
{5EE2DAEF-05DF-4590-B1FA-42917D859F73} = {1A13C5CC-69C9-42D9-8046-96DDECA0467A}
|
||||
{1A04C297-BA57-45F1-8D74-D1DD8FCE83B9} = {3D3C6EB5-8E66-4B4F-A2F6-8C66673F3116}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
RESX_NeutralResourcesLanguage = zh-Hans
|
||||
SolutionGuid = {789BA852-9F20-4421-A555-E665A12587D1}
|
||||
SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@@ -1,68 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using Microsoft.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,79 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using Microsoft.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,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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,39 +9,160 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 操作事件说明特性
|
||||
/// Aop拦截器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class OperDescAttribute : Attribute
|
||||
public class OperDescAttribute : MoAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作记录标识
|
||||
/// 日志消息队列(线程安全)
|
||||
/// </summary>
|
||||
/// <param name="description"></param>
|
||||
/// <param name="catcategory"></param>
|
||||
public OperDescAttribute(string description, string catcategory = CateGoryConst.Log_OPERATE)
|
||||
private static readonly ConcurrentQueue<SysOperateLog> _logMessageQueue = new();
|
||||
|
||||
static OperDescAttribute()
|
||||
{
|
||||
Description = description;
|
||||
Catcategory = catcategory;
|
||||
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
||||
Task.Factory.StartNew(ProcessQueue, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分类
|
||||
/// </summary>
|
||||
public string Catcategory { get; }
|
||||
public OperDescAttribute(string description, bool isRecordPar = true, object localizerType = null)
|
||||
{
|
||||
Description = description;
|
||||
IsRecordPar = isRecordPar;
|
||||
LocalizerType = (Type)localizerType;
|
||||
}
|
||||
|
||||
public override AccessFlags Flags => AccessFlags.Public | AccessFlags.Method;
|
||||
public override Feature Features => Feature.OnException | Feature.OnSuccess;
|
||||
|
||||
/// <summary>
|
||||
/// 说明
|
||||
/// 说明,需配置本地化json文件
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
public Type? LocalizerType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 记录参数,默认true
|
||||
/// 是否记录进出参数
|
||||
/// </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,262 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using Furion.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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 忽略Excel导入导出
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 需要角色授权权限
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,13 +9,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 种子数据忽略新增
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class IgnoreSeedDataAddAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -24,6 +25,7 @@ public class IgnoreSeedDataAddAttribute : Attribute
|
||||
/// <summary>
|
||||
/// 种子数据忽略修改
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class IgnoreSeedDataUpdateAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -31,6 +33,7 @@ public class IgnoreSeedDataUpdateAttribute : Attribute
|
||||
/// <summary>
|
||||
/// 忽略初始化表
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class IgnoreInitTableAttribute : Attribute
|
||||
{
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 管理员才能访问
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,11 +9,12 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 最小值校验
|
||||
@@ -25,18 +25,12 @@ public class MinValueAttribute : ValidationAttribute
|
||||
/// 最小值
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public MinValueAttribute(double value)
|
||||
public MinValueAttribute(UInt64 value)
|
||||
{
|
||||
MinValue = value;
|
||||
}
|
||||
|
||||
private double MinValue { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string FormatErrorMessage(string name)
|
||||
{
|
||||
return base.FormatErrorMessage(name);
|
||||
}
|
||||
private UInt64 MinValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最小值校验
|
||||
@@ -47,10 +41,10 @@ public class MinValueAttribute : ValidationAttribute
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
var input = Convert.ToDouble(value);
|
||||
return input > MinValue;
|
||||
var input = Convert.ToUInt64(value);
|
||||
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 @@
|
||||
{
|
||||
//日志配置
|
||||
"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,158 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 授权用户常量
|
||||
@@ -25,7 +25,7 @@ public class ClaimConst
|
||||
public const string Account = "Account";
|
||||
|
||||
/// <summary>
|
||||
/// 账号类型
|
||||
/// SuperAdmin
|
||||
/// </summary>
|
||||
public const string SuperAdmin = "SuperAdmin";
|
||||
|
@@ -1,99 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,7 +9,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -20,7 +20,22 @@ namespace ThingsGateway.Admin.Application;
|
||||
public class ResourceConst
|
||||
{
|
||||
/// <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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,7 +9,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -19,13 +19,23 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// </summary>
|
||||
public class RoleConst
|
||||
{
|
||||
/// <summary>
|
||||
/// 超级管理员Id
|
||||
/// </summary>
|
||||
public const long SuperAdminId = 212725263002001;
|
||||
|
||||
/// <summary>
|
||||
/// 超级管理员
|
||||
/// </summary>
|
||||
public const string SuperAdmin = "superAdmin";
|
||||
public const string SuperAdmin = "SuperAdmin";
|
||||
|
||||
/// <summary>
|
||||
/// 业务管理员
|
||||
/// </summary>
|
||||
public const string BizAdmin = "bizAdmin";
|
||||
}
|
||||
public const string BizAdmin = "BizAdmin";
|
||||
|
||||
/// <summary>
|
||||
/// api角色
|
||||
/// </summary>
|
||||
public const string ApiRole = "ApiRole";
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// SqlSugar系统常量
|
||||
@@ -29,6 +29,16 @@ public class SqlSugarConst
|
||||
/// </summary>
|
||||
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>
|
||||
/// DB_Custom
|
||||
/// </summary>
|
@@ -1,75 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,24 +9,18 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ThingsGateway.Demo.Web
|
||||
namespace ThingsGateway.Admin.Application
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件下载
|
||||
/// </summary>
|
||||
#if DEMO
|
||||
#else
|
||||
|
||||
[ApiDescriptionSettings(IgnoreApi = true)]
|
||||
#endif
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
[Route("api/file")]
|
||||
public class FileController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
@@ -35,7 +28,7 @@ namespace ThingsGateway.Demo.Web
|
||||
/// </summary>
|
||||
/// <param name="fileName">相对路径</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[HttpGet("download")]
|
||||
public IActionResult Download(string 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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,47 +9,50 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 权限按钮服务
|
||||
/// 规范化结果提供器
|
||||
/// </summary>
|
||||
public interface IButtonService : ISugarService, ITransient
|
||||
public interface IUnifyResultProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加按钮
|
||||
/// 异常返回值
|
||||
/// </summary>
|
||||
/// <param name="input">添加参数</param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
Task AddAsync(ButtonAddInput input);
|
||||
IActionResult OnException(ActionExecutedContext context);
|
||||
|
||||
/// <summary>
|
||||
/// 批量添加
|
||||
/// 成功返回值
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<long>> AddBatchAsync(ButtonAddInput input);
|
||||
IActionResult OnSucceeded(ActionExecutedContext context, object? data);
|
||||
|
||||
/// <summary>
|
||||
/// 删除按钮
|
||||
/// 验证失败返回值
|
||||
/// </summary>
|
||||
/// <param name="input">删除参数</param>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="errors"></param>
|
||||
/// <returns></returns>
|
||||
Task DeleteAsync(List<BaseIdInput> input);
|
||||
IActionResult OnValidateFailed(ActionExecutingContext context, string? errors);
|
||||
|
||||
/// <summary>
|
||||
/// 编辑按钮
|
||||
/// 拦截返回状态码
|
||||
/// </summary>
|
||||
/// <param name="input">编辑参数</param>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="statusCode"></param>
|
||||
/// <returns></returns>
|
||||
Task EditAsync(ButtonEditInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 按钮分页查询
|
||||
/// </summary>
|
||||
/// <param name="input">查询条件</param>
|
||||
/// <returns>按钮分页列表</returns>
|
||||
Task<SqlSugarPagedList<SysResource>> PageAsync(ButtonPageInput input);
|
||||
Task OnResponseStatusCodes(HttpContext context, int statusCode);
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,11 +9,17 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 全局返回结果
|
||||
@@ -28,17 +28,12 @@ public class UnifyResult<T>
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
public T Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 附加数据
|
||||
/// </summary>
|
||||
public object Extras { get; set; }
|
||||
public T? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public object Msg { get; set; }
|
||||
public object? Msg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,33 +9,32 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using Furion.DataValidation;
|
||||
using Furion.FriendlyException;
|
||||
using Furion.UnifyResult;
|
||||
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 规范化RESTful风格返回值
|
||||
/// </summary>
|
||||
[SuppressSniffer, UnifyModel(typeof(UnifyResult<>))]
|
||||
public class UnifyResultProvider : IUnifyResultProvider
|
||||
{
|
||||
private static IStringLocalizer Localizer = App.CreateLocalizerByType(typeof(UnifyResultProvider))!;
|
||||
|
||||
/// <summary>
|
||||
/// 异常返回
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="metadata"></param>
|
||||
/// <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>
|
||||
@@ -44,24 +42,18 @@ public class UnifyResultProvider : IUnifyResultProvider
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="statusCode"></param>
|
||||
/// <param name="unifyResultSettings"></param>
|
||||
/// <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)
|
||||
{
|
||||
// 处理 401 状态码
|
||||
case StatusCodes.Status401Unauthorized:
|
||||
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "登录已过期,请重新登录"),
|
||||
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
|
||||
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, Localizer["TokenOver"]));
|
||||
break;
|
||||
// 处理 403 状态码
|
||||
case StatusCodes.Status403Forbidden:
|
||||
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "禁止访问,没有权限"),
|
||||
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
|
||||
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, default, "NoPermission"));
|
||||
break;
|
||||
|
||||
default: break;
|
||||
@@ -74,7 +66,7 @@ public class UnifyResultProvider : IUnifyResultProvider
|
||||
/// <param name="context"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
|
||||
public IActionResult OnSucceeded(ActionExecutedContext context, object? data)
|
||||
{
|
||||
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
|
||||
}
|
||||
@@ -83,11 +75,11 @@ public class UnifyResultProvider : IUnifyResultProvider
|
||||
/// 验证失败返回
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="metadata"></param>
|
||||
/// <param name="errors"></param>
|
||||
/// <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>
|
||||
@@ -98,14 +90,13 @@ public class UnifyResultProvider : IUnifyResultProvider
|
||||
/// <param name="data">数据</param>
|
||||
/// <param name="errors">错误信息</param>
|
||||
/// <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>
|
||||
{
|
||||
Code = statusCode,
|
||||
Msg = statusCode == StatusCodes.Status200OK ? "请求成功" : errors,
|
||||
Msg = statusCode == StatusCodes.Status200OK ? "Success" : errors,
|
||||
Data = data,
|
||||
Extras = UnifyContext.Take(),
|
||||
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
|
||||
}));
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人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.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
[Route("api/[controller]/[action]")]
|
||||
[RolePermission]
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
public class TestController : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
public async Task Test(QueryPageOptions queryPageOptions)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,18 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using ThingsGateway.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 主键id基类
|
||||
@@ -24,6 +32,7 @@ public abstract class PrimaryIdEntity : IPrimaryIdEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Visible = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
||||
public virtual long Id { get; set; }
|
||||
}
|
||||
|
||||
@@ -37,6 +46,7 @@ public abstract class PrimaryKeyEntity : PrimaryIdEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "扩展信息", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual string? ExtJson { get; set; }
|
||||
}
|
||||
|
||||
@@ -50,6 +60,7 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "创建时间", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual DateTime? CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -57,13 +68,16 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "创建人", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
public virtual string CreateUser { get; set; }
|
||||
[NotNull]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual string? CreateUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者Id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "创建者Id", IsOnlyIgnoreUpdate = true, IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual long CreateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -71,6 +85,7 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "软删除", IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual bool IsDelete { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
@@ -78,6 +93,7 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "更新时间", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual DateTime? UpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -85,19 +101,21 @@ public abstract class BaseEntity : PrimaryKeyEntity
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "更新人", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
public virtual string UpdateUser { get; set; }
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual string? UpdateUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 修改者Id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "修改者Id", IsOnlyIgnoreInsert = true, IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual long? UpdateUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序码
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = false)]
|
||||
[IgnoreExcel]
|
||||
public virtual int SortCode { get; set; }
|
||||
}
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = true, Sortable = true, DefaultSortOrder = SortOrder.Asc)]
|
||||
public int? SortCode { get; set; }
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,7 +9,12 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -19,54 +23,118 @@ namespace ThingsGateway.Admin.Application;
|
||||
///</summary>
|
||||
[SugarTable("sys_operatelog", TableDescription = "操作日志表")]
|
||||
[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>
|
||||
[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; }
|
||||
|
||||
/// <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>
|
||||
[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; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求参数
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "请求参数", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||
[DataTable(Order = 24, IsShow = true, Sortable = true, DefaultFilter = false, CellClass = " table-text-truncate ")]
|
||||
public string ParamJson { get; set; }
|
||||
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
|
||||
public string? ParamJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求方式
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "请求方式", Length = 200, IsNullable = true)]
|
||||
[DataTable(Order = 25, IsShow = true, Sortable = true, DefaultFilter = true, CellClass = " table-text-truncate ")]
|
||||
public string ReqMethod { get; set; }
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public string? ReqMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求地址
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "请求地址", ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
||||
[DataTable(Order = 26, IsShow = true, Sortable = true, DefaultFilter = true, CellClass = " table-text-truncate ")]
|
||||
public string ReqUrl { get; set; }
|
||||
[SugarColumn(ColumnDescription = "请求地址", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public string? ReqUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回结果
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "返回结果", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||
[DataTable(Order = 27, IsShow = true, Sortable = true, DefaultFilter = false, CellClass = " table-text-truncate ")]
|
||||
public string ResultJson { get; set; }
|
||||
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
|
||||
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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,7 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -25,7 +27,7 @@ public class SysRelation : PrimaryKeyEntity
|
||||
/// 分类
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "分类", Length = 200)]
|
||||
public string Category { get; set; }
|
||||
public RelationCategoryEnum Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 对象ID
|
||||
@@ -37,5 +39,5 @@ public class SysRelation : PrimaryKeyEntity
|
||||
/// 目标ID
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "目标ID", IsNullable = true)]
|
||||
public string TargetId { get; set; }
|
||||
public string? TargetId { get; set; }
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,9 +9,18 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -26,91 +34,72 @@ public class SysResource : BaseEntity
|
||||
/// <summary>
|
||||
/// 父id
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "父id", IsNullable = true)]
|
||||
public virtual long? ParentId { get; set; }
|
||||
[SugarColumn(ColumnDescription = "父id")]
|
||||
[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>
|
||||
[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; }
|
||||
|
||||
/// <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>
|
||||
[SugarColumn(ColumnDescription = "图标", Length = 200, IsNullable = true)]
|
||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
||||
public virtual string Icon { get; set; }
|
||||
[AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)]
|
||||
public virtual string? Icon { get; set; }
|
||||
|
||||
/// <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>
|
||||
[SugarColumn(ColumnDescription = "隐藏", Length = 200)]
|
||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
||||
public bool Hidden { get; set; } = false;
|
||||
[SugarColumn(ColumnDescription = "菜单匹配类型", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||
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>
|
||||
[SugarColumn(IsIgnore = 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,
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public List<SysResource>? Children { get; set; }
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,7 +9,14 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -25,35 +31,26 @@ public class SysRole : BaseEntity
|
||||
/// 编码
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "编码", Length = 200)]
|
||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
||||
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
///</summary>
|
||||
[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; }
|
||||
|
||||
/// <summary>
|
||||
/// 分类
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "分类", Length = 200, IsNullable = false)]
|
||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
||||
public virtual string Category { get; set; }
|
||||
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||
public virtual RoleCategoryEnum Category { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null || !(obj is UserSelectorOutput))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Id == ((UserSelectorOutput)obj).Id;
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,7 +9,16 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -21,111 +29,159 @@ namespace ThingsGateway.Admin.Application;
|
||||
[Tenant(SqlSugarConst.DB_Admin)]
|
||||
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>
|
||||
[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; }
|
||||
|
||||
/// <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>
|
||||
[SugarColumn(ColumnDescription = "密码", ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
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>
|
||||
[SugarColumn(ColumnDescription = "是否启用")]
|
||||
[DataTable(Order = 2, IsShow = true, Sortable = true, DefaultFilter = false)]
|
||||
public bool UserStatus { get; set; }
|
||||
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
|
||||
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>
|
||||
|
@@ -1,87 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,11 +9,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 登录设备类型枚举
|
||||
@@ -24,18 +22,10 @@ public enum AuthDeviceTypeEnum
|
||||
/// <summary>
|
||||
/// PC端
|
||||
/// </summary>
|
||||
[Description("PC")]
|
||||
PC,
|
||||
|
||||
/// <summary>
|
||||
/// 移动端
|
||||
/// </summary>
|
||||
[Description("APP")]
|
||||
APP,
|
||||
|
||||
/// <summary>
|
||||
/// Api
|
||||
/// </summary>
|
||||
[Description("Api")]
|
||||
Api,
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,22 +9,23 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Components;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 键值表示
|
||||
/// 字典表类型
|
||||
/// </summary>
|
||||
public class StringFilters
|
||||
public enum DictTypeEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// 键
|
||||
/// 系统使用
|
||||
/// </summary>
|
||||
public string Key { get; set; }
|
||||
System,
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// 用户自定义
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
Define,
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,11 +9,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,8 +9,14 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
global using NewLife.Caching;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public enum ResourceCategoryEnum
|
||||
{
|
||||
Module,
|
||||
Menu,
|
||||
Button,
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,8 +9,13 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
global using System;
|
||||
|
||||
global using ThingsGateway.Components;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public enum RoleCategoryEnum
|
||||
{
|
||||
Global,
|
||||
Api,
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,8 +9,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
global using System;
|
||||
|
||||
global using ThingsGateway.Components;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public enum TargetEnum
|
||||
{
|
||||
_self,
|
||||
_blank,
|
||||
_parent,
|
||||
_top
|
||||
}
|
@@ -1,121 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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,90 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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,54 +0,0 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人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;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class UserFriendlyException : Exception
|
||||
{
|
||||
public UserFriendlyException()
|
||||
{
|
||||
}
|
||||
|
||||
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,4 +1,3 @@
|
||||
#region copyright
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
@@ -10,9 +9,12 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
using Furion.Extensions;
|
||||
|
||||
|
||||
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
@@ -21,14 +23,157 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 对象拓展类
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
public static class ObjectExtension
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将 DateTimeOffset 转换成本地 DateTime
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime ConvertToDateTime(this DateTimeOffset dateTime)
|
||||
{
|
||||
if (dateTime.Offset.Equals(TimeSpan.Zero))
|
||||
return dateTime.UtcDateTime;
|
||||
if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime)))
|
||||
return dateTime.ToLocalTime().DateTime;
|
||||
else
|
||||
return dateTime.DateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 DateTimeOffset? 转换成本地 DateTime?
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime? ConvertToDateTime(this DateTimeOffset? dateTime)
|
||||
{
|
||||
return dateTime.HasValue ? dateTime.Value.ConvertToDateTime() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 DateTime 转换成 DateTimeOffset
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime)
|
||||
{
|
||||
return DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 DateTime? 转换成 DateTimeOffset?
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTimeOffset? ConvertToDateTimeOffset(this DateTime? dateTime)
|
||||
{
|
||||
return dateTime.HasValue ? dateTime.Value.ConvertToDateTimeOffset() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将时间戳转换为 DateTime
|
||||
/// </summary>
|
||||
/// <param name="timestamp"></param>
|
||||
/// <returns></returns>
|
||||
internal static DateTime ConvertToDateTime(this long timestamp)
|
||||
{
|
||||
var timeStampDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
var digitCount = (int)Math.Floor(Math.Log10(timestamp) + 1);
|
||||
|
||||
if (digitCount != 13 && digitCount != 10)
|
||||
{
|
||||
throw new ArgumentException("Data is not a valid timestamp format.");
|
||||
}
|
||||
|
||||
return (digitCount == 13
|
||||
? timeStampDateTime.AddMilliseconds(timestamp) // 13 位时间戳
|
||||
: timeStampDateTime.AddSeconds(timestamp)).ToLocalTime(); // 10 位时间戳
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 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>
|
||||
@@ -109,32 +254,6 @@ public static class ObjectExtension
|
||||
|| 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>
|
||||
@@ -407,6 +526,12 @@ public static class ObjectExtension
|
||||
var _tempStr = !string.IsNullOrWhiteSpace(tempStr) ? tempStr : str;
|
||||
tempStr = _tempStr[..^affix.Length];
|
||||
endCleared = true;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tempStr))
|
||||
{
|
||||
tempStr = null;
|
||||
endCleared = false;
|
||||
}
|
||||
}
|
||||
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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,18 +9,31 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
using ThingsGateway.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Sqlsugar分页拓展类
|
||||
/// </summary>
|
||||
public static class SqlSugarPageExtension
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public static class SqlSugarExtensions
|
||||
{
|
||||
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)
|
||||
{
|
||||
return queryable.IgnoreColumns(
|
||||
@@ -31,7 +43,6 @@ public static class SqlSugarPageExtension
|
||||
nameof(BaseEntity.CreateTime),
|
||||
nameof(BaseEntity.CreateUser),
|
||||
nameof(BaseEntity.CreateUserId),
|
||||
nameof(BaseEntity.SortCode),
|
||||
nameof(BaseEntity.ExtJson),
|
||||
nameof(BaseEntity.IsDelete),
|
||||
nameof(BaseEntity.UpdateTime),
|
||||
@@ -41,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,
|
||||
int size)
|
||||
{
|
||||
@@ -67,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,
|
||||
int current, int size)
|
||||
{
|
||||
@@ -94,15 +89,50 @@ public static class SqlSugarPageExtension
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <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,
|
||||
int pageSize, Expression<Func<TEntity, TResult>> expression)
|
||||
{
|
||||
@@ -122,15 +152,9 @@ public static class SqlSugarPageExtension
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SqlSugar分页扩展
|
||||
/// SqlSugar分页扩展,查询前扩展转换实体类
|
||||
/// </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>(
|
||||
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,5 +1,4 @@
|
||||
#region copyright
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -10,14 +9,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Core.Extension;
|
||||
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// TextWriter扩展
|
||||
/// </summary>
|
||||
public static class TextWriterExtension
|
||||
public static class TextWriterExtensions
|
||||
{
|
||||
private const string DefaultBackgroundColor = "\x1B[49m";
|
||||
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;
|
||||
}
|
||||
}
|