Compare commits

...

739 Commits

Author SHA1 Message Date
Kimdiego2098
4f188ea6cc 整理文件 2023-11-18 23:04:51 +08:00
Kimdiego2098
acb17018ae 整理文件 2023-11-18 23:04:25 +08:00
Kimdiego2098
2affe2988d 整理文件 2023-11-18 23:03:24 +08:00
Kimdiego2098
4174dd2206 整理文件 2023-11-18 23:03:02 +08:00
Kimdiego2098
e1c492f238 整理文件 2023-11-18 23:02:28 +08:00
Kimdiego2098
fb08e34fa3 整理文件 2023-11-18 23:01:56 +08:00
Kimdiego2098
a1793a0afe 去除不必要的文件 2023-11-18 22:52:26 +08:00
Kimdiego2098
4da9763b49 去除不必要的文件 2023-11-18 22:50:03 +08:00
Kimdiego2098
81e0918bd0 迁移4.0 2023-11-18 22:45:24 +08:00
Kimdiego2098
c1e064f06d 迁移4.0 2023-11-18 22:43:36 +08:00
Kimdiego2098
1c52be8b47 添加停止线程等待时间 2023-11-16 22:32:22 +08:00
Kimdiego2098
bcd82055ca s7握手失败后,手动关闭连接 2023-11-11 10:26:05 +08:00
Kimdiego2098
c47d95d170 fix:修复线程阻塞检测触发重启时,后台变量列表不再刷新的问题! 2023-11-10 09:00:50 +08:00
Kimdiego2098
3e62f1ad51 历史数据上传成功后,才上传缓存数据 2023-11-09 18:55:56 +08:00
Kimdiego2098
8dcae973ef update 2023-11-08 16:19:46 +08:00
Kimdiego2098
4cf35f7294 发布3.0.1版本 2023-11-03 11:32:13 +08:00
Kimdiego2098
94c77d151b update touchsocket 2023-11-03 11:27:12 +08:00
Kimdiego2098
7f600e2b4b update touchsocket 2023-11-03 11:19:26 +08:00
Kimdiego2098
c809d0ba87 不存在插件时,报错内容优化 2023-11-01 14:51:08 +08:00
Kimdiego2098
50f038ec89 update webapiClient 2023-11-01 14:21:53 +08:00
Kimdiego2098
9199a255a2 调整Timeout属性为int数据类型 2023-10-31 23:49:46 +08:00
Kimdiego2098
d324537b47 更新发布版3.0.0.28 2023-10-31 17:47:07 +08:00
Kimdiego2098
d0c05685f7 更新OPCUAClient,订阅模式适配 分组 2023-10-31 10:55:50 +08:00
Kimdiego2098
1063c930b5 update 2023-10-31 00:24:00 +08:00
Kimdiego2098
79cbd44366 tcpservice停止时执行shutdown方法,修复demo发布不显示页面的问题 2023-10-30 21:31:30 +08:00
Kimdiego2098
7fdac1c5cb 添加部分Pro内容 2023-10-29 19:39:38 +08:00
Kimdiego2098
0c0cf72ebb 添加部分Pro内容 2023-10-29 19:34:17 +08:00
Kimdiego2098
8e2fe175ed 更新文档 2023-10-28 22:06:58 +08:00
Kimdiego2098
d1cff037c9 发布3.0.0.27版本;
优化设备线程启停逻辑
添加winform(blazor) demo
部分页面显示内容优化
2023-10-28 22:03:52 +08:00
Kimdiego2098
fc88a2fafa 发布3.0.0.27版本;
优化设备线程启停逻辑
添加winform(blazor) demo
部分页面显示内容优化
2023-10-28 22:00:35 +08:00
Kimdiego2098
45fcceb056 优化设备线程启停逻辑 2023-10-28 21:58:09 +08:00
Kimdiego2098
7043477038 添加部分Pro内容 2023-10-28 21:21:03 +08:00
Kimdiego2098
7dd685cf54 添加winform(blazor) demo 2023-10-28 15:20:12 +08:00
Kimdiego2098
5f5e4969c0 调整pro类库 2023-10-28 10:54:26 +08:00
Diego2098
8a53fd19e9 设备状态页面显示优化 2023-10-27 20:19:26 +08:00
Diego2098
baf4714c36 状态显示:已退出自动更新 2023-10-27 20:18:57 +08:00
Kimdiego2098
7ba9ac7a5b 报文显示限长500 2023-10-27 14:53:19 +08:00
Kimdiego2098
85b8f26e8e tcpserver 报文输出 添加 ip端口 提示 2023-10-27 14:41:35 +08:00
Kimdiego2098
594a0f1410 update the tcpserver class and add log output for start and stop 2023-10-27 14:24:26 +08:00
Kimdiego2098
d317d757d7 优化线程上下文转换 2023-10-26 19:16:24 +08:00
Kimdiego2098
fdf0ba6318 添加ConfigureAwait 2023-10-26 18:06:33 +08:00
Kimdiego2098
15bf7de5fa 优化设备导出逻辑 2023-10-26 15:15:43 +08:00
Kimdiego2098
d3402b058e 优化设备导出逻辑 2023-10-26 15:10:26 +08:00
Kimdiego2098
e7dfdd4031 3.0.0.25 2023-10-26 13:21:40 +08:00
Kimdiego2098
b2dd7b6364 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-10-26 13:20:54 +08:00
Kimdiego2098
9bd6d9abbf 3.0.0.25 2023-10-26 13:20:31 +08:00
Kimdiego2098
cd14428fea update 2023-10-26 13:19:18 +08:00
Kimdiego2098
19d9f03c2b s7添加错误返回码提示,组包处理 2023-10-26 13:05:06 +08:00
Kimdiego2098
0d57e72bbf 增加硬件信息 相关json配置 2023-10-26 10:54:48 +08:00
Kimdiego2098
329516a61b 更新nuget依赖 2023-10-26 09:37:01 +08:00
Kimdiego2098
d566869589 硬件信息页面添加刷新条件 2023-10-26 09:20:25 +08:00
Kimdiego2098
9cb8d8e6c7 更新文档 2023-10-25 00:53:27 +08:00
Kimdiego2098
9de3c57e5d update touchsocket 2023-10-25 00:53:17 +08:00
Kimdiego2098
f32ff92b0b <Version>3.0.0.24</Version> 2023-10-24 23:48:20 +08:00
Kimdiego2098
88d71e271e 更改S7协议 设备属性,注意删除了DstTSAP,改为较直观的 机架号/槽位号 2023-10-24 23:47:35 +08:00
Kimdiego2098
fd9c14612a 添加属性识别 16进制写入 2023-10-24 23:46:50 +08:00
Kimdiego2098
e26e5a160f 添加nuget依赖包 2023-10-24 20:50:39 +08:00
Kimdiego2098
b836bfed22 更新nuget包 2023-10-24 00:37:46 +08:00
Kimdiego2098
a4b598c6d0 调整解决方案文件夹 2023-10-24 00:13:03 +08:00
Kimdiego2098
c9ab755839 暂时屏蔽mqttserver 遗留消息错误 2023-10-23 20:51:11 +08:00
Kimdiego2098
9920edba53 调整编码 2023-10-23 20:47:00 +08:00
Kimdiego2098
12bd7280d1 调整mqttlog 2023-10-23 20:46:17 +08:00
Kimdiego2098
d30ea7f63b 调整mqttlog 2023-10-23 20:43:58 +08:00
Kimdiego2098
ebd3390db6 添加部分兼容方法 2023-10-22 02:42:16 +08:00
Kimdiego2098
9a374a9ebc 添加部分兼容方法 2023-10-22 02:26:18 +08:00
Kimdiego2098
b1bc22cb08 update touchsocket 2023-10-22 02:26:04 +08:00
Kimdiego2098
4930d53890 调整代码格式 2023-10-21 19:15:26 +08:00
Kimdiego2098
c31327b5bc update touchsocket 2023-10-21 19:08:21 +08:00
Kimdiego2098
3f2aa1f1e1 调整代码格式 2023-10-21 19:03:15 +08:00
Kimdiego2098
6e78c00a96 调整代码格式 2023-10-21 19:02:58 +08:00
Kimdiego2098
c27dde085e 3.0.0.23 2023-10-20 21:38:22 +08:00
Kimdiego2098
d26cc308c0 优化SQLDB实时表模式,插入/更新 2023-10-20 21:38:07 +08:00
Kimdiego2098
fb1efdf290 sqlsugar提示默认中文 2023-10-20 21:37:42 +08:00
Kimdiego2098
3c99f2a472 update touchsocket 2023-10-20 21:03:29 +08:00
Kimdiego2098
affe9a44e0 优化 ModbusServer 内存占用 2023-10-20 01:53:48 +08:00
Kimdiego2098
43730fa519 3.0.0.21 2023-10-20 01:19:18 +08:00
Kimdiego2098
d39aa22b09 优化OPCUAServer,取消注册,提供多url写入 2023-10-20 01:19:05 +08:00
Kimdiego2098
e232a6b6ea 更新赞助名单 2023-10-19 21:15:35 +08:00
Kimdiego2098
71ebb36fe9 更新文档 2023-10-19 20:36:28 +08:00
Kimdiego2098
78a0b86327 更新版本:3.0.0.20 2023-10-19 20:27:31 +08:00
Kimdiego2098
2636c16a97 优化modbusServer内存管理 2023-10-19 20:24:39 +08:00
Kimdiego2098
fd77c0242d update touchsocket 2023-10-19 20:23:43 +08:00
Kimdiego2098
e74819a900 update toucksocket 2023-10-18 21:51:49 +08:00
Kimdiego2098
9b7f696c9b 更新文档 2023-10-18 20:42:10 +08:00
Kimdiego2098
0230d614e7 发布驱动包 2023-10-18 18:04:37 +08:00
Diego2098
252d99ad78 !12 【轻量级 PR】:修正心跳事件中的参数
Merge pull request !12 from youthalan/N/A
2023-10-18 06:59:17 +00:00
youthalan
1ffc200350 修正心跳事件中的参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 06:58:03 +00:00
Kimdiego2098
807d89b2b2 更新版本号 2023-10-18 13:13:03 +08:00
Kimdiego2098
4013afa1f1 初始化 采集/上传线程时 直接返回线程控制 2023-10-18 13:11:59 +08:00
Kimdiego2098
a580927ceb 更新OPCUAClient类库 2023-10-18 13:09:25 +08:00
Kimdiego2098
bf2cf52034 更新OPCUAClient类库 2023-10-18 12:45:26 +08:00
Kimdiego2098
81bb8b7c31 更新OPCUAClient类库 2023-10-18 12:44:58 +08:00
Kimdiego2098
a825007fb5 更新版本号与nuget发布 2023-10-18 12:39:42 +08:00
Kimdiego2098
988124d96a 更新OPCUAClient类库 2023-10-18 12:38:20 +08:00
Diego2098
f0de815296 !11 补充OPCClient类库事件的缺失文件
Merge pull request !11 from youthalan/N/A
2023-10-18 04:34:34 +00:00
youthalan
0e2d58c887 补充OPCClient类库事件的缺失文件
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 04:32:39 +00:00
Diego2098
b155382626 !10 添加连接或断开事件
Merge pull request !10 from youthalan/N/A
2023-10-18 04:20:22 +00:00
youthalan
f362d740af 修改OPCUAClient类添加连接或断开事件,修改注入时不需要带参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 03:48:05 +00:00
Kimdiego2098
4a85e31a4f update 3.0.0.16 2023-10-18 00:38:36 +08:00
Kimdiego2098
302c270ad5 opcuaClient浏览空间 添加是否显示子变量的选项 2023-10-18 00:36:44 +08:00
Kimdiego2098
3c1517d0f3 更改日志输出内容 2023-10-18 00:15:07 +08:00
Kimdiego2098
f9fb222044 update 3.0.0.15 2023-10-18 00:07:24 +08:00
Kimdiego2098
e8edc02ba3 增加不支持单文件发布的说明 2023-10-17 23:47:24 +08:00
Kimdiego2098
95a44e3053 update tdengineDB plugin 2023-10-17 23:43:48 +08:00
Kimdiego2098
74a9fe9a87 update touchsocket 2023-10-17 23:12:19 +08:00
Kimdiego2098
4d03f9ea1a TD时序库插件,创建时间更改为主键 2023-10-17 23:08:09 +08:00
Kimdiego2098
67c96ca991 update touchsocket 2023-10-17 23:06:21 +08:00
Kimdiego2098
88fb793c68 更新nuget包 2023-10-17 21:00:50 +08:00
Kimdiego2098
d6d02d8cc5 update SQLDB 2023-10-16 20:50:10 +08:00
Kimdiego2098
c5a3f8e2e3 update touchsocket and other 2023-10-16 20:36:51 +08:00
Kimdiego2098
27e8653a1a 3.0.0.13 2023-10-16 17:44:09 +08:00
Kimdiego2098
863beda82c 增加关系库存储插件; 2023-10-16 17:40:17 +08:00
Kimdiego2098
bac84c3ecd 增加时序库存储插件; 2023-10-16 17:40:13 +08:00
Kimdiego2098
2fca2ad9f8 更新pro用户列表 2023-10-16 17:39:14 +08:00
Kimdiego2098
dd75286fe0 3.0.0.12 2023-10-16 08:47:39 +08:00
Kimdiego2098
7f91792cf1 opcuaclient添加是否加载服务端数据类型的选项 2023-10-16 08:46:46 +08:00
Kimdiego2098
0e0ccad311 fix:tcpservice dispose err 2023-10-16 08:46:32 +08:00
Diego2098
0691f72e67 !9 增加可选择安全订阅
Merge pull request !9 from youthalan/N/A
2023-10-16 00:33:19 +00:00
youthalan
7e38a51720 增加可选择安全订阅,以加快订阅速度
Signed-off-by: youthalan <youthalan@126.com>
2023-10-16 00:26:59 +00:00
Kimdiego2098
34ca8243a3 更新3.0.0.11 2023-10-15 20:26:18 +08:00
Diego2098
112fea7632 读取数据类型方法改为批量 2023-10-15 20:21:23 +08:00
Kimdiego2098
378763e4ee 同步Pro版本 2023-10-13 20:14:35 +08:00
Kimdiego2098
517bd0394d update touchsocket 2023-10-13 19:16:12 +08:00
Kimdiego2098
70adb97fb5 update nuget 2023-10-13 16:14:42 +08:00
Kimdiego2098
623d44cabe update 3.0.0.7 2023-10-13 11:54:27 +08:00
Kimdiego2098
0d479ca00b 添加三菱mc 3e帧二进制通讯协议文档 2023-10-13 11:51:51 +08:00
Kimdiego2098
8bc49ef437 update demo 2023-10-12 18:52:44 +08:00
Kimdiego2098
f83fcec786 参数名称修改 2023-10-12 14:57:58 +08:00
Kimdiego2098
93690ce40d fix:mqtt保留消息未更新/更新错误 2023-10-12 14:57:36 +08:00
Kimdiego2098
f82c5f2f27 3.0.0.6 2023-10-11 16:35:10 +08:00
Kimdiego2098
a83c1c3899 update nuget 2023-10-11 16:28:18 +08:00
Kimdiego2098
91d6aed109 调整代码执行顺序 2023-10-11 16:19:34 +08:00
Kimdiego2098
db8f8fe51d update ManageGateway 2023-10-11 10:59:33 +08:00
Kimdiego2098
4596004b17 update ManageGateway 2023-10-11 10:41:09 +08:00
Kimdiego2098
d5540906cb update 3.0.0.5 2023-10-10 21:09:34 +08:00
Kimdiego2098
90796a979d fix:modbusRtu写入返回报文缓存逻辑修复 2023-10-10 21:08:42 +08:00
Kimdiego2098
2190a87772 update touchsocket 2023-10-10 20:03:40 +08:00
Kimdiego2098
c5953b83f8 update touchsocket 2023-10-10 20:02:15 +08:00
Kimdiego2098
24bc60abf0 fix:signalR razor dispose接口 2023-10-09 18:11:23 +08:00
Kimdiego2098
31eee6b009 update uaparser 2023-10-09 10:56:22 +08:00
Kimdiego2098
c5da565a8f 添加MqttRpcDemo 2023-10-07 12:04:17 +08:00
Kimdiego2098
947cd712e1 添加清理日志任务配置参数 2023-10-06 18:28:06 +08:00
Kimdiego2098
edc208f96b update 3.0.0.2 2023-10-05 15:32:50 +08:00
Diego2098
1fb0296ee7 update Directory.Build.props 2023-10-05 15:21:51 +08:00
Kimdiego2098
6488d3df87 修复重启共享通道中的单个设备时,导致通道内其他设备变量异常 2023-10-05 00:34:12 +08:00
Kimdiego2098
56189d78e0 update opcuaclient 2023-10-04 17:12:47 +08:00
Kimdiego2098
bff18127b8 update opcuaclient 2023-10-04 16:59:15 +08:00
Kimdiego2098
363206e0ba update nuget 2023-10-04 02:04:09 +08:00
Kimdiego2098
fd3e378501 update 3.0.0.1 2023-10-04 01:42:27 +08:00
Kimdiego2098
4ba2fe4c9d 实时报警列表线程同步 2023-10-04 01:27:03 +08:00
Kimdiego2098
2c499626ad modbusRtu 适配器 过滤干扰头部数据 2023-10-04 01:26:44 +08:00
Kimdiego2098
2b581a03c3 更新种子数据 2023-10-04 01:24:34 +08:00
Kimdiego2098
450c15210a update windowsService bat 2023-10-03 19:11:42 +08:00
Kimdiego2098
65fed8cc93 update demo 2023-10-03 18:54:01 +08:00
Kimdiego2098
4b64771ea2 rpc调用提示优化 2023-10-03 18:20:39 +08:00
Kimdiego2098
f39977a6ff fix:rpc 特殊方法分类错误 2023-10-03 17:05:49 +08:00
Kimdiego2098
933b535caa update demo 2023-10-02 22:37:50 +08:00
Kimdiego2098
8abc5d2f20 update driverDebugPage 2023-10-02 18:34:55 +08:00
Kimdiego2098
d8783cd994 update opcuaClient 2023-10-02 18:30:58 +08:00
Diego2098
d5d087feb5 add s7 wstring addressSign 2023-10-01 16:54:42 +08:00
Diego2098
6ba3399df7 add s7 wstring addressSign 2023-10-01 16:49:07 +08:00
Diego2098
65124b3aa8 更新demo 2023-10-01 13:33:25 +08:00
Kimdiego2098
98597f4726 update demo csproj 2023-10-01 00:33:33 +08:00
Kimdiego2098
e7981f0d8e add EncodingMapper 2023-10-01 00:25:04 +08:00
Kimdiego2098
cf654427c3 更新文档 2023-09-30 23:28:50 +08:00
Kimdiego2098
ff2f628282 默认不启用远程更新 2023-09-30 23:23:44 +08:00
Kimdiego2098
ae818ca265 更新readme 2023-09-30 23:09:48 +08:00
Kimdiego2098
0f2aed458e 同步3.0.0版本代码 2023-09-30 23:05:53 +08:00
Kimdiego2098
d486c44ff6 更新文档 2023-09-26 20:03:58 +08:00
Kimdiego2098
ca7b9980bf 2.1.0.15 2023-09-20 11:50:06 +08:00
Kimdiego2098
3c71e6a8e3 2.1.0.15 2023-09-20 11:47:10 +08:00
Kimdiego2098
542442864c 添加kafka/mq/iotsharp的间隔上传 2023-09-20 11:46:17 +08:00
Diego2098
5edb64fa85 fix:上传设备选择驱动时没有正确刷新 2023-09-18 00:10:02 +08:00
Kimdiego2098
8dc1c898a3 2.1.0.14 2023-09-15 14:02:15 +08:00
Kimdiego2098
1ed35726b0 ExpressionEvaluator改为多实例 2023-09-15 14:01:54 +08:00
Kimdiego2098
27fae9ebaa 2.1.0.13 2023-09-15 13:23:59 +08:00
Kimdiego2098
b103f25c94 add dispose() 2023-09-15 13:23:39 +08:00
Kimdiego2098
abff450274 2.1.0.12 2023-09-15 13:20:29 +08:00
Kimdiego2098
c260736a11 更新多个依赖包版本 2023-09-15 13:19:04 +08:00
Kimdiego2098
166ac2307a 修复因重复注册cancellationToken.Register导致的内存暴涨! 2023-09-15 13:17:36 +08:00
Kimdiego2098
b21a4e1a4d CancellationToken代替CancellationTokenSource传入 2023-09-15 13:16:56 +08:00
Kimdiego2098
f7dc943fa3 2.1.0.11 2023-09-11 09:35:24 +08:00
Kimdiego2098
bfbd2693ec feat:ManageGatewayWorker part 2023-09-11 09:31:26 +08:00
Kimdiego2098
819e71c993 feat:ManageGatewayWorker part 2023-09-11 09:09:48 +08:00
Kimdiego2098
9fd0b489a2 feat:ManageGatewayWorker part 2023-09-11 09:09:03 +08:00
Kimdiego2098
f5fe9f8dae TGAPPInfp更名APPInfo 2023-09-11 08:57:13 +08:00
Kimdiego2098
f9ffc18145 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-09-11 08:54:01 +08:00
Kimdiego2098
08db5b983a feat:ManageGatewayWorker part 2023-09-11 08:53:52 +08:00
Diego2098
5b3b4c8c50 !7 修复无法修改变量值问题
Merge pull request !7 from 如阳如木/master
2023-09-07 10:14:45 +00:00
如阳如木
73f914ffc4 删除&& LastSetValue?.ToString() != data?.ToString()
Signed-off-by: 如阳如木 <970143933@qq.com>
2023-09-07 08:42:49 +00:00
Kimdiego2098
d6bdd73ed6 ManageGatewayConfig.json 2023-09-06 17:29:58 +08:00
Kimdiego2098
7370ee7349 fix:_mqttServer null error 2023-09-06 17:27:36 +08:00
Kimdiego2098
4574596bac 2.1.0.10 2023-09-06 17:16:28 +08:00
Kimdiego2098
4d16855e36 从数据库取原属性 2023-09-06 17:14:24 +08:00
Kimdiego2098
13a0d4d282 更改变量 最近一次值 为 上次值 2023-09-06 16:44:58 +08:00
Kimdiego2098
b9cd06b829 更改 变量最近一次值 为 上次值 2023-09-06 16:44:42 +08:00
Kimdiego2098
5b460e8fa2 2.1.0.9 2023-09-06 16:30:50 +08:00
Kimdiego2098
41087edf17 fix:串口断连/拔出/断电等情况,重新连接 2023-09-06 16:29:48 +08:00
Kimdiego2098
2afcc38e38 feat:ManageGatewayWorker part 2023-09-06 16:15:38 +08:00
Kimdiego2098
e59ccce25f feat:ManageGatewayWorker part 2023-09-06 16:10:29 +08:00
Kimdiego2098
d7425890e8 feat:ManageGatewayWorker part 2023-09-05 23:59:18 +08:00
Kimdiego2098
a989a837fb feat:ManageGatewayWorker part 2023-09-05 23:37:02 +08:00
Kimdiego2098
db1221da50 feat:ManageGatewayWorker part 2023-09-05 23:33:02 +08:00
Kimdiego2098
cf794569ed 2.1.0.8 2023-09-05 09:19:58 +08:00
Kimdiego2098
51e5bbab0d fix:PeriodicTimer dispose 2023-09-05 09:17:26 +08:00
Kimdiego2098
2c197ed2b2 2.1.0.7 2023-09-05 09:06:39 +08:00
Kimdiego2098
d8fc6665b3 fix:error!the stream dispose 2023-09-05 09:04:34 +08:00
Kimdiego2098
c671a79822 Revert "fix:error!the stearm dispose"
This reverts commit a6d99fe227.
2023-09-05 09:03:49 +08:00
Kimdiego2098
9d93ce4c41 feat:mqttBroker part 2023-09-05 08:58:49 +08:00
Kimdiego2098
a6d99fe227 fix:error!the stearm dispose 2023-09-05 08:57:42 +08:00
Diego2098
923b8bca31 feat:mqttBroker part 2023-09-04 22:34:31 +08:00
Diego2098
e2c30d1c88 fix:uploadDeivce复制多个,ID重复问题 2023-09-04 22:11:50 +08:00
Diego2098
b6d9f2a04e 刷新后选择行清空 2023-09-04 22:09:26 +08:00
Diego2098
57306ea664 fix:采集设备线程初始化时更新活跃时间 2023-09-04 20:13:49 +08:00
Diego2098
cd7f3fd02f fix:特殊情况下无法获取程序集文件修改日期,所以去除 页脚-编译时间显示 2023-09-04 19:49:17 +08:00
Kimdiego2098
0482e077a8 fix:sqlsugar json支持类型改为bigstring 2023-09-04 17:09:38 +08:00
Kimdiego2098
5f986a45ca 更新文档 2023-09-04 15:03:03 +08:00
Kimdiego2098
ca7b49c0d5 恢复Sqlite默认数据库 2023-09-02 19:17:32 +08:00
Kimdiego2098
52dd555e6c fix: page取消注入瞬时服务,改为App.GetService 2023-09-02 19:13:57 +08:00
Kimdiego2098
579b1a59f9 feat:mqttBroker part 2023-09-02 13:55:21 +08:00
Kimdiego2098
5299c5c4be 更新文档 2023-08-31 09:17:59 +08:00
Kimdiego2098
f7756bccef 更新 变量管理-上传设备筛选功能 2023-08-31 08:58:30 +08:00
Kimdiego2098
a6b874d160 2.1.0.6 2023-08-30 17:19:18 +08:00
Kimdiego2098
3e5fb3ddcf add windows service create/delete bat 2023-08-30 17:18:35 +08:00
Kimdiego2098
5e6bcb12d3 update statusPage; 2023-08-30 14:12:50 +08:00
Kimdiego2098
14303f1429 默认检测重连频率为10分钟 2023-08-29 17:56:59 +08:00
Kimdiego2098
96711ba022 2.1.0.5 2023-08-29 17:44:28 +08:00
Kimdiego2098
cbfc0fdbdc 添加报文日志入库选项 2023-08-29 17:42:56 +08:00
Kimdiego2098
6e81886c0e 添加 页脚 编译时间显示 2023-08-29 17:01:46 +08:00
Kimdiego2098
2d976bc132 调整导入excel错误提示 2023-08-29 16:45:54 +08:00
Kimdiego2098
57f6a476af update opcdaclient null error 2023-08-29 15:24:29 +08:00
Kimdiego2098
8491ed296e update GetBoolValue 2023-08-29 12:40:21 +08:00
Kimdiego2098
cd1288afdc 调整种子文件,增加Pro版本种子文件录入 2023-08-29 09:42:54 +08:00
Kimdiego2098
ec6c830cb0 更新2.1.0.4 2023-08-28 19:31:00 +08:00
Kimdiego2098
2f86ccc4bf 修复串口基类 缓存包 失效错误;完善modbusrtu长度校验 2023-08-28 19:25:22 +08:00
Kimdiego2098
8ca445aec0 更新touchsocket版本,更新2.1.0.3 2023-08-28 18:12:18 +08:00
Kimdiego2098
1e1f27c8a5 修复浏览大量节点空间时,BrowseNext方法参数releaseContinuationPoints改为false 2023-08-28 17:44:02 +08:00
Kimdiego2098
2b84bde367 修复调试界面-opcuaclient浏览节点展开逻辑错误 2023-08-28 10:51:35 +08:00
Kimdiego2098
b52e58551d 更新小版本2.1.0.2 2023-08-27 17:17:37 +08:00
Kimdiego2098
9aceed00bf 更新dlt645地址说明 2023-08-27 17:16:59 +08:00
Kimdiego2098
58814f7f74 添加日志查询 时间区间条件 2023-08-27 16:58:45 +08:00
Kimdiego2098
6a70ef9f31 2.1.0.1 2023-08-27 16:31:03 +08:00
Kimdiego2098
82cc4ca500 dlt645添加数据标识校验 2023-08-27 16:30:32 +08:00
Kimdiego2098
4567fa04ed 更新dlt645,添加地址/控制码的校验规则 2023-08-27 16:15:51 +08:00
Kimdiego2098
8b98b5d818 更新touchsocket 2023-08-27 15:59:57 +08:00
Kimdiego2098
176d0351af 更新 Opc.Ua Version="1.4.372.56" 2023-08-27 14:33:24 +08:00
Kimdiego2098
d63dc3384b opcda/ua浏览地址空间 初始只加载首层节点 2023-08-27 14:14:47 +08:00
Kimdiego2098
1ccd704e30 opcuaclient浏览地址空间 初始只加载首层节点 2023-08-27 13:06:03 +08:00
Diego2098
f5d23dbe79 update MqttNetLogger 2023-08-26 21:38:14 +08:00
Diego2098
75bfe53ac3 update MqttNetLogger 2023-08-26 20:40:23 +08:00
Diego2098
3308f916dd 添加在线/离线方法参数 2023-08-26 17:18:47 +08:00
Diego2098
e7140279ca 更新opcuaclient 2023-08-26 15:38:22 +08:00
Diego2098
1034719f5e 更新opcuaclient,添加checkDomain属性,去除多余代码,更新SelectEndpoint方法 2023-08-26 15:33:03 +08:00
Kimdiego2098
2c00043a7f 更新文档 2023-08-25 19:49:02 +08:00
Kimdiego2098
65c695d9ce 更新版本2.1.0 2023-08-25 19:45:28 +08:00
Kimdiego2098
57253fe46a 更新解决方案 2023-08-25 19:38:47 +08:00
Kimdiego2098
4e5c443440 更新DLT645文档 2023-08-25 19:33:33 +08:00
Kimdiego2098
0b3b73d8ec 添加DLT645_2007协议插件 2023-08-25 19:33:08 +08:00
Kimdiego2098
921eabc134 调整Test位置 2023-08-25 19:32:27 +08:00
Kimdiego2098
0faa428751 调整非主从协议的状态判断策略;OPCUA去除主动连接 2023-08-25 17:23:00 +08:00
Kimdiego2098
f71a2fdd63 crc校验优化 2023-08-22 17:01:10 +08:00
Kimdiego2098
4eb9ed8aba OPCUAClient添加是否使用SourceTime的选项 2023-08-22 16:15:34 +08:00
Kimdiego2098
d7b549abb8 更新文档 2023-08-22 12:21:45 +08:00
Kimdiego2098
95d723c578 2.0.9.3 2023-08-22 11:31:36 +08:00
Kimdiego2098
2fcd853e86 去除mqtt throw an _MqttConnectingFailedException_.报错信息 2023-08-22 11:26:22 +08:00
Kimdiego2098
07eef7c812 更新批量写入方法,注意rpc入口参数变化,mqtt等RPC接口参数变化 2023-08-22 11:23:03 +08:00
Kimdiego2098
b01e0757fa 修正部分代码格式 2023-08-21 19:59:07 +08:00
Kimdiego2098
32844a20c6 2.0.9.2 2023-08-19 12:08:49 +08:00
Kimdiego2098
5b6532c601 xml 2023-08-19 11:06:05 +08:00
Kimdiego2098
2c5b4b4027 修复TcpClient重复释放导致锁异常 2023-08-19 11:05:29 +08:00
Kimdiego2098
72d7ecf195 mas1.0.2默认中文 2023-08-18 18:36:30 +08:00
Kimdiego2098
2cfa6b4306 更新masa1.0.2 2023-08-18 13:59:17 +08:00
Kimdiego2098
6f6ffde0ab 2.0.9.1 2023-08-18 13:33:45 +08:00
Kimdiego2098
1694739a16 修改锁为EasyLock 2023-08-18 12:07:01 +08:00
Kimdiego2098
95d1e8bfca 代码文件编码统一utf-8 2023-08-18 09:57:03 +08:00
Kimdiego2098
60dec08e3c 更换DEMO Include为PackageReference 2023-08-17 15:30:35 +08:00
Kimdiego2098
a99d71be93 底层驱动添加netstandard输出 2023-08-17 15:24:07 +08:00
Kimdiego2098
f1331b6a0c 2.0.8 2023-08-17 10:58:48 +08:00
Kimdiego2098
10d66b642b 调整readme,方便copy账密 2023-08-17 10:28:47 +08:00
Kimdiego2098
cd2310e4a8 优化变量地址输入分割方法;s7读写字符串添加默认编码 2023-08-17 09:50:10 +08:00
Kimdiego2098
1b399cf6b0 优化s7读写字符串,更改特殊方法为读写共用 2023-08-16 17:20:20 +08:00
Kimdiego2098
877445bc0a 修复写入返回结果时的解析,0XFF为成功 2023-08-16 15:56:25 +08:00
Kimdiego2098
9a5b345bde 修复s7字符串打包读取 2023-08-16 15:47:37 +08:00
Kimdiego2098
fc9e8ea7b3 修复s7字符串读取 2023-08-16 15:35:15 +08:00
Kimdiego2098
32be6fcfc1 主动释放锁 2023-08-16 14:56:47 +08:00
Kimdiego2098
49847236c2 OPCUA死区不再固定传入 2023-08-16 14:21:19 +08:00
Kimdiego2098
d8424443e6 fix: 优化JsonUtils中的获取type语句顺序 2023-08-16 08:59:18 +08:00
Kimdiego2098
f3b571ec3f OPC系列增加导出excel/导入系统双选项 2023-08-15 17:38:40 +08:00
Kimdiego2098
99318bb5d7 OPCUA采集设备写入字符串,不再需要传入双引号 2023-08-15 14:15:39 +08:00
Kimdiego2098
1aa154c9aa 历史数据/报警 线程初始化时不再阻塞 2023-08-15 12:12:00 +08:00
Kimdiego2098
c65d8a445b 更改ItemGroup Condition条件 2023-08-14 17:36:59 +08:00
Kimdiego2098
80f4f85570 传播token;
更新admin解决方案
2023-08-14 16:43:30 +08:00
Kimdiego2098
5beee43a6b 优化适配器解析代码 2023-08-14 14:21:00 +08:00
Kimdiego2098
8d6ae203a0 2.0.6 2023-08-13 16:57:12 +08:00
Kimdiego2098
4353479a5c OPCUACClient订阅初始化添加trycatch 2023-08-13 16:29:59 +08:00
Kimdiego2098
34d7687f9e Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-08-13 15:53:33 +08:00
Kimdiego2098
b1dc3cf4af 更新opcuaclient,初始读取server类型改为动态读取 2023-08-13 15:50:47 +08:00
Diego2098
6a58b95933 更新nuget包 2023-08-12 12:09:50 +08:00
Kimdiego2098
d3badfd02b 修复批量令牌强退失效 2023-08-11 15:15:50 +08:00
Kimdiego2098
0098be057b 中间变量导入时添加标识 2023-08-11 14:15:22 +08:00
Kimdiego2098
6f972aa515 2.0.5 2023-08-10 18:23:40 +08:00
Kimdiego2098
7407ba6313 去除不需要的模板 2023-08-10 16:28:52 +08:00
Kimdiego2098
1c79de207b 修改详情模板 2023-08-10 11:23:18 +08:00
Kimdiego2098
257c79db92 添加自定义详情模板 2023-08-10 10:35:19 +08:00
Kimdiego2098
9d1934a308 底层修改:如果连接端口断开,停止等待返回 2023-08-10 10:35:01 +08:00
Kimdiego2098
d70f959902 上传DTO添加 单位 参数 2023-08-10 09:09:09 +08:00
Kimdiego2098
e4d810222f 调整调试页面 2023-08-09 23:45:53 +08:00
Kimdiego2098
bc1af4ae07 添加单文件发布配置 2023-08-09 15:12:55 +08:00
Kimdiego2098
6e688ef43f 去除页面多余引号 2023-08-09 13:45:27 +08:00
Kimdiego2098
f0fe1b23dc 更新2.0.4 2023-08-09 13:00:10 +08:00
Kimdiego2098
aaf2006401 更新opcua写入 2023-08-09 12:43:05 +08:00
Kimdiego2098
b821e26935 更新opcua调试页面 2023-08-09 12:01:23 +08:00
Kimdiego2098
7ae4287157 更新opcua 2023-08-09 11:47:04 +08:00
Kimdiego2098
c6fcc38a65 修复中间变量导入错误 2023-08-09 09:00:49 +08:00
Kimdiego2098
ab2d5c8853 修正错误注释 2023-08-08 18:09:23 +08:00
Kimdiego2098
5e557ff0bc opcua添加初始化连接错误重试 2023-08-08 18:04:24 +08:00
Kimdiego2098
918ca449a1 更新readme 2023-08-08 16:14:06 +08:00
Kimdiego2098
8e73368008 优化大量设备线程重启的耗时 2023-08-08 14:02:00 +08:00
Kimdiego2098
f3c1faf672 2.0.3 2023-08-08 12:59:14 +08:00
Kimdiego2098
d6df04dd6a 底层部分方法更改 2023-08-08 12:31:42 +08:00
Kimdiego2098
b1b9e51ab6 报警/历史值查询时区转换 2023-08-08 09:05:15 +08:00
Kimdiego2098
e49d4770ac 时间最小最大值时不再转换时区 2023-08-08 08:39:20 +08:00
Kimdiego2098
8fa1075511 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-08-08 08:28:37 +08:00
Kimdiego2098
9a70169b94 默认不启用单用户登录 2023-08-08 08:28:27 +08:00
Diego2098
fefb928237 更新文档 2023-08-07 22:31:51 +08:00
Diego2098
ad7e700d0d 去除DateTimeOffset类型 2023-08-07 22:30:38 +08:00
Kimdiego2098
1699c69147 更新文档 2023-08-07 17:38:53 +08:00
Kimdiego2098
1695f7cece 更新readme 2023-08-07 17:34:32 +08:00
Kimdiego2098
052c27f907 更新文件 2023-08-07 17:24:28 +08:00
Kimdiego2098
dc46c32b30 更新文件 2023-08-07 17:16:53 +08:00
Kimdiego2098
fa63349bb2 更新文件 2023-08-07 16:56:45 +08:00
Kimdiego2098
ffe26448a6 更新文件 2023-08-07 16:33:36 +08:00
Kimdiego2098
4af51e8a84 更新文件 2023-08-07 16:30:23 +08:00
Kimdiego2098
1e453cf5a5 更新文件 2023-08-07 15:46:30 +08:00
Kimdiego2098
591282b87d 更新文件 2023-08-07 15:36:49 +08:00
Kimdiego2098
e87528d520 去除多余解决方案配置 2023-08-07 15:31:22 +08:00
Kimdiego2098
d79eb0411d 添加发布脚本 2023-08-07 15:23:53 +08:00
Kimdiego2098
ac1e0a4cf7 更新readme 2023-08-07 15:18:42 +08:00
Kimdiego2098
9525eab130 更新readme 2023-08-07 15:13:21 +08:00
Kimdiego2098
89b317496c 更新文档 2023-08-07 15:10:42 +08:00
Kimdiego2098
13be91e78b 2.0.0 2023-08-07 15:09:53 +08:00
Kimdiego2098
f68c1437f3 调试更新Blazor代码时不再跳转登录界面 2023-07-25 22:00:27 +08:00
Kimdiego2098
4c64c969bb 取消不必要的错误日志 2023-07-25 20:50:33 +08:00
Kimdiego2098
b4bf3b5138 变量值更新错误提示; 2023-07-25 20:48:17 +08:00
Kimdiego2098
083bc4b400 优化大批量excel变量表导入效率 2023-07-25 18:26:48 +08:00
Kimdiego2098
e8683c5bcc 删除不必要延时 2023-07-25 16:52:56 +08:00
Kimdiego2098
80e0d1de91 发布1.7.6 2023-07-25 14:22:11 +08:00
Kimdiego2098
dbe841037e 优化导入excel效率 2023-07-25 14:21:17 +08:00
Kimdiego2098
bdd537c33c 添加上传插件获取采集设备属性值的快捷方法 2023-07-23 13:44:37 +08:00
Kimdiego2098
c0c3846094 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-07-23 09:59:11 +08:00
Kimdiego2098
9e8710e7d2 线程完成时不再需要全局数据移除,有可能会导致偶发全局数据丢失 2023-07-23 09:58:43 +08:00
Kimdiego2098
475553fdf6 线程完成时不要需要全局数据移除,有可能会导致偶发全局数据丢失 2023-07-23 09:57:03 +08:00
Kimdiego2098
9d570f5b45 更新icon包版本 2023-07-23 00:03:46 +08:00
Kimdiego2098
af7fafd34f 更新icon包版本 2023-07-22 23:59:03 +08:00
Kimdiego2098
d43130f4fc 修改插件日志为自定义日志 2023-07-22 23:34:09 +08:00
Kimdiego2098
7500194620 更新文档 2023-07-22 20:48:29 +08:00
Kimdiego2098
eb27c29144 发布1.7.4 2023-07-22 20:47:26 +08:00
Kimdiego2098
43260b3e24 修正错误名称 2023-07-22 20:32:34 +08:00
Kimdiego2098
f80713f0aa 添加上传插件最后错误原因 2023-07-22 20:10:45 +08:00
Kimdiego2098
0c4bdc7ad1 修复上传设备暂停按钮失效 2023-07-22 19:56:56 +08:00
Kimdiego2098
811cff7bd0 OPCUAServer 数组维度不再写any;rpc写入添加延时 2023-07-22 17:02:44 +08:00
Kimdiego2098
30269aa75c 更改假死检测间隔5分钟 2023-07-22 11:05:56 +08:00
Kimdiego2098
e345ef7083 添加OPCUAServer线程间隔配置项,优化最后一次错误提示 2023-07-22 11:03:56 +08:00
Kimdiego2098
f559c9b8f7 支持OPCUAServer数组类型,添加缓存文件存在判断 2023-07-21 21:28:39 +08:00
Kimdiego2098
f4af0916b2 去除OPCUA写入数组维度校验 2023-07-20 17:37:49 +08:00
Kimdiego2098
f15f14f28d 更新文档 2023-07-20 12:48:13 +08:00
Kimdiego2098
834f44f58d kafka尝试自动加载c库 2023-07-20 12:40:59 +08:00
Kimdiego2098
b36f45dcf4 修复中间变量Rpc写入 2023-07-20 10:55:37 +08:00
Kimdiego2098
11ba21c9a8 ModbusServer添加自定义循环间隔,修复中间变量写入 2023-07-20 10:55:17 +08:00
Kimdiego2098
b045557ce1 更新文档 2023-07-19 18:02:31 +08:00
Kimdiego2098
0dd251a3f6 更新文档 2023-07-19 15:43:28 +08:00
Kimdiego2098
793acb1725 更改左侧菜单为手风琴效果 2023-07-19 15:00:40 +08:00
Kimdiego2098
921243e8bd 更新文档 2023-07-19 14:34:27 +08:00
Kimdiego2098
bd9d7a90d9 修改OPCUAClient心跳频率默认3s 2023-07-18 14:49:27 +08:00
Kimdiego2098
cc444a4cea 更改OPCUAClient心跳频率为30000 2023-07-18 14:15:02 +08:00
Kimdiego2098
38ca1fa168 更新1.7.3版本 2023-07-18 12:16:33 +08:00
Kimdiego2098
7a552b87ec 更新masa 稳定版以及其他包 2023-07-18 12:11:37 +08:00
Kimdiego2098
36923d3190 修复添加订阅时,值死区过滤逻辑 2023-07-18 11:13:21 +08:00
Kimdiego2098
a9d3017123 修复异步锁上下文切换导致OPCUA 心跳事件出错 2023-07-18 09:58:01 +08:00
Kimdiego2098
313acd4976 修复OPCUAClient调试界面重复输出订阅值 2023-07-18 08:51:27 +08:00
Kimdiego2098
a4c91bb268 修改OPCUAClient的心跳频率配置项 2023-07-18 08:48:18 +08:00
Kimdiego2098
f9b566984b 添加重启锁 2023-07-17 21:40:47 +08:00
2248356998 qq.com
8dd261854d Merge branch 'master' of https://gitee.com/diego2098/thingsgateway-docs 2023-07-17 20:59:53 +08:00
2248356998 qq.com
7351e62d87 更新opcua心跳状态日志 2023-07-17 20:59:32 +08:00
Diego2098
0593ae720b 更新文档 2023-07-16 20:46:16 +08:00
Diego2098
a0a7b08e08 更新授权名单 2023-07-16 20:31:56 +08:00
2248356998 qq.com
9a3bc6b8b3 更新文档 2023-07-16 18:27:22 +08:00
2248356998 qq.com
5acae17f71 更新文档 2023-07-16 18:12:55 +08:00
2248356998 qq.com
f1e5b76ef2 更新文档 2023-07-16 18:07:31 +08:00
2248356998 qq.com
53c628fde9 更新文档 2023-07-16 17:49:33 +08:00
2248356998 qq.com
baca0a70c0 更新文档 2023-07-16 17:48:22 +08:00
2248356998 qq.com
3e8d0af404 更新文档 2023-07-16 17:41:52 +08:00
2248356998 qq.com
cf9a91d9d5 更新文档 2023-07-16 17:35:04 +08:00
2248356998 qq.com
02b9e282c6 更新文档地址 2023-07-16 17:32:49 +08:00
2248356998 qq.com
9ce87f235f 迁移文档 2023-07-16 17:28:26 +08:00
2248356998 qq.com
e329bea1b2 冗余设备删除后会导致后台出错 2023-07-16 11:36:45 +08:00
2248356998 qq.com
8086e7b54d 更新readme 2023-07-16 10:28:43 +08:00
2248356998 qq.com
f7a875606e 删除无用属性 2023-07-16 09:40:00 +08:00
2248356998 qq.com
196eaf85f4 修复1.7.0版本修改导致的mqttrpc映射错误 2023-07-15 22:56:28 +08:00
2248356998 qq.com
876a55668e 增加 上传插件的列表分割大小,因为某些情况下传输字节太大会导致失败 2023-07-15 22:42:34 +08:00
2248356998 qq.com
05bd21bdd5 导入提示的当前行显示未初始 2023-07-15 22:35:12 +08:00
2248356998 qq.com
fb51a08cc6 格式化整理 2023-07-15 22:32:54 +08:00
2248356998 qq.com
dd83d7f4d3 插件报文截取前200字符,防止页面渲染过多 2023-07-15 20:04:14 +08:00
2248356998 qq.com
842a56f7ce kafka插件增加超时选项 2023-07-15 17:27:35 +08:00
2248356998 qq.com
9246a6e797 历史服务修复变量在线状态显示错误 2023-07-15 15:46:39 +08:00
2248356998 qq.com
8ad693f717 发布1.7.2版本 2023-07-15 11:10:29 +08:00
2248356998 qq.com
f4c2ee7cc4 优化OPCUA错误提示 2023-07-14 17:33:11 +08:00
2248356998 qq.com
6043441faa OPCUA在取消订阅时应该走读取方法 2023-07-14 17:22:19 +08:00
2248356998 qq.com
4a065c3710 优化导入excel提示 2023-07-14 16:23:42 +08:00
2248356998 qq.com
0ef800bdd7 优化导入excel提示 2023-07-14 16:23:35 +08:00
2248356998 qq.com
56eaa1910d kafka 插件释放时取消事件注册 2023-07-14 14:35:40 +08:00
2248356998 qq.com
201788e286 更改属性说明 2023-07-14 12:23:46 +08:00
2248356998 qq.com
506e0f144f 添加kafka插件null传播 2023-07-14 10:41:04 +08:00
2248356998 qq.com
72f68bfdd9 opcda JValue转object 2023-07-14 10:14:05 +08:00
2248356998 qq.com
2f9869b11d opcua读取值JValue转为object 2023-07-14 10:04:05 +08:00
2248356998 qq.com
8ffcf6498c RabbitMQ,IotSharp添加离线缓存 2023-07-13 19:54:12 +08:00
2248356998 qq.com
d224ae1923 Variable Value数据类型改为object 2023-07-13 18:18:53 +08:00
2248356998 qq.com
fed2063a19 中间变量页种子ID重复 2023-07-13 17:47:22 +08:00
2248356998 qq.com
db2810cdd7 复制设备没有及时刷新缓存 2023-07-13 17:38:45 +08:00
2248356998 qq.com
4f1a6781ef 发布1.7.1 2023-07-13 11:19:17 +08:00
2248356998 qq.com
beffa5d5a4 代码格式化 2023-07-13 11:18:36 +08:00
2248356998 qq.com
7a20f1de07 OPCDA增加数组支持,增加写入动态类型支持 2023-07-13 11:16:31 +08:00
2248356998 qq.com
cd25cf726b 优化OPCUA数组类型转换 2023-07-13 09:02:36 +08:00
2248356998 qq.com
d6b1bc3842 修复blazor界面null错误 2023-07-12 22:12:11 +08:00
2248356998 qq.com
a4385fb9bb 更新ReadMe 2023-07-12 21:33:05 +08:00
2248356998 qq.com
7045f2b8ea 删除重复文件 2023-07-12 21:30:41 +08:00
2248356998 qq.com
07ca1a4de8 更新nuget包 2023-07-12 21:21:50 +08:00
2248356998 qq.com
24f289e692 V1.7.0发布
1、增加采集通道冗余
2、优化多个界面
3、导出导入功能优化
4、增加中间变量
5、OPCUAClient支持动态类型
6、离线缓存多处覆盖,包含上传插件/历史报警/时序库
7、增加通用调试界面,增加s7调试
8、其他优化
2023-07-12 21:16:38 +08:00
2248356998 qq.com
01bcdaae2d 修复AppDataTable清空MForm模型导致筛选列清空的问题 2023-07-10 13:18:14 +08:00
2248356998 qq.com
55890008d1 修复写入s7协议 bit值 偏移错误(以byte数据块为准) 2023-07-08 17:01:44 +08:00
2248356998 qq.com
5ab9b01879 修复读取s7协议 bit值 偏移错误(以byte数据块为准) 2023-07-08 11:44:57 +08:00
2248356998 qq.com
e4abb333b3 默认添加kafka插件 种子数据 2023-07-07 17:13:00 +08:00
2248356998 qq.com
09f476c745 修复布尔量解析时反转字节导致结果值不符的错误 2023-07-06 14:05:09 +08:00
2248356998 qq.com
8806e68dce null传播 2023-07-03 14:13:00 +08:00
2248356998 qq.com
2ef1e25cd8 添加x86架构 2023-07-03 10:48:52 +08:00
2248356998 qq.com
10e7f202aa 补充插件实例内容 2023-07-02 19:50:09 +08:00
2248356998 qq.com
ccd7000c09 blazor组件初始化并行执行,导致sugar单例DB出现线程偶发错误,暂增加copyNew()解决 2023-06-29 16:42:19 +08:00
2248356998 qq.com
8ee7b798cf blazor组件初始化并行执行,导致sugar单例DB出现线程偶发错误,暂增加copyNew()解决 2023-06-29 16:36:03 +08:00
2248356998 qq.com
7733cf5bf0 sqlsugar偶发线程故障,添加copyNew方法 2023-06-28 18:54:45 +08:00
2248356998 qq.com
a05ce86dd7 登录跳转uri优化 2023-06-28 15:47:35 +08:00
2248356998 qq.com
91f51c32e8 更新开源说明 2023-06-28 10:48:33 +08:00
2248356998 qq.com
f910202bba update openapiUser datatableUI 2023-06-26 19:51:02 +08:00
2248356998 qq.com
6d77194a8f css脚本结果不采用 System.Text.Json.JsonSerializer 2023-06-26 19:44:17 +08:00
2248356998 qq.com
9deb89c15f OPCUAClient修复当变量为string类型时的数值过滤失败导致添加订阅失效 2023-06-26 19:43:00 +08:00
2248356998 qq.com
4b62a092b4 停用采集设备时,变量获取运行态出错 2023-06-26 16:43:48 +08:00
2248356998 qq.com
81c8f626f9 登录后跳转原url 2023-06-26 14:46:24 +08:00
2248356998 qq.com
3e846c42fb cookie授权验证失败时返回登录界面 2023-06-26 14:36:41 +08:00
Diego2098
63ad7fd766 更新nuget类库 2023-06-25 18:59:09 +08:00
2248356998 qq.com
9ff1e9aa34 添加报警事件None枚举 2023-06-20 13:10:58 +08:00
2248356998 qq.com
8d162b6f3d 修复cpu核心等于1时报错,修正登录错误提示 2023-06-20 09:09:08 +08:00
Diego2098
9844d10bef 修复modbusServer 初始化错误 2023-06-17 18:14:13 +08:00
2248356998 qq.com
b908fa8489 sugar添加取消令箭 2023-06-15 17:38:36 +08:00
2248356998 qq.com
15a10643a7 增加变量运行态 CollectVariableRuntime 所在设备属性 2023-06-15 16:50:08 +08:00
2248356998 qq.com
299617aca1 parallel.foreach无序体验不好,退回为foreach 2023-06-14 11:04:09 +08:00
2248356998 qq.com
45647d697a update 1.6.1 version 2023-06-13 22:39:47 +08:00
2248356998 qq.com
48f5105d38 update nuget package 2023-06-13 22:39:25 +08:00
2248356998 qq.com
fe1c741d68 update driver messagesui 2023-06-13 22:26:19 +08:00
2248356998 qq.com
fa42cc1f00 并行关闭线程 2023-06-12 14:41:08 +08:00
2248356998 qq.com
42cf5e7a81 添加mqtt/kafka上传内容显示 2023-06-12 14:40:53 +08:00
2248356998 qq.com
47905e1aa1 优化大量变量excel上传的验证过程 2023-06-12 11:35:27 +08:00
2248356998 qq.com
9a8e907df3 更新文档站点地址 2023-06-11 18:37:50 +08:00
2248356998 qq.com
106fe85582 删除docs 2023-06-11 18:22:15 +08:00
2248356998 qq.com
4b3571bd57 更新1.6.0版本 2023-06-11 17:58:57 +08:00
2248356998 qq.com
96b537401a 缓存最大默认2000 2023-06-11 17:56:34 +08:00
2248356998 qq.com
721c9eb057 添加离线缓存大小限制配置 2023-06-11 17:55:03 +08:00
2248356998 qq.com
51701bf6d6 上传插件线程等待时间改为10ms 2023-06-11 17:47:16 +08:00
2248356998 qq.com
dbde68bd56 导入变量优化 2023-06-11 17:46:23 +08:00
2248356998 qq.com
ad2c9f585a 添加mqttClient离线缓存 2023-06-11 17:45:46 +08:00
2248356998 qq.com
562093c468 添加kafka离线缓存 2023-06-11 17:45:27 +08:00
Diego2098
b0295584a3 !6 部分配置会导致SqlServer自动建库失败
Merge pull request !6 from samisgod/master
2023-06-10 06:55:34 +00:00
samisgod
208c54de98 fix db init for SqlServer 2023-06-10 14:51:29 +08:00
2248356998 qq.com
63e2d941a1 增加pwa 2023-06-09 17:53:41 +08:00
2248356998 qq.com
3956838e9c 修复停用验证码时登录失败显示null报错 2023-06-09 17:09:17 +08:00
2248356998 qq.com
abeee58bb0 统一文件编码 2023-06-09 15:04:54 +08:00
2248356998 qq.com
d5b1b49722 update solution folder 2023-06-09 14:30:53 +08:00
2248356998 qq.com
564ed03ff8 upload deviceStatusPage 2023-06-09 10:32:11 +08:00
2248356998 qq.com
70db4c76b4 update deviceStatusPage 2023-06-09 09:49:03 +08:00
2248356998 qq.com
d059f7975b remove dotNET China declaration 2023-06-09 09:10:30 +08:00
2248356998 qq.com
4e74e6dc2d add dotNET China declaration 2023-06-09 09:02:17 +08:00
Diego2098
b6deb96658 重启线程时增加运行状态界面空传播防止报错 2023-06-08 21:35:07 +08:00
2248356998 qq.com
3839e966be add upload plugin code description 2023-06-08 17:47:53 +08:00
2248356998 qq.com
3dd035849c 迁移导入变量功能到驱动调试内 2023-06-08 15:11:58 +08:00
2248356998 qq.com
3d6532b5d6 plugin unload test,but failed 2023-06-08 10:52:39 +08:00
2248356998 qq.com
bf7c175ee7 更改默认api文档为Knife4j 2023-06-07 20:34:31 +08:00
2248356998 qq.com
f84af35ed6 缓存键增加Type-TypeHandle句柄 2023-06-07 18:42:58 +08:00
2248356998 qq.com
99063b3eb1 修改OPCUA证书路径,增加默认接收不收信任证书与其选项;
OPCDA整理;
2023-06-07 18:03:57 +08:00
2248356998 qq.com
3bec18f28d 重启线程WebApi修改 2023-06-07 11:32:18 +08:00
2248356998 qq.com
15de7a7894 添加注释提示 2023-06-07 11:28:11 +08:00
2248356998 qq.com
e20e04e677 GC策略更改,大量变量实例手动清空以快速内存释放 2023-06-07 11:19:24 +08:00
2248356998 qq.com
5fc6ae2835 上传1.5.1 2023-06-06 19:37:04 +08:00
2248356998 qq.com
7d281b8c96 Merge branch 'master' of https://gitee.com/diego2098/ThingsGateway 2023-06-05 08:33:50 +08:00
Diego2098
4880b801a7 S7-TCP连接修复死锁 2023-06-02 22:20:21 +08:00
2248356998 qq.com
74e354456a 增加KINGVIEW 配置 2023-05-29 01:32:37 +08:00
2248356998 qq.com
af2e03aa36 超管用户名称不可更改 2023-05-26 00:14:05 +08:00
2248356998 qq.com
d8fa660ab6 初步添加OPCUAClient调试界面;更新依赖 2023-05-25 23:41:11 +08:00
2248356998 qq.com
1a62d48297 OPCUA安全策略添加全部选项 2023-05-25 00:59:21 +08:00
2248356998 qq.com
7ba01be13d OPCUA安全策略添加全部选项 2023-05-25 00:58:47 +08:00
2248356998 qq.com
1a83d64db7 添加OPCDAClient调试页面 2023-05-25 00:11:00 +08:00
2248356998 qq.com
5b53014c40 修改程序根目录为文件所在目录 2023-05-24 14:37:45 +08:00
2248356998 qq.com
83685340af 默认添加服务守护支持 2023-05-24 14:32:21 +08:00
2248356998 qq.com
31e0cc4dec ModbusServer添加自定义数据类型;修复发布时静态文件路径错误 2023-05-24 12:31:20 +08:00
2248356998 qq.com
56b87fc1f5 Add copyright notices 2023-05-23 23:54:28 +08:00
2248356998 qq.com
6b956a2dd7 update TGTcpClient 2023-05-23 22:42:48 +08:00
2248356998 qq.com
1937623d7d 添加Modbus系列插件调试页面;添加Modbus组包解析缓存超时时间; 2023-05-23 20:50:44 +08:00
2248356998 qq.com
3b60b10945 update HardwareInfoService 2023-05-23 12:04:04 +08:00
2248356998 qq.com
7173acd350 nuget更新 2023-05-23 11:59:28 +08:00
2248356998 qq.com
6310d87338 硬件界面添加限值防报错 2023-05-23 11:46:19 +08:00
2248356998 qq.com
49a1ed7c18 初步添加插件驱动调试页面 2023-05-22 18:50:30 +08:00
2248356998 qq.com
d426e280d9 初步添加插件驱动调试页面 2023-05-22 18:41:09 +08:00
2248356998 qq.com
6154fb29f1 Razor文件格式化清理 2023-05-22 18:40:50 +08:00
2248356998 qq.com
97d48ef9d6 删除调试代码... 2023-05-22 17:09:21 +08:00
2248356998 qq.com
88992625c4 更改变量读取间隔限制最低为10ms 2023-05-22 15:17:22 +08:00
2248356998 qq.com
bc6eb44218 明确System.Management版本 2023-05-22 14:51:02 +08:00
2248356998 qq.com
cf9ccd799d 硬件信息获取添加异常拦截 2023-05-22 14:33:30 +08:00
2248356998 qq.com
ffa0e4e771 update adapter 2023-05-22 12:42:31 +08:00
2248356998 qq.com
60fa9c196c 代码清理 2023-05-21 22:39:33 +08:00
2248356998 qq.com
df860d22fb 优化opcua质量戳提示;添加数据转换基础方法;TCP等待返回时默认断开连接立即返回 2023-05-21 10:51:56 +08:00
2248356998 qq.com
cb46ff326c modbus 组包优化 2023-05-20 21:41:16 +08:00
2248356998 qq.com
f277a853ef 类型更换 2023-05-20 17:13:14 +08:00
2248356998 qq.com
9ae34f67c3 可指定OPCUA节点数据类型 2023-05-20 15:40:17 +08:00
2248356998 qq.com
c9223218cc 更新文档 2023-05-20 13:52:23 +08:00
2248356998 qq.com
c0dd645aba 添加自定义OPCUAServer数据类型 2023-05-20 12:34:06 +08:00
2248356998 qq.com
2e948eb5b6 导入规则优化,主动抛出名称重复错误 2023-05-19 22:55:53 +08:00
2248356998 qq.com
c3276889cf 更改插件时开启刷新设备属性 2023-05-19 22:26:37 +08:00
2248356998 qq.com
a76ca8282d 判断OPCUAServer状态时增加tryCatch 2023-05-19 21:27:20 +08:00
2248356998 qq.com
8ce6b8362f 判断OPCUAServer状态时增加tryCatch 2023-05-19 21:24:56 +08:00
2248356998 qq.com
842fb12f05 优化tcp拆包组包 2023-05-19 20:28:51 +08:00
2248356998 qq.com
d63e1511af 快捷方式null错误 2023-05-19 16:33:57 +08:00
2248356998 qq.com
278783b8e0 更新依赖包 2023-05-19 16:23:21 +08:00
2248356998 qq.com
d24e3c922d UDP通讯优化 2023-05-19 16:21:42 +08:00
2248356998 qq.com
1d02cd2283 添加链路锁 2023-05-19 14:27:47 +08:00
2248356998 qq.com
8edeb82a87 分包数量显示错误 2023-05-19 10:13:48 +08:00
2248356998 qq.com
146e9279de Modbus读写锁更新 2023-05-19 10:05:54 +08:00
2248356998 qq.com
47105f50a9 Modbus读写锁更新 2023-05-19 09:53:56 +08:00
2248356998 qq.com
16c9c80f37 删除sqlsugar旧版本代码,会导致sqlite不兼容 2023-05-19 09:15:32 +08:00
2248356998 qq.com
8e7e4bc95a 适配mysql;修复写入表达式转换;优化web页面写入体验 2023-05-18 23:38:40 +08:00
2248356998 qq.com
0aa3d2f930 数据库初始化修复 2023-05-18 22:24:27 +08:00
2248356998 qq.com
ce77755a1e 数据库连接自动释放 2023-05-18 21:06:06 +08:00
2248356998 qq.com
0f31f20c87 编辑页面添加 变量 允许远程写入选项 2023-05-18 21:03:44 +08:00
2248356998 qq.com
ee6da2aaa5 修复枚举类型在mysql中类型出错的问题 2023-05-18 20:58:14 +08:00
Diego2098
a35f087cd9 修改文件大小限制 2023-05-17 22:29:59 +08:00
Diego2098
6e029b44dd 更新设备读写锁 2023-05-17 22:05:31 +08:00
2248356998 qq.com
973c0cff34 touchSocket修复UDP在windows下重置连接的问题;更新nuget 2023-05-17 16:54:33 +08:00
2248356998 qq.com
2027eea6ac 代码格式清理 2023-05-17 16:49:25 +08:00
2248356998 qq.com
2f43692f33 修复CancellationTokenSource未释放导致Linux下偶发内存问题 2023-05-17 16:44:52 +08:00
2248356998 qq.com
6d24992f88 OPCUAServer节点数据类型增加在读取表达式转换后的判断 2023-05-16 09:05:58 +08:00
Diego2098
b4388a58d6 ModbusClient添加帧前时间 2023-05-15 22:45:55 +08:00
2248356998 qq.com
158aa05fac 修复using作用域导致获取的服务可能被释放的问题 2023-05-15 19:21:35 +08:00
2248356998 qq.com
f2731bf55e nuget更新 2023-05-15 18:55:27 +08:00
2248356998 qq.com
7304e99fce mqttClient同步间隔上传的实体类 2023-05-12 18:22:06 +08:00
2248356998 qq.com
02700b83eb update mqttClient 2023-05-12 16:40:55 +08:00
2248356998 qq.com
676b25acf9 MqttClient增加间隔上传选项;更改线程循环间隔说明定义 2023-05-12 16:31:57 +08:00
Diego2098
556359ea2d update readme 2023-05-10 21:18:43 +08:00
Diego2098
b72923e0f5 增加api控制采集启停等方法 2023-05-10 21:17:34 +08:00
Diego2098
115ac9f75e 提交kafka插件 2023-05-10 21:17:09 +08:00
2248356998 qq.com
32e36f6708 update readme 2023-05-10 17:49:52 +08:00
2248356998 qq.com
d949b7a4f9 共享链路写入变量修复;Nuget更新 2023-05-10 16:33:59 +08:00
2248356998 qq.com
eae1171ff5 更新文档 2023-05-09 17:51:55 +08:00
2248356998 qq.com
76a1b75a51 更新文档 2023-05-09 17:47:22 +08:00
2248356998 qq.com
8882c0daea OPCUAClient/Server 修改证书有效期为100年 2023-05-09 14:37:18 +08:00
2248356998 qq.com
07ebc16d59 添加控制台logo 2023-05-08 18:10:07 +08:00
2248356998 qq.com
0ceb109964 Code Cleanup 2023-05-08 18:03:32 +08:00
2248356998 qq.com
118b0d0038 add GetSciptValue 2023-05-08 17:45:13 +08:00
2248356998 qq.com
5e87067792 modbus rtu 粘包优化 2023-05-08 15:59:35 +08:00
2248356998 qq.com
c946a252e8 modbus rtu报文粘包优化;共享链路切换延时;上传插件添加报文界面 2023-05-08 15:57:33 +08:00
2248356998 qq.com
f9ad2ba1dd plc read with CancellationToken 2023-05-08 13:33:01 +08:00
2248356998 qq.com
0d0ecd33bd 粘包优化 2023-05-08 10:55:16 +08:00
2248356998 qq.com
e4b98fd05b Rpc条件bug修复 2023-05-07 18:06:27 +08:00
2248356998 qq.com
95a5933303 OPCUAServer修复匿名登录 2023-05-07 18:06:09 +08:00
2248356998 qq.com
da3b55fa64 更新文档 2023-05-07 16:56:41 +08:00
2248356998 qq.com
fbbabfb90e 属性顺序调整 2023-05-05 09:46:11 +08:00
2248356998 qq.com
f13da6830d 更新文档 2023-05-04 22:58:36 +08:00
2248356998 qq.com
f560a8e2f8 update CollectDeviceThread 2023-05-04 10:58:45 +08:00
2248356998 qq.com
56f1139c2f 更新文档 2023-05-02 22:11:55 +08:00
2248356998 qq.com
773bdfc1e2 更新1.5.0 2023-05-02 22:06:09 +08:00
2248356998 qq.com
f449666628 1、共享链路支持;
2、设备报文查看;
3、采集线程重构;
2023-05-02 21:58:11 +08:00
2248356998 qq.com
3f282de0ab mqtt重连锁优化 2023-04-27 11:19:32 +08:00
2248356998 qq.com
440dd8d22f 添加常用转换 2023-04-24 17:54:00 +08:00
2248356998 qq.com
dcff9de2f7 masa更新 2023-04-24 11:16:28 +08:00
2248356998 qq.com
a192866543 更新包 2023-04-24 09:26:47 +08:00
Diego2098
10081416de 报警后台服务去除多余接口
Signed-off-by: Diego2098 <2248356998@qq.com>
2023-04-19 00:48:13 +00:00
2248356998 qq.com
e2bed618f9 添加docker文件 2023-04-17 17:39:12 +08:00
2248356998 qq.com
03ab1f3823 单文件发布 2023-04-17 15:13:40 +08:00
2248356998 qq.com
ac8aeb63d9 update SqlSugarConfig 2023-04-17 11:58:41 +08:00
2248356998 qq.com
2e16d822fa 删除其他信息 2023-04-17 11:05:38 +08:00
2248356998 qq.com
e407d873fa 验证码更新修复 2023-04-17 09:05:44 +08:00
2248356998 qq.com
fd712a1dbe 1、字段null约束修改
2、ModbusServer绑定端口优化
2023-04-16 15:02:09 +08:00
2248356998 qq.com
e9028b40ce 更新readme 2023-04-16 13:34:29 +08:00
2248356998 qq.com
c9da3dee7c 更新1.4.0
注意Excel导入已不适用以前版本
1、去除动态更新插件
2、改用MiniExcel,支持动态导入,excel配置更简单
3、优化多处界面与后台逻辑,部分方法规范更名
4、修复外网地址获取错误等
2023-04-15 20:48:56 +08:00
2248356998 qq.com
c8c224e202 修复报警文本逻辑 2023-04-11 13:40:13 +08:00
2248356998 qq.com
f34559daaf 1,修复控制台报错(echarts.js问题,已删除)2,多处Dispose修正 2023-04-07 08:48:34 +08:00
2248356998 qq.com
9fefbf4c27 过滤 2023-04-06 14:46:24 +08:00
2248356998 qq.com
1af9fd73ea 历史数据库选择为sqlite时查询转换日期错误 2023-04-05 18:04:33 +08:00
2248356998 qq.com
75ef394eff 启用开发环境web详细日志,调整App.Razor位置,添加网页ico 2023-04-05 17:14:26 +08:00
2248356998 qq.com
ec6cc2c63e update console/file datetime format 2023-04-05 15:49:00 +08:00
2248356998 qq.com
06bc2e192b 更新readme 2023-04-04 18:41:56 +08:00
2248356998 qq.com
78701ec7c1 😀版本1.3.1 2023-04-04 17:55:52 +08:00
2248356998 qq.com
c925fab7e4 网关软件时间统一UTC 2023-04-04 17:55:15 +08:00
2248356998 qq.com
42fd72c164 update iotSharpClient 2023-04-04 11:07:05 +08:00
2248356998 qq.com
7fd160e1a2 😀 更新1.3.0 2023-04-04 10:15:34 +08:00
2248356998 qq.com
97a0d940eb update iotSharpClient 2023-04-04 09:25:16 +08:00
2248356998 qq.com
efaa099d81 update iotSharpClient 2023-04-04 09:23:22 +08:00
2248356998 qq.com
47864a804b IosSharpClient Rpc优化 2023-04-04 09:22:22 +08:00
2248356998 qq.com
91136c0e43 IotSharp Rpc方法完善 2023-04-04 09:19:48 +08:00
2248356998 qq.com
28c3b1bd61 添加写入多个变量的api方法 2023-04-04 09:18:58 +08:00
2248356998 qq.com
551352bc40 update IotSharpClient 2023-04-03 20:16:38 +08:00
2248356998 qq.com
e73c24c925 更新种子 2023-04-03 19:34:12 +08:00
2248356998 qq.com
7ec4c286cc 添加IotSharp插件 2023-04-03 19:30:52 +08:00
2248356998 qq.com
6705e2ec4b 分页显示令牌 2023-04-03 15:29:35 +08:00
2248356998 qq.com
6f0373063b 后台启动时Furion RootServices NULL值 2023-04-03 15:19:59 +08:00
2248356998 qq.com
f64eef60b5 修复不存在采集设备时,初始化报警/历史服务bug 2023-04-03 14:12:56 +08:00
2248356998 qq.com
89546bf86b 弹窗消息在SignalR订阅方法中需InvokeAsync 2023-04-03 13:35:14 +08:00
2248356998 qq.com
793678feca 修复规范化结果包装2次导致登录返回结果不正确的问题 2023-04-03 12:45:19 +08:00
2248356998 qq.com
923cc3019a 更新演示地址 2023-04-03 10:47:49 +08:00
2248356998 qq.com
10eb98a5f6 readme 2023-04-02 18:12:18 +08:00
2248356998 qq.com
bd9e89d8dd readme 2023-04-02 18:08:56 +08:00
2248356998 qq.com
1926b4ce73 更新readme 2023-04-02 18:05:54 +08:00
2248356998 qq.com
4ef3062d74 更新readme 2023-04-02 18:05:28 +08:00
2248356998 qq.com
abb6e0f60f 更新包 2023-04-02 17:10:32 +08:00
2248356998 qq.com
f204d8d84e 添加注释 2023-04-02 16:59:46 +08:00
2248356998 qq.com
fa301656f1 调整依赖,添加关系图 2023-04-01 17:28:35 +08:00
2248356998 qq.com
7e1221028f 调整依赖,更新版本1.2.1 2023-04-01 15:45:02 +08:00
2248356998 qq.com
41308cb2dd 整理 2023-04-01 13:57:57 +08:00
2248356998 qq.com
130600521c 😀 OPCUAServer支持历史查询数据 2023-03-31 18:32:55 +08:00
2248356998 qq.com
cd57548a48 添加ThingsGateway.Foundation注释 2023-03-31 16:25:33 +08:00
2248356998 qq.com
efacc99f76 硬件信息获取添加延时 2023-03-30 20:51:08 +08:00
2248356998 qq.com
f0d236e172 脚本显示优化 2023-03-30 19:51:38 +08:00
2248356998 qq.com
a8118bd8c6 更新文档 2023-03-30 19:39:35 +08:00
2248356998 qq.com
0e58f2ef53 mqtt/mq上传 添加上传实体自定义脚本 2023-03-30 19:07:18 +08:00
2248356998 qq.com
f4b22b3a0c 表达式整理 2023-03-30 16:23:03 +08:00
2248356998 qq.com
df5bd281c7 更新版本 2023-03-30 14:12:39 +08:00
2248356998 qq.com
a3f23837ce 更新文档 2023-03-30 14:11:16 +08:00
2248356998 qq.com
612d989b97 readme 2023-03-30 14:06:03 +08:00
2248356998 qq.com
42c01ee9a2 更新readme,nuget 2023-03-30 14:05:00 +08:00
2248356998 qq.com
14074db591 删除多余代码 2023-03-30 13:53:57 +08:00
2248356998 qq.com
43dfdd7942 opcua 写入添加用户名日志 2023-03-30 13:32:32 +08:00
2248356998 qq.com
f397b97ccf 去除ua数据类型验证 2023-03-30 13:20:26 +08:00
2248356998 qq.com
95f8716144 更新readme 2023-03-30 13:13:44 +08:00
2248356998 qq.com
17ba472b2e 类命名错误更改 2023-03-30 13:11:46 +08:00
2248356998 qq.com
42d82571ab 发布文件添加 2023-03-30 13:11:43 +08:00
2248356998 qq.com
9119a28141 添加OPCUAServer插件,修复个别bug 2023-03-30 13:10:31 +08:00
Diego2098
a32263d838 1、OPCUAClient修复客户端证书未自动生成
2、修复异步方法错误使用
2023-03-30 00:15:13 +08:00
2248356998 qq.com
208ae2bb88 添加部分代码注释 2023-03-29 16:22:01 +08:00
2248356998 qq.com
4d85462a85 驱动支持主机名称 2023-03-29 11:07:21 +08:00
2248356998 qq.com
f601aa9ca0 修复初始化失败导致的一系列问题 2023-03-29 10:53:42 +08:00
2248356998 qq.com
8aee3ad455 上传插件间隔时间修正 2023-03-28 16:40:00 +08:00
2248356998 qq.com
6a2a1e9561 历史变量enable失效 2023-03-28 16:38:29 +08:00
2248356998 qq.com
5f8786c9dc 历史报表时间格式改为yyyy-MM-dd HH:mm:ss ffffff 2023-03-28 16:31:53 +08:00
2248356998 qq.com
73f1d3eead 采集设备状态判断错误 2023-03-28 16:21:30 +08:00
2248356998 qq.com
2bf21bb3c3 采集设备状态判断错误 2023-03-28 15:55:01 +08:00
2248356998 qq.com
f80f0dbb11 修复历史保存值 频繁时出现值相同的问题 2023-03-28 15:14:30 +08:00
2248356998 qq.com
37518c70c4 opc 活动时间修正 2023-03-28 14:15:14 +08:00
2248356998 qq.com
e5951b5bef 添加写入采集时间的接口,而不是固定DateTime.Now 2023-03-28 14:03:11 +08:00
2248356998 qq.com
ab320bd90b 更新tcpclient 2023-03-28 13:52:33 +08:00
2248356998 qq.com
7bd36b5371 修复linux 分隔符错误问题 2023-03-28 11:48:16 +08:00
2248356998 qq.com
b882b0f2bc 更改插件文件不存在时的日志信息 2023-03-28 10:21:39 +08:00
2248356998 qq.com
38d7ae73cc 更改项目引用结构 2023-03-28 10:21:23 +08:00
2248356998 qq.com
4527c6ee5d swagger文档 2023-03-27 19:01:04 +08:00
Iot边缘设备
85829e70c1 !5 添加部署文档
* 添加部署文档
2023-03-27 10:22:30 +00:00
2248356998 qq.com
256c08d82a 属性/字段缓存获取只包含public 2023-03-27 14:49:09 +08:00
2248356998 qq.com
c2ce03c047 更新masa-1.0.0-preview.10 2023-03-27 14:27:15 +08:00
2248356998 qq.com
f2af19e198 优化变量上传属性页 2023-03-27 12:00:10 +08:00
2248356998 qq.com
930b7c092d 采集设备活动时间应正常刷新 2023-03-27 10:51:54 +08:00
2248356998 qq.com
00757c69c6 添加设备复制功能 2023-03-27 10:42:02 +08:00
2248356998 qq.com
55f267d0fc update code collation 2023-03-27 08:49:10 +08:00
2248356998 qq.com
6b96aff6e8 update code collation 2023-03-26 19:48:08 +08:00
2248356998 qq.com
32b773a8fa 更新nuget包 2023-03-26 18:52:16 +08:00
2248356998 qq.com
03089adad6 发行1.10版本 2023-03-26 18:10:12 +08:00
2248356998 qq.com
4a1fe746ab 实时数据界面优化 2023-03-26 17:39:05 +08:00
2248356998 qq.com
aa52c05d2c 1、采用异步AutoResetEvent,修复多个设备下采集太慢的问题
2、弃用IntelligentConcurrentQueue(touchsocket集成类)
2023-03-26 17:32:29 +08:00
2248356998 qq.com
26407a43e7 1、优化设备状态页,减少signalr刷新所需包大小
2、日志前置等级添加
2023-03-24 18:32:10 +08:00
2248356998 qq.com
a02934bf19 修复导出表格时,null值报错 2023-03-23 22:09:53 +08:00
2248356998 qq.com
09c65fba09 导入错误时,忽略大于10行的错误信息显示 2023-03-23 20:01:19 +08:00
2248356998 qq.com
4305c727d0 更新readme 2023-03-22 15:29:35 +08:00
2248356998 qq.com
188339897f 优化log等级、json配置注解 2023-03-22 15:26:42 +08:00
2248356998 qq.com
4ecff9a707 OPCUA写入测试 2023-03-22 14:06:32 +08:00
2248356998 qq.com
355aed49c6 设备运行状态页面改为扩展 2023-03-22 13:46:23 +08:00
2248356998 qq.com
4717b6b0f0 更新文档 2023-03-22 10:44:44 +08:00
Diego2098
45ebe9048d rbmq插件优化 2023-03-21 23:38:00 +08:00
Diego2098
b2170c49a3 分包失败提示 2023-03-21 23:28:30 +08:00
Diego2098
dc2f4d6115 设备组列宽调整,补充上传设备组 2023-03-21 23:10:59 +08:00
Diego2098
1eb132440f 1、更新DataTypeEnum
2、OPCUA写入时需实际数据类型
2023-03-21 22:47:10 +08:00
Diego2098
a464bbc37a 更新Tests 2023-03-21 22:46:12 +08:00
2248356998 qq.com
ed995697c2 表格无数据时,修改默认宽度 2023-03-21 18:17:18 +08:00
2248356998 qq.com
163cd84c7b S7nuget版本 2023-03-21 15:44:50 +08:00
2248356998 qq.com
293d7cc292 masa nuget版本更改 2023-03-21 14:31:36 +08:00
2248356998 qq.com
5de1b4e74c GC策略修改 2023-03-21 14:07:17 +08:00
2248356998 qq.com
7b474975da 更新文档 2023-03-21 14:03:49 +08:00
2248356998 qq.com
beab51516b 更新文档 2023-03-21 13:56:14 +08:00
2248356998 qq.com
fe8685a50c 修改s7属性注释 2023-03-21 13:51:05 +08:00
2248356998 qq.com
f9af5d0885 更新readme 2023-03-21 13:49:10 +08:00
2248356998 qq.com
e8136a9720 更新S7协议插件 2023-03-21 13:45:58 +08:00
2248356998 qq.com
531e5d4556 修改S7特殊方法 2023-03-21 13:41:32 +08:00
2248356998 qq.com
e66255963a 修改s7 2023-03-21 13:38:43 +08:00
2248356998 qq.com
246aac8ee4 添加西门子S7协议插件 2023-03-21 13:37:17 +08:00
2248356998 qq.com
23cfeff685 版本恢复 2023-03-21 11:45:18 +08:00
2248356998 qq.com
a5e7e0d126 更新nuget,暂缓链路复用功能 2023-03-20 13:34:59 +08:00
2248356998 qq.com
5bebc30ba0 默认不开启转储 2023-03-20 09:22:11 +08:00
2248356998 qq.com
0e7057f5b9 更新opcda文档 2023-03-19 23:18:24 +08:00
2248356998 qq.com
7c6c365ba4 格式清理 2023-03-19 23:12:58 +08:00
2248356998 qq.com
424c9bb0c5 修复null值报错 2023-03-19 23:02:08 +08:00
2248356998 qq.com
9d0f26594c 添加设备组,变量按设备组别搜索 2023-03-19 22:56:02 +08:00
2248356998 qq.com
99c17de079 更新readme,opcda核心库提示 2023-03-19 20:40:54 +08:00
2248356998 qq.com
b1e3dd0af6 尝试修复中文在mac上乱码 2023-03-19 17:29:01 +08:00
2248356998 qq.com
261cb89530 修正中文在mac上乱码 2023-03-19 16:45:14 +08:00
2248356998 qq.com
ff6773ba37 删除未知文件 2023-03-19 16:35:24 +08:00
2248356998 qq.com
bdfbbfcbbd Merge branch 'master' of https://gitee.com/diego2098/ThingsGateway 2023-03-19 16:34:26 +08:00
2248356998 qq.com
0c4cd56758 添加设备组功能 2023-03-19 16:34:15 +08:00
士心
4a36658321 !4 修复MacOS环境下,数据库报错的问题
* 修复macos环境下,数据库报错
2023-03-19 08:28:54 +00:00
2248356998 qq.com
7aae938685 准备更新设备组/变量组功能 2023-03-19 11:49:06 +08:00
2248356998 qq.com
3723401e7a 更新ReadMe 2023-03-18 17:56:18 +08:00
2248356998 qq.com
70631366a9 1、最后校验失败时应该提示日志
2、暂停时设备状态修正
2023-03-18 17:49:08 +08:00
2248356998 qq.com
0e40bbda3e 更新文档 2023-03-18 16:47:29 +08:00
2248356998 qq.com
e9aa475398 ByteBlock实际长度 2023-03-18 16:45:39 +08:00
2248356998 qq.com
8d2a811184 修正串口描述类ToString 2023-03-18 16:32:15 +08:00
2248356998 qq.com
dd7f5b6700 更新文档 2023-03-18 16:23:23 +08:00
2248356998 qq.com
a4f6277737 更新nuget 2023-03-18 16:16:27 +08:00
2248356998 qq.com
c2bfaacbb7 更新readme 2023-03-18 16:15:14 +08:00
2248356998 qq.com
a17cbfa2d4 添加ModbusRtu种子数据 2023-03-18 16:14:37 +08:00
2248356998 qq.com
fb9a101555 添加ModbusRtu插件 2023-03-18 16:14:25 +08:00
2248356998 qq.com
e319cf0200 添加串口基础类 2023-03-18 16:14:08 +08:00
2248356998 qq.com
0a8395ef6a 更新文档 2023-03-17 18:19:14 +08:00
2248356998 qq.com
38df5e01be 更新文档 2023-03-17 17:52:04 +08:00
2248356998 qq.com
ebd891a868 种子数据更改 2023-03-17 17:51:58 +08:00
2248356998 qq.com
4ab2395cbe 采集检测间隔修改 2023-03-17 17:40:34 +08:00
2248356998 qq.com
5f1f989fc9 上传插件添加循环间隔属性 2023-03-17 17:30:06 +08:00
2248356998 qq.com
44b709eee3 添加rbmq插件种子数据 2023-03-17 16:30:02 +08:00
2248356998 qq.com
d0d7726597 rbmq插件文件夹更名 2023-03-17 16:16:23 +08:00
2248356998 qq.com
054c342aeb 优化内存queue,修复mqtt插件可能超出字节限制的情况 2023-03-17 16:04:24 +08:00
2248356998 qq.com
c79c33baf7 添加rbmq插件 2023-03-17 16:03:44 +08:00
2248356998 qq.com
23b00e35b2 设备禁用时,变量绑定的设备对应选项会显示禁用(灰色) 2023-03-17 11:29:14 +08:00
2248356998 qq.com
fe51079266 更新readme 2023-03-16 17:33:25 +08:00
2248356998 qq.com
0791b0bbee 选择插件时自动更新属性 2023-03-16 17:33:17 +08:00
2248356998 qq.com
dbf04c8eeb 更新赞助名单 2023-03-16 11:35:58 +08:00
2248356998 qq.com
6204256df8 限流服务默认不开启 2023-03-16 09:47:20 +08:00
2248356998 qq.com
93cc8c2327 增加简易定时看板 2023-03-15 16:01:54 +08:00
2248356998 qq.com
68a2e5bbbc 更新历史配置说明 2023-03-15 09:11:33 +08:00
Diego2098
72792153f2 !3 update handbook/docs/05、网关配置/5.5、其他配置.mdx.
Merge pull request !3 from zhubanghao/N/A
2023-03-14 13:39:16 +00:00
zhubanghao
88b6ef1897 update handbook/docs/05、网关配置/5.5、其他配置.mdx.
Signed-off-by: zhubanghao <58813184@qq.com>
2023-03-14 13:05:33 +00:00
2328 changed files with 186285 additions and 92250 deletions

9
.gitignore vendored
View File

@@ -362,8 +362,7 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/src/ThingsGateway.Web.Server/Default.db
/src/PluginPro*/
/src/*Pro*
/src/TestResults*/
/src/ThingsGateway.Web.Server/ThingsGateway.db
/framework/*pro*
/framework/*Pro*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

View File

@@ -1,3 +1,4 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -186,16 +187,16 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2023-present Diego
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,78 +1,38 @@
<div align="center"><h1 align="center">ThingsGateway</a></h1></div>
<div align="center"><h3 align="center">边缘采集网关</h3></div>
#### 介绍
# ThingsGateway
基于[ThingsBlazor](https://gitee.com/diego2098/ThingsBlazor)权限管理框架开发的跨平台边缘采集网关,支持南北端插件式开发,
动态更新插件,
并拥有较完善的北端Rpc权限管理。
## 介绍
**NetCore** 跨平台边缘采集网关(工业设备采集)
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
#### 功能亮点
## 文档
- Blazor Server架构开发部署更简单
- 采集/上传配置完全支持Excel导入导出
- 插件式驱动,方便驱动二次开发,并支持动态更新
- Modbus、OPCDA、OPCUA采集插件支持导入节点
- Modbus Server、Mqtt Server(Client)、OPC UAServer插件,支持Rpc写入支持WebApi
- 支持时序数据库存储
- 实时/历史报警(Sql转储),支持布尔/高低限值
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
#### nuget
## 协议
- Modbus库支持ModbusTcp、ModbusRtuOverTcp、ModbusUdp、ModbusServer等
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.Modbus
```
- OPCDA客户端库支持X64支持NetCore支持检测重连
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.OPCDA
```
- OPCUA客户端库
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.OPCUA
```
#### 效果图
<table>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/1.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/2.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/3.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/4.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/5.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/6.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/7.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/8.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/9.png"/></td>
</tr>
</table>
[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/)
使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway/)
账户 : **superAdmin**
#### 补充说明
* 使用OPC相关插件时请遵循OPC基金会的授权规则
密码 : **111111**
#### 开源协议
## 赞助
请仔细阅读 [授权协议](https://diego2098.gitee.io/thingsgateway/docs/)
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
## 社区
QQ群605534569
#### 支持作者
如果对您有帮助请点击右上角⭐Star关注感谢支持开源
若希望捐赠项目请跳转到文档站点查看捐赠码或使用Gitee捐赠功能
#### 联系作者
* QQ群605534569
* 邮箱2248356998@qq.com

280
framework/ThingsGateway.sln Normal file
View File

@@ -0,0 +1,280 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33927.249
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "foundation\ThingsGateway.foundation\ThingsGateway.Foundation.csproj", "{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{0874CBC5-C583-4FAD-BA93-94571D446898}"
ProjectSection(SolutionItems) = preProject
foundation\Directory.Build.props = foundation\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.DLT645", "foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj", "{92963877-0185-45A5-A0EB-CEC0D55FF9B0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Modbus", "foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj", "{24510CCE-0B60-4A03-9719-CF367C3528E9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCDA", "foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj", "{566783A4-222B-46F5-AA12-0753997B3254}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCUA", "foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj", "{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Siemens", "foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj", "{9695B353-D773-40DD-B65E-7B10EB0C16EC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}"
ProjectSection(SolutionItems) = preProject
demo\Directory.Build.props = demo\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Photino", "demo\ThingsGateway.Foundation.Demo.Photino\ThingsGateway.Foundation.Demo.Photino.csproj", "{2192217C-CF77-422E-9E63-DF4003ABF01A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Rcl", "demo\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj", "{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Winform", "demo\ThingsGateway.Foundation.Demo.Winform\ThingsGateway.Foundation.Demo.Winform.csproj", "{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gateway", "gateway", "{DFB97103-FC3A-4DC3-A327-DA8C65BDA607}"
ProjectSection(SolutionItems) = preProject
gateway\Directory.Build.props = gateway\Directory.Build.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{4E66C22C-0636-4949-BF6A-9E3BBE1550BA}"
ProjectSection(SolutionItems) = preProject
admin\Directory.Build.props = admin\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "admin\ThingsGateway.Components\ThingsGateway.Components.csproj", "{0A891D8E-23B3-46AD-8D30-565EE5004F93}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "admin\ThingsGateway.Core\ThingsGateway.Core.csproj", "{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "admin\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{5DA3D2BD-6768-4479-B52F-49E022EFF310}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "admin\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "admin\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{D6685A42-2712-417A-92C5-5EFF90B9FA94}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "admin\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Core", "gateway\ThingsGateway.Gateway.Core\ThingsGateway.Gateway.Core.csproj", "{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Blazor", "gateway\ThingsGateway.Gateway.Blazor\ThingsGateway.Gateway.Blazor.csproj", "{69EA3D89-CB40-425A-8D70-5E4A33337BE5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{90AB5C24-1AA3-4F58-9987-B307B92B5193}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.ApiController", "gateway\ThingsGateway.Gateway.ApiController\ThingsGateway.Gateway.ApiController.csproj", "{4C7A5A90-8292-413B-8848-419D9DCDE66F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web", "web", "{F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}"
ProjectSection(SolutionItems) = preProject
web\Directory.Build.props = web\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{D37EC028-EA46-4510-8261-6E780A906314}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{C5F662EB-991F-438D-BF61-EF87E7371C04}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugin", "plugin", "{E65490B8-D2E2-4693-B39C-15703B1EBFBB}"
ProjectSection(SolutionItems) = preProject
plugin\Directory.Build.props = plugin\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Modbus", "plugin\ThingsGateway.Plugin.Modbus\ThingsGateway.Plugin.Modbus.csproj", "{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Mqtt", "plugin\ThingsGateway.Plugin.Mqtt\ThingsGateway.Plugin.Mqtt.csproj", "{53C870AE-0249-4485-AE63-F8A16EA4BCB4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{97B23D8B-C6C0-4746-A21F-C7B49354B284}"
ProjectSection(SolutionItems) = preProject
..\.gitignore = ..\.gitignore
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Kafka", "plugin\ThingsGateway.Plugin.Kafka\ThingsGateway.Plugin.Kafka.csproj", "{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLDB", "plugin\ThingsGateway.Plugin.SQLDB\ThingsGateway.Plugin.SQLDB.csproj", "{C102AB3A-377E-4753-AFA7-C13250D7DF00}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.QuestDB", "plugin\ThingsGateway.Plugin.QuestDB\ThingsGateway.Plugin.QuestDB.csproj", "{0DE1D254-0BD2-4D98-B939-5440BE5EB552}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.TDengineDB", "plugin\ThingsGateway.Plugin.TDengineDB\ThingsGateway.Plugin.TDengineDB.csproj", "{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.RabbitMQ", "plugin\ThingsGateway.Plugin.RabbitMQ\ThingsGateway.Plugin.RabbitMQ.csproj", "{A8E68E17-4EBF-4E4C-8272-B489329A68BF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Siemens", "plugin\ThingsGateway.Plugin.Siemens\ThingsGateway.Plugin.Siemens.csproj", "{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.DLT645", "plugin\ThingsGateway.Plugin.DLT645\ThingsGateway.Plugin.DLT645.csproj", "{AC4295F2-AB2F-4137-99EF-80FA5C83896B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCDA", "plugin\ThingsGateway.Plugin.OPCDA\ThingsGateway.Plugin.OPCDA.csproj", "{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCUA", "plugin\ThingsGateway.Plugin.OPCUA\ThingsGateway.Plugin.OPCUA.csproj", "{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.Build.0 = Release|Any CPU
{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Release|Any CPU.Build.0 = Release|Any CPU
{24510CCE-0B60-4A03-9719-CF367C3528E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24510CCE-0B60-4A03-9719-CF367C3528E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24510CCE-0B60-4A03-9719-CF367C3528E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24510CCE-0B60-4A03-9719-CF367C3528E9}.Release|Any CPU.Build.0 = Release|Any CPU
{566783A4-222B-46F5-AA12-0753997B3254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{566783A4-222B-46F5-AA12-0753997B3254}.Debug|Any CPU.Build.0 = Debug|Any CPU
{566783A4-222B-46F5-AA12-0753997B3254}.Release|Any CPU.ActiveCfg = Release|Any CPU
{566783A4-222B-46F5-AA12-0753997B3254}.Release|Any CPU.Build.0 = Release|Any CPU
{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Release|Any CPU.Build.0 = Release|Any CPU
{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Release|Any CPU.Build.0 = Release|Any CPU
{2192217C-CF77-422E-9E63-DF4003ABF01A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2192217C-CF77-422E-9E63-DF4003ABF01A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2192217C-CF77-422E-9E63-DF4003ABF01A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2192217C-CF77-422E-9E63-DF4003ABF01A}.Release|Any CPU.Build.0 = Release|Any CPU
{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Release|Any CPU.Build.0 = Release|Any CPU
{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Release|Any CPU.Build.0 = Release|Any CPU
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.Build.0 = Release|Any CPU
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.Build.0 = Release|Any CPU
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.Build.0 = Release|Any CPU
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.Build.0 = Release|Any CPU
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.Build.0 = Release|Any CPU
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.Build.0 = Release|Any CPU
{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Release|Any CPU.Build.0 = Release|Any CPU
{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Release|Any CPU.Build.0 = Release|Any CPU
{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Release|Any CPU.Build.0 = Release|Any CPU
{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Release|Any CPU.Build.0 = Release|Any CPU
{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.Build.0 = Release|Any CPU
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.Build.0 = Release|Any CPU
{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Release|Any CPU.Build.0 = Release|Any CPU
{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Release|Any CPU.Build.0 = Release|Any CPU
{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Release|Any CPU.Build.0 = Release|Any CPU
{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Release|Any CPU.Build.0 = Release|Any CPU
{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Release|Any CPU.Build.0 = Release|Any CPU
{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Release|Any CPU.Build.0 = Release|Any CPU
{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Release|Any CPU.Build.0 = Release|Any CPU
{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Release|Any CPU.Build.0 = Release|Any CPU
{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Release|Any CPU.Build.0 = Release|Any CPU
{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Release|Any CPU.Build.0 = Release|Any CPU
{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2070116F-7A1B-47E2-A2F2-7BEC29ECE891} = {0874CBC5-C583-4FAD-BA93-94571D446898}
{92963877-0185-45A5-A0EB-CEC0D55FF9B0} = {0874CBC5-C583-4FAD-BA93-94571D446898}
{24510CCE-0B60-4A03-9719-CF367C3528E9} = {0874CBC5-C583-4FAD-BA93-94571D446898}
{566783A4-222B-46F5-AA12-0753997B3254} = {0874CBC5-C583-4FAD-BA93-94571D446898}
{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF} = {0874CBC5-C583-4FAD-BA93-94571D446898}
{9695B353-D773-40DD-B65E-7B10EB0C16EC} = {0874CBC5-C583-4FAD-BA93-94571D446898}
{2192217C-CF77-422E-9E63-DF4003ABF01A} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}
{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}
{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}
{0A891D8E-23B3-46AD-8D30-565EE5004F93} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
{5DA3D2BD-6768-4479-B52F-49E022EFF310} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
{D6685A42-2712-417A-92C5-5EFF90B9FA94} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
{69EA3D89-CB40-425A-8D70-5E4A33337BE5} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
{90AB5C24-1AA3-4F58-9987-B307B92B5193} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
{4C7A5A90-8292-413B-8848-419D9DCDE66F} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
{D37EC028-EA46-4510-8261-6E780A906314} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
{C5F662EB-991F-438D-BF61-EF87E7371C04} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
{ECF3A7A3-2363-4A38-BC78-8FF9A8564603} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{53C870AE-0249-4485-AE63-F8A16EA4BCB4} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{C102AB3A-377E-4753-AFA7-C13250D7DF00} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{0DE1D254-0BD2-4D98-B939-5440BE5EB552} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{C1911F84-6FDB-4DEC-8938-69B2E3901CC4} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{A8E68E17-4EBF-4E4C-8272-B489329A68BF} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{AC4295F2-AB2F-4137-99EF-80FA5C83896B} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{9DA9AED3-9572-4378-A2A6-4D792D67ADDC} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,25 @@
extensions: .cs
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
extensions: .razor
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@

View File

@@ -0,0 +1,19 @@
<Project>
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFrameworks>net6.0;net8.0;</TargetFrameworks>
<Version>4.0.0.0</Version>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Authors>Diego</Authors>
<Product>ThingsGateway</Product>
<Copyright>© 2023-present Diego</Copyright>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<SignAssembly>True</SignAssembly>
<DelaySign>False</DelaySign>
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,62 @@
#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;
namespace ThingsGateway.Admin.ApiController;
/// <summary>
/// 后台登录控制器
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
[Route("auth/b")]
[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.Logout)]
[Authorize]
public async Task LogoutAsync()
{
await _authService.LogoutAsync();
}
}

View File

@@ -0,0 +1,67 @@
#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("file")]
[LoggingMonitor]
public class FileController : IDynamicApiController
{
private readonly IOperateLogService _operateLogService;
private readonly IVisitLogService _visitLogService;
/// <summary>
/// <inheritdoc cref="FileController"/>
/// </summary>
public FileController(
IOperateLogService operateLogService,
IVisitLogService visitLogService
)
{
_operateLogService = operateLogService;
_visitLogService = visitLogService;
}
/// <summary>
/// 下载操作日志
/// </summary>
/// <returns></returns>
[HttpGet("operateLog")]
public async Task<IActionResult> DownloadOperateLogAsync([FromQuery] OperateLogInput input)
{
var memoryStream = await _operateLogService.ExportFileAsync(input);
var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"operateLog{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx"
};
return data;
}
/// <summary>
/// 下载访问日志
/// </summary>
/// <returns></returns>
[HttpGet("visitLog")]
public async Task<IActionResult> DownloadVisitLogAsync([FromQuery] VisitLogInput input)
{
var memoryStream = await _visitLogService.ExportFileAsync(input);
var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"operateLog{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx"
};
return data;
}
}

View File

@@ -0,0 +1,63 @@
#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;
namespace ThingsGateway.Admin.ApiController;
/// <summary>
/// OpenApi登录控制器
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayOpenApi, Order = 200)]
[Route("auth/openapi")]
[LoggingMonitor]
[Description("OpenApi登录")]
[Authorize(AuthenticationSchemes = "Bearer")]
public class OpenApiAuthController : IDynamicApiController
{
private readonly IOpenApiAuthService _authService;
/// <summary>
/// <inheritdoc cref="OpenApiAuthController"/>
/// </summary>
/// <param name="authService"></param>
public OpenApiAuthController(IOpenApiAuthService authService)
{
_authService = authService;
}
/// <summary>
/// OpenApi登录
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost("login")]
[Description(EventSubscriberConst.LoginOpenApi)]
public async Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input)
{
return await _authService.LoginOpenApiAsync(input);
}
/// <summary>
/// 登出
/// </summary>
/// <returns></returns>
[HttpPost("logout")]
[Description(EventSubscriberConst.LogoutOpenApi)]
public async Task LogoutAsync()
{
await _authService.LogoutAsync();
}
}

View File

@@ -0,0 +1,69 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.SpecificationDocument;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Admin.ApiController;
/// <summary>
/// Swagger登录授权服务
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
[Route("Swagger")]
public class SwaggerController : IDynamicApiController, IScoped
{
private readonly ConfigService _configService;
/// <summary>
/// <inheritdoc cref="SwaggerController"/>
/// </summary>
/// <param name="sysConfigService"></param>
public SwaggerController(ConfigService sysConfigService)
{
_configService = sysConfigService;
}
/// <summary>
/// Swagger登录检查
/// </summary>
/// <returns></returns>
[HttpPost("CheckUrl")]
[AllowAnonymous, NonUnify]
public async Task<int> SwaggerCheckUrlAsync()
{
var enable = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGERLOGIN_OPEN)).ConfigValue.ToBool(false);
return enable ? 401 : 200;
}
/// <summary>
/// Swagger登录
/// </summary>
/// <param name="auth"></param>
/// <returns></returns>
[HttpPost("SubmitUrl")]
[AllowAnonymous, NonUnify]
public async Task<int> SwaggerSubmitUrlAsync([FromForm] SpecificationAuth auth)
{
var userName = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGER_NAME)).ConfigValue;
var password = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGER_PASSWORD)).ConfigValue;
if (auth.UserName == userName && auth.Password == password)
{
return 200;
}
return 401;
}
}

View File

@@ -0,0 +1,19 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
global using Furion.DynamicApiController;
global using System;
global using System.Threading.Tasks;
global using ThingsGateway.Admin.Application;
global using ThingsGateway.Foundation.Core;

View File

@@ -0,0 +1,12 @@
<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>

View File

@@ -0,0 +1,102 @@
<?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">
<summary>
后台登出
</summary>
<returns></returns>
</member>
<member name="T:ThingsGateway.Admin.ApiController.FileController">
<summary>
文件下载
</summary>
</member>
<member name="M:ThingsGateway.Admin.ApiController.FileController.#ctor(ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)">
<summary>
<inheritdoc cref="T:ThingsGateway.Admin.ApiController.FileController"/>
</summary>
</member>
<member name="M:ThingsGateway.Admin.ApiController.FileController.DownloadOperateLogAsync(ThingsGateway.Admin.Application.OperateLogInput)">
<summary>
下载操作日志
</summary>
<returns></returns>
</member>
<member name="M:ThingsGateway.Admin.ApiController.FileController.DownloadVisitLogAsync(ThingsGateway.Admin.Application.VisitLogInput)">
<summary>
下载访问日志
</summary>
<returns></returns>
</member>
<member name="T:ThingsGateway.Admin.ApiController.OpenApiAuthController">
<summary>
OpenApi登录控制器
</summary>
</member>
<member name="M:ThingsGateway.Admin.ApiController.OpenApiAuthController.#ctor(ThingsGateway.Admin.Application.IOpenApiAuthService)">
<summary>
<inheritdoc cref="T:ThingsGateway.Admin.ApiController.OpenApiAuthController"/>
</summary>
<param name="authService"></param>
</member>
<member name="M:ThingsGateway.Admin.ApiController.OpenApiAuthController.LoginOpenApiAsync(ThingsGateway.Admin.Application.LoginOpenApiInput)">
<summary>
OpenApi登录
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:ThingsGateway.Admin.ApiController.OpenApiAuthController.LogoutAsync">
<summary>
登出
</summary>
<returns></returns>
</member>
<member name="T:ThingsGateway.Admin.ApiController.SwaggerController">
<summary>
Swagger登录授权服务
</summary>
</member>
<member name="M:ThingsGateway.Admin.ApiController.SwaggerController.#ctor(ThingsGateway.Admin.Application.ConfigService)">
<summary>
<inheritdoc cref="T:ThingsGateway.Admin.ApiController.SwaggerController"/>
</summary>
<param name="sysConfigService"></param>
</member>
<member name="M:ThingsGateway.Admin.ApiController.SwaggerController.SwaggerCheckUrlAsync">
<summary>
Swagger登录检查
</summary>
<returns></returns>
</member>
<member name="M:ThingsGateway.Admin.ApiController.SwaggerController.SwaggerSubmitUrlAsync(Furion.SpecificationDocument.SpecificationAuth)">
<summary>
Swagger登录
</summary>
<param name="auth"></param>
<returns></returns>
</member>
</members>
</doc>

View File

@@ -0,0 +1,43 @@
#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>
[AttributeUsage(AttributeTargets.Method)]
public class OperDescAttribute : Attribute
{
/// <summary>
/// 操作记录标识
/// </summary>
/// <param name="description"></param>
/// <param name="catcategory"></param>
public OperDescAttribute(string description, string catcategory = LogConst.LOG_OPERATE)
{
Description = description;
Catcategory = catcategory;
}
/// <summary>
/// 分类
/// </summary>
public string Catcategory { get; }
/// <summary>
/// 说明
/// </summary>
public string Description { get; }
/// <summary>
/// 记录参数默认true
/// </summary>
public bool IsRecordPar { get; set; } = true;
}

View File

@@ -0,0 +1,224 @@
#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 System.Reflection;
using System.Text;
using ThingsGateway.Foundation.Extension.String;
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 = Target.GetCustomAttribute<OperDescAttribute>(method.ToString(), true);
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);
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);
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)
{
var uaParser = Parser.GetDefault();
clientInfo = uaParser.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.LOG_SUCCESS,
OpIp = App.HttpContext?.Connection?.RemoteIpAddress?.MapToIPv4().ToString(),
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
OpTime = DateTimeExtensions.CurrentDateTime,
OpAccount = UserManager.UserAccount,
ReqUrl = "",
ReqMethod = LogConst.LOG_REQMETHOD,
ResultJson = resultJson,
ClassName = method.ReflectedType.Name,
MethodName = method.Name,
ParamJson = paramJson,
VerificatId = UserManager.VerificatId.ToLong(),
};
//如果异常不为空
if (exception != null)
{
log.ExeStatus = LogConst.LOG_FAIL;//操作状态为失败
log.ExeMessage = exception.Source + ":" + exception.Message + Environment.NewLine + exception.StackTrace;
}
DbContext.Db.CopyNew().InsertableWithAttr(log).ExecuteCommand();//入库
}
}

View File

@@ -0,0 +1,38 @@
#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 AdminConst
{
#region
/// <summary>
/// 禁用操作
/// </summary>
public const string Disable = "禁用";
/// <summary>
/// 启用操作
/// </summary>
public const string Enable = "启用";
/// <summary>
/// 用户授权操作
/// </summary>
public const string GrantRole = "授权";
#endregion
}

View File

@@ -0,0 +1,83 @@
#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>
/// Cache常量
/// </summary>
public class CacheConst
{
/// <summary>
/// 登录验证码缓存Key
/// </summary>
public const string LOGIN_CAPTCHA = "LOGIN_CAPTCHA";
/// <summary>
/// 配置缓存Key
/// </summary>
public const string SYS_CONFIGCATEGORY = "SYS_CONFIGCATEGORY";
#region OpenApi
/// <summary>
/// OpenApi用户表缓存Key
/// </summary>
public const string CACHE_OPENAPIUSER = "CACHE_OPENAPIUSER";
/// <summary>
/// OpenApi关系缓存Key
/// </summary>
public const string CACHE_OPENAPIUSERACCOUNT = "CACHE_OPENAPIUSERACCOUNT";
/// <summary>
/// UserVerificat缓存Key
/// </summary>
public const string CACHE_OPENAPIUSERVERIFICAT = "CACHE_OPENAPIUSERVERIFICAT";
/// <summary>
/// UserVerificat缓存Key
/// </summary>
public const string CACHE_USERVERIFICAT = "CACHE_USERVERIFICAT";
#endregion OpenApi
/// <summary>
/// 用户表缓存Key
/// </summary>
public const string CACHE_SYSUSER = "CACHE_SYSUSER";
/// <summary>
/// 用户表缓存Key
/// </summary>
public const string CAHCE_SYSUSERACCOUNT = "CAHCE_SYSUSERACCOUNT";
/// <summary>
/// 关系表缓存Key
/// </summary>
public const string CACHE_SYSRELATION = "CACHE_SYSRELATION";
/// <summary>
/// 资源表缓存Key
/// </summary>
public const string CACHE_SYSRESOURCE = "CACHE_SYSRESOURCE";
/// <summary>
/// 角色表缓存Key
/// </summary>
public const string CACHE_SYSROLE = "CACHE_SYSROLE";
}

View File

@@ -0,0 +1,61 @@
#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 static class CateGoryConst
{
/// <summary>
/// ThingsGateway.Admin
/// </summary>
public const string ThingsGatewayAdmin = "ThingsGateway.Admin";
/// <summary>
/// ThingsGateway.OpenApi
/// </summary>
public const string ThingsGatewayOpenApi = "ThingsGateway.OpenApi";
#region
/// <summary>
/// 用户主页
/// </summary>
public const string Relation_SYS_USER_DEFAULTRAZOR = "Relation_SYS_USER_DEFAULTRAZOR";
/// <summary>
/// 用户工作台数据
/// </summary>
public const string Relation_SYS_USER_WORKBENCH_DATA = "SYS_USER_WORKBENCH_DATA";
/// <summary>
/// 角色有哪些权限
/// </summary>
public const string Relation_SYS_ROLE_HAS_PERMISSION = "SYS_ROLE_HAS_PERMISSION";
/// <summary>
/// 角色有哪些资源
/// </summary>
public const string Relation_SYS_ROLE_HAS_RESOURCE = "SYS_ROLE_HAS_RESOURCE";
/// <summary>
/// 用户有哪些角色
/// </summary>
public const string Relation_SYS_USER_HAS_ROLE = "SYS_USER_HAS_ROLE";
#endregion
}

View File

@@ -0,0 +1,96 @@
#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 static class ConfigConst
{
/// <summary>
/// 系统固定配置
/// </summary>
public const string SYS_CONFIGBASEDEFAULT = "SYS_CONFIGBASEDEFAULT";
/// <summary>
/// 其他自定义配置
/// </summary>
public const string SYS_CONFIGOTHER = "SYS_CONFIGOTHER";
#region config
/// <summary>
/// 版权标识
/// </summary>
public const string CONFIG_COPYRIGHT = "CONFIG_COPYRIGHT";
/// <summary>
/// 版权跳转url
/// </summary>
public const string CONFIG_COPYRIGHT_URL = "CONFIG_COPYRIGHT_URL";
/// <summary>
/// 是否启用PageTab
/// </summary>
public const string CONFIG_PAGETAB = "CONFIG_PAGETAB";
/// <summary>
/// 登录验证码开关
/// </summary>
public const string CONFIG_CAPTCHA_OPEN = "CONFIG_CAPTCHA_OPEN";
/// <summary>
/// 默认用户密码
/// </summary>
public const string CONFIG_PASSWORD = "CONFIG_PASSWORD";
/// <summary>
/// 登录界面的介绍文本
/// </summary>
public const string CONFIG_REMARK = "CONFIG_REMARK";
/// <summary>
/// 单用户登录开关
/// </summary>
public const string CONFIG_SINGLE_OPEN = "CONFIG_SINGLE_OPEN";
/// <summary>
/// swagger用户
/// </summary>
public const string CONFIG_SWAGGER_NAME = "CONFIG_SWAGGER_NAME";
/// <summary>
/// swagger密码
/// </summary>
public const string CONFIG_SWAGGER_PASSWORD = "CONFIG_SWAGGER_PASSWORD";
/// <summary>
/// 系统标题
/// </summary>
public const string CONFIG_TITLE = "CONFIG_TITLE";
/// <summary>
/// 系统登录过期时间
/// </summary>
public const string CONFIG_VERIFICAT_EXPIRES = "CONFIG_VERIFICAT_EXPIRES";
/// <summary>
/// Swagger是否需要登录
/// </summary>
public const string CONFIG_SWAGGERLOGIN_OPEN = "CONFIG_SWAGGERLOGIN_OPEN";
#endregion
}

View File

@@ -0,0 +1,47 @@
#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 EventSubscriberConst
{
/// <summary>
/// 清除用户缓存
/// </summary>
public const string ClearUserCache = "清除用户缓存";
/// <summary>
/// 页面登录
/// </summary>
public const string Login = "登录";
/// <summary>
/// OpenApi登录
/// </summary>
public const string LoginOpenApi = "OpenApi登录";
/// <summary>
/// 后台登出
/// </summary>
public const string Logout = "退出";
/// <summary>
/// OpenApi登出
/// </summary>
public const string LogoutOpenApi = "OpenApi退出";
}

View File

@@ -0,0 +1,24 @@
#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 HubConst
{
/// <summary>
/// 系统HubUrl
/// </summary>
public const string SysHubUrl = "/hubs/thingsgateway";
}

View File

@@ -0,0 +1,69 @@
#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 LogConst
{
#region
/// <summary>
/// 登录
/// </summary>
public const string LOG_LOGIN = "LOGIN";
/// <summary>
/// 登出
/// </summary>
public const string LOG_LOGOUT = "LOGOUT";
/// <summary>
/// 第三方登录
/// </summary>
public const string LOG_OPENAPILOGIN = "OPENAPILOGIN";
/// <summary>
/// 第三方登出
/// </summary>
public const string LOG_OPENAPILOGOUT = "OPENAPILOGOUT";
/// <summary>
/// 第三方操作来源
/// </summary>
public const string LOG_OPENAPIOPERATE = "OPENAPIOPERATE";
/// <summary>
/// 操作分类
/// </summary>
public const string LOG_OPERATE = "OPERATE";
/// <summary>
/// 内部操作来源
/// </summary>
public const string LOG_REQMETHOD = "BLAZORSERVER";
/// <summary>
/// 操作成功
/// </summary>
public const string LOG_SUCCESS = "SUCCESS";
/// <summary>
/// 操作失败
/// </summary>
public const string LOG_FAIL = "FAIL";
#endregion
}

View File

@@ -0,0 +1,24 @@
#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.Core;
/// <summary>
/// 资源表常量
/// </summary>
public class ResourceConst
{
/// <summary>
/// 系统内置编码
/// </summary>
public const string System = "system";
}

View File

@@ -0,0 +1,44 @@
#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 RoleConst
{
/// <summary>
/// 超级管理员
/// </summary>
public const string SuperAdmin = "superAdmin";
#region
/// <summary>
/// 角色有哪些权限
/// </summary>
public const string Relation_SYS_ROLE_HAS_PERMISSION = "SYS_ROLE_HAS_PERMISSION";
/// <summary>
/// 角色有哪些资源
/// </summary>
public const string Relation_SYS_ROLE_HAS_RESOURCE = "SYS_ROLE_HAS_RESOURCE";
/// <summary>
/// 用户有哪些角色
/// </summary>
public const string Relation_SYS_USER_HAS_ROLE = "SYS_USER_HAS_ROLE";
#endregion
}

View File

@@ -0,0 +1,24 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
global using Furion;
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
global using ThingsGateway.Admin.Core;
global using ThingsGateway.Core;
global using ThingsGateway.Foundation.Core;

View File

@@ -0,0 +1,68 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.Schedule;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IJobPersistence"/>
public class JobPersistence : IJobPersistence
{
private readonly IServiceScope _serviceScope;
/// <inheritdoc/>
public JobPersistence(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
public void Dispose()
{
_serviceScope?.Dispose();
}
/// <inheritdoc/>
public void OnChanged(PersistenceContext context)
{
}
/// <summary>
/// 作业计划初始化通知
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public SchedulerBuilder OnLoading(SchedulerBuilder builder)
{
return builder;
}
/// <inheritdoc/>
public void OnTriggerChanged(PersistenceTriggerContext context)
{
}
/// <summary>
/// 作业调度服务启动时
/// </summary>
/// <returns></returns>
public IEnumerable<SchedulerBuilder> Preload()
{
// 获取所有定义的作业
var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
return allJobs;
}
}

View File

@@ -0,0 +1,32 @@
#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.Schedule;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 清理日志作业任务
/// </summary>
[JobDetail("job_log", Description = "清理访问/操作日志", GroupName = "default", Concurrent = false)]
[Daily(TriggerId = "trigger_log", Description = "清理访问/操作日志", RunOnStart = true)]
public class LogJob : IJob
{
/// <inheritdoc/>
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
var db = DbContext.Db.CopyNew();
var daysAgo = App.GetConfig<int?>("Logging:LogJob:DaysAgo") ?? 30;
await db.DeleteableWithAttr<SysVisitLog>().Where(u => u.CreateTime < DateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除访问日志
await db.DeleteableWithAttr<SysOperateLog>().Where(u => u.CreateTime < DateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除操作日志
}
}

View File

@@ -0,0 +1,49 @@
#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 LoginOpenApiEvent
{
/// <summary>
/// 时间
/// </summary>
public DateTime DateTime = DateTimeExtensions.CurrentDateTime;
/// <summary>
/// 登录设备
/// </summary>
public AuthDeviceTypeEnum Device { get; set; }
/// <summary>
/// 过期时间(分)
/// </summary>
public int Expire { get; set; }
/// <summary>
/// Ip地址
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 用户信息
/// </summary>
public OpenApiUser OpenApiUser { get; set; }
/// <summary>
/// 验证Id
/// </summary>
public long VerificatId { get; set; }
}

View File

@@ -0,0 +1,52 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.EventBus;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 认证模块事件总线
/// </summary>
public class OpenApiAuthEventSubscriber : IEventSubscriber, ISingleton
{
/// <summary>
/// 登录事件
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
[EventSubscribe(EventSubscriberConst.LoginOpenApi)]
public async Task LoginOpenApi(EventHandlerExecutingContext context)
{
LoginOpenApiEvent loginEvent = (LoginOpenApiEvent)context.Source.Payload;//获取参数
OpenApiUser openApiUser = loginEvent.OpenApiUser;
var db = DbContext.Db.CopyNew();
#region ,
db.Tracking(openApiUser);//创建跟踪,只更新修改字段
openApiUser.LastLoginDevice = openApiUser.LatestLoginDevice;
openApiUser.LastLoginIp = openApiUser.LatestLoginIp;
openApiUser.LastLoginTime = openApiUser.LatestLoginTime;
openApiUser.LatestLoginDevice = loginEvent.Device.ToString();
openApiUser.LatestLoginIp = loginEvent.Ip;
openApiUser.LatestLoginTime = loginEvent.DateTime;
#endregion ,
//更新用户信息
if (await db.UpdateableWithAttr(openApiUser).ExecuteCommandAsync() > 0)
{
App.GetService<MemoryCache>().Set(CacheConst.CACHE_OPENAPIUSER + openApiUser.Id, openApiUser, false); //更新Cache信息
}
}
}

View File

@@ -0,0 +1,33 @@
#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.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 登录输入参数
/// </summary>
public class LoginOpenApiInput
{
/// <summary>
/// 账号
///</summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public string Account { get; set; }
/// <summary>
/// 密码
///</summary>
[Required(ErrorMessage = "密码不能为空"), MinLength(3, ErrorMessage = "密码不能少于3个字符")]
public string Password { get; set; }
}

View File

@@ -0,0 +1,24 @@
#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 LoginOpenApiOutput : BaseLoginOutput
{
/// <summary>
/// TOKEN
/// </summary>
public string Token { get; set; }
}

View File

@@ -0,0 +1,33 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 登录服务
/// </summary>
public interface IOpenApiAuthService : ITransient
{
/// <summary>
/// 登录
/// </summary>
/// <param name="input">登录参数</param>
/// <returns>Token信息</returns>
Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input);
/// <summary>
/// 登出
/// </summary>
/// <returns></returns>
Task LogoutAsync();
}

View File

@@ -0,0 +1,172 @@
#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.DataEncryption;
using Furion.DependencyInjection;
using Furion.EventBus;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using ThingsGateway.Foundation.Extension.String;
using Yitter.IdGenerator;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IOpenApiAuthService"/>
public class OpenApiAuthService : IOpenApiAuthService, ITransient
{
private readonly IConfigService _configService;
private readonly IEventPublisher _eventPublisher;
private readonly IOpenApiUserService _openApiUserService;
private readonly IVerificatService _verificatService;
/// <inheritdoc cref="IOpenApiAuthService"/>
public OpenApiAuthService(
IEventPublisher eventPublisher,
IOpenApiUserService openApiUserService,
IVerificatService verificatService,
IConfigService configService)
{
_verificatService = verificatService;
_eventPublisher = eventPublisher;
_openApiUserService = openApiUserService;
_configService = configService;
}
/// <inheritdoc/>
public async Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input)
{
var password = input.Password;
var userInfo = await _openApiUserService.GetUserByAccountAsync(input.Account);//获取用户信息
if (userInfo == null) throw Oops.Bah("用户不存在");//用户不存在
if (userInfo.Password != password) throw Oops.Bah("账号密码错误");//账号密码错误
return await PrivateLoginOpenApiAsync(userInfo);
}
/// <inheritdoc/>
public async Task LogoutAsync()
{
//获取用户信息
var userinfo = await _openApiUserService.GetUserByAccountAsync(UserManager.UserAccount);
if (userinfo != null)
{
LoginOpenApiEvent loginEvent = new()
{
Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
OpenApiUser = userinfo,
VerificatId = UserManager.VerificatId.ToLong(),
};
await RemoveVerificatFromCacheAsync(loginEvent);
}
}
private async Task<List<VerificatInfo>> GetVerificatInfos(long userId)
{
List<VerificatInfo> verificatInfos = await _verificatService.GetOpenApiVerificatIdAsync(userId);
return verificatInfos;
}
private async Task<LoginOpenApiOutput> PrivateLoginOpenApiAsync(OpenApiUser openApiUser)
{
if (openApiUser.UserEnable == false) throw Oops.Bah("账号已停用");//账号冻结
var sessionid = YitIdHelper.NextId();
var expire = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_VERIFICAT_EXPIRES)).ConfigValue.ToInt();
//生成Token
var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
{
{ClaimConst.UserId, openApiUser.Id},
{ClaimConst.Account, openApiUser.Account},
{ ClaimConst.VerificatId, sessionid.ToString()},
{ ClaimConst.IsOpenApi, true},
}, expire);
// 生成刷新Token令牌
var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, expire * 2);
// 设置Swagger自动登录
App.HttpContext.SigninToSwagger(accessToken);
// 设置响应报文头
App.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);
//登录事件参数
var logingEvent = new LoginOpenApiEvent
{
Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
Device = AuthDeviceTypeEnum.Api,
Expire = expire,
OpenApiUser = openApiUser,
VerificatId = sessionid
};
await WriteVerificatToCacheAsync(logingEvent);//写入verificat到cache
await _eventPublisher.PublishAsync(EventSubscriberConst.LoginOpenApi, logingEvent); //发布登录事件总线
//返回结果
return new LoginOpenApiOutput { VerificatId = sessionid, Token = accessToken, Account = openApiUser.Account };
}
private async Task RemoveVerificatFromCacheAsync(LoginOpenApiEvent loginEvent)
{
//获取verificat列表
var verificatInfos = await GetVerificatInfos(loginEvent.OpenApiUser.Id);
if (verificatInfos != null)
{
//获取当前用户的verificat
var verificat = verificatInfos.Where(it => it.Id == loginEvent.VerificatId).FirstOrDefault();
if (verificat != null)
verificatInfos.Remove(verificat);
//更新verificat列表
await _verificatService.SetOpenApiVerificatIdAsync(loginEvent.OpenApiUser.Id, verificatInfos);
}
await App.HttpContext?.SignOutAsync();
App.HttpContext?.SignoutToSwagger();
}
private async Task WriteVerificatToCacheAsync(LoginOpenApiEvent loginEvent)
{
//获取verificat列表
List<VerificatInfo> verificatInfos = await GetVerificatInfos(loginEvent.OpenApiUser.Id);
var verificatTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
//生成verificat信息
var verificatInfo = new VerificatInfo
{
Device = loginEvent.Device.ToString(),
Expire = loginEvent.Expire,
VerificatTimeout = verificatTimeout,
Id = loginEvent.VerificatId,
UserId = loginEvent.OpenApiUser.Id,
};
if (verificatInfos != null)
{
bool isSingle = false;//默认不开启单用户登录
var singleConfig = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SINGLE_OPEN);//获取系统单用户登录选项
if (singleConfig != null) isSingle = singleConfig.ConfigValue.ToBool(false);//如果配置不为空则设置单用户登录选项为系统配置的值
//判断是否单用户登录
if (isSingle)
{
verificatInfos = verificatInfos.ToList();//去掉当前登录类型的verificat
verificatInfos.Add(verificatInfo);//添加到列表
}
else
{
verificatInfos.Add(verificatInfo);
}
}
else
{
verificatInfos = new List<VerificatInfo> { verificatInfo };//直接就一个
}
//添加到verificat列表
await _verificatService.SetOpenApiVerificatIdAsync(loginEvent.OpenApiUser.Id, verificatInfos);
}
}

View File

@@ -0,0 +1,45 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 会话分页查询
/// </summary>
public class OpenApiSessionPageInput : BasePageInput
{
/// <summary>
/// 账号
/// </summary>
[Description("账号")]
public string Account { get; set; }
/// <summary>
/// 最新登录IP
/// </summary>
[Description("最新登录IP")]
public string LatestLoginIp { get; set; }
}
/// <summary>
/// 退出参数
/// </summary>
public class OpenApiExitVerificatInput : BaseIdInput
{
/// <summary>
/// 验证ID列表
/// </summary>
[Required(ErrorMessage = "VerificatIds不能为空")]
public List<long> VerificatIds { get; set; }
}

View File

@@ -0,0 +1,55 @@
#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 OpenApiSessionOutput : PrimaryKeyEntity
{
/// <summary>
/// 账号
///</summary>
[Description("账号")]
[DataTable(Order = 1, IsShow = true, Sortable = true)]
public virtual string Account { get; set; }
/// <summary>
/// 最新登录ip
///</summary>
[Description("最新登录ip")]
[DataTable(Order = 2, IsShow = true, Sortable = true)]
public string LatestLoginIp { get; set; }
/// <summary>
/// 最新登录时间
///</summary>
[Description("最新登录时间")]
[DataTable(Order = 3, IsShow = true, Sortable = true)]
public DateTime? LatestLoginTime { get; set; }
/// <summary>
/// 令牌数量
/// </summary>
[Description("令牌数量")]
[DataTable(Order = 4, IsShow = true, Sortable = false)]
public int VerificatCount { get; set; }
/// <summary>
/// 令牌信息集合
/// </summary>
[Description("令牌列表")]
public List<VerificatInfo> VerificatSignList { get; set; }
}

View File

@@ -0,0 +1,40 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 会话管理服务
/// </summary>
public interface IOpenApiSessionService : ITransient
{
/// <summary>
/// 强退会话
/// </summary>
/// <param name="input">用户ID</param>
Task ExitSessionAsync(long input);
/// <summary>
/// 强退cookie
/// </summary>
/// <param name="input">cookie列表</param>
Task ExitVerificatAsync(OpenApiExitVerificatInput input);
/// <summary>
/// B端会话分页查询
/// </summary>
/// <param name="input">查询参数</param>
/// <returns>B端会话列表</returns>
Task<ISqlSugarPagedList<OpenApiSessionOutput>> PageAsync(OpenApiSessionPageInput input);
}

View File

@@ -0,0 +1,106 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="IOpenApiSessionService"/>
/// </summary>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class OpenApiSessionService : DbRepository<OpenApiUser>, IOpenApiSessionService
{
private readonly IVerificatService _verificatService;
/// <inheritdoc cref="IOpenApiSessionService"/>
public OpenApiSessionService(IVerificatService verificatService)
{
_verificatService = verificatService;
}
/// <inheritdoc/>
[OperDesc("强退OPENAPI会话")]
public async Task ExitSessionAsync(long input)
{
//从列表中删除
await _verificatService.SetOpenApiVerificatIdAsync(input, new());
}
/// <inheritdoc/>
[OperDesc("强退OPENAPI令牌")]
public async Task ExitVerificatAsync(OpenApiExitVerificatInput input)
{
//获取该用户的verificat信息
List<VerificatInfo> verificatInfos = await _verificatService.GetOpenApiVerificatIdAsync(input.Id);
//当前需要踢掉用户的verificat
var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList();
await _verificatService.SetOpenApiVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat
}
/// <summary>
/// 获取verificat剩余时间信息
/// </summary>
/// <param name="verificatInfos">verificat列表</param>
private static void GetVerificatInfos(ref List<VerificatInfo> verificatInfos)
{
verificatInfos = verificatInfos.ToList();
verificatInfos.ForEach(it =>
{
var now = DateTimeExtensions.CurrentDateTime;
it.VerificatRemain = now.GetDiffTime(it.VerificatTimeout);//获取时间差
var verificatSecond = it.VerificatTimeout.AddMinutes(-it.Expire).ToLong();//颁发时间转为时间戳
var timeoutSecond = it.VerificatTimeout.ToLong();//过期时间转为时间戳
});
}
/// <inheritdoc/>
public async Task<ISqlSugarPagedList<OpenApiSessionOutput>> PageAsync(OpenApiSessionPageInput input)
{
var query = Context.Queryable<OpenApiUser>()
.WhereIF(!string.IsNullOrEmpty(input.Account), it => it.Account.Contains(input.Account))//根据账号查询
.WhereIF(!string.IsNullOrEmpty(input.LatestLoginIp), it => it.LatestLoginIp.Contains(input.LatestLoginIp))//根据IP查询
.OrderBy(it => it.LatestLoginTime, OrderByType.Desc)
.Select<OpenApiSessionOutput>()
.Mapper(async it =>
{
var verificatInfos = await _verificatService.GetVerificatIdAsync(it.Id);
if (verificatInfos != null)
{
OpenApiSessionService.GetVerificatInfos(ref verificatInfos);//获取剩余时间
it.VerificatCount = verificatInfos.Count;//令牌数量
it.VerificatSignList = verificatInfos;//令牌列表
}
else
{
it.VerificatSignList = new();
}
});
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
pageInfo.Records = pageInfo.Records.OrderByDescending(it => it.VerificatCount);
return pageInfo;
}
}

View File

@@ -0,0 +1,148 @@
#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 SqlSugar;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// Api授权资源树
/// </summary>
public class OpenApiPermissionTreeSelector
{
/// <summary>
/// 接口描述
/// </summary>
[Description("Api说明")]
public string ApiName { get; set; }
/// <summary>
/// 路由名称
/// </summary>
[Description("Api路径")]
public string ApiRoute { get; set; }
/// <summary>
/// 子节点
/// </summary>
public List<OpenApiPermissionTreeSelector> Children { get; set; } = new();
/// <summary>
/// ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 父ID
/// </summary>
public long ParentId { get; set; }
/// <summary>
/// 权限名称
/// </summary>
[Description("权限名称")]
public string PermissionName { get; set; }
/// <summary>
/// 多个树转列表
/// </summary>
public static List<OpenApiPermissionTreeSelector> TreeToList(IList<OpenApiPermissionTreeSelector> data)
{
List<OpenApiPermissionTreeSelector> list = new();
foreach (var item in data)
{
list.Add(item);
if (item.Children != null && item.Children.Count > 0)
{
list.AddRange(TreeToList(item.Children));
}
}
return list;
}
}
/// <summary>
/// 添加用户参数
/// </summary>
public class OpenApiUserAddInput : OpenApiUser
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
/// <summary>
/// 密码
/// </summary>
[Required(ErrorMessage = "密码不能为空"), MinLength(2, ErrorMessage = "密码不能少于3个字符")]
public override string Password { get; set; }
/// <summary>
/// <inheritdoc/>
/// </summary>
public override bool UserEnable { get; set; } = true;
}
/// <summary>
/// 编辑用户参数
/// </summary>
public class OpenApiUserEditInput : OpenApiUser
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
/// <summary>
/// Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
/// <summary>
/// 密码
/// </summary>
[Required(ErrorMessage = "密码不能为空"), MinLength(2, ErrorMessage = "密码不能少于3个字符")]
public override string Password { get; set; }
}
/// <summary>
/// 用户分页查询参数
/// </summary>
public class OpenApiUserPageInput : BasePageInput
{
/// <summary>
/// 动态查询条件
/// </summary>
public Expressionable<SysUser> Expression { get; set; }
}
/// <summary>
/// 用户授权参数
/// </summary>
public class OpenApiUserGrantPermissionInput
{
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "Id不能为空")]
public long? Id { get; set; }
/// <summary>
/// 授权权限信息
/// </summary>
[Required(ErrorMessage = "PermissionList不能为空")]
public List<string> PermissionList { get; set; }
}

View File

@@ -0,0 +1,104 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 用户服务
/// </summary>
public interface IOpenApiUserService : ITransient
{
/// <summary>
/// 添加用户
/// </summary>
/// <param name="input">添加参数</param>
/// <returns></returns>
Task AddAsync(OpenApiUserAddInput input);
/// <summary>
/// 删除用户
/// </summary>
/// <param name="input">Id列表</param>
/// <returns></returns>
Task DeleteAsync(params long[] input);
/// <summary>
/// 从cache中删除用户信息
/// </summary>
/// <param name="ids">用户ID列表</param>
void DeleteUserFromCache(params long[] ids);
/// <summary>
/// 禁用用户
/// </summary>
/// <param name="input">用户Id</param>
/// <returns></returns>
Task DisableUserAsync(long input);
/// <summary>
/// 编辑
/// </summary>
/// <param name="input">编辑参数</param>
/// <returns></returns>
Task EditAsync(OpenApiUserEditInput input);
/// <summary>
/// 启用用户
/// </summary>
/// <param name="input">用户Id</param>
/// <returns></returns>
Task EnableUserAsync(long input);
/// <summary>
///根据用户账号获取用户ID
/// </summary>
/// <param name="account">用户账号</param>
/// <returns></returns>
Task<long> GetIdByAccountAsync(string account);
/// <summary>
/// 根据账号获取用户信息
/// </summary>
/// <param name="account">用户名</param>
/// <returns>用户信息</returns>
Task<OpenApiUser> GetUserByAccountAsync(string account);
/// <summary>
/// 根据ID获取用户信息
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
Task<OpenApiUser> GetUsertByIdAsync(long Id);
/// <summary>
/// 给用户授权
/// </summary>
/// <param name="input">授权参数</param>
/// <returns></returns>
Task GrantRoleAsync(OpenApiUserGrantPermissionInput input);
/// <summary>
/// 获取用户拥有权限,返回的是服务方法名称
/// </summary>
/// <param name="input">用户ID</param>
/// <returns></returns>
Task<List<string>> OwnPermissionsAsync(BaseIdInput input);
/// <summary>
/// 用户分页查询
/// </summary>
/// <param name="input">查询参数</param>
/// <returns>用户分页列表</returns>
Task<ISqlSugarPagedList<OpenApiUser>> PageAsync(OpenApiUserPageInput input);
}

View File

@@ -0,0 +1,284 @@
#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.DataEncryption;
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Mapster;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="IOpenApiUserService"/>
/// </summary>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class OpenApiUserService : DbRepository<OpenApiUser>, IOpenApiUserService
{
private readonly IVerificatService _verificatService;
private readonly IServiceScope _serviceScope;
/// <inheritdoc cref="IOpenApiUserService"/>
public OpenApiUserService(
IVerificatService verificatService, IServiceScopeFactory serviceScopeFactory
)
{
_verificatService = verificatService;
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
[OperDesc("添加用户")]
public async Task AddAsync(OpenApiUserAddInput input)
{
var account_Id = await GetIdByAccountAsync(input.Account);
if (account_Id > 0)
throw Oops.Bah($"存在重复的账号:{input.Account}");
var openApiUser = input.Adapt<OpenApiUser>();//实体转换
await InsertAsync(openApiUser);//添加数据
}
/// <inheritdoc/>
[OperDesc("删除用户")]
public async Task DeleteAsync(params long[] ids)
{
//获取所有ID
if (ids.Length > 0)
{
var result = await DeleteByIdsAsync(ids.Cast<object>().ToArray());
if (result)
{
//从列表中删除
foreach (var id in ids)
{
await _verificatService.SetOpenApiVerificatIdAsync(id, new());
}
DeleteUserFromCache(ids);
}
}
}
/// <inheritdoc />
public void DeleteUserFromCache(params long[] ids)
{
var userIds = ids.Select(it => it.ToString()).ToArray();//id转string列表
List<OpenApiUser> openApiUsers = new();
foreach (var item in userIds)
{
var user = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<OpenApiUser>(CacheConst.CACHE_OPENAPIUSER + item, false);//获取用户列表
openApiUsers.Add(user);
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_OPENAPIUSER + item);
}
openApiUsers = openApiUsers.Where(it => it != null).ToList();//过滤掉不存在的
if (openApiUsers.Count > 0)
{
var accounts = openApiUsers.Select(it => it.Account).ToArray();//账号集合
foreach (var item in accounts)
{
//删除账号
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_OPENAPIUSERACCOUNT + item);
}
}
}
/// <inheritdoc/>
[OperDesc("禁用用户")]
public async Task DisableUserAsync(long input)
{
var openApiUser = await GetUsertByIdAsync(input);//获取用户信息
if (openApiUser != null)
{
if (await UpdateAsync(it => new OpenApiUser { UserEnable = false }, it => it.Id == input))
{
await _verificatService.SetOpenApiVerificatIdAsync(input, new());
DeleteUserFromCache(input);//从cache删除用户信息
}
}
}
/// <inheritdoc/>
[OperDesc("编辑用户")]
public async Task EditAsync(OpenApiUserEditInput input)
{
await CheckInputAsync(input);//检查参数
var exist = await GetUsertByIdAsync(input.Id);//获取用户信息
if (exist != null)
{
var openApiUser = input.Adapt<OpenApiUser>();//实体转换
openApiUser.Password = DESCEncryption.Encrypt(openApiUser.Password, DESCKeyConst.DESCKey);
if (await Context.Updateable(openApiUser).IgnoreColumns(it =>
new
{
//忽略更新字段
it.LastLoginDevice,
it.LastLoginIp,
it.LastLoginTime,
it.LatestLoginDevice,
it.LatestLoginIp,
it.LatestLoginTime
}).ExecuteCommandAsync() > 0)//修改数据
DeleteUserFromCache(openApiUser.Id);//用户缓存到cache
}
//编辑操作可能会修改用户密码等信息,认证时需要实时获取用户并验证
}
/// <inheritdoc/>
[OperDesc("启用用户")]
public async Task EnableUserAsync(long input)
{
//设置状态为启用
if (await UpdateAsync(it => new OpenApiUser { UserEnable = true }, it => it.Id == input))
DeleteUserFromCache(input);//从cache删除用户信息
}
/// <inheritdoc/>
public async Task<long> GetIdByAccountAsync(string account)
{
//先从Cache拿
var userId = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<long>(CacheConst.CACHE_OPENAPIUSERACCOUNT + account, false);
if (userId == 0)
{
//单查获取用户账号对应ID
userId = await GetFirstAsync(it => it.Account == account, it => it.Id);
if (userId != 0)
{
//插入Cache
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_OPENAPIUSERACCOUNT + account, userId, false);
}
}
return userId;
}
/// <inheritdoc/>
public async Task<OpenApiUser> GetUserByAccountAsync(string account)
{
var userId = await GetIdByAccountAsync(account);//获取用户ID
if (userId > 0)
{
var openApiUser = await GetUsertByIdAsync(userId);//获取用户信息
if (openApiUser.Account == account)//这里做了比较用来限制大小写
return openApiUser;
else
return null;
}
else
{
return null;
}
}
/// <inheritdoc/>
public async Task<OpenApiUser> GetUsertByIdAsync(long Id)
{
//先从Cache拿需要获取新的对象避免操作导致缓存中对象改变
var openApiUser = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<OpenApiUser>(CacheConst.CACHE_OPENAPIUSER + Id.ToString(), true);
if (openApiUser == null)
{
openApiUser = await Context.Queryable<OpenApiUser>()
.Where(u => u.Id == Id)
.FirstAsync();
if (openApiUser != null)
{
//插入Cache
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_OPENAPIUSER + openApiUser.Id.ToString(), openApiUser, true);
}
}
return openApiUser;
}
/// <inheritdoc />
[OperDesc("用户授权")]
public async Task GrantRoleAsync(OpenApiUserGrantPermissionInput input)
{
var openApiUser = await GetUsertByIdAsync(input.Id.Value);//获取用户信息
if (openApiUser != null)
{
openApiUser.PermissionCodeList = input.PermissionList;
await CheckInputAsync(openApiUser);
if (await Context.Updateable(openApiUser).IgnoreColumns(it =>
new
{
//忽略更新字段
it.Password,
it.LastLoginDevice,
it.LastLoginIp,
it.LastLoginTime,
it.LatestLoginDevice,
it.LatestLoginIp,
it.LatestLoginTime
}).ExecuteCommandAsync() > 0)//修改数据
DeleteUserFromCache(input.Id.Value);//从cache删除用户信息
}
}
/// <inheritdoc/>
public async Task<List<string>> OwnPermissionsAsync(BaseIdInput input)
{
var openApiUser = await GetUsertByIdAsync(input.Id);//获取用户信息
return openApiUser.PermissionCodeList;
}
/// <inheritdoc/>
public async Task<ISqlSugarPagedList<OpenApiUser>> PageAsync(OpenApiUserPageInput input)
{
var query = Context.Queryable<OpenApiUser>()
.WhereIF(!string.IsNullOrEmpty(input.SearchKey), u => u.Account.Contains(input.SearchKey));//根据关键字查询
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
query = query.OrderBy(it => it.SortCode);//排序
query = query.OrderBy(u => u.Id);//排序
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
return pageInfo;
}
/// <summary>
/// 检查输入参数
/// </summary>
/// <param name="openApiUser"></param>
private async Task CheckInputAsync(OpenApiUser openApiUser)
{
//判断账号重复,直接从cache拿
var account_Id = await GetIdByAccountAsync(openApiUser.Account);
if (account_Id > 0 && account_Id != openApiUser.Id)
throw Oops.Bah($"存在重复的账号:{openApiUser.Account}");
//如果手机号不是空
if (!string.IsNullOrEmpty(openApiUser.Phone))
{
if (!openApiUser.Phone.MatchPhoneNumber())//验证手机格式
throw Oops.Bah($"手机号码:{openApiUser.Phone} 格式错误");
openApiUser.Phone = DESCEncryption.Encrypt(openApiUser.Phone, DESCKeyConst.DESCKey);
}
//如果邮箱不是空
if (!string.IsNullOrEmpty(openApiUser.Email))
{
var ismatch = openApiUser.Email.MatchEmail();//验证邮箱格式
if (!ismatch)
throw Oops.Bah($"邮箱:{openApiUser.Email} 格式错误");
if (await IsAnyAsync(it => it.Email == openApiUser.Email && it.Id != openApiUser.Id))
throw Oops.Bah($"存在重复的邮箱:{openApiUser.Email}");
}
}
}

View File

@@ -0,0 +1,18 @@
{
"RECORDS": [
{
"Id": 444657867911429,
"Category": "SYS_USER_HAS_ROLE",
"ObjectId": 212725263002001,
"TargetId": "212725263001001",
"ExtJson": null
},
{
"Id": 444657879060741,
"Category": "SYS_USER_HAS_ROLE",
"ObjectId": 201725263002001,
"TargetId": "212725263001002",
"ExtJson": null
}
]
}

View File

@@ -0,0 +1,564 @@
{
"RECORDS": [
{
"Id": "100",
"Title": "系统首页",
"Icon": "mdi-home-account",
"Name": "index",
"Component": "/index",
"Category": "SPA",
"Code": "system",
"ParentId": "0",
"SortCode": "1",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100001",
"Title": "权限管理",
"Icon": "mdi-account-hard-hat",
"Category": "MENU",
"Code": "system",
"ParentId": "0",
"SortCode": "4",
"TargetType": "None",
"IsDelete": "false",
"UpdateTime": "2023-02-26 00:55:23.977",
"UpdateUser": "superAdmin",
"UpdateUserId": "212725263002001"
},
{
"Id": "100001001",
"Title": "用户管理",
"Icon": "mdi-account-edit",
"Name": "sysUser",
"Component": "/admin/user",
"Category": "MENU",
"Code": "system",
"ParentId": "100001",
"SortCode": "1",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100001002",
"Title": "角色管理",
"Icon": "mdi-account-hard-hat",
"Name": "sysRole",
"Component": "/admin/role",
"Category": "MENU",
"Code": "system",
"ParentId": "100001",
"SortCode": "2",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100001003",
"Title": "菜单管理",
"Icon": "mdi-menu",
"Name": "sysMenu",
"Component": "/admin/menu",
"Category": "MENU",
"Code": "system",
"ParentId": "100001",
"SortCode": "3",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100002002",
"Title": "访问日志",
"Icon": "mdi-account-switch-outline",
"Name": "sysVislog",
"Component": "/admin/vislog",
"Category": "MENU",
"Code": "system",
"ParentId": "100002",
"SortCode": "2",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100002003",
"Title": "操作日志",
"Icon": "mdi-database-search-outline",
"Name": "sysOplog",
"Component": "/admin/oplog",
"Category": "MENU",
"Code": "system",
"ParentId": "100002",
"SortCode": "3",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100002010",
"Title": "定时看板",
"Icon": "mdi-database-cog-outline",
"Name": "schedulePage",
"Component": "/schedulePage",
"Category": "MENU",
"Code": "system",
"ParentId": "100002",
"SortCode": "4",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100002",
"Title": "系统运维",
"Icon": "mdi-cogs",
"Category": "MENU",
"Code": "system",
"ParentId": "0",
"SortCode": "5",
"TargetType": "None",
"IsDelete": "false",
"UpdateTime": "2023-02-26 00:55:33.503",
"UpdateUser": "superAdmin",
"UpdateUserId": "212725263002001"
},
{
"Id": "100002001",
"Title": "系统配置",
"Icon": "mdi-cog-transfer-outline",
"Name": "sysConfig",
"Component": "/admin/config",
"Category": "MENU",
"Code": "system",
"ParentId": "100002",
"SortCode": "1",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100003",
"Title": "第三方授权",
"Icon": "mdi-transit-transfer",
"Category": "MENU",
"Code": "system",
"ParentId": "0",
"SortCode": "6",
"TargetType": "None",
"IsDelete": "false",
"UpdateTime": "2023-02-26 00:55:29.094",
"UpdateUser": "superAdmin",
"UpdateUserId": "212725263002001"
},
{
"Id": "100003001",
"Title": "令牌列表",
"Icon": "mdi-transit-transfer",
"Name": "sysOpenApiSession",
"Component": "/admin/openapisession",
"Category": "MENU",
"Code": "system",
"ParentId": "100003",
"SortCode": "1",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100003002",
"Title": "授权用户",
"Icon": "mdi-transit-transfer",
"Name": "sysOpenApiUser",
"Component": "/admin/openapiuser",
"Category": "MENU",
"Code": "system",
"ParentId": "100003",
"SortCode": "2",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100003003",
"Title": "接口文档",
"Icon": "mdi-cog-transfer-outline",
"Name": "swaggerUrl",
"Component": "/api/index.html",
"Category": "MENU",
"Code": "system",
"ParentId": "100003",
"SortCode": "3",
"TargetType": "BLANK",
"IsDelete": "false"
},
{
"Id": "100002004",
"Title": "会话管理",
"Icon": "mdi-transit-transfer",
"Name": "session",
"Component": "/admin/session",
"Category": "MENU",
"Code": "system",
"ParentId": "100002",
"SortCode": "4",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100001001001",
"Title": "添加",
"Name": "add",
"Category": "BUTTON",
"Code": "sysuseradd",
"ParentId": "100001001",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001004",
"Title": "单页管理",
"Icon": "mdi-menu",
"Name": "sysSpa",
"Component": "/admin/spa",
"Category": "MENU",
"Code": "system",
"ParentId": "100001",
"SortCode": "4",
"TargetType": "SELF",
"IsDelete": "false"
},
{
"Id": "100001001002",
"Title": "编辑",
"Name": "edit",
"Category": "BUTTON",
"Code": "sysuseredit",
"ParentId": "100001001",
"SortCode": "2",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001001003",
"Title": "删除",
"Name": "delete",
"Category": "BUTTON",
"Code": "sysuserdelete",
"ParentId": "100001001",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001002001",
"Title": "添加",
"Name": "add",
"Category": "BUTTON",
"Code": "sysroleadd",
"ParentId": "100001002",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001002002",
"Title": "编辑",
"Name": "edit",
"Category": "BUTTON",
"Code": "sysroleedit",
"ParentId": "100001002",
"SortCode": "2",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001002003",
"Title": "删除",
"Name": "delete",
"Category": "BUTTON",
"Code": "sysroledelete",
"ParentId": "100001002",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001003001",
"Title": "添加",
"Name": "add",
"Category": "BUTTON",
"Code": "sysmenuadd",
"ParentId": "100001003",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001003002",
"Title": "编辑",
"Name": "edit",
"Category": "BUTTON",
"Code": "sysmenuedit",
"ParentId": "100001003",
"SortCode": "2",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001003003",
"Title": "删除",
"Name": "delete",
"Category": "BUTTON",
"Code": "sysmenudelete",
"ParentId": "100001003",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001004001",
"Title": "添加",
"Name": "add",
"Category": "BUTTON",
"Code": "sysspaadd",
"ParentId": "100001004",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001004002",
"Title": "编辑",
"Name": "edit",
"Category": "BUTTON",
"Code": "sysspaedit",
"ParentId": "100001004",
"SortCode": "2",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001004003",
"Title": "删除",
"Name": "delete",
"Category": "BUTTON",
"Code": "sysspadelete",
"ParentId": "100001004",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100002001001",
"Title": "添加",
"Name": "add",
"Category": "BUTTON",
"Code": "sysconfigadd",
"ParentId": "100002001",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100002001002",
"Title": "编辑",
"Name": "edit",
"Category": "BUTTON",
"Code": "sysconfigedit",
"ParentId": "100002001",
"SortCode": "2",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100002001003",
"Title": "删除",
"Name": "delete",
"Category": "BUTTON",
"Code": "sysconfigdelete",
"ParentId": "100002001",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100003002001",
"Title": "添加",
"Name": "add",
"Category": "BUTTON",
"Code": "openapiuseradd",
"ParentId": "100003002",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100003002002",
"Title": "编辑",
"Name": "edit",
"Category": "BUTTON",
"Code": "openapiuseredit",
"ParentId": "100003002",
"SortCode": "2",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100003002003",
"Title": "删除",
"Name": "delete",
"Category": "BUTTON",
"Code": "openapiuserdelete",
"ParentId": "100003002",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001001004",
"Title": "重置密码",
"Name": "resetpassword",
"Category": "BUTTON",
"Code": "sysuserresetpassword",
"ParentId": "100001001",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001001005",
"Title": "角色授权",
"Name": "perrole",
"Category": "BUTTON",
"Code": "sysuserperrole",
"ParentId": "100001001",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001002004",
"Title": "资源授权",
"Name": "perresuorce",
"Category": "BUTTON",
"Code": "sysroleperresuorce",
"ParentId": "100001002",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100001002005",
"Title": "用户授权",
"Name": "peruser",
"Category": "BUTTON",
"Code": "sysroleperuser",
"ParentId": "100001002",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100002004004",
"Title": "会话强退",
"Name": "exit",
"Category": "BUTTON",
"Code": "syssessionexit",
"ParentId": "100002004",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100002004005",
"Title": "令牌删除",
"Name": "verificatdelete",
"Category": "BUTTON",
"Code": "sysverificatdelete",
"ParentId": "100002004",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100003001004",
"Title": "会话强退",
"Name": "exit",
"Category": "BUTTON",
"Code": "openapisessionexit",
"ParentId": "100003001",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100003001005",
"Title": "令牌删除",
"Name": "verificatdelete",
"Category": "BUTTON",
"Code": "openapiverificatdelete",
"ParentId": "100003001",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100002002003",
"Title": "清空",
"Name": "clear",
"Category": "BUTTON",
"Code": "sysoplogclear",
"ParentId": "100002002",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100002003003",
"Title": "清空",
"Name": "clear",
"Category": "BUTTON",
"Code": "sysvislogclear",
"ParentId": "100002003",
"SortCode": "3",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100003002004",
"Title": "修改密码",
"Name": "editpassword",
"Category": "BUTTON",
"Code": "openapiusereditpassword",
"ParentId": "100003002",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "100003002005",
"Title": "授权Api",
"Name": "per",
"Category": "BUTTON",
"Code": "openapiuserper",
"ParentId": "100003002",
"SortCode": "1",
"TargetType": "None",
"IsDelete": "false"
},
{
"Id": "391545543004421",
"Title": "个人中心",
"Icon": "mdi-home-account",
"Component": "/usercenter",
"Category": "SPA",
"Code": "391545542885637",
"ParentId": "0",
"SortCode": "2",
"TargetType": "SELF",
"CreateTime": "2023-03-02 19:42:55.6049703",
"CreateUser": "superAdmin",
"CreateUserId": "212725263002001",
"IsDelete": "false",
"UpdateTime": "2023-03-02 19:46:13.3919053",
"UpdateUser": "superAdmin",
"UpdateUserId": "212725263002001"
}
]
}

View File

@@ -0,0 +1,145 @@
#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 SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
{
/// <inheritdoc/>
public IEnumerable<SysConfig> SeedData()
{
List<SysConfig> configList = new List<SysConfig>
{
new SysConfig
{
Id = 22222222222222,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_SWAGGER_NAME,
ConfigValue = "admin",
Remark = "swagger账号",
SortCode = 1,
},
new SysConfig
{
Id = 22222222222223,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_SWAGGER_PASSWORD,
ConfigValue = "123456",
Remark = "swagger密码",
SortCode = 2,
},
new SysConfig
{
Id = 22222222222224,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_SWAGGERLOGIN_OPEN,
ConfigValue = "false",
Remark = "swagger开启登录",
SortCode = 3,
},
new SysConfig
{
Id = 22222222222226,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_TITLE,
ConfigValue = "ThingsGateway",
Remark = "标题",
SortCode = 5,
},
new SysConfig
{
Id = 22222222222228,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_COPYRIGHT,
ConfigValue = "ThingsGateway ©2023 Diego",
Remark = "系统版权",
SortCode = 6,
},
new SysConfig
{
Id = 22222222222299,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_COPYRIGHT_URL,
ConfigValue = "https://gitee.com/diego2098/ThingsGateway",
Remark = "系统版权链接地址",
SortCode = 7,
},
new SysConfig
{
Id = 22222222222229,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_PAGETAB,
ConfigValue = "false",
Remark = "开启标签页",
SortCode = 7,
},
new SysConfig
{
Id = 22222222222231,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_PASSWORD,
ConfigValue = "111111",
Remark = "默认用户密码",
SortCode = 8,
},
new SysConfig
{
Id = 22222222222227,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_VERIFICAT_EXPIRES,
ConfigValue = "14400",
Remark = "Verificat过期时间(分)",
SortCode = 9,
},
new SysConfig
{
Id = 22222222222232,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_SINGLE_OPEN,
ConfigValue = "false",
Remark = "单用户登录开关",
SortCode = 10,
},
new SysConfig
{
Id = 22222222222230,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_CAPTCHA_OPEN,
ConfigValue = "true",
Remark = "登录验证码开关",
SortCode = 11,
},
new SysConfig
{
Id = 22222222222225,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_REMARK,
ConfigValue = "边缘采集网关",
Remark = "说明",
SortCode = 12,
}
};
return configList;
}
}

View File

@@ -0,0 +1,25 @@
#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 SysRelationSeedData : ISqlSugarEntitySeedData<SysRelation>
{
/// <inheritdoc/>
public IEnumerable<SysRelation> SeedData()
{
return SeedDataUtil.GetSeedData<SysRelation>("sys_relation.json");
}
}

View File

@@ -0,0 +1,25 @@
#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 SysResourceSeedData : ISqlSugarEntitySeedData<SysResource>
{
/// <inheritdoc/>
public IEnumerable<SysResource> SeedData()
{
return SeedDataUtil.GetSeedData<SysResource>("sys_resource.json");
}
}

View File

@@ -0,0 +1,43 @@
#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 SysRoleSeedData : ISqlSugarEntitySeedData<SysRole>
{
/// <inheritdoc/>
public IEnumerable<SysRole> SeedData()
{
List<SysRole> configList = new List<SysRole>
{
new SysRole
{
Id = 212725263001001,
Code = RoleConst.SuperAdmin,
Name = "超级管理员",
SortCode = 1,
},
new SysRole
{
Id = 212725263001002,
Code = "admin",
Name = "业务管理员",
SortCode = 2,
},
};
return configList;
}
}

View File

@@ -0,0 +1,45 @@
#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 SysUserSeedData : ISqlSugarEntitySeedData<SysUser>
{
/// <inheritdoc/>
public IEnumerable<SysUser> SeedData()
{
List<SysUser> configList = new List<SysUser>
{
new SysUser
{
Id = 212725263002001,
Account = RoleConst.SuperAdmin,
Password = "111111",
UserEnable = true,
SortCode = 1,
},
new SysUser
{
Id = 201725263002001,
Account = "admin",
Password = "111111",
UserEnable = true,
SortCode = 2,
},
};
return configList;
}
}

View File

@@ -0,0 +1,26 @@
#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 interface ISysHub
{
/// <summary>
/// 退出登录
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task Logout(object context);
}

View File

@@ -0,0 +1,112 @@
#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.InstantMessaging;
using Microsoft.AspNetCore.Http.Connections.Features;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="ISysHub"/>
/// </summary>
[MapHub(HubConst.SysHubUrl)]
public class SysHub : Hub<ISysHub>
{
/// <summary>
/// 分隔符
/// </summary>
public const string Separate = "_s_e_p_a_r_a_t_e_";
private readonly ILogger<ISysHub> _logger;
private readonly IServiceScope _serviceScope;
/// <inheritdoc cref="ISysHub"/>
public SysHub(IServiceScopeFactory scopeFactory, ILogger<ISysHub> logger)
{
_serviceScope = scopeFactory.CreateScope();
_logger = logger;
}
/// <summary>
/// 连接
/// </summary>
/// <returns></returns>
public override async Task OnConnectedAsync()
{
var feature = Context.Features.Get<IHttpContextFeature>();
var VerificatId = feature.HttpContext.Request.Headers[ClaimConst.VerificatId].FirstOrDefault().ToLong();
var userIdentifier = Context.UserIdentifier;//自定义的Id
await UpdateVerificatAsync(userIdentifier, verificat: VerificatId);//更新cache
await base.OnConnectedAsync();
}
/// <summary>
/// 断开连接
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
public override async Task OnDisconnectedAsync(Exception exception)
{
var userIdentifier = Context.UserIdentifier;//自定义的Id
await UpdateVerificatAsync(userIdentifier, false);//更新cache
await base.OnDisconnectedAsync(exception);
}
#region
/// <summary>
/// 更新cache
/// </summary>
/// <param name="userIdentifier">用户id</param>
/// <param name="verificat">上线时的验证id</param>
/// <param name="isConnect">是否是上线</param>
private async Task UpdateVerificatAsync(string userIdentifier, bool isConnect = true, long verificat = 0)
{
var userId = userIdentifier.Split(Separate)[0].ToLong();//分割取第一个
if (userId > 0)
{
var _verificatService = _serviceScope.ServiceProvider.GetService<IVerificatService>();
//获取cache当前用户的verificat信息列表
List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(userId);
if (verificatInfos != null)
{
if (isConnect)
{
//获取cache中当前verificat
var verificatInfo = verificatInfos.Where(it => it.Id == verificat).FirstOrDefault();
if (verificatInfo != null)
{
verificatInfo.ClientIds.Add(userIdentifier);//添加到客户端列表
await _verificatService.SetVerificatIdAsync(userId, verificatInfos);
}
}
else
{
//获取当前客户端ID所在的verificat信息
var verificatInfo = verificatInfos.Where(it => it.ClientIds.Contains(userIdentifier)).FirstOrDefault();
if (verificatInfo != null)
{
verificatInfo.ClientIds.RemoveWhere(it => it == userIdentifier);//从客户端列表删除
await _verificatService.SetVerificatIdAsync(userId, verificatInfos);
}
}
}
}
}
#endregion
}

View File

@@ -0,0 +1,40 @@
#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.Http.Connections.Features;
using Microsoft.AspNetCore.SignalR;
using ThingsGateway.Foundation.Extension.String;
using Yitter.IdGenerator;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 用户ID提供器
/// </summary>
public class UserIdProvider : IUserIdProvider
{
/// <inheritdoc/>
public string GetUserId(HubConnectionContext connection)
{
var feature = connection.Features.Get<IHttpContextFeature>();
var UserId = feature.HttpContext.Request.Headers[ClaimConst.UserId].FirstOrDefault()?.ToLong();
if (UserId > 0)
{
return $"{UserId}{SysHub.Separate}{YitIdHelper.NextId()}";//返回用户ID
}
return connection.ConnectionId;
}
}

View File

@@ -0,0 +1,39 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// AppStartup启动类
/// </summary>
[AppStartup(9997)]
public class Startup : AppStartup
{
/// <summary>
/// 配置
/// </summary>
public void ConfigureServices(IServiceCollection services)
{
// 任务调度
services.AddSchedule(options =>
{
options.AddPersistence<JobPersistence>();
});
//事件总线
services.AddEventBus();
}
}

View File

@@ -0,0 +1,53 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.EventBus;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 认证模块事件总线
/// </summary>
public class AuthEventSubscriber : IEventSubscriber, ISingleton
{
/// <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;
var db = DbContext.Db.CopyNew();
#region ,
db.Tracking(sysUser);//创建跟踪,只更新修改字段
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).ExecuteCommandAsync() > 0)
{
App.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSUSER + sysUser.Id, sysUser, false); //更新Cache信息
}
}
}

View File

@@ -0,0 +1,59 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.EventBus;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 用户模块事件总线
/// </summary>
public class UserEventSubscriber : IEventSubscriber, ISingleton
{
private readonly IServiceProvider _services;
/// <summary>
/// <inheritdoc cref="UserEventSubscriber"/>
/// </summary>
/// <param name="services"></param>
public UserEventSubscriber(IServiceProvider services)
{
_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).ToArray();//用户ID列表
// 解析用户服务
var userService = scope.ServiceProvider.GetRequiredService<ISysUserService>();
//从缓存中删除
userService.DeleteUserFromCache(userIds);
}
}
}

View File

@@ -0,0 +1,240 @@
#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.DataEncryption;
using Furion.EventBus;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Security.Claims;
using ThingsGateway.Foundation.Extension.String;
using Yitter.IdGenerator;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IAuthService"/>
public class AuthService : IAuthService
{
private readonly IConfigService _configService;
private readonly IEventPublisher _eventPublisher;
private readonly INoticeService _noticeService;
private readonly IServiceScope _serviceScope;
private readonly ISysUserService _userService;
private readonly IVerificatService _verificatService;
/// <inheritdoc cref="IAuthService"/>
public AuthService(
IEventPublisher eventPublisher,
ISysUserService userService,
IConfigService configService,
IVerificatService verificatService,
INoticeService noticeService, IServiceScopeFactory serviceScopeFactory
)
{
_eventPublisher = eventPublisher;
_userService = userService;
_configService = configService;
_verificatService = verificatService;
_noticeService = noticeService;
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
public ValidCodeOutput GetCaptchaInfo()
{
//生成验证码
var captchInfo = new Random().Next(1111, 9999).ToString();
//生成请求号并将验证码放入cache
var reqNo = YitIdHelper.NextId();
//插入cache
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.LOGIN_CAPTCHA + reqNo, captchInfo, TimeSpan.FromMinutes(1), false);
//返回验证码和请求号
return new ValidCodeOutput { CodeValue = captchInfo, ValidCodeReqNo = reqNo };
}
/// <inheritdoc/>
public async Task<SysUser> GetLoginUserAsync()
{
return await _userService.GetUserByIdAsync(UserManager.UserId);
}
/// <inheritdoc/>
public async Task<LoginOutput> LoginAsync(LoginInput input)
{
//判断是否有验证码
var sysBase = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_CAPTCHA_OPEN);
if (sysBase != null)//如果有这个配置项
{
if (sysBase.ConfigValue.ToBool(false))//如果需要验证码
{
//如果没填验证码,提示验证码不能为空
if (input.ValidCode.IsNullOrEmpty() || input.ValidCodeReqNo == 0) throw Oops.Bah("验证码不能为空").StatusCode(410);
ValidValidCode(input.ValidCode, input.ValidCodeReqNo);//校验验证码
}
}
var password = DESCEncryption.Decrypt(input.Password, DESCKeyConst.DESCKey); // 解密
var userInfo = await _userService.GetUserByAccountAsync(input.Account) ?? throw Oops.Bah("用户不存在");//获取用户信息
if (userInfo.Password != password) throw Oops.Bah("账号密码错误");//账号密码错误
return await LoginAsync(userInfo, input.Device);
}
/// <inheritdoc/>
public async Task LogoutAsync()
{
//获取用户信息
var userinfo = await _userService.GetUserByAccountAsync(UserManager.UserAccount);
if (userinfo != null)
{
LoginEvent loginEvent = new()
{
Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
SysUser = userinfo,
VerificatId = UserManager.VerificatId.ToLong(),
};
await RemoveVerificatAsync(loginEvent);//移除验证Id
}
}
/// <summary>
/// 校验验证码方法
/// </summary>
/// <param name="validCode">验证码</param>
/// <param name="validCodeReqNo">请求号</param>
/// <param name="isDelete">是否从Cache删除</param>
private void ValidValidCode(string validCode, long validCodeReqNo, bool isDelete = true)
{
var code = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<string>(CacheConst.LOGIN_CAPTCHA + validCodeReqNo, false);//从cache拿数据
if (isDelete) _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.LOGIN_CAPTCHA + validCodeReqNo);//删除验证码
if (code != null)//如果有
{
//验证码如果不匹配直接抛错误,这里忽略大小写
if (validCode.ToLower() != code.ToLower()) throw Oops.Bah("验证码错误");
}
else
{
throw Oops.Bah("验证码已过期");
}
}
/// <summary>
/// 执行B端登录
/// </summary>
/// <param name="sysUser">用户信息</param>
/// <param name="device">登录设备</param>
/// <returns></returns>
private async Task<LoginOutput> LoginAsync(SysUser sysUser, AuthDeviceTypeEnum device)
{
if (sysUser.UserEnable == false) throw Oops.Bah("账号已停用");//账号已停用
var sysBase = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_VERIFICAT_EXPIRES);
var sessionid = YitIdHelper.NextId();
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimConst.VerificatId, sessionid.ToString()));
identity.AddClaim(new Claim(ClaimConst.UserId, sysUser.Id.ToString()));
identity.AddClaim(new Claim(ClaimConst.Account, sysUser.Account));
identity.AddClaim(new Claim(ClaimConst.IsSuperAdmin, sysUser.RoleCodeList.Contains(RoleConst.SuperAdmin).ToString()));
identity.AddClaim(new Claim(ClaimConst.IsOpenApi, false.ToString()));
var config = sysBase.ConfigValue.ToInt(2880);
var diffTime = DateTimeExtensions.CurrentDateTime.AddMinutes(config);
await App.HttpContext.SignInAsync(new ClaimsPrincipal(identity), new AuthenticationProperties()
{
IsPersistent = true,
ExpiresUtc = diffTime,
});
//登录事件参数
var loginEvent = new LoginEvent
{
Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
Device = device,
Expire = config,
SysUser = sysUser,
VerificatId = sessionid,
};
await SetVerificatAsync(loginEvent);//写入verificat
await _eventPublisher.PublishAsync(EventSubscriberConst.Login, loginEvent); //发布登录事件总线
return new LoginOutput { VerificatId = sessionid, Account = sysUser.Account };
}
private async Task RemoveVerificatAsync(LoginEvent loginEvent)
{
//获取verificat列表
List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(loginEvent.SysUser.Id);
if (verificatInfos != null)
{
//获取当前用户的verificat
var verificat = verificatInfos.Where(it => it.Id == loginEvent.VerificatId).FirstOrDefault();
if (verificat != null)
verificatInfos.Remove(verificat);
//更新verificat列表
await _verificatService.SetVerificatIdAsync(loginEvent.SysUser.Id, verificatInfos);
}
await App.HttpContext?.SignOutAsync();
App.HttpContext?.SignoutToSwagger();
}
/// <summary>
/// 写入验证信息到缓存
/// </summary>
/// <param name="loginEvent"></param>
/// <returns></returns>
private async Task SetVerificatAsync(LoginEvent loginEvent)
{
//获取verificat列表
List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(loginEvent.SysUser.Id);
var verificatTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
//生成verificat信息
var verificatInfo = new VerificatInfo
{
Device = loginEvent.Device.ToString(),
Expire = loginEvent.Expire,
VerificatTimeout = verificatTimeout,
Id = loginEvent.VerificatId,
UserId = loginEvent.SysUser.Id,
};
if (verificatInfos != null)
{
bool isSingle = false;//默认不开启单用户登录
var singleConfig = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SINGLE_OPEN);//获取系统单用户登录选项
if (singleConfig != null) isSingle = singleConfig.ConfigValue.ToBool(false);//如果配置不为空则设置单用户登录选项为系统配置的值
if (isSingle)//判断是否单用户登录
{
await _noticeService.LogoutAsync(loginEvent.SysUser.Id, verificatInfos.Where(it => it.Device == loginEvent.Device.ToString()).ToList(), "该账号已在别处登录!");//通知其他用户下线
verificatInfos = verificatInfos.Where(it => it.Device != loginEvent.Device.ToString()).ToList();//去掉当前登录类型
verificatInfos.Add(verificatInfo);//添加到列表
}
else
{
verificatInfos.Add(verificatInfo);
}
}
else
{
verificatInfos = new List<VerificatInfo> { verificatInfo };//直接就一个
}
//添加到verificat列表
await _verificatService.SetVerificatIdAsync(loginEvent.SysUser.Id, verificatInfos);
}
}

View File

@@ -0,0 +1,78 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 登录输入参数
/// </summary>
public class LoginInput : ValidCodeInput
{
/// <summary>
/// 账号
///</summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public string Account { get; set; }
/// <summary>
/// 设备类型默认PC
/// </summary>
/// <example>0</example>
public AuthDeviceTypeEnum Device { get; set; } = AuthDeviceTypeEnum.PC;
/// <summary>
/// 密码
///</summary>
[Required(ErrorMessage = "密码不能为空"), MinLength(3, ErrorMessage = "密码不能少于3个字符")]
public string Password { get; set; }
}
/// <summary>
/// 验证码输入
/// </summary>
public class ValidCodeInput
{
/// <summary>
/// 验证码
/// </summary>
public string ValidCode { get; set; }
/// <summary>
/// 请求号
/// </summary>
public long ValidCodeReqNo { get; set; }
}
/// <summary>
/// 登录设备类型枚举
/// </summary>
public enum AuthDeviceTypeEnum
{
/// <summary>
/// PC端
/// </summary>
[Description("PC端")]
PC,
/// <summary>
/// 移动端
/// </summary>
[Description("移动端")]
APP,
/// <summary>
/// Api
/// </summary>
[Description("Api")]
Api,
}

View File

@@ -0,0 +1,51 @@
#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 BaseLoginOutput
{
/// <summary>
/// 账号
/// </summary>
public string Account { get; set; }
/// <summary>
/// 验证ID
/// </summary>
public long VerificatId { get; set; }
}
/// <summary>
/// 验证码值返回
/// </summary>
public class ValidCodeOutput
{
/// <summary>
/// 验证码值
/// </summary>
public string CodeValue { get; set; }
/// <summary>
/// 验证码请求号
/// </summary>
public long ValidCodeReqNo { get; set; }
}
/// <summary>
/// 登录返回参数
/// </summary>
public class LoginOutput : BaseLoginOutput
{
}

View File

@@ -0,0 +1,49 @@
#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 LoginEvent
{
/// <summary>
/// 时间
/// </summary>
public DateTime DateTime = DateTimeExtensions.CurrentDateTime;
/// <summary>
/// 登录设备
/// </summary>
public AuthDeviceTypeEnum Device { get; set; }
/// <summary>
/// 过期时间(分)
/// </summary>
public int Expire { get; set; }
/// <summary>
/// Ip地址
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 用户信息
/// </summary>
public SysUser SysUser { get; set; }
/// <summary>
/// 验证Id
/// </summary>
public long VerificatId { get; set; }
}

View File

@@ -0,0 +1,44 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 权限校验服务
/// </summary>
public interface IAuthService : ITransient
{
/// <summary>
/// 生成验证码
/// </summary>
/// <returns></returns>
ValidCodeOutput GetCaptchaInfo();
/// <summary>
/// 获取登录用户信息
/// </summary>
/// <returns></returns>
Task<SysUser> GetLoginUserAsync();
/// <summary>
/// 登录
/// </summary>
Task<LoginOutput> LoginAsync(LoginInput input);
/// <summary>
/// 退出登录
/// </summary>
/// <returns></returns>
Task LogoutAsync();
}

View File

@@ -0,0 +1,163 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Mapster;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="IButtonService"/>
/// </summary>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class ButtonService : DbRepository<SysResource>, IButtonService
{
private readonly IRelationService _relationService;
private readonly IResourceService _resourceService;
/// <inheritdoc cref="IButtonService"/>
public ButtonService(
IResourceService resourceService,
IRelationService relationService
)
{
_resourceService = resourceService;
_relationService = relationService;
}
/// <inheritdoc />
public async Task AddAsync(ButtonAddInput input)
{
await CheckInputAsync(input);//检查参数
var sysResource = input.Adapt<SysResource>();//实体转换
if (await InsertAsync(sysResource))//插入数据
_resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//刷新缓存
}
/// <inheritdoc />
[OperDesc("删除按钮")]
public async Task DeleteAsync(params long[] input)
{
//获取所有ID
var ids = input.ToList();
//获取所有按钮集合
var buttonList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.BUTTON);
#region
//获取所有菜单集合
var menuList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
//获取按钮的父菜单id集合
var parentIds = buttonList.Where(it => ids.Contains(it.Id)).Select(it => it.ParentId.ToString()).ToList();
//获取关系表分类为SYS_ROLE_HAS_RESOURCE数据
var roleResources = await _relationService.GetRelationByCategoryAsync(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);
//获取相关关系表数据
var relationList = roleResources
.Where(it => parentIds.Contains(it.TargetId))//目标ID是父ID中
.Where(it => it.ExtJson != null).ToList();//扩展信息不为空
//遍历关系表
relationList.ForEach(it =>
{
var relationRoleResuorce = it.ExtJson.FromJsonString<RelationRoleResuorce>();//拓展信息转实体
var buttonInfo = relationRoleResuorce.ButtonInfo;//获取按钮信息
if (buttonInfo.Count > 0)
{
var diffArr = buttonInfo.Where(it => !buttonInfo.Contains(it)).ToList(); //找出不同的元素(即交集的补集)
relationRoleResuorce.ButtonInfo = diffArr;//重新赋值按钮信息
it.ExtJson = relationRoleResuorce.ToJsonString();//重新赋值拓展信息
}
});
#endregion
//事务
var result = await itenant.UseTranAsync(async () =>
{
await DeleteByIdsAsync(ids.Cast<object>().ToArray());//删除按钮
if (relationList.Count > 0)
{
await Context.Updateable(relationList).UpdateColumns(it => it.ExtJson).ExecuteCommandAsync();//修改拓展信息
}
});
if (result.IsSuccess)//如果成功了
{
_resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//资源表按钮刷新缓存
}
else
{
throw Oops.Oh(result.ErrorMessage);
}
}
/// <inheritdoc />
[OperDesc("编辑按钮")]
public async Task EditAsync(ButtonEditInput input)
{
await CheckInputAsync(input);//检查参数
var sysResource = input.Adapt<SysResource>();//实体转换
//事务
var result = await itenant.UseTranAsync(async () =>
{
await UpdateAsync(sysResource); //更新按钮
});
if (result.IsSuccess)//如果成功了
{
_resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//资源表按钮刷新缓存
}
else
{
throw Oops.Oh(result.ErrorMessage);
}
}
/// <inheritdoc/>
public async Task<ISqlSugarPagedList<SysResource>> PageAsync(ButtonPageInput input)
{
var query = Context.Queryable<SysResource>()
.Where(it => it.ParentId == input.ParentId && it.Category == ResourceCategoryEnum.BUTTON)
.WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Title.Contains(input.SearchKey) || it.Component.Contains(input.SearchKey));//根据关键字查询
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
query = query.OrderBy(it => it.SortCode);//排序
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
return pageInfo;
}
#region
/// <summary>
/// 检查输入参数
/// </summary>
/// <param name="sysResource"></param>
private async Task CheckInputAsync(SysResource sysResource)
{
//获取所有按钮和菜单
var buttonList = await _resourceService.GetListByCategorysAsync(new List<ResourceCategoryEnum> { ResourceCategoryEnum.BUTTON, ResourceCategoryEnum.MENU });
//判断code是否重复
if (buttonList.Any(it => it.Code == sysResource.Code && it.Id != sysResource.Id))
throw Oops.Bah($"存在重复的按钮编码:{sysResource.Code}");
//判断菜单是否存在
if (!buttonList.Any(it => it.Id == sysResource.ParentId))
throw Oops.Bah($"不存在的父级菜单:{sysResource.ParentId}");
sysResource.Category = ResourceCategoryEnum.BUTTON;//设置分类为按钮
}
#endregion
}

View File

@@ -0,0 +1,61 @@
#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.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 添加按钮参数
/// </summary>
public class ButtonAddInput : SysResource
{
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "Code不能为空")]
public override string Code { get; set; }
/// <summary>
/// 父ID
/// </summary>
[Required(ErrorMessage = "ParentId不能为空")]
public override long ParentId { get; set; }
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "Title不能为空")]
public override string Title { get; set; }
}
/// <summary>
/// 按钮分页
/// </summary>
public class ButtonPageInput : BasePageInput
{
/// <summary>
/// 父ID
/// </summary>
[Required(ErrorMessage = "ParentId不能为空")]
public long? ParentId { get; set; }
}
/// <summary>
/// 按钮编辑
/// </summary>
public class ButtonEditInput : ButtonAddInput
{
/// <summary>
/// ID
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}

View File

@@ -0,0 +1,51 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 权限按钮服务
/// </summary>
public interface IButtonService : ITransient
{
/// <summary>
/// 添加按钮
/// </summary>
/// <param name="input">添加参数</param>
/// <returns></returns>
Task AddAsync(ButtonAddInput input);
/// <summary>
/// 删除按钮
/// </summary>
/// <param name="input">删除参数</param>
/// <returns></returns>
Task DeleteAsync(params long[] input);
/// <summary>
/// 编辑按钮
/// </summary>
/// <param name="input">编辑参数</param>
/// <returns></returns>
Task EditAsync(ButtonEditInput input);
/// <summary>
/// 按钮分页查询
/// </summary>
/// <param name="input">查询条件</param>
/// <returns>按钮分页列表</returns>
Task<ISqlSugarPagedList<SysResource>> PageAsync(ButtonPageInput input);
}

View File

@@ -0,0 +1,137 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Mapster;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IConfigService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class ConfigService : DbRepository<SysConfig>, IConfigService
{
private readonly IServiceScope _serviceScope;
public ConfigService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
[OperDesc("编辑网关系统配置")]
public async Task EditBatchAsync(List<SysConfig> sysConfigs)
{
if (await UpdateRangeAsync(sysConfigs))
RefreshCache(sysConfigs.FirstOrDefault()?.Category);//刷新缓存
}
/// <inheritdoc/>
[OperDesc("添加配置项")]
public async Task AddAsync(ConfigAddInput input)
{
await CheckInputAsync(input);//检查
var sysConfig = input.Adapt<SysConfig>();//实体转换
if (await InsertAsync(sysConfig))//插入数据)
RefreshCache(input.Category);//刷新缓存
}
/// <inheritdoc/>
[OperDesc("删除配置项")]
public async Task DeleteAsync(params long[] input)
{
await AsDeleteable().Where(it => input.Contains(it.Id)).ExecuteCommandAsync();
RefreshCache(ConfigConst.SYS_CONFIGOTHER);//刷新缓存
}
/// <inheritdoc/>
[OperDesc("编辑配置项")]
public async Task EditAsync(ConfigEditInput input)
{
await CheckInputAsync(input);
var sysConfig = input.Adapt<SysConfig>();//实体转换
if (await UpdateAsync(sysConfig))//更新数据
RefreshCache(input.Category);//刷新缓存
}
/// <inheritdoc/>
public async Task<SysConfig> GetByConfigKeyAsync(string category, string configKey)
{
var configList = await GetListByCategoryAsync(category);//获取系统配置列表
return configList.FirstOrDefault(it => it.ConfigKey == configKey);//根据configkey获取对应值
}
/// <inheritdoc/>
public async Task<List<SysConfig>> GetListByCategoryAsync(string category)
{
//先从Cache拿需要获取新的对象避免操作导致缓存中对象改变
var configList = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysConfig>>(CacheConst.SYS_CONFIGCATEGORY + category, true);
if (configList == null)
{
//cache没有再去数据可拿
configList = await Context.Queryable<SysConfig>().Where(it => it.Category == category).OrderBy(it => it.SortCode).ToListAsync();//获取系统配置列表
if (configList.Count > 0)
{
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.SYS_CONFIGCATEGORY + category, configList, true);//如果不为空,插入cache
}
}
return configList;
}
/// <inheritdoc/>
public async Task<ISqlSugarPagedList<SysConfig>> PageAsync(ConfigPageInput input)
{
var query = Context.Queryable<SysConfig>()
.Where(it => it.Category == ConfigConst.SYS_CONFIGOTHER)//自定义配置
.WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.ConfigKey.Contains(input.SearchKey) || it.ConfigKey.Contains(input.SearchKey));
//根据关键字查询
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
query = query.OrderBy(it => it.SortCode);//排序
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
return pageInfo;
}
#region
/// <summary>
/// 检查输入参数,并设置分类为自定义分类
/// </summary>
/// <param name="sysConfig"></param>
private async Task CheckInputAsync(SysConfig sysConfig)
{
var configs = await GetListByCategoryAsync(sysConfig.Category);//获取全部字典
var hasSameKey = configs.Any(it => it.ConfigKey == sysConfig.ConfigKey && it.Id != sysConfig.Id);
//判断是否从存在重复字典名
if (hasSameKey)
{
throw Oops.Bah($"存在重复的配置键:{sysConfig.ConfigKey}");
}
sysConfig.Category = ConfigConst.SYS_CONFIGOTHER;
}
/// <summary>
/// 刷新缓存
/// </summary>
/// <param name="category">分类</param>
/// <returns></returns>
private void RefreshCache(string category)
{
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.SYS_CONFIGCATEGORY + category);//cache删除
}
#endregion
}

View File

@@ -0,0 +1,65 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 添加配置参数
/// </summary>
public class ConfigAddInput : SysConfig
{
/// <summary>
/// 分类
/// </summary>
[Required(ErrorMessage = "Category不能为空")]
public override string Category { get; set; } = ConfigConst.SYS_CONFIGOTHER;
/// <summary>
/// 配置键
/// </summary>
[Required(ErrorMessage = "configKey不能为空")]
public override string ConfigKey { get; set; }
/// <summary>
/// 配置值
/// </summary>
[Required(ErrorMessage = "ConfigValue不能为空")]
public override string ConfigValue { get; set; }
}
/// <summary>
/// 编辑配置参数
/// </summary>
public class ConfigEditInput : ConfigAddInput
{
/// <summary>
/// ID
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 配置分页参数
/// </summary>
public class ConfigPageInput : BasePageInput
{
/// <summary>
/// 分类
/// </summary>
[Description("分类")]
public string Category { get; set; }
}

View File

@@ -0,0 +1,70 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 系统配置服务
/// </summary>
public interface IConfigService : ITransient
{
/// <summary>
/// 批量编辑系统配置
/// </summary>
/// <param name="configs">配置列表</param>
/// <returns></returns>
Task EditBatchAsync(List<SysConfig> configs);
/// <summary>
/// 新增自定义配置
/// </summary>
/// <param name="input">新增参数</param>
/// <returns></returns>
Task AddAsync(ConfigAddInput input);
/// <summary>
/// 删除自定义配置
/// </summary>
/// <param name="input">删除</param>
/// <returns></returns>
Task DeleteAsync(params long[] input);
/// <summary>
/// 修改自定义配置
/// </summary>
/// <param name="input">修改参数</param>
/// <returns></returns>
Task EditAsync(ConfigEditInput input);
/// <summary>
/// 根据分类和配置键获配置
/// </summary>
/// <param name="category">分类</param>
/// <param name="configKey">配置键</param>
/// <returns>配置信息</returns>
Task<SysConfig> GetByConfigKeyAsync(string category, string configKey);
/// <summary>
/// 根据分类获取配置列表
/// </summary>
/// <param name="category">分类名称</param>
/// <returns>配置列表</returns>
Task<List<SysConfig>> GetListByCategoryAsync(string category);
/// <summary>
/// 分页查询自定义配置
/// </summary>
/// <param name="input">查询参数</param>
/// <returns>其他配置列表</returns>
Task<ISqlSugarPagedList<SysConfig>> PageAsync(ConfigPageInput input);
}

View File

@@ -0,0 +1,38 @@
#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.FriendlyException;
using Microsoft.AspNetCore.Components.Forms;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="IFileService"/>
/// </summary>
public class FileService : IFileService
{
/// <inheritdoc/>
public void ImportVerification(IBrowserFile file, int maxSzie = 300, string[] allowTypes = null)
{
if (file == null) throw Oops.Bah("文件不能为空");
if (file.Size > maxSzie * 1024 * 1024) throw Oops.Bah($"文件大小不允许超过{maxSzie}M");
var fileSuffix = Path.GetExtension(file.Name).ToLower().Split(".")[1]; // 文件后缀
string[] allowTypeS = allowTypes ?? new string[] { "xlsx" };//允许上传的文件类型
if (!allowTypeS.Contains(fileSuffix)) throw Oops.Bah(errorMessage: "文件格式错误");
}
}

View File

@@ -0,0 +1,35 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Microsoft.AspNetCore.Components.Forms;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 文件管理服务
/// </summary>
public interface IFileService : ITransient
{
/// <summary>
/// 验证上传文件
/// </summary>
/// <param name="file">文件</param>
/// <param name="maxSzie">最大体积(M)</param>
/// <param name="allowTypes">允许上传类型</param>
void ImportVerification(IBrowserFile file, int maxSzie = 30, string[] allowTypes = null);
}

View File

@@ -0,0 +1,91 @@
#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.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 添加菜单参数
/// </summary>
public class MenuAddInput : SysResource, IValidatableObject
{
/// <summary>
/// 路径
/// </summary>
public override string Component { get; set; }
/// <summary>
/// 图标
/// </summary>
[Required(ErrorMessage = "Icon不能为空")]
public override string Icon { get; set; }
/// <summary>
/// 父ID
/// </summary>
[Required(ErrorMessage = "ParentId不能为空")]
public override long ParentId { get; set; }
/// <summary>
/// 菜单类型
/// </summary>
public override TargetTypeEnum TargetType { get; set; } = TargetTypeEnum.SELF;
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "Title不能为空")]
public override string Title { get; set; }
/// <summary>
/// 特殊验证
/// </summary>
/// <param name="validationContext"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
//如果菜单类型是菜单
if (TargetType == TargetTypeEnum.SELF)
{
if (string.IsNullOrEmpty(Component))
yield return new ValidationResult("路径不能为空", new[] { nameof(Component) });
}
//设置分类为菜单
Category = ResourceCategoryEnum.MENU;
}
}
/// <summary>
/// 编辑菜单输入参数
/// </summary>
public class MenuEditInput : MenuAddInput
{
/// <summary>
/// ID
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 菜单树查询参数
/// </summary>
public class MenuPageInput : BasePageInput
{
/// <summary>
/// 父ID
/// </summary>
[Required(ErrorMessage = "ParentId不能为空")]
public long ParentId { get; set; }
}

View File

@@ -0,0 +1,56 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 菜单服务
/// </summary>
public interface IMenuService : ITransient
{
/// <summary>
/// 添加菜单
/// </summary>
/// <param name="input">添加参数</param>
/// <returns></returns>
Task AddAsync(MenuAddInput input);
/// <summary>
/// 删除菜单
/// </summary>
/// <param name="input">删除菜单参数</param>
/// <returns></returns>
Task DeleteAsync(params long[] input);
/// <summary>
/// 详情
/// </summary>
/// <param name="input">id</param>
/// <returns>详细信息</returns>
Task<SysResource> DetailAsync(BaseIdInput input);
/// <summary>
/// 编辑菜单
/// </summary>
/// <param name="input">菜单编辑参数</param>
/// <returns></returns>
Task EditAsync(MenuEditInput input);
/// <summary>
/// 根据模块获取菜单树
/// </summary>
/// <param name="input">菜单树查询参数</param>
/// <returns>菜单树列表</returns>
Task<List<SysResource>> TreeAsync(MenuPageInput input);
}

View File

@@ -0,0 +1,166 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Mapster;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="IMenuService"/>
/// </summary>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class MenuService : DbRepository<SysResource>, IMenuService
{
private readonly IRelationService _relationService;
private readonly IResourceService _resourceService;
private readonly IRoleService _roleService;
/// <inheritdoc cref="IMenuService"/>
public MenuService(IResourceService resourceService, IRelationService relationService, IRoleService roleService)
{
_roleService = roleService;
_resourceService = resourceService;
_relationService = relationService;
}
/// <inheritdoc />
[OperDesc("添加菜单")]
public async Task AddAsync(MenuAddInput input)
{
await CheckInputAsync(input);//检查参数
var sysResource = input.Adapt<SysResource>();//实体转换
if (await InsertAsync(sysResource))//插入数据
_resourceService.RefreshCache(ResourceCategoryEnum.MENU);//刷新菜单缓存
}
/// <inheritdoc />
[OperDesc("删除菜单")]
public async Task DeleteAsync(params long[] input)
{
//获取所有ID
var ids = input.ToList();
if (ids.Count > 0)
{
//获取所有菜单和按钮
var resourceList = await _resourceService.GetListByCategorysAsync(new List<ResourceCategoryEnum> { ResourceCategoryEnum.MENU, ResourceCategoryEnum.BUTTON });
//找到要删除的菜单
var sysResources = resourceList.Where(it => ids.Contains(it.Id)).ToList();
//查找内置菜单
var system = sysResources.Where(it => it.Code == ResourceConst.System).FirstOrDefault();
if (system != null)
throw Oops.Bah($"不可删除系统菜单:{system.Title}");
//需要删除的资源ID列表
var resourceIds = new List<long>();
//遍历菜单列表
sysResources.ForEach(it =>
{
//获取菜单所有子节点
var child = _resourceService.GetChildListById(resourceList, it.Id, false);
//将子节点ID添加到删除资源ID列表
resourceIds.AddRange(child.Select(it => it.Id).ToList());
resourceIds.Add(it.Id);//添加到删除资源ID列表
});
ids.AddRange(resourceIds);//添加到删除ID列表
//事务
var result = await itenant.UseTranAsync(async () =>
{
await DeleteByIdsAsync(ids.Cast<object>().ToArray());//删除菜单和按钮
await Context.Deleteable<SysRelation>()//关系表删除对应SYS_ROLE_HAS_RESOURCE
.Where(it => it.Category == CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE && resourceIds.Contains(SqlFunc.ToInt64(it.TargetId))).ExecuteCommandAsync();
});
if (result.IsSuccess)//如果成功了
{
_resourceService.RefreshCache(ResourceCategoryEnum.MENU);//资源表菜单刷新缓存
_resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//资源表按钮刷新缓存
_relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);//关系表刷新缓存
}
else
{
//写日志
throw Oops.Oh(result.ErrorMessage);
}
}
}
/// <inheritdoc />
public async Task<SysResource> DetailAsync(BaseIdInput input)
{
var sysResources = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
var resource = sysResources.Where(it => it.Id == input.Id).FirstOrDefault();
return resource;
}
/// <inheritdoc />
[OperDesc("编辑菜单")]
public async Task EditAsync(MenuEditInput input)
{
await CheckInputAsync(input);//检查参数
var sysResource = input.Adapt<SysResource>();//实体转换
if (await UpdateAsync(sysResource))//更新数据
{
_resourceService.RefreshCache(ResourceCategoryEnum.MENU);//刷新菜单缓存
//需要更新资源权限,因为地址可能改变,页面权限需要更改
await _roleService.RefreshResourceAsync(input.Id);
}
}
/// <inheritdoc />
public async Task<List<SysResource>> TreeAsync(MenuPageInput input)
{
//获取所有菜单
var sysResources = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
sysResources = sysResources
.Where(it => it.ParentId == input.ParentId)
.WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Title == input.SearchKey)//根据关键字查找
.ToList();
//构建菜单树
var tree = _resourceService.ResourceListToTree(sysResources, input.ParentId);
return tree;
}
#region
/// <summary>
/// 检查输入参数
/// </summary>
/// <param name="sysResource"></param>
private async Task CheckInputAsync(SysResource sysResource)
{
//获取所有菜单列表
var menList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
//判断是否有同级且同名的菜单
if (menList.Any(it => it.ParentId == sysResource.ParentId && it.Title == sysResource.Title && it.Id != sysResource.Id))
throw Oops.Bah($"存在重复的菜单名称:{sysResource.Title}");
if (sysResource.ParentId != 0)
{
//获取父级,判断父级ID正不正确
var parent = menList.Where(it => it.Id == sysResource.ParentId).FirstOrDefault();
if (parent != null)
{
if (parent.Id == sysResource.Id)
throw Oops.Bah($"上级菜单不能选择自己");
}
else
{
throw Oops.Bah($"上级菜单不存在:{sysResource.ParentId}");
}
}
}
#endregion
}

View File

@@ -0,0 +1,30 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 通知服务
/// </summary>
public interface INoticeService : ITransient
{
/// <summary>
/// 通知用户下线
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="verificatInfos">验证列表</param>
/// <param name="message">消息内容</param>
/// <returns></returns>
Task LogoutAsync(long userId, List<VerificatInfo> verificatInfos, string message);
}

View File

@@ -0,0 +1,43 @@
#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.SignalR;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="INoticeService"/>
/// </summary>
public class NoticeService : INoticeService
{
private readonly IServiceScope _serviceScope;
public NoticeService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
public async Task LogoutAsync(long userId, List<VerificatInfo> verificatInfos, string message)
{
//客户端ID列表
var clientIds = new List<string>();
//遍历cancellationToken列表获取客户端ID列表
verificatInfos.ForEach(it =>
{
clientIds.AddRange(it.ClientIds);
});
//获取signalr实例
var signalr = _serviceScope.ServiceProvider.GetService<IHubContext<SysHub, ISysHub>>();
//发送其他客户端登录消息
await signalr.Clients.Users(clientIds).Logout(message);
}
}

View File

@@ -0,0 +1,27 @@
#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 OperateLogPageInput : VisitLogPageInput
{
}
/// <summary>
/// 操作日志分页输入
/// </summary>
public class OperateLogInput : VisitLogInput
{
}

View File

@@ -0,0 +1,47 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 操作日志服务
/// </summary>
public interface IOperateLogService : ITransient
{
/// <summary>
/// 根据分类删除操作日志
/// </summary>
/// <param name="category">分类名称</param>
/// <returns></returns>
Task DeleteAsync(params string[] category);
/// <summary>
/// 导出后台日志
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<MemoryStream> ExportFileAsync(List<SysOperateLog> input = null);
/// <summary>
/// 导出后台日志
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<MemoryStream> ExportFileAsync(OperateLogInput input);
/// <summary>
/// 操作日志分页查询
/// </summary>
/// <param name="input">查询参数</param>
/// <returns>分页列表</returns>
Task<ISqlSugarPagedList<SysOperateLog>> PageAsync(OperateLogPageInput input);
}

View File

@@ -0,0 +1,105 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Mapster;
using MiniExcelLibs;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="IOperateLogService"/>
/// </summary>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class OperateLogService : DbRepository<SysOperateLog>, IOperateLogService
{
/// <inheritdoc />
[OperDesc("删除操作日志")]
public async Task DeleteAsync(params string[] category)
{
await AsDeleteable().Where(it => category.Contains(it.Category)).ExecuteCommandAsync();
}
/// <inheritdoc/>
[OperDesc("导出操作日志", IsRecordPar = false)]
public async Task<MemoryStream> ExportFileAsync(List<SysOperateLog> input = null)
{
input ??= await GetListAsync();
//总数据
Dictionary<string, object> sheets = new();
List<Dictionary<string, object>> devExports = new();
foreach (var devData in input)
{
#region sheet
//变量页
var data = devData.GetType().GetProperties();
Dictionary<string, object> devExport = new();
foreach (var item in data)
{
//描述
var desc = item.FindDisplayAttribute();
//数据源增加
devExport.Add(desc ?? item.Name, item.GetValue(devData)?.ToString());
}
devExports.Add(devExport);
#endregion
}
sheets.Add("操作日志", devExports);
var memoryStream = new MemoryStream();
await memoryStream.SaveAsAsync(sheets);
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
/// <inheritdoc/>
[OperDesc("导出操作日志", IsRecordPar = false)]
public async Task<MemoryStream> ExportFileAsync(OperateLogInput input)
{
var query = GetPage(input.Adapt<OperateLogPageInput>());
var data = await query.ToListAsync();
return await ExportFileAsync(data);
}
/// <inheritdoc />
public async Task<ISqlSugarPagedList<SysOperateLog>> PageAsync(OperateLogPageInput input)
{
var query = GetPage(input);
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
return pageInfo;
}
private ISugarQueryable<SysOperateLog> GetPage(OperateLogPageInput input)
{
var query = Context.Queryable<SysOperateLog>()
.WhereIF(input.StartTime != null, a => a.OpTime >= input.StartTime.Value.ToLocalTime())
.WhereIF(input.EndTime != null, a => a.OpTime <= input.EndTime.Value.ToLocalTime())
.WhereIF(!string.IsNullOrEmpty(input.Account), it => it.OpAccount == input.Account)//根据账号查询
.WhereIF(!string.IsNullOrEmpty(input.Category), it => it.Category == input.Category)//根据分类查询
.WhereIF(!string.IsNullOrEmpty(input.ExeStatus), it => it.ExeStatus == input.ExeStatus)//根据结果查询
.WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Name.Contains(input.SearchKey) || it.OpIp.Contains(input.SearchKey));//根据关键字查询
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
query = query.OrderBy(it => it.Id, OrderByType.Desc);//排序
return query;
}
}

View File

@@ -0,0 +1,97 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 关系服务
/// </summary>
public interface IRelationService : ITransient
{
/// <summary>
/// 获取关系表用户工作台
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>关系表数据</returns>
Task<SysRelation> GetWorkbenchAsync(long userId);
/// <summary>
/// 根据分类获取关系表信息
/// </summary>
/// <param name="category">分类名称</param>
/// <returns>关系表</returns>
Task<List<SysRelation>> GetRelationByCategoryAsync(string category);
/// <summary>
/// 通过对象ID和分类获取关系列表
/// </summary>
/// <param name="objectId">对象ID</param>
/// <param name="category">分类</param>
/// <returns></returns>
Task<List<SysRelation>> GetRelationListByObjectIdAndCategoryAsync(long objectId, string category);
/// <summary>
/// 通过对象ID列表和分类获取关系列表
/// </summary>
/// <param name="objectIds">对象ID</param>
/// <param name="category">分类</param>
/// <returns></returns>
Task<List<SysRelation>> GetRelationListByObjectIdListAndCategoryAsync(List<long> objectIds, string category);
/// <summary>
/// 通过目标ID和分类获取关系列表
/// </summary>
/// <param name="targetId">目标ID</param>
/// <param name="category">分类</param>
/// <returns></returns>
Task<List<SysRelation>> GetRelationListByTargetIdAndCategoryAsync(string targetId, string category);
/// <summary>
/// 通过目标ID列表和分类获取关系列表
/// </summary>
/// <param name="targetIds"></param>
/// <param name="category"></param>
/// <returns></returns>
Task<List<SysRelation>> GetRelationListByTargetIdListAndCategoryAsync(List<string> targetIds, string category);
/// <summary>
/// 更新缓存
/// </summary>
/// <param name="category">分类</param>
/// <returns></returns>
void RefreshCache(string category);
/// <summary>
/// 保存关系
/// </summary>
/// <param name="category">分类</param>
/// <param name="objectId">对象ID</param>
/// <param name="targetId">目标ID</param>
/// <param name="extJson">拓展信息</param>
/// <param name="clear">是否清除老的数据</param>
/// <param name="refreshCache">是否刷新缓存</param>
/// <returns></returns>
Task SaveRelationAsync(string category, long objectId, string targetId, string extJson, bool clear, bool refreshCache = true);
/// <summary>
/// 批量保存关系
/// </summary>
/// <param name="category">分类</param>
/// <param name="objectId">对象ID</param>
/// <param name="targetIds">目标ID列表</param>
/// <param name="extJsons">拓展信息列表</param>
/// <param name="clear">是否清除老的数据</param>
/// <returns></returns>
Task SaveRelationBatchAsync(string category, long objectId, List<string> targetIds, List<string> extJsons, bool clear);
}

View File

@@ -0,0 +1,150 @@
#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.FriendlyException;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IRelationService"/>
public class RelationService : DbRepository<SysRelation>, IRelationService
{
private readonly IServiceScope _serviceScope;
public RelationService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationByCategoryAsync(string category)
{
//先从Cache拿需要获取新的对象避免操作导致缓存中对象改变
var sysRelations = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysRelation>>(CacheConst.CACHE_SYSRELATION + category, true);
if (sysRelations == null)
{
//cache没有就去数据库拿
sysRelations = await GetListAsync(it => it.Category == category);
if (sysRelations.Count > 0)
{
//插入Cache
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSRELATION + category, sysRelations, true);
}
}
return sysRelations;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByObjectIdAndCategoryAsync(long objectId, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => it.ObjectId == objectId).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByObjectIdListAndCategoryAsync(List<long> objectIds, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => objectIds.Contains(it.ObjectId)).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByTargetIdAndCategoryAsync(string targetId, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => it.TargetId == targetId).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByTargetIdListAndCategoryAsync(List<string> targetIds, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => targetIds.Contains(it.TargetId)).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<SysRelation> GetWorkbenchAsync(long userId)
{
var sysRelations = await GetRelationByCategoryAsync(CateGoryConst.Relation_SYS_USER_WORKBENCH_DATA);
var result = sysRelations.FirstOrDefault(it => it.ObjectId == userId);//获取个人工作台
return result;
}
/// <inheritdoc/>
public void RefreshCache(string category)
{
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRELATION + category);//删除cache
}
/// <inheritdoc/>
public async Task SaveRelationAsync(string category, long objectId, string targetId, string extJson, bool clear, bool refreshCache = true)
{
var sysRelation = new SysRelation
{
ObjectId = objectId,
TargetId = targetId,
Category = category,
ExtJson = extJson
};
//事务
var result = await itenant.UseTranAsync(async () =>
{
if (clear)
await DeleteAsync(it => it.ObjectId == objectId && it.Category == category);//删除老的
await InsertAsync(sysRelation);//添加新的
});
if (result.IsSuccess)//如果成功了
{
if (refreshCache)
RefreshCache(category);
}
else
{
//写日志
throw Oops.Oh(result.ErrorMessage);
}
}
/// <inheritdoc/>
public async Task SaveRelationBatchAsync(string category, long objectId, List<string> targetIds, List<string> extJsons, bool clear)
{
var sysRelations = new List<SysRelation>();//要添加的列表
for (int i = 0; i < targetIds.Count; i++)
{
sysRelations.Add(new SysRelation
{
ObjectId = objectId,
TargetId = targetIds[i],
Category = category,
ExtJson = extJsons?[i]
});
}
var result = await itenant.UseTranAsync(async () =>
{
if (clear)
await DeleteAsync(it => it.ObjectId == objectId && it.Category == category);//删除老的
await InsertRangeAsync(sysRelations);//添加新的
});
if (result.IsSuccess)//如果成功了
{
RefreshCache(category);
}
else
{
throw Oops.Oh(result.ErrorMessage);
}
}
}

View File

@@ -0,0 +1,72 @@
#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 RoleGrantResourceButton
{
/// <summary>
/// 按钮id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
}
/// <summary>
/// 授权菜单类
/// </summary>
public class RoleGrantResourceMenu
{
/// <summary>
/// 菜单下按钮集合
/// </summary>
public List<RoleGrantResourceButton> Button { get; set; } = new List<RoleGrantResourceButton>();
/// <summary>
/// 菜单id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 父id
/// </summary>
public long ParentId { get; set; }
/// <summary>
/// 父名称
/// </summary>
public string ParentName { get; set; }
/// <summary>
/// 菜单名称
/// </summary>
public string Title { get; set; }
}
/// <summary>
/// Blazor Server的组件路由内容
/// </summary>
public class PermissionTreeSelector
{
/// <summary>
/// 路由名称
/// </summary>
public string ApiRoute { get; set; }
}

View File

@@ -0,0 +1,98 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 资源服务
/// </summary>
public interface IResourceService : ITransient
{
/// <summary>
/// 获取所有的菜单和模块以及单页面列表,并按分类和排序码排序,不会形成树列表
/// </summary>
/// <returns>所有的菜单和模块以及单页面列表</returns>
Task<List<SysResource>> GetaMenuAndSpaListAsync();
/// <summary>
/// 获取子资源
/// </summary>
/// <param name="sysResources"></param>
/// <param name="resId"></param>
/// <param name="isContainOneself"></param>
/// <returns></returns>
List<SysResource> GetChildListById(List<SysResource> sysResources, long resId, bool isContainOneself = true);
/// <summary>
/// 获取ID获取Code列表
/// </summary>
/// <param name="ids">id列表</param>
/// <param name="category">分类</param>
/// <returns>Code列表</returns>
Task<List<string>> GetCodeByIdsAsync(List<long> ids, ResourceCategoryEnum category);
/// <summary>
/// 根据分类获取资源列表
/// </summary>
/// <param name="category">分类名称</param>
/// <returns>资源列表</returns>
Task<List<SysResource>> GetListByCategoryAsync(ResourceCategoryEnum category);
/// <summary>
/// 资源分类列表,如果是空的则获取全部资源
/// </summary>
/// <param name="categorys">资源分类列表</param>
/// <returns></returns>
Task<List<SysResource>> GetListByCategorysAsync(List<ResourceCategoryEnum> categorys = null);
/// <summary>
/// 获取资源所有下级
/// </summary>
/// <param name="resourceList">资源列表</param>
/// <param name="parentId">父ID</param>
/// <returns></returns>
List<SysResource> GetResourceChilden(List<SysResource> resourceList, long parentId);
/// <summary>
/// 获取上级
/// </summary>
/// <returns></returns>
List<SysResource> GetResourceParent(List<SysResource> resourceList, long parentId);
/// <summary>
/// 获取授权菜单
/// </summary>
/// <returns></returns>
Task<List<RoleGrantResourceMenu>> GetRoleGrantResourceMenusAsync();
/// <summary>
/// 刷新缓存
/// </summary>
/// <param name="category">分类名称</param>
/// <returns></returns>
void RefreshCache(ResourceCategoryEnum category);
/// <summary>
/// 构建菜单树形结构
/// </summary>
/// <param name="resourceList">菜单列表</param>
/// <param name="parentId">父ID</param>
/// <returns>菜单形结构</returns>
/// <inheritdoc/>
List<SysResource> ResourceListToTree(List<SysResource> resourceList, long parentId = 0);
/// <summary>
/// 多个树转列表
/// </summary>
/// <param name="data"></param>
List<SysResource> ResourceTreeToList(List<SysResource> data);
}

View File

@@ -0,0 +1,269 @@
#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 Mapster;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IResourceService"/>
public class ResourceService : DbRepository<SysResource>, IResourceService
{
private readonly IServiceScope _serviceScope;
public ResourceService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
public async Task<List<SysResource>> GetaMenuAndSpaListAsync()
{
//获取所有的菜单以及单页面
var sysResources = await GetListByCategorysAsync((List<ResourceCategoryEnum>)new() { ResourceCategoryEnum.MENU, ResourceCategoryEnum.SPA });
return sysResources?.OrderBy(it => it.Category).ThenBy(it => it.SortCode).ToList();
}
/// <inheritdoc />
public List<SysResource> GetChildListById(List<SysResource> sysResources, long resId, bool isContainOneself = true)
{
//查找下级
var childLsit = GetResourceChilden(sysResources, resId);
if (isContainOneself)//如果包含自己
{
//获取自己
var self = sysResources.Where(it => it.Id == resId).FirstOrDefault();
if (self != null) childLsit.Insert(0, self);//如果不为空就插到第一个
}
return childLsit;
}
/// <inheritdoc />
public async Task<List<string>> GetCodeByIdsAsync(List<long> ids, ResourceCategoryEnum category)
{
//根据分类获取所有
var sysResources = await GetListByCategoryAsync(category);
return sysResources.Where(it => ids.Contains(it.Id)).Select(it => it.Code).ToList();
}
/// <inheritdoc />
public async Task<List<SysResource>> GetListByCategoryAsync(ResourceCategoryEnum category)
{
//先从Cache拿需要获取新的对象避免操作导致缓存中对象改变
var sysResources = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysResource>>(CacheConst.CACHE_SYSRESOURCE + category.ToString(), true);
if (sysResources == null)
{
//cache没有就去数据库拿
sysResources = await GetListAsync(it => it.Category == category);
if (sysResources.Count > 0)
{
//插入Cache
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSRESOURCE + category.ToString(), sysResources, true);
}
}
return sysResources;
}
/// <inheritdoc/>
public async Task<List<SysResource>> GetListByCategorysAsync(List<ResourceCategoryEnum> categoryList = null)
{
//定义结果
var sysResources = new List<SysResource>();
//定义资源分类列表,如果是空的则获取全部资源
categoryList = categoryList != null ? categoryList
: new List<ResourceCategoryEnum> { ResourceCategoryEnum.MENU, ResourceCategoryEnum.BUTTON, ResourceCategoryEnum.SPA };
//遍历列表
foreach (var category in categoryList)
{
//根据分类获取到资源列表
var data = await GetListByCategoryAsync(category);
//添加到结果集
sysResources.AddRange(data);
}
return sysResources;
}
/// <inheritdoc/>
public List<SysResource> GetResourceChilden(List<SysResource> resourceList, long parentId)
{
//找下级资源ID列表
var resources = resourceList.Where(it => it.ParentId == parentId).ToList();
if (resources.Count > 0)//如果数量大于0
{
var data = new List<SysResource>();
foreach (var item in resources)//遍历资源
{
var res = GetResourceChilden(resourceList, item.Id);
data.AddRange(res);//添加子节点;
data.Add(item);//添加到列表
}
return data;//返回结果
}
return new List<SysResource>();
}
/// <inheritdoc/>
public List<SysResource> GetResourceParent(List<SysResource> resourceList, long parentId)
{
//找上级资源ID列表
var resources = resourceList.Where(it => it.Id == parentId).FirstOrDefault();
if (resources != null)//如果数量大于0
{
var data = new List<SysResource>();
var parents = GetResourceParent(resourceList, resources.ParentId);
data.AddRange(parents);//添加子节点;
data.Add(resources);//添加到列表
return data;//返回结果
}
return new List<SysResource>();
}
/// <inheritdoc/>
public async Task<List<RoleGrantResourceMenu>> GetRoleGrantResourceMenusAsync()
{
var roleGrantResourceMenus = new List<RoleGrantResourceMenu>();//定义结果
List<SysResource> allMenuList = (await GetListByCategoryAsync(ResourceCategoryEnum.MENU));//获取所有菜单列表
List<SysResource> allButtonList = await GetListByCategoryAsync(ResourceCategoryEnum.BUTTON);//获取所有按钮列表
var parentMenuList = allMenuList.Where(it => it.ParentId == 0).ToList();//获取一级目录
//遍历一级目录
foreach (var parent in parentMenuList)
{
//如果是目录则去遍历下级
if (parent.TargetType == TargetTypeEnum.None)
{
//获取所有下级菜单
var menuList = GetChildListById(allMenuList, parent.Id, false);
//遍历下级菜单
foreach (var menu in menuList)
{
//如果菜单类型是菜单
if (menu.TargetType == TargetTypeEnum.SELF)
{
//获取菜单下按钮集合并转换成对应实体
var buttonList = allButtonList.Where(it => it.ParentId == menu.Id).ToList();
var buttons = buttonList.Adapt<List<RoleGrantResourceButton>>();
roleGrantResourceMenus.Add(new()
{
Id = menu.Id,
ParentId = parent.Id,
ParentName = parent.Title,
Title = GetRoleGrantResourceMenuTitle(parentMenuList, menu),//菜单名称需要特殊处理因为有二级菜单
Button = buttons
});
}
else if (menu.TargetType == TargetTypeEnum.BLANK || menu.TargetType == TargetTypeEnum.CALLBACK)//如果是内链或者外链
{
//直接加到资源列表
roleGrantResourceMenus.Add(new()
{
Id = menu.Id,
ParentId = parent.Id,
ParentName = parent.Title,
Title = menu.Title,
});
}
}
}
else
{
//否则就将自己加到一级目录里面
roleGrantResourceMenus.Add(new()
{
Id = parent.Id,
ParentId = parent.Id,
ParentName = parent.Title,
Title = parent.Title,
});
}
}
return roleGrantResourceMenus;
}
/// <inheritdoc/>
public void RefreshCache(ResourceCategoryEnum category)
{
//如果分类是空的
if (category == ResourceCategoryEnum.None)
{
//删除全部key
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.SPA.ToString());
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.BUTTON.ToString());
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.MENU.ToString());
}
else
{
//否则只删除一个Key
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + category.ToString());
}
}
/// <inheritdoc />
public List<SysResource> ResourceListToTree(List<SysResource> resourceList, long parentId = 0)
{
//找下级资源ID列表
var resources = resourceList
.Where(it => it.ParentId == parentId).OrderBy(it => it.SortCode).ToList();
if (resources.Count > 0)//如果数量大于0
{
var data = new List<SysResource>();
foreach (var item in resources)//遍历资源
{
var children = ResourceListToTree(resourceList, item.Id);//添加子节点
item.Children = children.Count > 0 ? children : null;
data.Add(item);//添加到列表
}
return data;//返回结果
}
return new List<SysResource>();
}
/// <inheritdoc />
public List<SysResource> ResourceTreeToList(List<SysResource> data)
{
List<SysResource> list = new();
foreach (var item in data)
{
list.Add(item);
if (item.Children != null && item.Children.Count > 0)
{
list.AddRange(ResourceTreeToList(item.Children));
}
}
return list;
}
/// <summary>
/// 获取授权菜单类菜单名称
/// </summary>
/// <param name="menuList">菜单列表</param>
/// <param name="menu">当前菜单</param>
/// <returns></returns>
private string GetRoleGrantResourceMenuTitle(List<SysResource> menuList, SysResource menu)
{
//查找菜单上级
var parentList = GetResourceParent(menuList, menu.ParentId);
//如果有父级菜单
if (parentList.Count > 0)
{
var titles = parentList.Select(it => it.Title).ToList();//提取出父级的name
var title = $"{string.Join("- ", titles)}-{menu.Title}";//根据-分割,转换成字符串并在最后加上菜单的title
return title;
}
else
{
return menu.Title;//原路返回
}
}
}

View File

@@ -0,0 +1,82 @@
#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.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 角色授权资源参数
/// </summary>
public class GrantResourceInput : RoleOwnResourceOutput
{
/// <summary>
/// 授权资源信息
/// </summary>
[Required(ErrorMessage = "授权资源信息表不能为空")]
public override List<RelationRoleResuorce> GrantInfoList { get; set; }
/// <summary>
/// 角色Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 角色授权用户参数
/// </summary>
public class GrantUserInput
{
/// <summary>
/// 授权权限信息
/// </summary>
[Required(ErrorMessage = "GrantInfoList不能为空")]
public List<long> GrantInfoList { get; set; }
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "Id不能为空")]
public long? Id { get; set; }
}
/// <summary>
/// 角色添加参数
/// </summary>
public class RoleAddInput : SysRole
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "Name不能为空")]
public override string Name { get; set; }
}
/// <summary>
/// 角色编辑参数
/// </summary>
public class RoleEditInput : RoleAddInput
{
/// <summary>
/// Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 角色查询参数
/// </summary>
public class RolePageInput : BasePageInput
{
}

View File

@@ -0,0 +1,105 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 角色服务
/// </summary>
public interface IRoleService : ITransient
{
/// <summary>
/// 添加角色
/// </summary>
/// <param name="input">添加参数</param>
/// <returns></returns>
Task AddAsync(RoleAddInput input);
/// <summary>
/// 删除角色
/// </summary>
/// <param name="input">删除参数</param>
/// <returns></returns>
Task DeleteAsync(params long[] input);
/// <summary>
/// 编辑角色
/// </summary>
/// <param name="input">编辑角色</param>
/// <returns></returns>
Task EditAsync(RoleEditInput input);
/// <summary>
/// 根据用户ID获取用户角色Id集合
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns></returns>
Task<List<long>> GetRoleIdListByUserIdAsync(long userId);
/// <summary>
/// 根据用户ID获取用户角色集合
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns></returns>
Task<List<SysRole>> GetRoleListByUserIdAsync(long userId);
/// <summary>
/// 给角色授权资源
/// </summary>
/// <param name="input">授权参数</param>
/// <returns></returns>
Task GrantResourceAsync(GrantResourceInput input);
/// <summary>
/// 给角色授权用户
/// </summary>
/// <param name="input">授权信息</param>
/// <returns></returns>
Task GrantUserAsync(GrantUserInput input);
/// <summary>
/// 角色拥有资源
/// </summary>
/// <param name="input">角色id</param>
/// <returns>角色拥有资源信息</returns>
Task<RoleOwnResourceOutput> OwnResourceAsync(long input);
/// <summary>
/// 获取角色下的用户
/// </summary>
/// <param name="input">角色ID</param>
/// <returns></returns>
Task<List<long>> OwnUserAsync(long input);
/// <summary>
/// 分页查询角色
/// </summary>
/// <param name="input">查询参数</param>
/// <returns></returns>
Task<ISqlSugarPagedList<SysRole>> PageAsync(RolePageInput input);
/// <summary>
/// 刷新缓存
/// </summary>
/// <returns></returns>
void RefreshCache();
/// <summary>
/// 角色刷新资源
/// </summary>
Task RefreshResourceAsync(long? menuId = null);
/// <summary>
/// 角色选择器
/// </summary>
Task<List<SysRole>> RoleSelectorAsync(string searchKey = null);
}

View File

@@ -0,0 +1,410 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.EventBus;
using Furion.FriendlyException;
using Mapster;
using Microsoft.Extensions.DependencyInjection;
using ThingsGateway.Foundation.Extension.String;
using Yitter.IdGenerator;
namespace ThingsGateway.Admin.Application
{
/// <inheritdoc cref="IRoleService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class RoleService : DbRepository<SysRole>, IRoleService
{
private readonly IEventPublisher _eventPublisher;
private readonly IRelationService _relationService;
private readonly IResourceService _resourceService;
private readonly IServiceScope _serviceScope;
/// <inheritdoc cref="IRoleService"/>
public RoleService(
IRelationService relationService,
IResourceService resourceService,
IEventPublisher eventPublisher,
IServiceScopeFactory serviceScopeFactory)
{
_relationService = relationService;
_resourceService = resourceService;
_eventPublisher = eventPublisher;
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc />
[OperDesc("添加角色")]
public async Task AddAsync(RoleAddInput input)
{
await CheckInput(input);//检查参数
var sysRole = input.Adapt<SysRole>();//实体转换
sysRole.Code = YitIdHelper.NextId().ToString();//赋值Code
if (await InsertAsync(sysRole))//插入数据
RefreshCache();//刷新缓存
}
/// <inheritdoc />
[OperDesc("删除角色")]
public async Task DeleteAsync(params long[] input)
{
//获取所有ID
var ids = input.ToList();
if (ids.Count > 0)
{
var sysRoles = await GetListAsync();//获取所有角色
var hasSuperAdmin = sysRoles.Any(it => it.Code == RoleConst.SuperAdmin && ids.Contains(it.Id));//判断是否有超级管理员
if (hasSuperAdmin) throw Oops.Bah($"不可删除系统内置超管角色");
//数据库是string所以这里转下
var targetIds = ids.Select(it => it.ToString()).ToList();
//定义删除的关系
var delRelations = new List<string> { CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE, CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION };
//事务
var result = await itenant.UseTranAsync(async () =>
{
await DeleteByIdsAsync(ids.Cast<object>().ToArray());//删除按钮
//删除关系表角色与资源关系,角色与权限关系
await Context.Deleteable<SysRelation>().Where(it => ids.Contains(it.ObjectId) && delRelations.Contains(it.Category)).ExecuteCommandAsync();
//删除关系表角色与用户关系
await Context.Deleteable<SysRelation>().Where(it => targetIds.Contains(it.TargetId) && it.Category == CateGoryConst.Relation_SYS_USER_HAS_ROLE).ExecuteCommandAsync();
});
if (result.IsSuccess)//如果成功了
{
RefreshCache();//刷新缓存
_relationService.RefreshCache(CateGoryConst.Relation_SYS_USER_HAS_ROLE);//关系表刷新SYS_USER_HAS_ROLE缓存
_relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);//关系表刷新SYS_ROLE_HAS_RESOURCE缓存
_relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION);//关系表刷新SYS_ROLE_HAS_PERMISSION缓存
await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, ids);//清除角色下用户缓存
}
else
{
//写日志
throw Oops.Oh(result.ErrorMessage);
}
}
}
/// <inheritdoc />
[OperDesc("编辑角色")]
public async Task EditAsync(RoleEditInput input)
{
//判断是否超管
if (input.Code == RoleConst.SuperAdmin)
throw Oops.Bah($"不可编辑超管角色");
await CheckInput(input);//检查参数
var role = await GetFirstAsync(it => it.Id == input.Id);//获取角色
if (role != null)
{
var permissions = new List<SysRelation>();
var sysRole = input.Adapt<SysRole>();//实体转换
//事务
var result = await itenant.UseTranAsync(async () =>
{
await UpdateAsync(sysRole);//更新角色
if (permissions.Any())//如果有授权权限就更新
await Context.Updateable(permissions).ExecuteCommandAsync();
});
if (result.IsSuccess)//如果成功了
{
RefreshCache();//刷新缓存
if (permissions.Any())//如果有授权
_relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION);//关系表刷新SYS_ROLE_HAS_PERMISSION缓存
await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, new List<long> { input.Id });//清除角色下用户缓存
}
else
{
//写日志
throw Oops.Oh(result.ErrorMessage);
}
}
}
/// <summary>
/// 获取所有角色
/// </summary>
/// <returns></returns>
public override async Task<List<SysRole>> GetListAsync()
{
//先从Cache拿需要获取新的对象避免操作导致缓存中对象改变
var sysRoles = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysRole>>(CacheConst.CACHE_SYSROLE, true);
if (sysRoles == null)
{
//cache没有就去数据库拿
sysRoles = await base.GetListAsync();
if (sysRoles.Count > 0)
{
//插入Cache
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSROLE, sysRoles, true);
}
}
return sysRoles;
}
/// <inheritdoc/>
public async Task<List<long>> GetRoleIdListByUserIdAsync(long userId)
{
List<SysRole> cods = new();//角色代码集合
var roleList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, CateGoryConst.Relation_SYS_USER_HAS_ROLE);//根据用户ID获取角色ID
var roleIdList = roleList.Select(x => x.TargetId.ToLong()).ToList();//角色ID列表
return roleIdList;
}
/// <inheritdoc/>
public async Task<List<SysRole>> GetRoleListByUserIdAsync(long userId)
{
List<SysRole> cods = new();//角色代码集合
var roleList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, CateGoryConst.Relation_SYS_USER_HAS_ROLE);//根据用户ID获取角色ID
var roleIdList = roleList.Select(x => x.TargetId.ToLong()).ToList();//角色ID列表
if (roleIdList.Count > 0)
{
cods = await GetListAsync(it => roleIdList.Contains(it.Id));
}
return cods;
}
/// <inheritdoc />
[OperDesc("角色授权")]
public async Task GrantResourceAsync(GrantResourceInput input)
{
var menuIds = input.GrantInfoList.Select(it => it.MenuId).ToList();//菜单ID
var extJsons = input.GrantInfoList.Select(it => it.ToJsonString()).ToList();//拓展信息
var relationRoles = new List<SysRelation>();//要添加的角色资源和授权关系表
var sysRole = (await GetListAsync()).Where(it => it.Id == input.Id).FirstOrDefault();//获取角色
if (sysRole != null)
{
#region
//遍历角色列表
for (int i = 0; i < menuIds.Count; i++)
{
//将角色资源添加到列表
relationRoles.Add(new SysRelation
{
ObjectId = sysRole.Id,
TargetId = menuIds[i].ToString(),
Category = CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE,
ExtJson = extJsons?[i]
});
}
#endregion
#region
var relationRolePer = new List<SysRelation>();//要添加的角色有哪些权限列表
//获取菜单信息
var menus = await GetMenuByMenuIds(menuIds);
if (menus.Count > 0)
{
//获取权限授权树
var permissions = PermissionUtil.PermissionTreeSelector(menus.Select(it => it.Component).ToList());
permissions.ForEach(it =>
{
//新建角色权限关系
relationRolePer.Add(new SysRelation
{
ObjectId = sysRole.Id,
TargetId = it.ApiRoute,
Category = CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION,
ExtJson = new RelationRolePermission
{
ApiUrl = it.ApiRoute,
}.ToJsonString()
});
});
}
relationRoles.AddRange(relationRolePer);//合并列表
#endregion
#region
//事务
var result = await itenant.UseTranAsync(async () =>
{
//删除老的
await Context.Deleteable<SysRelation>().Where(it => it.ObjectId == sysRole.Id
&&
(it.Category == CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION
|| it.Category == CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE)
)
.ExecuteCommandAsync();
await Context.Insertable(relationRoles).ExecuteCommandAsync();
});
if (result.IsSuccess)//如果成功了
{
_relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);//刷新关系缓存
_relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION);//刷新关系缓存
await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, new List<long> { input.Id });//发送事件清除角色下用户缓存
}
else
{
//写日志
throw Oops.Oh(result.ErrorMessage);
}
#endregion
}
}
/// <inheritdoc />
[OperDesc("用户授权")]
public async Task GrantUserAsync(GrantUserInput input)
{
var sysRelations = new List<SysRelation>();//关系列表
//遍历用户ID
input.GrantInfoList.ForEach(it =>
{
sysRelations.Add(new SysRelation
{
ObjectId = it,
TargetId = input.Id.ToString(),
Category = CateGoryConst.Relation_SYS_USER_HAS_ROLE
});
});
//事务
var result = await itenant.UseTranAsync(async () =>
{
//删除老的
await Context.Deleteable<SysRelation>().Where(it => it.TargetId == input.Id.ToString() && it.Category == CateGoryConst.Relation_SYS_USER_HAS_ROLE).ExecuteCommandAsync();
await Context.Insertable(sysRelations).ExecuteCommandAsync();//添加新的
});
if (result.IsSuccess)//如果成功了
{
_relationService.RefreshCache(CateGoryConst.Relation_SYS_USER_HAS_ROLE);//刷新关系表SYS_USER_HAS_ROLE缓存
await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, new List<long> { input.Id.Value });//清除角色下用户缓存
}
else
{
//写日志
throw Oops.Oh(result.ErrorMessage);
}
}
/// <inheritdoc />
public async Task<RoleOwnResourceOutput> OwnResourceAsync(long input)
{
RoleOwnResourceOutput roleOwnResource = new() { Id = input };//定义结果集
List<RelationRoleResuorce> GrantInfoList = new();//已授权信息集合
//获取关系列表
var relations = await _relationService.GetRelationListByObjectIdAndCategoryAsync(input, CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);
//遍历关系表
relations.ForEach(it =>
{
//将扩展信息转为实体
var relationRole = it.ExtJson.FromJsonString<RelationRoleResuorce>();
GrantInfoList.Add(relationRole);//添加到已授权信息
});
roleOwnResource.GrantInfoList = GrantInfoList;//赋值已授权信息
return roleOwnResource;
}
/// <inheritdoc />
public async Task<List<long>> OwnUserAsync(long input)
{
//获取关系列表
var relations = await _relationService.GetRelationListByTargetIdAndCategoryAsync(input.ToString(), CateGoryConst.Relation_SYS_USER_HAS_ROLE);
return relations.Select(it => it.ObjectId).ToList();
}
/// <inheritdoc/>
public async Task<ISqlSugarPagedList<SysRole>> PageAsync(RolePageInput input)
{
var query = Context.Queryable<SysRole>()
.WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Name.Contains(input.SearchKey));//根据关键字查询
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
query = query.OrderBy(it => it.SortCode);//排序
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
return pageInfo;
}
/// <inheritdoc />
public void RefreshCache()
{
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSROLE);//删除KEY
}
/// <inheritdoc />
public async Task RefreshResourceAsync(long? menuId = null)
{
var data = await GetListAsync();
foreach (var item in data)
{
var r1 = await OwnResourceAsync(item.Id);
if (menuId == null || r1.GrantInfoList.Any(a => a.MenuId == menuId))
{
await GrantResourceAsync(new GrantResourceInput() { Id = item.Id, GrantInfoList = r1.GrantInfoList });
}
}
}
/// <inheritdoc />
public async Task<List<SysRole>> RoleSelectorAsync(string searchKey = null)
{
var result = await Context.Queryable<SysRole>()
.WhereIF(!string.IsNullOrEmpty(searchKey), it => it.Name.Contains(searchKey))//根据关键字查询
.ToListAsync();
return result;
}
#region
/// <summary>
/// 检查输入参数
/// </summary>
/// <param name="sysRole"></param>
private async Task CheckInput(SysRole sysRole)
{
var sysRoles = await GetListAsync();//获取所有
var repeatName = sysRoles.Any(it => it.Name == sysRole.Name && it.Id != sysRole.Id);//是否有重复角色名称
if (repeatName)//如果有
{
throw Oops.Bah($"存在重复的角色:{sysRole.Name}");
}
}
/// <summary>
/// 根据菜单ID获取菜单
/// </summary>
/// <param name="menuIds"></param>
/// <returns></returns>
private async Task<List<SysResource>> GetMenuByMenuIds(List<long> menuIds)
{
//获取所有菜单
var menuList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
//获取菜单信息
var menus = menuList.Where(it => menuIds.Contains(it.Id)).ToList();
return menus;
}
#endregion
}
}

View File

@@ -0,0 +1,56 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 会话分页查询
/// </summary>
public class SessionPageInput : BasePageInput
{
/// <summary>
/// 账号
/// </summary>
[Description("账号")]
public string Account { get; set; }
/// <summary>
/// 最新登录IP
/// </summary>
[Description("最新登录IP")]
public string LatestLoginIp { get; set; }
/// <summary>
/// 姓名
/// </summary>
[Description("姓名")]
public string Name { get; set; }
}
/// <summary>
/// 退出参数
/// </summary>
public class ExitVerificatInput : BaseIdInput
{
/// <summary>
/// 验证ID列表
/// </summary>
[Required(ErrorMessage = "VerificatIds不能为空")]
public List<long> VerificatIds { get; set; }
/// <summary>
/// 用户Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}

View File

@@ -0,0 +1,62 @@
#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 SessionOutput : PrimaryKeyEntity
{
/// <summary>
/// 账号
///</summary>
[Description("账号")]
[DataTable(Order = 1, IsShow = true, Sortable = true)]
public virtual string Account { get; set; }
/// <summary>
/// 最新登录ip
///</summary>
[Description("最新登录ip")]
[DataTable(Order = 3, IsShow = true, Sortable = true)]
public string LatestLoginIp { get; set; }
/// <summary>
/// 最新登录时间
///</summary>
[Description("最新登录时间")]
[DataTable(Order = 4, IsShow = true, Sortable = true)]
public DateTime? LatestLoginTime { get; set; }
/// <summary>
/// 在线状态
/// </summary>
[Description("在线状态")]
[DataTable(Order = 2, IsShow = true, Sortable = true)]
public bool OnlineStatus { get; set; }
/// <summary>
/// 令牌数量
/// </summary>
[Description("令牌数量")]
[DataTable(Order = 5, IsShow = true, Sortable = false)]
public int VerificatCount { get; set; }
/// <summary>
/// 令牌信息集合
/// </summary>
[Description("令牌列表")]
public List<VerificatInfo> VerificatSignList { get; set; }
}

View File

@@ -0,0 +1,40 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 会话管理服务
/// </summary>
public interface ISessionService : ITransient
{
/// <summary>
/// 强退会话
/// </summary>
/// <param name="input">用户ID</param>
Task ExitSessionAsync(long input);
/// <summary>
/// 强退verificat
/// </summary>
/// <param name="input">verificat列表</param>
Task ExitVerificatAsync(ExitVerificatInput input);
/// <summary>
/// 会话分页查询
/// </summary>
/// <param name="input">查询参数</param>
/// <returns>会话列表</returns>
Task<ISqlSugarPagedList<SessionOutput>> PageAsync(SessionPageInput input);
}

View File

@@ -0,0 +1,116 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="ISessionService"/>
/// </summary>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class SessionService : DbRepository<SysUser>, ISessionService
{
private readonly INoticeService _noticeService;
private readonly IVerificatService _verificatService;
/// <inheritdoc cref="ISessionService"/>
public SessionService(IVerificatService verificatService, INoticeService noticeService)
{
_verificatService = verificatService;
_noticeService = noticeService;
}
/// <inheritdoc/>
[OperDesc("强退会话")]
public async Task ExitSessionAsync(long input)
{
//verificat列表
List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(input);
//从列表中删除
await _verificatService.SetVerificatIdAsync(input, new());
var message = "您已被强制下线!";
await _noticeService.LogoutAsync(input, verificatInfos, message);//通知下线
}
/// <inheritdoc/>
[OperDesc("强退令牌")]
public async Task ExitVerificatAsync(ExitVerificatInput input)
{
//获取该用户的verificat信息
List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(input.Id);
//踢掉包含verificat列表的verificat信息
var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList();
var deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList();
await _verificatService.SetVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat
var message = "您已被强制下线!";
await _noticeService.LogoutAsync(input.Id, deleteVerificats, message);//通知下线
}
/// <inheritdoc/>
public async Task<ISqlSugarPagedList<SessionOutput>> PageAsync(SessionPageInput input)
{
var query = Context.Queryable<SysUser>()
.WhereIF(!string.IsNullOrEmpty(input.Account), it => it.Account.Contains(input.Account))//根据账号查询
.WhereIF(!string.IsNullOrEmpty(input.LatestLoginIp), it => it.LatestLoginIp.Contains(input.LatestLoginIp))//根据IP查询
.OrderBy(it => it.LatestLoginTime, OrderByType.Desc)
.Select<SessionOutput>()
.Mapper(async it =>
{
var verificatInfos = await _verificatService.GetVerificatIdAsync(it.Id);
if (verificatInfos != null)
{
SessionService.GetVerificatInfos(ref verificatInfos);//获取剩余时间
it.VerificatCount = verificatInfos.Count;//令牌数量
it.VerificatSignList = verificatInfos;//令牌列表
//如果有客户端ID就是在线
it.OnlineStatus = verificatInfos.Any(it => it.ClientIds.Count > 0);
}
else
{
it.VerificatSignList = new();
}
});
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
return pageInfo;
}
#region
/// <summary>
/// 获取verificat剩余时间信息
/// </summary>
private static void GetVerificatInfos(ref List<VerificatInfo> verificatInfos)
{
verificatInfos.ForEach(it =>
{
var now = DateTimeExtensions.CurrentDateTime;
it.VerificatRemain = now.GetDiffTime(it.VerificatTimeout);//获取时间差
var verificatSecond = it.VerificatTimeout.AddMinutes(-it.Expire).ToLong();//颁发时间转为时间戳
var timeoutSecond = it.VerificatTimeout.ToLong();//过期时间转为时间戳
});
}
#endregion
}

View File

@@ -0,0 +1,67 @@
#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.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 单页输入参数
/// </summary>
public class SpaAddInput : SysResource
{
/// <summary>
/// 路径
/// </summary>
[Required(ErrorMessage = "Component不能为空")]
public override string Component { get; set; }
/// <summary>
/// 图标
/// </summary>
[Required(ErrorMessage = "Icon不能为空")]
public override string Icon { get; set; }
/// <summary>
/// 菜单类型
/// </summary>
public override TargetTypeEnum TargetType { get; set; } = TargetTypeEnum.SELF;
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "Title不能为空")]
public override string Title { get; set; }
}
/// <summary>
/// 单页输入参数
/// </summary>
public class SpaPageInput : BasePageInput
{
/// <summary>
/// 跳转类型
/// </summary>
public TargetTypeEnum TargetType { get; set; }
}
/// <summary>
/// 单页修改参数
/// </summary>
public class SpaEditInput : SpaAddInput
{
/// <summary>
/// ID
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}

View File

@@ -0,0 +1,49 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 单页服务
/// </summary>
public interface ISpaService : ITransient
{
/// <summary>
/// 添加单页
/// </summary>
/// <param name="input">添加参数</param>
/// <returns></returns>
Task AddAsync(SpaAddInput input);
/// <summary>
/// 删除单页
/// </summary>
/// <param name="input">删除参数</param>
/// <returns></returns>
Task DeleteAsync(params long[] input);
/// <summary>
/// 编辑单页
/// </summary>
/// <param name="input">编辑参数</param>
/// <returns></returns>
Task EditAsync(SpaEditInput input);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<ISqlSugarPagedList<SysResource>> PageAsync(SpaPageInput input);
}

View File

@@ -0,0 +1,125 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Mapster;
using Yitter.IdGenerator;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="ISpaService"/>
/// </summary>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class SpaService : DbRepository<SysResource>, ISpaService
{
private readonly IResourceService _resourceService;
/// <inheritdoc cref="ISpaService"/>
public SpaService(IResourceService resourceService)
{
this._resourceService = resourceService;
}
/// <inheritdoc />
[OperDesc("添加单页")]
public async Task AddAsync(SpaAddInput input)
{
CheckInput(input);//检查参数
input.Code = YitIdHelper.NextId().ToString();//code取随机值
var sysResource = input.Adapt<SysResource>();//实体转换
if (await InsertAsync(sysResource))//插入数据
_resourceService.RefreshCache(ResourceCategoryEnum.SPA);//刷新缓存
}
/// <inheritdoc />
[OperDesc("删除单页")]
public async Task DeleteAsync(params long[] input)
{
//获取所有ID
var ids = input.ToList();
if (ids.Count > 0)
{
//获取所有
var resourceList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.SPA);
//找到要删除的
var sysresources = resourceList.Where(it => ids.Contains(it.Id)).ToList();
//查找内置单页面
var system = sysresources.Where(it => it.Code == ResourceConst.System).FirstOrDefault();
if (system != null)
throw Oops.Bah($"不可删除系统内置单页面:{system.Title}");
//删除菜单
await DeleteAsync(sysresources);
_resourceService.RefreshCache(ResourceCategoryEnum.SPA);//刷新缓存
}
}
/// <inheritdoc />
[OperDesc("编辑单页")]
public async Task EditAsync(SpaEditInput input)
{
CheckInput(input);//检查参数
var sysResource = input.Adapt<SysResource>();//实体转换
if (await UpdateAsync(sysResource))//更新数据
_resourceService.RefreshCache(ResourceCategoryEnum.SPA);//刷新缓存
}
/// <inheritdoc/>
public async Task<ISqlSugarPagedList<SysResource>> PageAsync(SpaPageInput input)
{
var query = Context.Queryable<SysResource>()
.Where(it => it.Category == ResourceCategoryEnum.SPA)//单页
.WhereIF(input.TargetType != 0, it => it.TargetType == input.TargetType)//根据菜单类型查询
.WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Title.Contains(input.SearchKey) || it.Component.Contains(input.SearchKey));//根据关键字查询
for (int i = input.SortField.Count - 1; i >= 0; i--)
{
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
}
query = query.OrderBy(it => it.SortCode);//排序
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
return pageInfo;
}
#region
/// <summary>
/// 检查输入参数
/// </summary>
/// <param name="sysResource"></param>
private void CheckInput(SysResource sysResource)
{
//判断菜单类型
if (sysResource.TargetType == TargetTypeEnum.SELF)
{
if (string.IsNullOrEmpty(sysResource.Component))
{
throw Oops.Bah($"组件地址不能为空");
}
}
else if (sysResource.TargetType == TargetTypeEnum.BLANK)//如果是内链或者外链
{
sysResource.Component = null;
}
else
{
throw Oops.Bah($"单页类型错误:{sysResource.TargetType}");//都不是
}
//设置为单页
sysResource.Category = ResourceCategoryEnum.SPA;
}
#endregion
}

View File

@@ -0,0 +1,76 @@
#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 SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 添加用户参数
/// </summary>
public class UserAddInput : SysUser
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
}
/// <summary>
/// 编辑用户参数
/// </summary>
public class UserEditInput : UserAddInput
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
/// <summary>
/// Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 用户分页查询参数
/// </summary>
public class UserPageInput : BasePageInput
{
/// <summary>
/// 动态查询条件
/// </summary>
public Expressionable<SysUser> Expression { get; set; }
}
/// <summary>
/// 用户授权角色参数
/// </summary>
public class UserGrantRoleInput
{
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "Id不能为空")]
public long Id { get; set; }
/// <summary>
/// 授权权限信息
/// </summary>
[Required(ErrorMessage = "RoleIdList不能为空")]
public List<long> RoleIdList { get; set; }
}

View File

@@ -0,0 +1,35 @@
#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 UserSelectorOutput
{
/// <summary>
/// 账号
/// </summary>
public string Account { get; set; }
/// <summary>
/// Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
}

Some files were not shown because too many files have changed in this diff Show More